4 from zope.interface import implements
5 from twisted.python.components import registerAdapter
7 from allmydata.storage.server import si_a2b, si_b2a
8 from allmydata.util import base32, hashutil
9 from allmydata.util.assertutil import _assert
10 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI, \
11 IVerifierURI, IMutableFileURI, IDirectoryURI, IReadonlyDirectoryURI, \
12 MustBeDeepImmutableError, MustBeReadonlyError, CapConstraintError
14 class BadURIError(CapConstraintError):
17 # The URI shall be an ASCII representation of a reference to the file/directory.
18 # It shall contain enough information to retrieve and validate the contents.
19 # It shall be expressed in a limited character set (currently base32 plus ':' and
20 # capital letters, but future URIs might use a larger charset).
23 # - rename all of the *URI classes/interfaces to *Cap
24 # - make variable and method names consistently use _uri for an URI string,
25 # and _cap for a Cap object (decoded URI)
27 BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits)
28 BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
35 return self.to_string().__hash__()
37 def __eq__(self, them):
38 if isinstance(them, _BaseURI):
39 return self.to_string() == them.to_string()
43 def __ne__(self, them):
44 if isinstance(them, _BaseURI):
45 return self.to_string() != them.to_string()
49 def get_storage_index(self):
50 return self.storage_index
53 class CHKFileURI(_BaseURI):
54 implements(IURI, IImmutableFileURI)
56 BASE_STRING='URI:CHK:'
57 STRING_RE=re.compile('^URI:CHK:'+BASE32STR_128bits+':'+
58 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
61 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
64 self.uri_extension_hash = uri_extension_hash
65 self.needed_shares = needed_shares
66 self.total_shares = total_shares
68 self.storage_index = hashutil.storage_index_hash(self.key)
69 if not len(self.storage_index) == 16: # sha256 hash truncated to 128
70 raise BadURIError("storage index must be 16 bytes long")
73 def init_from_string(cls, uri):
74 mo = cls.STRING_RE.search(uri)
76 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
77 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
78 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
81 assert isinstance(self.needed_shares, int)
82 assert isinstance(self.total_shares, int)
83 assert isinstance(self.size, (int,long))
85 return ('URI:CHK:%s:%s:%d:%d:%d' %
86 (base32.b2a(self.key),
87 base32.b2a(self.uri_extension_hash),
92 def is_readonly(self):
98 def get_readonly(self):
104 def get_verify_cap(self):
105 return CHKFileVerifierURI(storage_index=self.storage_index,
106 uri_extension_hash=self.uri_extension_hash,
107 needed_shares=self.needed_shares,
108 total_shares=self.total_shares,
112 class CHKFileVerifierURI(_BaseURI):
113 implements(IVerifierURI)
115 BASE_STRING='URI:CHK-Verifier:'
116 STRING_RE=re.compile('^URI:CHK-Verifier:'+BASE32STR_128bits+':'+
117 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
119 def __init__(self, storage_index, uri_extension_hash,
120 needed_shares, total_shares, size):
121 assert len(storage_index) == 16
122 self.storage_index = storage_index
123 self.uri_extension_hash = uri_extension_hash
124 self.needed_shares = needed_shares
125 self.total_shares = total_shares
129 def init_from_string(cls, uri):
130 mo = cls.STRING_RE.search(uri)
132 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
133 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)),
134 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
137 assert isinstance(self.needed_shares, int)
138 assert isinstance(self.total_shares, int)
139 assert isinstance(self.size, (int,long))
141 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
142 (si_b2a(self.storage_index),
143 base32.b2a(self.uri_extension_hash),
148 def is_readonly(self):
151 def is_mutable(self):
154 def get_readonly(self):
157 def get_verify_cap(self):
161 class LiteralFileURI(_BaseURI):
162 implements(IURI, IImmutableFileURI)
164 BASE_STRING='URI:LIT:'
165 STRING_RE=re.compile('^URI:LIT:'+base32.BASE32STR_anybytes+'$')
167 def __init__(self, data=None):
169 assert isinstance(data, str)
173 def init_from_string(cls, uri):
174 mo = cls.STRING_RE.search(uri)
176 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
177 return cls(base32.a2b(mo.group(1)))
180 return 'URI:LIT:%s' % base32.b2a(self.data)
182 def is_readonly(self):
185 def is_mutable(self):
188 def get_readonly(self):
191 def get_storage_index(self):
194 def get_verify_cap(self):
195 # LIT files need no verification, all the data is present in the URI
199 return len(self.data)
202 class WriteableSSKFileURI(_BaseURI):
203 implements(IURI, IMutableFileURI)
205 BASE_STRING='URI:SSK:'
206 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+
207 BASE32STR_256bits+'$')
209 def __init__(self, writekey, fingerprint):
210 self.writekey = writekey
211 self.readkey = hashutil.ssk_readkey_hash(writekey)
212 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
213 assert len(self.storage_index) == 16
214 self.fingerprint = fingerprint
217 def init_from_string(cls, uri):
218 mo = cls.STRING_RE.search(uri)
220 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
221 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
224 assert isinstance(self.writekey, str)
225 assert isinstance(self.fingerprint, str)
226 return 'URI:SSK:%s:%s' % (base32.b2a(self.writekey),
227 base32.b2a(self.fingerprint))
230 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
233 return base32.b2a(self.writekey[:5])
236 return base32.b2a(self.storage_index)[:5]
238 def is_readonly(self):
241 def is_mutable(self):
244 def get_readonly(self):
245 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
247 def get_verify_cap(self):
248 return SSKVerifierURI(self.storage_index, self.fingerprint)
251 class ReadonlySSKFileURI(_BaseURI):
252 implements(IURI, IMutableFileURI)
254 BASE_STRING='URI:SSK-RO:'
255 STRING_RE=re.compile('^URI:SSK-RO:'+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
257 def __init__(self, readkey, fingerprint):
258 self.readkey = readkey
259 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
260 assert len(self.storage_index) == 16
261 self.fingerprint = fingerprint
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.readkey, str)
272 assert isinstance(self.fingerprint, str)
273 return 'URI:SSK-RO:%s:%s' % (base32.b2a(self.readkey),
274 base32.b2a(self.fingerprint))
277 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
280 return base32.b2a(self.readkey[:5])
283 return base32.b2a(self.storage_index)[:5]
285 def is_readonly(self):
288 def is_mutable(self):
291 def get_readonly(self):
294 def get_verify_cap(self):
295 return SSKVerifierURI(self.storage_index, self.fingerprint)
298 class SSKVerifierURI(_BaseURI):
299 implements(IVerifierURI)
301 BASE_STRING='URI:SSK-Verifier:'
302 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
304 def __init__(self, storage_index, fingerprint):
305 assert len(storage_index) == 16
306 self.storage_index = storage_index
307 self.fingerprint = fingerprint
310 def init_from_string(cls, uri):
311 mo = cls.STRING_RE.search(uri)
313 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
314 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
317 assert isinstance(self.storage_index, str)
318 assert isinstance(self.fingerprint, str)
319 return 'URI:SSK-Verifier:%s:%s' % (si_b2a(self.storage_index),
320 base32.b2a(self.fingerprint))
322 def is_readonly(self):
325 def is_mutable(self):
328 def get_readonly(self):
331 def get_verify_cap(self):
335 class WriteableMDMFFileURI(_BaseURI):
336 implements(IURI, IMutableFileURI)
338 BASE_STRING='URI:MDMF:'
339 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
341 def __init__(self, writekey, fingerprint):
342 self.writekey = writekey
343 self.readkey = hashutil.ssk_readkey_hash(writekey)
344 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
345 assert len(self.storage_index) == 16
346 self.fingerprint = fingerprint
349 def init_from_string(cls, uri):
350 mo = cls.STRING_RE.search(uri)
352 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
353 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
356 assert isinstance(self.writekey, str)
357 assert isinstance(self.fingerprint, str)
358 ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
359 base32.b2a(self.fingerprint))
363 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
366 return base32.b2a(self.writekey[:5])
369 return base32.b2a(self.storage_index)[:5]
371 def is_readonly(self):
374 def is_mutable(self):
377 def get_readonly(self):
378 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
380 def get_verify_cap(self):
381 return MDMFVerifierURI(self.storage_index, self.fingerprint)
384 class ReadonlyMDMFFileURI(_BaseURI):
385 implements(IURI, IMutableFileURI)
387 BASE_STRING='URI:MDMF-RO:'
388 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
390 def __init__(self, readkey, fingerprint):
391 self.readkey = readkey
392 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
393 assert len(self.storage_index) == 16
394 self.fingerprint = fingerprint
397 def init_from_string(cls, uri):
398 mo = cls.STRING_RE.search(uri)
400 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
402 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
405 assert isinstance(self.readkey, str)
406 assert isinstance(self.fingerprint, str)
407 ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
408 base32.b2a(self.fingerprint))
412 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
415 return base32.b2a(self.readkey[:5])
418 return base32.b2a(self.storage_index)[:5]
420 def is_readonly(self):
423 def is_mutable(self):
426 def get_readonly(self):
429 def get_verify_cap(self):
430 return MDMFVerifierURI(self.storage_index, self.fingerprint)
433 class MDMFVerifierURI(_BaseURI):
434 implements(IVerifierURI)
436 BASE_STRING='URI:MDMF-Verifier:'
437 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
439 def __init__(self, storage_index, fingerprint):
440 assert len(storage_index) == 16
441 self.storage_index = storage_index
442 self.fingerprint = fingerprint
445 def init_from_string(cls, uri):
446 mo = cls.STRING_RE.search(uri)
448 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
449 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
452 assert isinstance(self.storage_index, str)
453 assert isinstance(self.fingerprint, str)
454 ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
455 base32.b2a(self.fingerprint))
458 def is_readonly(self):
461 def is_mutable(self):
464 def get_readonly(self):
467 def get_verify_cap(self):
471 class _DirectoryBaseURI(_BaseURI):
472 implements(IURI, IDirnodeURI)
473 def __init__(self, filenode_uri=None):
474 self._filenode_uri = filenode_uri
477 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
480 def init_from_string(cls, uri):
481 mo = cls.BASE_STRING_RE.search(uri)
483 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
484 bits = uri[mo.end():]
485 fn = cls.INNER_URI_CLASS.init_from_string(
486 cls.INNER_URI_CLASS.BASE_STRING+bits)
490 fnuri = self._filenode_uri.to_string()
491 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
493 bits = fnuri[mo.end():]
494 return self.BASE_STRING+bits
497 return self._filenode_uri.to_string().split(':')[2][:5]
500 si = self._filenode_uri.get_storage_index()
503 return base32.b2a(si)[:5]
505 def is_mutable(self):
508 def get_filenode_cap(self):
509 return self._filenode_uri
511 def get_verify_cap(self):
512 return DirectoryURIVerifier(self._filenode_uri.get_verify_cap())
514 def get_storage_index(self):
515 return self._filenode_uri.get_storage_index()
518 class DirectoryURI(_DirectoryBaseURI):
519 implements(IDirectoryURI)
521 BASE_STRING='URI:DIR2:'
522 BASE_STRING_RE=re.compile('^'+BASE_STRING)
523 INNER_URI_CLASS=WriteableSSKFileURI
525 def __init__(self, filenode_uri=None):
527 assert not filenode_uri.is_readonly()
528 _DirectoryBaseURI.__init__(self, filenode_uri)
530 def is_readonly(self):
533 def get_readonly(self):
534 return ReadonlyDirectoryURI(self._filenode_uri.get_readonly())
537 class ReadonlyDirectoryURI(_DirectoryBaseURI):
538 implements(IReadonlyDirectoryURI)
540 BASE_STRING='URI:DIR2-RO:'
541 BASE_STRING_RE=re.compile('^'+BASE_STRING)
542 INNER_URI_CLASS=ReadonlySSKFileURI
544 def __init__(self, filenode_uri=None):
546 assert filenode_uri.is_readonly()
547 _DirectoryBaseURI.__init__(self, filenode_uri)
549 def is_readonly(self):
552 def get_readonly(self):
556 class _ImmutableDirectoryBaseURI(_DirectoryBaseURI):
557 def __init__(self, filenode_uri=None):
559 assert isinstance(filenode_uri, self.INNER_URI_CLASS), filenode_uri
560 assert not filenode_uri.is_mutable()
561 _DirectoryBaseURI.__init__(self, filenode_uri)
563 def is_readonly(self):
566 def is_mutable(self):
569 def get_readonly(self):
573 class ImmutableDirectoryURI(_ImmutableDirectoryBaseURI):
574 BASE_STRING='URI:DIR2-CHK:'
575 BASE_STRING_RE=re.compile('^'+BASE_STRING)
576 INNER_URI_CLASS=CHKFileURI
578 def get_verify_cap(self):
579 vcap = self._filenode_uri.get_verify_cap()
580 return ImmutableDirectoryURIVerifier(vcap)
583 class LiteralDirectoryURI(_ImmutableDirectoryBaseURI):
584 BASE_STRING='URI:DIR2-LIT:'
585 BASE_STRING_RE=re.compile('^'+BASE_STRING)
586 INNER_URI_CLASS=LiteralFileURI
588 def get_verify_cap(self):
589 # LIT caps have no verifier, since they aren't distributed
593 class MDMFDirectoryURI(_DirectoryBaseURI):
594 implements(IDirectoryURI)
596 BASE_STRING='URI:DIR2-MDMF:'
597 BASE_STRING_RE=re.compile('^'+BASE_STRING)
598 INNER_URI_CLASS=WriteableMDMFFileURI
600 def __init__(self, filenode_uri=None):
602 assert not filenode_uri.is_readonly()
603 _DirectoryBaseURI.__init__(self, filenode_uri)
605 def is_readonly(self):
608 def get_readonly(self):
609 return ReadonlyMDMFDirectoryURI(self._filenode_uri.get_readonly())
611 def get_verify_cap(self):
612 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
615 class ReadonlyMDMFDirectoryURI(_DirectoryBaseURI):
616 implements(IReadonlyDirectoryURI)
618 BASE_STRING='URI:DIR2-MDMF-RO:'
619 BASE_STRING_RE=re.compile('^'+BASE_STRING)
620 INNER_URI_CLASS=ReadonlyMDMFFileURI
622 def __init__(self, filenode_uri=None):
624 assert filenode_uri.is_readonly()
625 _DirectoryBaseURI.__init__(self, filenode_uri)
627 def is_readonly(self):
630 def get_readonly(self):
633 def get_verify_cap(self):
634 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
637 def wrap_dirnode_cap(filecap):
638 if isinstance(filecap, WriteableSSKFileURI):
639 return DirectoryURI(filecap)
640 if isinstance(filecap, ReadonlySSKFileURI):
641 return ReadonlyDirectoryURI(filecap)
642 if isinstance(filecap, CHKFileURI):
643 return ImmutableDirectoryURI(filecap)
644 if isinstance(filecap, LiteralFileURI):
645 return LiteralDirectoryURI(filecap)
646 if isinstance(filecap, WriteableMDMFFileURI):
647 return MDMFDirectoryURI(filecap)
648 if isinstance(filecap, ReadonlyMDMFFileURI):
649 return ReadonlyMDMFDirectoryURI(filecap)
650 raise AssertionError("cannot interpret as a directory cap: %s" % filecap.__class__)
653 class MDMFDirectoryURIVerifier(_DirectoryBaseURI):
654 implements(IVerifierURI)
656 BASE_STRING='URI:DIR2-MDMF-Verifier:'
657 BASE_STRING_RE=re.compile('^'+BASE_STRING)
658 INNER_URI_CLASS=MDMFVerifierURI
660 def __init__(self, filenode_uri=None):
662 _assert(IVerifierURI.providedBy(filenode_uri))
663 self._filenode_uri = filenode_uri
665 def get_filenode_cap(self):
666 return self._filenode_uri
668 def is_mutable(self):
671 def is_readonly(self):
674 def get_readonly(self):
678 class DirectoryURIVerifier(_DirectoryBaseURI):
679 implements(IVerifierURI)
681 BASE_STRING='URI:DIR2-Verifier:'
682 BASE_STRING_RE=re.compile('^'+BASE_STRING)
683 INNER_URI_CLASS=SSKVerifierURI
685 def __init__(self, filenode_uri=None):
687 _assert(IVerifierURI.providedBy(filenode_uri))
688 self._filenode_uri = filenode_uri
690 def get_filenode_cap(self):
691 return self._filenode_uri
693 def is_mutable(self):
696 def is_readonly(self):
699 def get_readonly(self):
703 class ImmutableDirectoryURIVerifier(DirectoryURIVerifier):
704 implements(IVerifierURI)
705 BASE_STRING='URI:DIR2-CHK-Verifier:'
706 BASE_STRING_RE=re.compile('^'+BASE_STRING)
707 INNER_URI_CLASS=CHKFileVerifierURI
711 def __init__(self, uri, error=None):
718 def get_readonly(self):
724 def get_verify_cap(self):
728 ALLEGED_READONLY_PREFIX = 'ro.'
729 ALLEGED_IMMUTABLE_PREFIX = 'imm.'
731 def from_string(u, deep_immutable=False, name=u"<unknown name>"):
732 if not isinstance(u, str):
733 raise TypeError("URI must be str: %r" % (u,))
735 # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
736 # on all URIs, even though we would only strictly need to do so for caps of
737 # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
738 # prefix are treated as unknown. This should be revisited when we add the
739 # new cap formats. See ticket #833 comment:31.
741 can_be_mutable = can_be_writeable = not deep_immutable
742 if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
743 can_be_mutable = can_be_writeable = False
744 s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
745 elif s.startswith(ALLEGED_READONLY_PREFIX):
746 can_be_writeable = False
747 s = s[len(ALLEGED_READONLY_PREFIX):]
752 if s.startswith('URI:CHK:'):
753 return CHKFileURI.init_from_string(s)
754 elif s.startswith('URI:CHK-Verifier:'):
755 return CHKFileVerifierURI.init_from_string(s)
756 elif s.startswith('URI:LIT:'):
757 return LiteralFileURI.init_from_string(s)
758 elif s.startswith('URI:SSK:'):
760 return WriteableSSKFileURI.init_from_string(s)
761 kind = "URI:SSK file writecap"
762 elif s.startswith('URI:SSK-RO:'):
764 return ReadonlySSKFileURI.init_from_string(s)
765 kind = "URI:SSK-RO readcap to a mutable file"
766 elif s.startswith('URI:SSK-Verifier:'):
767 return SSKVerifierURI.init_from_string(s)
768 elif s.startswith('URI:MDMF:'):
770 return WriteableMDMFFileURI.init_from_string(s)
771 kind = "URI:MDMF file writecap"
772 elif s.startswith('URI:MDMF-RO:'):
774 return ReadonlyMDMFFileURI.init_from_string(s)
775 kind = "URI:MDMF-RO readcap to a mutable file"
776 elif s.startswith('URI:MDMF-Verifier:'):
777 return MDMFVerifierURI.init_from_string(s)
778 elif s.startswith('URI:DIR2:'):
780 return DirectoryURI.init_from_string(s)
781 kind = "URI:DIR2 directory writecap"
782 elif s.startswith('URI:DIR2-RO:'):
784 return ReadonlyDirectoryURI.init_from_string(s)
785 kind = "URI:DIR2-RO readcap to a mutable directory"
786 elif s.startswith('URI:DIR2-Verifier:'):
787 return DirectoryURIVerifier.init_from_string(s)
788 elif s.startswith('URI:DIR2-CHK:'):
789 return ImmutableDirectoryURI.init_from_string(s)
790 elif s.startswith('URI:DIR2-CHK-Verifier:'):
791 return ImmutableDirectoryURIVerifier.init_from_string(s)
792 elif s.startswith('URI:DIR2-LIT:'):
793 return LiteralDirectoryURI.init_from_string(s)
794 elif s.startswith('URI:DIR2-MDMF:'):
796 return MDMFDirectoryURI.init_from_string(s)
797 kind = "URI:DIR2-MDMF directory writecap"
798 elif s.startswith('URI:DIR2-MDMF-RO:'):
800 return ReadonlyMDMFDirectoryURI.init_from_string(s)
801 kind = "URI:DIR2-MDMF-RO readcap to a mutable directory"
802 elif s.startswith('URI:DIR2-MDMF-Verifier:'):
803 return MDMFDirectoryURIVerifier.init_from_string(s)
804 elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable:
805 # For testing how future writeable caps would behave in read-only contexts.
806 kind = "x-tahoe-future-test-writeable: testing cap"
807 elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable:
808 # For testing how future mutable readcaps would behave in immutable contexts.
809 kind = "x-tahoe-future-test-mutable: testing cap"
813 # We fell through because a constraint was not met.
814 # Prefer to report the most specific constraint.
815 if not can_be_mutable:
816 error = MustBeDeepImmutableError(kind + " used in an immutable context", name)
818 error = MustBeReadonlyError(kind + " used in a read-only context", name)
820 except BadURIError, e:
823 return UnknownURI(u, error=error)
827 from_string(s, deep_immutable=False)
829 except (TypeError, AssertionError):
832 def is_literal_file_uri(s):
833 if not isinstance(s, str):
835 return (s.startswith('URI:LIT:') or
836 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
837 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
839 def has_uri_prefix(s):
840 if not isinstance(s, str):
842 return (s.startswith("URI:") or
843 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:') or
844 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:'))
847 # These take the same keyword arguments as from_string above.
849 def from_string_dirnode(s, **kwargs):
850 u = from_string(s, **kwargs)
851 _assert(IDirnodeURI.providedBy(u))
854 registerAdapter(from_string_dirnode, str, IDirnodeURI)
856 def from_string_filenode(s, **kwargs):
857 u = from_string(s, **kwargs)
858 _assert(IFileURI.providedBy(u))
861 registerAdapter(from_string_filenode, str, IFileURI)
863 def from_string_mutable_filenode(s, **kwargs):
864 u = from_string(s, **kwargs)
865 _assert(IMutableFileURI.providedBy(u))
867 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
869 def from_string_verifier(s, **kwargs):
870 u = from_string(s, **kwargs)
871 _assert(IVerifierURI.providedBy(u))
873 registerAdapter(from_string_verifier, str, IVerifierURI)
876 def pack_extension(data):
878 for k in sorted(data.keys()):
880 if isinstance(value, (int, long)):
882 assert isinstance(value, str), k
883 assert re.match(r'^[a-zA-Z_\-]+$', k)
884 pieces.append(k + ':' + hashutil.netstring(value))
885 uri_extension = ''.join(pieces)
888 def unpack_extension(data):
891 colon = data.index(':')
893 data = data[colon+1:]
895 colon = data.index(':')
896 number = data[:colon]
898 data = data[colon+1:]
900 value = data[:length]
901 assert data[length] == ','
902 data = data[length+1:]
906 # convert certain things to numbers
907 for intkey in ('size', 'segment_size', 'num_segments',
908 'needed_shares', 'total_shares'):
910 d[intkey] = int(d[intkey])
914 def unpack_extension_readable(data):
915 unpacked = unpack_extension(data)
916 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
917 for k in sorted(unpacked.keys()):
919 unpacked[k] = base32.b2a(unpacked[k])