3 from zope.interface import implements
4 from twisted.python.components import registerAdapter
5 from allmydata.storage.server import si_a2b, si_b2a
6 from allmydata.util import base32, hashutil
7 from allmydata.util.assertutil import _assert
8 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI, \
9 IVerifierURI, IMutableFileURI, IDirectoryURI, IReadonlyDirectoryURI, \
10 MustBeDeepImmutableError, MustBeReadonlyError, CapConstraintError
12 class BadURIError(CapConstraintError):
15 # The URI shall be an ASCII representation of a reference to the file/directory.
16 # It shall contain enough information to retrieve and validate the contents.
17 # It shall be expressed in a limited character set (currently base32 plus ':' and
18 # capital letters, but future URIs might use a larger charset).
21 # - rename all of the *URI classes/interfaces to *Cap
22 # - make variable and method names consistently use _uri for an URI string,
23 # and _cap for a Cap object (decoded URI)
24 # - remove the human_encoding methods?
26 BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits)
27 BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
31 NUMBER_IGNORE='(?:[0-9]+)'
33 # "human-encoded" URIs are allowed to come with a leading
34 # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
35 # Note that nothing in the Tahoe code currently uses the human encoding.
36 OPTIONALHTTPLEAD=r'(?:https?://(?:[^:/]+)(?::%s)?/uri/)?' % NUMBER_IGNORE
41 return self.to_string().__hash__()
43 def __eq__(self, them):
44 if isinstance(them, _BaseURI):
45 return self.to_string() == them.to_string()
49 def __ne__(self, them):
50 if isinstance(them, _BaseURI):
51 return self.to_string() != them.to_string()
55 def to_human_encoding(self):
56 return 'http://127.0.0.1:3456/uri/'+self.to_string()
58 def get_storage_index(self):
59 return self.storage_index
62 class CHKFileURI(_BaseURI):
63 implements(IURI, IImmutableFileURI)
65 BASE_STRING='URI:CHK:'
66 STRING_RE=re.compile('^URI:CHK:'+BASE32STR_128bits+':'+
67 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
69 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK'+SEP+
70 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
71 SEP+NUMBER+SEP+NUMBER+'$')
73 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
76 self.uri_extension_hash = uri_extension_hash
77 self.needed_shares = needed_shares
78 self.total_shares = total_shares
80 self.storage_index = hashutil.storage_index_hash(self.key)
81 if not len(self.storage_index) == 16: # sha256 hash truncated to 128
82 raise BadURIError("storage index must be 16 bytes long")
85 def init_from_human_encoding(cls, uri):
86 mo = cls.HUMAN_RE.search(uri)
88 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
89 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
90 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
93 def init_from_string(cls, uri):
94 mo = cls.STRING_RE.search(uri)
96 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
97 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
98 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
101 assert isinstance(self.needed_shares, int)
102 assert isinstance(self.total_shares, int)
103 assert isinstance(self.size, (int,long))
105 return ('URI:CHK:%s:%s:%d:%d:%d' %
106 (base32.b2a(self.key),
107 base32.b2a(self.uri_extension_hash),
112 def is_readonly(self):
115 def is_mutable(self):
118 def get_readonly(self):
124 def get_verify_cap(self):
125 return CHKFileVerifierURI(storage_index=self.storage_index,
126 uri_extension_hash=self.uri_extension_hash,
127 needed_shares=self.needed_shares,
128 total_shares=self.total_shares,
131 class CHKFileVerifierURI(_BaseURI):
132 implements(IVerifierURI)
134 BASE_STRING='URI:CHK-Verifier:'
135 STRING_RE=re.compile('^URI:CHK-Verifier:'+BASE32STR_128bits+':'+
136 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
137 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK-Verifier'+SEP+
138 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
139 SEP+NUMBER+SEP+NUMBER)
141 def __init__(self, storage_index, uri_extension_hash,
142 needed_shares, total_shares, size):
143 assert len(storage_index) == 16
144 self.storage_index = storage_index
145 self.uri_extension_hash = uri_extension_hash
146 self.needed_shares = needed_shares
147 self.total_shares = total_shares
151 def init_from_human_encoding(cls, uri):
152 mo = cls.HUMAN_RE.search(uri)
154 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
155 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
156 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
159 def init_from_string(cls, uri):
160 mo = cls.STRING_RE.search(uri)
162 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
163 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)),
164 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
167 assert isinstance(self.needed_shares, int)
168 assert isinstance(self.total_shares, int)
169 assert isinstance(self.size, (int,long))
171 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
172 (si_b2a(self.storage_index),
173 base32.b2a(self.uri_extension_hash),
178 def is_readonly(self):
181 def is_mutable(self):
184 def get_readonly(self):
187 def get_verify_cap(self):
191 class LiteralFileURI(_BaseURI):
192 implements(IURI, IImmutableFileURI)
194 BASE_STRING='URI:LIT:'
195 STRING_RE=re.compile('^URI:LIT:'+base32.BASE32STR_anybytes+'$')
196 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'LIT'+SEP+base32.BASE32STR_anybytes+'$')
198 def __init__(self, data=None):
200 assert isinstance(data, str)
204 def init_from_human_encoding(cls, uri):
205 mo = cls.HUMAN_RE.search(uri)
207 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
208 return cls(base32.a2b(mo.group(1)))
211 def init_from_string(cls, uri):
212 mo = cls.STRING_RE.search(uri)
214 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
215 return cls(base32.a2b(mo.group(1)))
218 return 'URI:LIT:%s' % base32.b2a(self.data)
220 def is_readonly(self):
223 def is_mutable(self):
226 def get_readonly(self):
229 def get_storage_index(self):
232 def get_verify_cap(self):
233 # LIT files need no verification, all the data is present in the URI
237 return len(self.data)
240 class WriteableSSKFileURI(_BaseURI):
241 implements(IURI, IMutableFileURI)
243 BASE_STRING='URI:SSK:'
244 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+
245 BASE32STR_256bits+'$')
246 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK'+SEP+
247 BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
249 def __init__(self, writekey, fingerprint):
250 self.writekey = writekey
251 self.readkey = hashutil.ssk_readkey_hash(writekey)
252 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
253 assert len(self.storage_index) == 16
254 self.fingerprint = fingerprint
257 def init_from_human_encoding(cls, uri):
258 mo = cls.HUMAN_RE.search(uri)
260 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
261 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
264 def init_from_string(cls, uri):
265 mo = cls.STRING_RE.search(uri)
267 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
268 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
271 assert isinstance(self.writekey, str)
272 assert isinstance(self.fingerprint, str)
273 return 'URI:SSK:%s:%s' % (base32.b2a(self.writekey),
274 base32.b2a(self.fingerprint))
277 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
280 return base32.b2a(self.writekey[:5])
283 return base32.b2a(self.storage_index)[:5]
285 def is_readonly(self):
288 def is_mutable(self):
291 def get_readonly(self):
292 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
294 def get_verify_cap(self):
295 return SSKVerifierURI(self.storage_index, self.fingerprint)
297 class ReadonlySSKFileURI(_BaseURI):
298 implements(IURI, IMutableFileURI)
300 BASE_STRING='URI:SSK-RO:'
301 STRING_RE=re.compile('^URI:SSK-RO:'+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
302 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
304 def __init__(self, readkey, fingerprint):
305 self.readkey = readkey
306 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
307 assert len(self.storage_index) == 16
308 self.fingerprint = fingerprint
311 def init_from_human_encoding(cls, uri):
312 mo = cls.HUMAN_RE.search(uri)
314 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
315 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
318 def init_from_string(cls, uri):
319 mo = cls.STRING_RE.search(uri)
321 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
322 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
325 assert isinstance(self.readkey, str)
326 assert isinstance(self.fingerprint, str)
327 return 'URI:SSK-RO:%s:%s' % (base32.b2a(self.readkey),
328 base32.b2a(self.fingerprint))
331 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
334 return base32.b2a(self.readkey[:5])
337 return base32.b2a(self.storage_index)[:5]
339 def is_readonly(self):
342 def is_mutable(self):
345 def get_readonly(self):
348 def get_verify_cap(self):
349 return SSKVerifierURI(self.storage_index, self.fingerprint)
351 class SSKVerifierURI(_BaseURI):
352 implements(IVerifierURI)
354 BASE_STRING='URI:SSK-Verifier:'
355 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
356 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
358 def __init__(self, storage_index, fingerprint):
359 assert len(storage_index) == 16
360 self.storage_index = storage_index
361 self.fingerprint = fingerprint
364 def init_from_human_encoding(cls, uri):
365 mo = cls.HUMAN_RE.search(uri)
367 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
368 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
371 def init_from_string(cls, uri):
372 mo = cls.STRING_RE.search(uri)
374 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
375 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
378 assert isinstance(self.storage_index, str)
379 assert isinstance(self.fingerprint, str)
380 return 'URI:SSK-Verifier:%s:%s' % (si_b2a(self.storage_index),
381 base32.b2a(self.fingerprint))
383 def is_readonly(self):
386 def is_mutable(self):
389 def get_readonly(self):
392 def get_verify_cap(self):
395 class WriteableMDMFFileURI(_BaseURI):
396 implements(IURI, IMutableFileURI)
398 BASE_STRING='URI:MDMF:'
399 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
400 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
402 def __init__(self, writekey, fingerprint):
403 self.writekey = writekey
404 self.readkey = hashutil.ssk_readkey_hash(writekey)
405 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
406 assert len(self.storage_index) == 16
407 self.fingerprint = fingerprint
410 def init_from_human_encoding(cls, uri):
411 mo = cls.HUMAN_RE.search(uri)
413 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
414 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
417 def init_from_string(cls, uri):
418 mo = cls.STRING_RE.search(uri)
420 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
421 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
424 assert isinstance(self.writekey, str)
425 assert isinstance(self.fingerprint, str)
426 ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
427 base32.b2a(self.fingerprint))
431 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
434 return base32.b2a(self.writekey[:5])
437 return base32.b2a(self.storage_index)[:5]
439 def is_readonly(self):
442 def is_mutable(self):
445 def get_readonly(self):
446 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
448 def get_verify_cap(self):
449 return MDMFVerifierURI(self.storage_index, self.fingerprint)
451 class ReadonlyMDMFFileURI(_BaseURI):
452 implements(IURI, IMutableFileURI)
454 BASE_STRING='URI:MDMF-RO:'
455 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
456 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
458 def __init__(self, readkey, fingerprint):
459 self.readkey = readkey
460 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
461 assert len(self.storage_index) == 16
462 self.fingerprint = fingerprint
465 def init_from_human_encoding(cls, uri):
466 mo = cls.HUMAN_RE.search(uri)
468 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
469 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
472 def init_from_string(cls, uri):
473 mo = cls.STRING_RE.search(uri)
475 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
477 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
480 assert isinstance(self.readkey, str)
481 assert isinstance(self.fingerprint, str)
482 ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
483 base32.b2a(self.fingerprint))
487 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
490 return base32.b2a(self.readkey[:5])
493 return base32.b2a(self.storage_index)[:5]
495 def is_readonly(self):
498 def is_mutable(self):
501 def get_readonly(self):
504 def get_verify_cap(self):
505 return MDMFVerifierURI(self.storage_index, self.fingerprint)
507 class MDMFVerifierURI(_BaseURI):
508 implements(IVerifierURI)
510 BASE_STRING='URI:MDMF-Verifier:'
511 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
512 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
514 def __init__(self, storage_index, fingerprint):
515 assert len(storage_index) == 16
516 self.storage_index = storage_index
517 self.fingerprint = fingerprint
520 def init_from_human_encoding(cls, uri):
521 mo = cls.HUMAN_RE.search(uri)
523 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
524 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
527 def init_from_string(cls, uri):
528 mo = cls.STRING_RE.search(uri)
530 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
531 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
534 assert isinstance(self.storage_index, str)
535 assert isinstance(self.fingerprint, str)
536 ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
537 base32.b2a(self.fingerprint))
540 def is_readonly(self):
543 def is_mutable(self):
546 def get_readonly(self):
549 def get_verify_cap(self):
552 class _DirectoryBaseURI(_BaseURI):
553 implements(IURI, IDirnodeURI)
554 def __init__(self, filenode_uri=None):
555 self._filenode_uri = filenode_uri
558 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
561 def init_from_string(cls, uri):
562 mo = cls.BASE_STRING_RE.search(uri)
564 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
565 bits = uri[mo.end():]
566 fn = cls.INNER_URI_CLASS.init_from_string(
567 cls.INNER_URI_CLASS.BASE_STRING+bits)
571 def init_from_human_encoding(cls, uri):
572 mo = cls.BASE_HUMAN_RE.search(uri)
574 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
575 bits = uri[mo.end():]
576 while bits and bits[-1] == '/':
578 fn = cls.INNER_URI_CLASS.init_from_string(
579 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
583 fnuri = self._filenode_uri.to_string()
584 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
586 bits = fnuri[mo.end():]
587 return self.BASE_STRING+bits
590 return self._filenode_uri.to_string().split(':')[2][:5]
593 si = self._filenode_uri.get_storage_index()
596 return base32.b2a(si)[:5]
598 def is_mutable(self):
601 def get_filenode_cap(self):
602 return self._filenode_uri
604 def get_verify_cap(self):
605 return DirectoryURIVerifier(self._filenode_uri.get_verify_cap())
607 def get_storage_index(self):
608 return self._filenode_uri.get_storage_index()
610 class DirectoryURI(_DirectoryBaseURI):
611 implements(IDirectoryURI)
613 BASE_STRING='URI:DIR2:'
614 BASE_STRING_RE=re.compile('^'+BASE_STRING)
615 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
616 INNER_URI_CLASS=WriteableSSKFileURI
618 def __init__(self, filenode_uri=None):
620 assert not filenode_uri.is_readonly()
621 _DirectoryBaseURI.__init__(self, filenode_uri)
623 def is_readonly(self):
626 def get_readonly(self):
627 return ReadonlyDirectoryURI(self._filenode_uri.get_readonly())
630 class ReadonlyDirectoryURI(_DirectoryBaseURI):
631 implements(IReadonlyDirectoryURI)
633 BASE_STRING='URI:DIR2-RO:'
634 BASE_STRING_RE=re.compile('^'+BASE_STRING)
635 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
636 INNER_URI_CLASS=ReadonlySSKFileURI
638 def __init__(self, filenode_uri=None):
640 assert filenode_uri.is_readonly()
641 _DirectoryBaseURI.__init__(self, filenode_uri)
643 def is_readonly(self):
646 def get_readonly(self):
650 class _ImmutableDirectoryBaseURI(_DirectoryBaseURI):
651 def __init__(self, filenode_uri=None):
653 assert isinstance(filenode_uri, self.INNER_URI_CLASS), filenode_uri
654 assert not filenode_uri.is_mutable()
655 _DirectoryBaseURI.__init__(self, filenode_uri)
657 def is_readonly(self):
660 def is_mutable(self):
663 def get_readonly(self):
667 class ImmutableDirectoryURI(_ImmutableDirectoryBaseURI):
668 BASE_STRING='URI:DIR2-CHK:'
669 BASE_STRING_RE=re.compile('^'+BASE_STRING)
670 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK'+SEP)
671 INNER_URI_CLASS=CHKFileURI
673 def get_verify_cap(self):
674 vcap = self._filenode_uri.get_verify_cap()
675 return ImmutableDirectoryURIVerifier(vcap)
678 class LiteralDirectoryURI(_ImmutableDirectoryBaseURI):
679 BASE_STRING='URI:DIR2-LIT:'
680 BASE_STRING_RE=re.compile('^'+BASE_STRING)
681 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-LIT'+SEP)
682 INNER_URI_CLASS=LiteralFileURI
684 def get_verify_cap(self):
685 # LIT caps have no verifier, since they aren't distributed
689 class MDMFDirectoryURI(_DirectoryBaseURI):
690 implements(IDirectoryURI)
692 BASE_STRING='URI:DIR2-MDMF:'
693 BASE_STRING_RE=re.compile('^'+BASE_STRING)
694 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF'+SEP)
695 INNER_URI_CLASS=WriteableMDMFFileURI
697 def __init__(self, filenode_uri=None):
699 assert not filenode_uri.is_readonly()
700 _DirectoryBaseURI.__init__(self, filenode_uri)
702 def is_readonly(self):
705 def get_readonly(self):
706 return ReadonlyMDMFDirectoryURI(self._filenode_uri.get_readonly())
708 def get_verify_cap(self):
709 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
712 class ReadonlyMDMFDirectoryURI(_DirectoryBaseURI):
713 implements(IReadonlyDirectoryURI)
715 BASE_STRING='URI:DIR2-MDMF-RO:'
716 BASE_STRING_RE=re.compile('^'+BASE_STRING)
717 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-RO'+SEP)
718 INNER_URI_CLASS=ReadonlyMDMFFileURI
720 def __init__(self, filenode_uri=None):
722 assert filenode_uri.is_readonly()
723 _DirectoryBaseURI.__init__(self, filenode_uri)
725 def is_readonly(self):
728 def get_readonly(self):
731 def get_verify_cap(self):
732 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
735 def wrap_dirnode_cap(filecap):
736 if isinstance(filecap, WriteableSSKFileURI):
737 return DirectoryURI(filecap)
738 if isinstance(filecap, ReadonlySSKFileURI):
739 return ReadonlyDirectoryURI(filecap)
740 if isinstance(filecap, CHKFileURI):
741 return ImmutableDirectoryURI(filecap)
742 if isinstance(filecap, LiteralFileURI):
743 return LiteralDirectoryURI(filecap)
744 if isinstance(filecap, WriteableMDMFFileURI):
745 return MDMFDirectoryURI(filecap)
746 if isinstance(filecap, ReadonlyMDMFFileURI):
747 return ReadonlyMDMFDirectoryURI(filecap)
748 raise AssertionError("cannot interpret as a directory cap: %s" % filecap.__class__)
751 class MDMFDirectoryURIVerifier(_DirectoryBaseURI):
752 implements(IVerifierURI)
754 BASE_STRING='URI:DIR2-MDMF-Verifier:'
755 BASE_STRING_RE=re.compile('^'+BASE_STRING)
756 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-Verifier'+SEP)
757 INNER_URI_CLASS=MDMFVerifierURI
759 def __init__(self, filenode_uri=None):
761 _assert(IVerifierURI.providedBy(filenode_uri))
762 self._filenode_uri = filenode_uri
764 def get_filenode_cap(self):
765 return self._filenode_uri
767 def is_mutable(self):
770 def is_readonly(self):
773 def get_readonly(self):
777 class DirectoryURIVerifier(_DirectoryBaseURI):
778 implements(IVerifierURI)
780 BASE_STRING='URI:DIR2-Verifier:'
781 BASE_STRING_RE=re.compile('^'+BASE_STRING)
782 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
783 INNER_URI_CLASS=SSKVerifierURI
785 def __init__(self, filenode_uri=None):
787 _assert(IVerifierURI.providedBy(filenode_uri))
788 self._filenode_uri = filenode_uri
790 def get_filenode_cap(self):
791 return self._filenode_uri
793 def is_mutable(self):
796 def is_readonly(self):
799 def get_readonly(self):
803 class ImmutableDirectoryURIVerifier(DirectoryURIVerifier):
804 implements(IVerifierURI)
805 BASE_STRING='URI:DIR2-CHK-Verifier:'
806 BASE_STRING_RE=re.compile('^'+BASE_STRING)
807 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK-VERIFIER'+SEP)
808 INNER_URI_CLASS=CHKFileVerifierURI
812 def __init__(self, uri, error=None):
819 def get_readonly(self):
825 def get_verify_cap(self):
829 ALLEGED_READONLY_PREFIX = 'ro.'
830 ALLEGED_IMMUTABLE_PREFIX = 'imm.'
832 def from_string(u, deep_immutable=False, name=u"<unknown name>"):
833 if not isinstance(u, str):
834 raise TypeError("unknown URI type: %s.." % str(u)[:100])
836 # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
837 # on all URIs, even though we would only strictly need to do so for caps of
838 # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
839 # prefix are treated as unknown. This should be revisited when we add the
840 # new cap formats. See ticket #833 comment:31.
842 can_be_mutable = can_be_writeable = not deep_immutable
843 if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
844 can_be_mutable = can_be_writeable = False
845 s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
846 elif s.startswith(ALLEGED_READONLY_PREFIX):
847 can_be_writeable = False
848 s = s[len(ALLEGED_READONLY_PREFIX):]
853 if s.startswith('URI:CHK:'):
854 return CHKFileURI.init_from_string(s)
855 elif s.startswith('URI:CHK-Verifier:'):
856 return CHKFileVerifierURI.init_from_string(s)
857 elif s.startswith('URI:LIT:'):
858 return LiteralFileURI.init_from_string(s)
859 elif s.startswith('URI:SSK:'):
861 return WriteableSSKFileURI.init_from_string(s)
862 kind = "URI:SSK file writecap"
863 elif s.startswith('URI:SSK-RO:'):
865 return ReadonlySSKFileURI.init_from_string(s)
866 kind = "URI:SSK-RO readcap to a mutable file"
867 elif s.startswith('URI:SSK-Verifier:'):
868 return SSKVerifierURI.init_from_string(s)
869 elif s.startswith('URI:MDMF:'):
871 return WriteableMDMFFileURI.init_from_string(s)
872 kind = "URI:MDMF file writecap"
873 elif s.startswith('URI:MDMF-RO:'):
875 return ReadonlyMDMFFileURI.init_from_string(s)
876 kind = "URI:MDMF-RO readcap to a mutable file"
877 elif s.startswith('URI:MDMF-Verifier:'):
878 return MDMFVerifierURI.init_from_string(s)
879 elif s.startswith('URI:DIR2:'):
881 return DirectoryURI.init_from_string(s)
882 kind = "URI:DIR2 directory writecap"
883 elif s.startswith('URI:DIR2-RO:'):
885 return ReadonlyDirectoryURI.init_from_string(s)
886 kind = "URI:DIR2-RO readcap to a mutable directory"
887 elif s.startswith('URI:DIR2-Verifier:'):
888 return DirectoryURIVerifier.init_from_string(s)
889 elif s.startswith('URI:DIR2-CHK:'):
890 return ImmutableDirectoryURI.init_from_string(s)
891 elif s.startswith('URI:DIR2-CHK-Verifier:'):
892 return ImmutableDirectoryURIVerifier.init_from_string(s)
893 elif s.startswith('URI:DIR2-LIT:'):
894 return LiteralDirectoryURI.init_from_string(s)
895 elif s.startswith('URI:DIR2-MDMF:'):
897 return MDMFDirectoryURI.init_from_string(s)
898 kind = "URI:DIR2-MDMF directory writecap"
899 elif s.startswith('URI:DIR2-MDMF-RO:'):
901 return ReadonlyMDMFDirectoryURI.init_from_string(s)
902 kind = "URI:DIR2-MDMF-RO readcap to a mutable directory"
903 elif s.startswith('URI:DIR2-MDMF-Verifier:'):
904 return MDMFDirectoryURIVerifier.init_from_string(s)
905 elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable:
906 # For testing how future writeable caps would behave in read-only contexts.
907 kind = "x-tahoe-future-test-writeable: testing cap"
908 elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable:
909 # For testing how future mutable readcaps would behave in immutable contexts.
910 kind = "x-tahoe-future-test-mutable: testing cap"
914 # We fell through because a constraint was not met.
915 # Prefer to report the most specific constraint.
916 if not can_be_mutable:
917 error = MustBeDeepImmutableError(kind + " used in an immutable context", name)
919 error = MustBeReadonlyError(kind + " used in a read-only context", name)
921 except BadURIError, e:
924 return UnknownURI(u, error=error)
928 from_string(s, deep_immutable=False)
930 except (TypeError, AssertionError):
933 def is_literal_file_uri(s):
934 if not isinstance(s, str):
936 return (s.startswith('URI:LIT:') or
937 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
938 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
940 def has_uri_prefix(s):
941 if not isinstance(s, str):
943 return (s.startswith("URI:") or
944 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:') or
945 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:'))
948 # These take the same keyword arguments as from_string above.
950 def from_string_dirnode(s, **kwargs):
951 u = from_string(s, **kwargs)
952 _assert(IDirnodeURI.providedBy(u))
955 registerAdapter(from_string_dirnode, str, IDirnodeURI)
957 def from_string_filenode(s, **kwargs):
958 u = from_string(s, **kwargs)
959 _assert(IFileURI.providedBy(u))
962 registerAdapter(from_string_filenode, str, IFileURI)
964 def from_string_mutable_filenode(s, **kwargs):
965 u = from_string(s, **kwargs)
966 _assert(IMutableFileURI.providedBy(u))
968 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
970 def from_string_verifier(s, **kwargs):
971 u = from_string(s, **kwargs)
972 _assert(IVerifierURI.providedBy(u))
974 registerAdapter(from_string_verifier, str, IVerifierURI)
977 def pack_extension(data):
979 for k in sorted(data.keys()):
981 if isinstance(value, (int, long)):
983 assert isinstance(value, str), k
984 assert re.match(r'^[a-zA-Z_\-]+$', k)
985 pieces.append(k + ':' + hashutil.netstring(value))
986 uri_extension = ''.join(pieces)
989 def unpack_extension(data):
992 colon = data.index(':')
994 data = data[colon+1:]
996 colon = data.index(':')
997 number = data[:colon]
999 data = data[colon+1:]
1001 value = data[:length]
1002 assert data[length] == ','
1003 data = data[length+1:]
1007 # convert certain things to numbers
1008 for intkey in ('size', 'segment_size', 'num_segments',
1009 'needed_shares', 'total_shares'):
1011 d[intkey] = int(d[intkey])
1015 def unpack_extension_readable(data):
1016 unpacked = unpack_extension(data)
1017 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
1018 for k in sorted(unpacked.keys()):
1020 unpacked[k] = base32.b2a(unpacked[k])