3 from zope.interface import implements
4 from twisted.python.components import registerAdapter
5 from allmydata.util import idlib, hashutil
6 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI
8 # the URI shall be an ascii representation of the file. It shall contain
9 # enough information to retrieve and validate the contents. It shall be
10 # expressed in a limited character set (namely [TODO]).
15 return hash((self.__class__, self.to_string()))
16 def __cmp__(self, them):
17 if cmp(type(self), type(them)):
18 return cmp(type(self), type(them))
19 if cmp(self.__class__, them.__class__):
20 return cmp(self.__class__, them.__class__)
21 return cmp(self.to_string(), them.to_string())
23 class CHKFileURI(_BaseURI):
24 implements(IURI, IFileURI)
26 def __init__(self, **kwargs):
27 # construct me with kwargs, since there are so many of them
30 keys = ("key", "uri_extension_hash",
31 "needed_shares", "total_shares", "size")
35 setattr(self, name, value)
37 raise TypeError("CHKFileURI does not accept '%s=' argument"
39 self.storage_index = hashutil.storage_index_chk_hash(self.key)
41 def init_from_string(self, uri):
42 assert uri.startswith("URI:CHK:"), uri
44 (header_uri, header_chk,
45 key_s, uri_extension_hash_s,
46 needed_shares_s, total_shares_s, size_s) = uri.split(":")
47 assert header_uri == "URI"
48 assert header_chk == "CHK"
50 self.key = idlib.a2b(key_s)
51 assert isinstance(self.key, str)
52 assert len(self.key) == 16 # AES-128
54 self.storage_index = hashutil.storage_index_chk_hash(self.key)
55 assert isinstance(self.storage_index, str)
56 assert len(self.storage_index) == 32 # sha256 hash
58 self.uri_extension_hash = idlib.a2b(uri_extension_hash_s)
59 assert isinstance(self.uri_extension_hash, str)
60 assert len(self.uri_extension_hash) == 32 # sha56 hash
62 self.needed_shares = int(needed_shares_s)
63 self.total_shares = int(total_shares_s)
64 self.size = int(size_s)
68 assert isinstance(self.needed_shares, int)
69 assert isinstance(self.total_shares, int)
70 assert isinstance(self.size, (int,long))
72 return ("URI:CHK:%s:%s:%d:%d:%d" %
74 idlib.b2a(self.uri_extension_hash),
79 def is_readonly(self):
83 def get_readonly(self):
89 class LiteralFileURI(_BaseURI):
90 implements(IURI, IFileURI)
92 def __init__(self, data=None):
96 def init_from_string(self, uri):
97 assert uri.startswith("URI:LIT:")
98 data_s = uri[len("URI:LIT:"):]
99 self.data = idlib.a2b(data_s)
103 return "URI:LIT:%s" % idlib.b2a(self.data)
105 def is_readonly(self):
107 def is_mutable(self):
109 def get_readonly(self):
113 return len(self.data)
115 class DirnodeURI(_BaseURI):
116 implements(IURI, IDirnodeURI)
118 def __init__(self, furl=None, writekey=None):
119 if furl is not None or writekey is not None:
120 assert furl is not None
121 assert writekey is not None
123 self.writekey = writekey
124 self._derive_values()
126 def init_from_string(self, uri):
128 # but note that the furl contains colons
130 assert uri.startswith(prefix)
131 uri = uri[len(prefix):]
132 colon = uri.rindex(":")
133 self.furl = uri[:colon]
134 self.writekey = idlib.a2b(uri[colon+1:])
135 self._derive_values()
138 def _derive_values(self):
139 wk, we, rk, index = \
140 hashutil.generate_dirnode_keys_from_writekey(self.writekey)
141 self.write_enabler = we
143 self.storage_index = index
146 return "URI:DIR:%s:%s" % (self.furl, idlib.b2a(self.writekey))
148 def is_readonly(self):
150 def is_mutable(self):
152 def get_readonly(self):
153 return ReadOnlyDirnodeURI(self.furl, self.readkey)
155 class ReadOnlyDirnodeURI(_BaseURI):
156 implements(IURI, IDirnodeURI)
158 def __init__(self, furl=None, readkey=None):
159 if furl is not None or readkey is not None:
160 assert furl is not None
161 assert readkey is not None
163 self.readkey = readkey
164 self._derive_values()
166 def init_from_string(self, uri):
167 # URI:DIR-RO:furl:key
168 # but note that the furl contains colons
169 prefix = "URI:DIR-RO:"
170 assert uri.startswith(prefix)
171 uri = uri[len(prefix):]
172 colon = uri.rindex(":")
173 self.furl = uri[:colon]
174 self.readkey = idlib.a2b(uri[colon+1:])
175 self._derive_values()
178 def _derive_values(self):
179 wk, we, rk, index = \
180 hashutil.generate_dirnode_keys_from_readkey(self.readkey)
181 self.writekey = wk # None
182 self.write_enabler = we # None
183 self.storage_index = index
186 return "URI:DIR-RO:%s:%s" % (self.furl, idlib.b2a(self.readkey))
188 def is_readonly(self):
190 def is_mutable(self):
192 def get_readonly(self):
196 if s.startswith("URI:CHK:"):
197 return CHKFileURI().init_from_string(s)
198 elif s.startswith("URI:LIT:"):
199 return LiteralFileURI().init_from_string(s)
200 elif s.startswith("URI:DIR:"):
201 return DirnodeURI().init_from_string(s)
202 elif s.startswith("URI:DIR-RO:"):
203 return ReadOnlyDirnodeURI().init_from_string(s)
205 raise RuntimeError("unknown URI type: %s.." % s[:10])
207 registerAdapter(from_string, str, IURI)
209 def from_string_dirnode(s):
211 assert IDirnodeURI.providedBy(u)
214 registerAdapter(from_string_dirnode, str, IDirnodeURI)
216 def from_string_filenode(s):
218 assert IFileURI.providedBy(u)
221 registerAdapter(from_string_filenode, str, IFileURI)
224 def pack_extension(data):
226 for k in sorted(data.keys()):
228 if isinstance(value, (int, long)):
230 assert isinstance(value, str), k
231 assert re.match(r'^[a-zA-Z_\-]+$', k)
232 pieces.append(k + ":" + hashutil.netstring(value))
233 uri_extension = "".join(pieces)
236 def unpack_extension(data):
239 colon = data.index(":")
241 data = data[colon+1:]
243 colon = data.index(":")
244 number = data[:colon]
246 data = data[colon+1:]
248 value = data[:length]
249 assert data[length] == ","
250 data = data[length+1:]
254 # convert certain things to numbers
255 for intkey in ("size", "segment_size", "num_segments",
256 "needed_shares", "total_shares"):
258 d[intkey] = int(d[intkey])
262 def unpack_extension_readable(data):
263 unpacked = unpack_extension(data)
264 for k in sorted(unpacked.keys()):
266 unpacked[k] = idlib.b2a(unpacked[k])