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]).
16 return hash((self.__class__, self.to_string()))
17 def __cmp__(self, them):
18 if cmp(type(self), type(them)):
19 return cmp(type(self), type(them))
20 if cmp(self.__class__, them.__class__):
21 return cmp(self.__class__, them.__class__)
22 return cmp(self.to_string(), them.to_string())
24 class CHKFileURI(_BaseURI):
25 implements(IURI, IFileURI)
27 def __init__(self, **kwargs):
28 # construct me with kwargs, since there are so many of them
31 keys = ("key", "uri_extension_hash",
32 "needed_shares", "total_shares", "size")
36 setattr(self, name, value)
38 raise TypeError("CHKFileURI does not accept '%s=' argument"
40 self.storage_index = hashutil.storage_index_chk_hash(self.key)
42 def init_from_string(self, uri):
43 assert uri.startswith("URI:CHK:"), uri
45 (header_uri, header_chk,
46 key_s, uri_extension_hash_s,
47 needed_shares_s, total_shares_s, size_s) = uri.split(":")
48 assert header_uri == "URI"
49 assert header_chk == "CHK"
51 self.key = idlib.a2b(key_s)
52 assert isinstance(self.key, str)
53 assert len(self.key) == 16 # AES-128
55 self.storage_index = hashutil.storage_index_chk_hash(self.key)
56 assert isinstance(self.storage_index, str)
57 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
59 self.uri_extension_hash = idlib.a2b(uri_extension_hash_s)
60 assert isinstance(self.uri_extension_hash, str)
61 assert len(self.uri_extension_hash) == 32 # sha56 hash
63 self.needed_shares = int(needed_shares_s)
64 self.total_shares = int(total_shares_s)
65 self.size = int(size_s)
69 assert isinstance(self.needed_shares, int)
70 assert isinstance(self.total_shares, int)
71 assert isinstance(self.size, (int,long))
73 return ("URI:CHK:%s:%s:%d:%d:%d" %
75 idlib.b2a(self.uri_extension_hash),
80 def is_readonly(self):
84 def get_readonly(self):
90 def get_verifier(self):
91 return CHKFileVerifierURI(storage_index=self.storage_index,
92 uri_extension_hash=self.uri_extension_hash,
93 needed_shares=self.needed_shares,
94 total_shares=self.total_shares,
97 class CHKFileVerifierURI(_BaseURI):
98 implements(IVerifierURI)
100 def __init__(self, **kwargs):
101 # construct me with kwargs, since there are so many of them
104 keys = ("storage_index", "uri_extension_hash",
105 "needed_shares", "total_shares", "size")
109 setattr(self, name, value)
111 raise TypeError("CHKFileVerifierURI does not accept "
115 def init_from_string(self, uri):
116 assert uri.startswith("URI:CHK-Verifier:"), uri
118 (header_uri, header_chk,
119 storage_index_s, uri_extension_hash_s,
120 needed_shares_s, total_shares_s, size_s) = uri.split(":")
121 assert header_uri == "URI"
122 assert header_chk == "CHK-Verifier"
124 self.storage_index = idlib.a2b(storage_index_s)
125 assert isinstance(self.storage_index, str)
126 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
128 self.uri_extension_hash = idlib.a2b(uri_extension_hash_s)
129 assert isinstance(self.uri_extension_hash, str)
130 assert len(self.uri_extension_hash) == 32 # sha56 hash
132 self.needed_shares = int(needed_shares_s)
133 self.total_shares = int(total_shares_s)
134 self.size = int(size_s)
138 assert isinstance(self.needed_shares, int)
139 assert isinstance(self.total_shares, int)
140 assert isinstance(self.size, (int,long))
142 return ("URI:CHK-Verifier:%s:%s:%d:%d:%d" %
143 (idlib.b2a(self.storage_index),
144 idlib.b2a(self.uri_extension_hash),
150 class LiteralFileURI(_BaseURI):
151 implements(IURI, IFileURI)
153 def __init__(self, data=None):
157 def init_from_string(self, uri):
158 assert uri.startswith("URI:LIT:")
159 data_s = uri[len("URI:LIT:"):]
160 self.data = idlib.a2b(data_s)
164 return "URI:LIT:%s" % idlib.b2a(self.data)
166 def is_readonly(self):
168 def is_mutable(self):
170 def get_readonly(self):
173 def get_verifier(self):
174 # LIT files need no verification, all the data is present in the URI
178 return len(self.data)
180 class WriteableSSKFileURI(_BaseURI):
181 implements(IURI, IMutableFileURI)
183 def __init__(self, *args, **kwargs):
184 if not args and not kwargs:
186 self.populate(*args, **kwargs)
188 def populate(self, writekey, fingerprint):
189 self.writekey = writekey
190 self.readkey = hashutil.ssk_readkey_hash(writekey)
191 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
192 self.fingerprint = fingerprint
194 def init_from_string(self, uri):
195 assert uri.startswith("URI:SSK:"), uri
196 (header_uri, header_ssk, writekey_s, fingerprint_s) = uri.split(":")
197 self.populate(idlib.a2b(writekey_s), idlib.a2b(fingerprint_s))
201 assert isinstance(self.writekey, str)
202 assert isinstance(self.fingerprint, str)
203 return "URI:SSK:%s:%s" % (idlib.b2a(self.writekey),
204 idlib.b2a(self.fingerprint))
207 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
210 return idlib.b2a(self.writekey[:5])
212 def is_readonly(self):
214 def is_mutable(self):
216 def get_readonly(self):
217 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
218 def get_verifier(self):
219 return SSKVerifierURI(self.storage_index, self.fingerprint)
221 class ReadonlySSKFileURI(_BaseURI):
222 implements(IURI, IMutableFileURI)
224 def __init__(self, *args, **kwargs):
225 if not args and not kwargs:
227 self.populate(*args, **kwargs)
229 def populate(self, readkey, fingerprint):
230 self.readkey = readkey
231 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
232 self.fingerprint = fingerprint
234 def init_from_string(self, uri):
235 assert uri.startswith("URI:SSK-RO:"), uri
236 (header_uri, header_ssk, readkey_s, fingerprint_s) = uri.split(":")
237 self.populate(idlib.a2b(readkey_s), idlib.a2b(fingerprint_s))
241 assert isinstance(self.readkey, str)
242 assert isinstance(self.fingerprint, str)
243 return "URI:SSK-RO:%s:%s" % (idlib.b2a(self.readkey),
244 idlib.b2a(self.fingerprint))
247 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
250 return idlib.b2a(self.readkey[:5])
252 def is_readonly(self):
254 def is_mutable(self):
256 def get_readonly(self):
258 def get_verifier(self):
259 return SSKVerifierURI(self.storage_index, self.fingerprint)
261 class SSKVerifierURI(_BaseURI):
262 implements(IVerifierURI)
264 def __init__(self, *args, **kwargs):
265 if not args and not kwargs:
267 self.populate(*args, **kwargs)
269 def populate(self, storage_index, fingerprint):
270 self.storage_index = storage_index
271 self.fingerprint = fingerprint
273 def init_from_string(self, uri):
274 assert uri.startswith("URI:SSK-Verifier:"), uri
275 (header_uri, header_ssk,
276 storage_index_s, fingerprint_s) = uri.split(":")
277 self.populate(idlib.a2b(storage_index_s), idlib.a2b(fingerprint_s))
281 assert isinstance(self.storage_index, str)
282 assert isinstance(self.fingerprint, str)
283 return "URI:SSK-Verifier:%s:%s" % (idlib.b2a(self.storage_index),
284 idlib.b2a(self.fingerprint))
286 class _NewDirectoryBaseURI(_BaseURI):
287 implements(IURI, IDirnodeURI)
288 def __init__(self, filenode_uri=None):
289 self._filenode_uri = filenode_uri
292 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
295 return self._filenode_uri.to_string().split(':')[2][:5]
297 def get_filenode_uri(self):
298 return self._filenode_uri
300 def is_mutable(self):
303 def get_verifier(self):
304 return NewDirectoryURIVerifier(self._filenode_uri.get_verifier())
306 class NewDirectoryURI(_NewDirectoryBaseURI):
307 implements(INewDirectoryURI)
308 def __init__(self, filenode_uri=None):
310 assert not filenode_uri.is_readonly()
311 _NewDirectoryBaseURI.__init__(self, filenode_uri)
313 def init_from_string(self, uri):
314 assert uri.startswith("URI:DIR2:")
315 (header_uri, header_dir2, bits) = uri.split(":", 2)
316 fn = WriteableSSKFileURI()
317 fn.init_from_string("URI:SSK:" + bits)
318 self._filenode_uri = fn
322 assert isinstance(self._filenode_uri, WriteableSSKFileURI)
323 fn_u = self._filenode_uri.to_string()
324 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
325 return "URI:DIR2:" + bits
327 def is_readonly(self):
330 def get_readonly(self):
331 return ReadonlyNewDirectoryURI(self._filenode_uri.get_readonly())
333 class ReadonlyNewDirectoryURI(_NewDirectoryBaseURI):
334 implements(IReadonlyNewDirectoryURI)
335 def __init__(self, filenode_uri=None):
337 assert filenode_uri.is_readonly()
338 _NewDirectoryBaseURI.__init__(self, filenode_uri)
340 def init_from_string(self, uri):
341 assert uri.startswith("URI:DIR2-RO:")
342 (header_uri, header_dir2, bits) = uri.split(":", 2)
343 fn = ReadonlySSKFileURI()
344 fn.init_from_string("URI:SSK-RO:" + bits)
345 self._filenode_uri = fn
349 assert isinstance(self._filenode_uri, ReadonlySSKFileURI)
350 fn_u = self._filenode_uri.to_string()
351 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
352 return "URI:DIR2-RO:" + bits
354 def is_readonly(self):
357 def get_readonly(self):
360 class NewDirectoryURIVerifier(_BaseURI):
361 implements(IVerifierURI)
363 def __init__(self, filenode_uri=None):
365 filenode_uri = IVerifierURI(filenode_uri)
366 self._filenode_uri = filenode_uri
368 def init_from_string(self, uri):
369 assert uri.startswith("URI:DIR2-Verifier:")
370 (header_uri, header_dir2, bits) = uri.split(":", 2)
371 fn = SSKVerifierURI()
372 fn.init_from_string("URI:SSK-Verifier:" + bits)
373 self._filenode_uri = fn
377 assert isinstance(self._filenode_uri, SSKVerifierURI)
378 fn_u = self._filenode_uri.to_string()
379 (header_uri, header_ssk, bits) = fn_u.split(":", 2)
380 return "URI:DIR2-Verifier:" + bits
382 def get_filenode_uri(self):
383 return self._filenode_uri
387 class DirnodeURI(_BaseURI):
388 implements(IURI, IDirnodeURI)
390 def __init__(self, furl=None, writekey=None):
391 if furl is not None or writekey is not None:
392 assert furl is not None
393 assert writekey is not None
395 self.writekey = writekey
396 self._derive_values()
398 def init_from_string(self, uri):
400 # but note that the furl contains colons
402 assert uri.startswith(prefix)
403 uri = uri[len(prefix):]
404 colon = uri.rindex(":")
405 self.furl = uri[:colon]
406 self.writekey = idlib.a2b(uri[colon+1:])
407 self._derive_values()
410 def _derive_values(self):
411 wk, we, rk, index = \
412 hashutil.generate_dirnode_keys_from_writekey(self.writekey)
413 self.write_enabler = we
415 self.storage_index = index
418 return "URI:DIR:%s:%s" % (self.furl, idlib.b2a(self.writekey))
420 def is_readonly(self):
422 def is_mutable(self):
424 def get_readonly(self):
425 return ReadOnlyDirnodeURI(self.furl, self.readkey)
426 def get_verifier(self):
427 return DirnodeVerifierURI(self.furl, self.storage_index)
429 class ReadOnlyDirnodeURI(_BaseURI):
430 implements(IURI, IDirnodeURI)
432 def __init__(self, furl=None, readkey=None):
433 if furl is not None or readkey is not None:
434 assert furl is not None
435 assert readkey is not None
437 self.readkey = readkey
438 self._derive_values()
440 def init_from_string(self, uri):
441 # URI:DIR-RO:furl:key
442 # but note that the furl contains colons
443 prefix = "URI:DIR-RO:"
444 assert uri.startswith(prefix)
445 uri = uri[len(prefix):]
446 colon = uri.rindex(":")
447 self.furl = uri[:colon]
448 self.readkey = idlib.a2b(uri[colon+1:])
449 self._derive_values()
452 def _derive_values(self):
453 wk, we, rk, index = \
454 hashutil.generate_dirnode_keys_from_readkey(self.readkey)
455 self.writekey = wk # None
456 self.write_enabler = we # None
457 self.storage_index = index
460 return "URI:DIR-RO:%s:%s" % (self.furl, idlib.b2a(self.readkey))
462 def is_readonly(self):
464 def is_mutable(self):
466 def get_readonly(self):
468 def get_verifier(self):
469 return DirnodeVerifierURI(self.furl, self.storage_index)
471 class DirnodeVerifierURI(_BaseURI):
472 implements(IVerifierURI)
474 def __init__(self, furl=None, storage_index=None):
475 if furl is not None or storage_index is not None:
476 assert furl is not None
477 assert storage_index is not None
479 self.storage_index = storage_index
481 def init_from_string(self, uri):
482 # URI:DIR-Verifier:furl:storageindex
483 # but note that the furl contains colons
484 prefix = "URI:DIR-Verifier:"
485 assert uri.startswith(prefix)
486 uri = uri[len(prefix):]
487 colon = uri.rindex(":")
488 self.furl = uri[:colon]
489 self.storage_index = idlib.a2b(uri[colon+1:])
493 return "URI:DIR-Verifier:%s:%s" % (self.furl,
494 idlib.b2a(self.storage_index))
499 if s.startswith("URI:CHK:"):
500 return CHKFileURI().init_from_string(s)
501 elif s.startswith("URI:CHK-Verifier:"):
502 return CHKFileVerifierURI().init_from_string(s)
503 elif s.startswith("URI:LIT:"):
504 return LiteralFileURI().init_from_string(s)
505 elif s.startswith("URI:DIR:"):
506 return DirnodeURI().init_from_string(s)
507 elif s.startswith("URI:DIR-RO:"):
508 return ReadOnlyDirnodeURI().init_from_string(s)
509 elif s.startswith("URI:DIR-Verifier:"):
510 return DirnodeVerifierURI().init_from_string(s)
511 elif s.startswith("URI:SSK:"):
512 return WriteableSSKFileURI().init_from_string(s)
513 elif s.startswith("URI:SSK-RO:"):
514 return ReadonlySSKFileURI().init_from_string(s)
515 elif s.startswith("URI:SSK-Verifier:"):
516 return SSKVerifierURI().init_from_string(s)
517 elif s.startswith("URI:DIR2:"):
518 return NewDirectoryURI().init_from_string(s)
519 elif s.startswith("URI:DIR2-RO:"):
520 return ReadonlyNewDirectoryURI().init_from_string(s)
521 elif s.startswith("URI:DIR2-Verifier:"):
522 return NewDirectoryURIVerifier().init_from_string(s)
524 raise TypeError("unknown URI type: %s.." % s[:12])
526 registerAdapter(from_string, str, IURI)
528 def from_string_dirnode(s):
530 assert IDirnodeURI.providedBy(u)
533 registerAdapter(from_string_dirnode, str, IDirnodeURI)
535 def is_string_newdirnode_rw(s):
536 if not s.startswith("URI:DIR2:"):
539 (header_uri, header_dir2, writekey_s, fingerprint_s) = s.split(":", 2)
542 return idlib.could_be_base32_encoded(writekey_s) and idlib.could_be_base32_encoded(fingerprint_s)
544 def from_string_filenode(s):
546 assert IFileURI.providedBy(u)
549 registerAdapter(from_string_filenode, str, IFileURI)
551 def from_string_mutable_filenode(s):
553 assert IMutableFileURI.providedBy(u)
555 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
557 def from_string_verifier(s):
559 assert IVerifierURI.providedBy(u)
561 registerAdapter(from_string_verifier, str, IVerifierURI)
564 def pack_extension(data):
566 for k in sorted(data.keys()):
568 if isinstance(value, (int, long)):
570 assert isinstance(value, str), k
571 assert re.match(r'^[a-zA-Z_\-]+$', k)
572 pieces.append(k + ":" + hashutil.netstring(value))
573 uri_extension = "".join(pieces)
576 def unpack_extension(data):
579 colon = data.index(":")
581 data = data[colon+1:]
583 colon = data.index(":")
584 number = data[:colon]
586 data = data[colon+1:]
588 value = data[:length]
589 assert data[length] == ","
590 data = data[length+1:]
594 # convert certain things to numbers
595 for intkey in ("size", "segment_size", "num_segments",
596 "needed_shares", "total_shares"):
598 d[intkey] = int(d[intkey])
602 def unpack_extension_readable(data):
603 unpacked = unpack_extension(data)
604 for k in sorted(unpacked.keys()):
606 unpacked[k] = idlib.b2a(unpacked[k])