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, IVerifierURI, \
7 IMutableFileURI, INewDirectoryURI, IReadonlyNewDirectoryURI
9 # the URI shall be an ascii representation of the file. It shall contain
10 # enough information to retrieve and validate the contents. It shall be
11 # expressed in a limited character set (namely [TODO]).
13 ZBASE32CHAR = "[ybndrfg8ejkmcpqxot1uwisza345h769]" # excludes l, 0, 2, and v
14 ZBASE32CHAR_3bits = "[yoearcwh]"
15 ZBASE32CHAR_1bits = "[yo]"
16 ZBASE32STR_128bits = "%s{25}%s" % (ZBASE32CHAR, ZBASE32CHAR_3bits)
17 ZBASE32STR_256bits = "%s{51}%s" % (ZBASE32CHAR, ZBASE32CHAR_1bits)
21 WSSKBITS= "%s%s%s" % (ZBASE32STR_128bits, COLON, ZBASE32STR_256bits)
23 # URIs (soon to be renamed "caps") are always allowed to come with a leading
24 # "http://127.0.0.1:8123/uri/" that will be ignored.
25 OPTIONALHTTPLEAD=r'(https?://(127.0.0.1|localhost):8123/uri/)?'
28 WriteableSSKFileURI_RE=re.compile("^%sURI%sSSK%s%s$" % (OPTIONALHTTPLEAD, COLON, COLON, WSSKBITS))
30 # NewDirectory Read-Write URI
31 DirnodeURI_RE=re.compile("^%sURI%sDIR2%s%s/?$" % (OPTIONALHTTPLEAD, COLON, COLON, WSSKBITS))
36 return hash((self.__class__, self.to_string()))
37 def __cmp__(self, them):
38 if cmp(type(self), type(them)):
39 return cmp(type(self), type(them))
40 if cmp(self.__class__, them.__class__):
41 return cmp(self.__class__, them.__class__)
42 return cmp(self.to_string(), them.to_string())
44 class CHKFileURI(_BaseURI):
45 implements(IURI, IFileURI)
47 def __init__(self, **kwargs):
48 # construct me with kwargs, since there are so many of them
51 keys = ("key", "uri_extension_hash",
52 "needed_shares", "total_shares", "size")
56 setattr(self, name, value)
58 raise TypeError("CHKFileURI does not accept '%s=' argument"
60 self.storage_index = hashutil.storage_index_chk_hash(self.key)
62 def init_from_string(self, uri):
63 assert uri.startswith("URI:CHK:"), uri
65 (header_uri, header_chk,
66 key_s, uri_extension_hash_s,
67 needed_shares_s, total_shares_s, size_s) = uri.split(":")
68 assert header_uri == "URI"
69 assert header_chk == "CHK"
71 self.key = idlib.a2b(key_s)
72 assert isinstance(self.key, str)
73 assert len(self.key) == 16 # AES-128
75 self.storage_index = hashutil.storage_index_chk_hash(self.key)
76 assert isinstance(self.storage_index, str)
77 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
79 self.uri_extension_hash = idlib.a2b(uri_extension_hash_s)
80 assert isinstance(self.uri_extension_hash, str)
81 assert len(self.uri_extension_hash) == 32 # sha56 hash
83 self.needed_shares = int(needed_shares_s)
84 self.total_shares = int(total_shares_s)
85 self.size = int(size_s)
89 assert isinstance(self.needed_shares, int)
90 assert isinstance(self.total_shares, int)
91 assert isinstance(self.size, (int,long))
93 return ("URI:CHK:%s:%s:%d:%d:%d" %
95 idlib.b2a(self.uri_extension_hash),
100 def is_readonly(self):
102 def is_mutable(self):
104 def get_readonly(self):
110 def get_verifier(self):
111 return CHKFileVerifierURI(storage_index=self.storage_index,
112 uri_extension_hash=self.uri_extension_hash,
113 needed_shares=self.needed_shares,
114 total_shares=self.total_shares,
117 class CHKFileVerifierURI(_BaseURI):
118 implements(IVerifierURI)
120 def __init__(self, **kwargs):
121 # construct me with kwargs, since there are so many of them
124 self.populate(**kwargs)
126 def populate(self, storage_index, uri_extension_hash,
127 needed_shares, total_shares, size):
128 self.storage_index = storage_index
129 self.uri_extension_hash = uri_extension_hash
130 self.needed_shares = needed_shares
131 self.total_shares = total_shares
134 def init_from_string(self, uri):
135 assert uri.startswith("URI:CHK-Verifier:"), uri
137 (header_uri, header_chk,
138 storage_index_s, uri_extension_hash_s,
139 needed_shares_s, total_shares_s, size_s) = uri.split(":")
140 assert header_uri == "URI"
141 assert header_chk == "CHK-Verifier"
143 self.storage_index = idlib.a2b(storage_index_s)
144 assert isinstance(self.storage_index, str)
145 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
147 self.uri_extension_hash = idlib.a2b(uri_extension_hash_s)
148 assert isinstance(self.uri_extension_hash, str)
149 assert len(self.uri_extension_hash) == 32 # sha56 hash
151 self.needed_shares = int(needed_shares_s)
152 self.total_shares = int(total_shares_s)
153 self.size = int(size_s)
157 assert isinstance(self.needed_shares, int)
158 assert isinstance(self.total_shares, int)
159 assert isinstance(self.size, (int,long))
161 return ("URI:CHK-Verifier:%s:%s:%d:%d:%d" %
162 (idlib.b2a(self.storage_index),
163 idlib.b2a(self.uri_extension_hash),
169 class LiteralFileURI(_BaseURI):
170 implements(IURI, IFileURI)
172 def __init__(self, data=None):
176 def init_from_string(self, uri):
177 assert uri.startswith("URI:LIT:")
178 data_s = uri[len("URI:LIT:"):]
179 self.data = idlib.a2b(data_s)
183 return "URI:LIT:%s" % idlib.b2a(self.data)
185 def is_readonly(self):
187 def is_mutable(self):
189 def get_readonly(self):
192 def get_verifier(self):
193 # LIT files need no verification, all the data is present in the URI
197 return len(self.data)
199 class WriteableSSKFileURI(_BaseURI):
200 implements(IURI, IMutableFileURI)
202 def __init__(self, *args, **kwargs):
203 if not args and not kwargs:
205 self.populate(*args, **kwargs)
207 def populate(self, writekey, fingerprint):
208 self.writekey = writekey
209 self.readkey = hashutil.ssk_readkey_hash(writekey)
210 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
211 self.fingerprint = fingerprint
213 def init_from_string(self, uri):
214 assert uri.startswith("URI:SSK:"), uri
215 (header_uri, header_ssk, writekey_s, fingerprint_s) = uri.split(":")
216 self.populate(idlib.a2b(writekey_s), idlib.a2b(fingerprint_s))
220 assert isinstance(self.writekey, str)
221 assert isinstance(self.fingerprint, str)
222 return "URI:SSK:%s:%s" % (idlib.b2a(self.writekey),
223 idlib.b2a(self.fingerprint))
226 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
229 return idlib.b2a(self.writekey[:5])
231 def is_readonly(self):
233 def is_mutable(self):
235 def get_readonly(self):
236 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
237 def get_verifier(self):
238 return SSKVerifierURI(self.storage_index, self.fingerprint)
240 class ReadonlySSKFileURI(_BaseURI):
241 implements(IURI, IMutableFileURI)
243 def __init__(self, *args, **kwargs):
244 if not args and not kwargs:
246 self.populate(*args, **kwargs)
248 def populate(self, readkey, fingerprint):
249 self.readkey = readkey
250 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
251 self.fingerprint = fingerprint
253 def init_from_string(self, uri):
254 assert uri.startswith("URI:SSK-RO:"), uri
255 (header_uri, header_ssk, readkey_s, fingerprint_s) = uri.split(":")
256 self.populate(idlib.a2b(readkey_s), idlib.a2b(fingerprint_s))
260 assert isinstance(self.readkey, str)
261 assert isinstance(self.fingerprint, str)
262 return "URI:SSK-RO:%s:%s" % (idlib.b2a(self.readkey),
263 idlib.b2a(self.fingerprint))
266 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
269 return idlib.b2a(self.readkey[:5])
271 def is_readonly(self):
273 def is_mutable(self):
275 def get_readonly(self):
277 def get_verifier(self):
278 return SSKVerifierURI(self.storage_index, self.fingerprint)
280 class SSKVerifierURI(_BaseURI):
281 implements(IVerifierURI)
283 def __init__(self, *args, **kwargs):
284 if not args and not kwargs:
286 self.populate(*args, **kwargs)
288 def populate(self, storage_index, fingerprint):
289 self.storage_index = storage_index
290 self.fingerprint = fingerprint
292 def init_from_string(self, uri):
293 assert uri.startswith("URI:SSK-Verifier:"), uri
294 (header_uri, header_ssk,
295 storage_index_s, fingerprint_s) = uri.split(":")
296 self.populate(idlib.a2b(storage_index_s), idlib.a2b(fingerprint_s))
300 assert isinstance(self.storage_index, str)
301 assert isinstance(self.fingerprint, str)
302 return "URI:SSK-Verifier:%s:%s" % (idlib.b2a(self.storage_index),
303 idlib.b2a(self.fingerprint))
305 class _NewDirectoryBaseURI(_BaseURI):
306 implements(IURI, IDirnodeURI)
307 def __init__(self, filenode_uri=None):
308 self._filenode_uri = filenode_uri
311 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
314 return self._filenode_uri.to_string().split(':')[2][:5]
316 def get_filenode_uri(self):
317 return self._filenode_uri
319 def is_mutable(self):
322 def get_verifier(self):
323 return NewDirectoryURIVerifier(self._filenode_uri.get_verifier())
325 class NewDirectoryURI(_NewDirectoryBaseURI):
326 implements(INewDirectoryURI)
327 def __init__(self, filenode_uri=None):
329 assert not filenode_uri.is_readonly()
330 _NewDirectoryBaseURI.__init__(self, filenode_uri)
332 def init_from_string(self, uri):
333 assert uri.startswith("URI:DIR2:")
334 (header_uri, header_dir2, bits) = uri.split(":", 2)
335 fn = WriteableSSKFileURI()
336 fn.init_from_string("URI:SSK:" + bits)
337 self._filenode_uri = fn
341 assert isinstance(self._filenode_uri, WriteableSSKFileURI)
342 fn_u = self._filenode_uri.to_string()
343 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
344 return "URI:DIR2:" + bits
346 def is_readonly(self):
349 def get_readonly(self):
350 return ReadonlyNewDirectoryURI(self._filenode_uri.get_readonly())
352 class ReadonlyNewDirectoryURI(_NewDirectoryBaseURI):
353 implements(IReadonlyNewDirectoryURI)
354 def __init__(self, filenode_uri=None):
356 assert filenode_uri.is_readonly()
357 _NewDirectoryBaseURI.__init__(self, filenode_uri)
359 def init_from_string(self, uri):
360 assert uri.startswith("URI:DIR2-RO:")
361 (header_uri, header_dir2, bits) = uri.split(":", 2)
362 fn = ReadonlySSKFileURI()
363 fn.init_from_string("URI:SSK-RO:" + bits)
364 self._filenode_uri = fn
368 assert isinstance(self._filenode_uri, ReadonlySSKFileURI)
369 fn_u = self._filenode_uri.to_string()
370 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
371 return "URI:DIR2-RO:" + bits
373 def is_readonly(self):
376 def get_readonly(self):
379 class NewDirectoryURIVerifier(_BaseURI):
380 implements(IVerifierURI)
382 def __init__(self, filenode_uri=None):
384 filenode_uri = IVerifierURI(filenode_uri)
385 self._filenode_uri = filenode_uri
387 def init_from_string(self, uri):
388 assert uri.startswith("URI:DIR2-Verifier:")
389 (header_uri, header_dir2, bits) = uri.split(":", 2)
390 fn = SSKVerifierURI()
391 fn.init_from_string("URI:SSK-Verifier:" + bits)
392 self._filenode_uri = fn
396 assert isinstance(self._filenode_uri, SSKVerifierURI)
397 fn_u = self._filenode_uri.to_string()
398 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
399 return "URI:DIR2-Verifier:" + bits
401 def get_filenode_uri(self):
402 return self._filenode_uri
409 if s.startswith("URI:CHK:"):
410 return CHKFileURI().init_from_string(s)
411 elif s.startswith("URI:CHK-Verifier:"):
412 return CHKFileVerifierURI().init_from_string(s)
413 elif s.startswith("URI:LIT:"):
414 return LiteralFileURI().init_from_string(s)
415 elif s.startswith("URI:SSK:"):
416 return WriteableSSKFileURI().init_from_string(s)
417 elif s.startswith("URI:SSK-RO:"):
418 return ReadonlySSKFileURI().init_from_string(s)
419 elif s.startswith("URI:SSK-Verifier:"):
420 return SSKVerifierURI().init_from_string(s)
421 elif s.startswith("URI:DIR2:"):
422 return NewDirectoryURI().init_from_string(s)
423 elif s.startswith("URI:DIR2-RO:"):
424 return ReadonlyNewDirectoryURI().init_from_string(s)
425 elif s.startswith("URI:DIR2-Verifier:"):
426 return NewDirectoryURIVerifier().init_from_string(s)
428 raise TypeError("unknown URI type: %s.." % s[:12])
430 registerAdapter(from_string, str, IURI)
432 def from_string_dirnode(s):
434 assert IDirnodeURI.providedBy(u)
437 registerAdapter(from_string_dirnode, str, IDirnodeURI)
439 def is_string_newdirnode_rw(s):
440 return DirnodeURI_RE.search(s)
442 def from_string_filenode(s):
444 assert IFileURI.providedBy(u)
447 registerAdapter(from_string_filenode, str, IFileURI)
449 def from_string_mutable_filenode(s):
451 assert IMutableFileURI.providedBy(u)
453 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
455 def from_string_verifier(s):
457 assert IVerifierURI.providedBy(u)
459 registerAdapter(from_string_verifier, str, IVerifierURI)
462 def pack_extension(data):
464 for k in sorted(data.keys()):
466 if isinstance(value, (int, long)):
468 assert isinstance(value, str), k
469 assert re.match(r'^[a-zA-Z_\-]+$', k)
470 pieces.append(k + ":" + hashutil.netstring(value))
471 uri_extension = "".join(pieces)
474 def unpack_extension(data):
477 colon = data.index(":")
479 data = data[colon+1:]
481 colon = data.index(":")
482 number = data[:colon]
484 data = data[colon+1:]
486 value = data[:length]
487 assert data[length] == ","
488 data = data[length+1:]
492 # convert certain things to numbers
493 for intkey in ("size", "segment_size", "num_segments",
494 "needed_shares", "total_shares"):
496 d[intkey] = int(d[intkey])
500 def unpack_extension_readable(data):
501 unpacked = unpack_extension(data)
502 for k in sorted(unpacked.keys()):
504 unpacked[k] = idlib.b2a(unpacked[k])