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.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI, \
8 IVerifierURI, IMutableFileURI, IDirectoryURI, IReadonlyDirectoryURI, \
9 MustBeDeepImmutableError, MustBeReadonlyError, CapConstraintError
11 class BadURIError(CapConstraintError):
14 # The URI shall be an ASCII representation of a reference to the file/directory.
15 # It shall contain enough information to retrieve and validate the contents.
16 # It shall be expressed in a limited character set (currently base32 plus ':' and
17 # capital letters, but future URIs might use a larger charset).
20 # - rename all of the *URI classes/interfaces to *Cap
21 # - make variable and method names consistently use _uri for an URI string,
22 # and _cap for a Cap object (decoded URI)
23 # - remove the human_encoding methods?
25 BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits)
26 BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
30 NUMBER_IGNORE='(?:[0-9]+)'
31 OPTIONAL_EXTENSION_FIELD = '(' + SEP + '[0-9' + SEP + ']+|)'
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 def get_extension_params(self):
300 def set_extension_params(self, params):
303 class ReadonlySSKFileURI(_BaseURI):
304 implements(IURI, IMutableFileURI)
306 BASE_STRING='URI:SSK-RO:'
307 STRING_RE=re.compile('^URI:SSK-RO:'+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
308 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
310 def __init__(self, readkey, fingerprint):
311 self.readkey = readkey
312 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
313 assert len(self.storage_index) == 16
314 self.fingerprint = fingerprint
317 def init_from_human_encoding(cls, uri):
318 mo = cls.HUMAN_RE.search(uri)
320 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
321 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
324 def init_from_string(cls, uri):
325 mo = cls.STRING_RE.search(uri)
327 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
328 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
331 assert isinstance(self.readkey, str)
332 assert isinstance(self.fingerprint, str)
333 return 'URI:SSK-RO:%s:%s' % (base32.b2a(self.readkey),
334 base32.b2a(self.fingerprint))
337 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
340 return base32.b2a(self.readkey[:5])
343 return base32.b2a(self.storage_index)[:5]
345 def is_readonly(self):
348 def is_mutable(self):
351 def get_readonly(self):
354 def get_verify_cap(self):
355 return SSKVerifierURI(self.storage_index, self.fingerprint)
357 def get_extension_params(self):
360 def set_extension_params(self, params):
363 class SSKVerifierURI(_BaseURI):
364 implements(IVerifierURI)
366 BASE_STRING='URI:SSK-Verifier:'
367 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
368 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
370 def __init__(self, storage_index, fingerprint):
371 assert len(storage_index) == 16
372 self.storage_index = storage_index
373 self.fingerprint = fingerprint
376 def init_from_human_encoding(cls, uri):
377 mo = cls.HUMAN_RE.search(uri)
379 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
380 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
383 def init_from_string(cls, uri):
384 mo = cls.STRING_RE.search(uri)
386 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
387 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
390 assert isinstance(self.storage_index, str)
391 assert isinstance(self.fingerprint, str)
392 return 'URI:SSK-Verifier:%s:%s' % (si_b2a(self.storage_index),
393 base32.b2a(self.fingerprint))
395 def is_readonly(self):
398 def is_mutable(self):
401 def get_readonly(self):
404 def get_verify_cap(self):
407 def get_extension_params(self):
410 def set_extension_params(self, params):
413 class WritableMDMFFileURI(_BaseURI):
414 implements(IURI, IMutableFileURI)
416 BASE_STRING='URI:MDMF:'
417 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
418 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
420 def __init__(self, writekey, fingerprint, params=[]):
421 self.writekey = writekey
422 self.readkey = hashutil.ssk_readkey_hash(writekey)
423 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
424 assert len(self.storage_index) == 16
425 self.fingerprint = fingerprint
426 self.extension = params
429 def init_from_human_encoding(cls, uri):
430 mo = cls.HUMAN_RE.search(uri)
432 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
433 params = filter(lambda x: x != '', re.split(SEP, mo.group(3)))
434 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
437 def init_from_string(cls, uri):
438 mo = cls.STRING_RE.search(uri)
440 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
442 params = filter(lambda x: x != '', params.split(":"))
443 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
446 assert isinstance(self.writekey, str)
447 assert isinstance(self.fingerprint, str)
448 ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
449 base32.b2a(self.fingerprint))
452 ret += ":".join(self.extension)
457 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
460 return base32.b2a(self.writekey[:5])
463 return base32.b2a(self.storage_index)[:5]
465 def is_readonly(self):
468 def is_mutable(self):
471 def get_readonly(self):
472 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint, self.extension)
474 def get_verify_cap(self):
475 return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension)
477 def get_extension_params(self):
478 return self.extension
480 def set_extension_params(self, params):
481 params = map(str, params)
482 self.extension = params
484 class ReadonlyMDMFFileURI(_BaseURI):
485 implements(IURI, IMutableFileURI)
487 BASE_STRING='URI:MDMF-RO:'
488 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
489 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
491 def __init__(self, readkey, fingerprint, params=[]):
492 self.readkey = readkey
493 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
494 assert len(self.storage_index) == 16
495 self.fingerprint = fingerprint
496 self.extension = params
499 def init_from_human_encoding(cls, uri):
500 mo = cls.HUMAN_RE.search(uri)
502 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
504 params = filter(lambda x: x!= '', re.split(SEP, params))
505 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
508 def init_from_string(cls, uri):
509 mo = cls.STRING_RE.search(uri)
511 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
514 params = filter(lambda x: x != '', params.split(":"))
515 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
518 assert isinstance(self.readkey, str)
519 assert isinstance(self.fingerprint, str)
520 ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
521 base32.b2a(self.fingerprint))
524 ret += ":".join(self.extension)
529 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
532 return base32.b2a(self.readkey[:5])
535 return base32.b2a(self.storage_index)[:5]
537 def is_readonly(self):
540 def is_mutable(self):
543 def get_readonly(self):
546 def get_verify_cap(self):
547 return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension)
549 def get_extension_params(self):
550 return self.extension
552 def set_extension_params(self, params):
553 params = map(str, params)
554 self.extension = params
556 class MDMFVerifierURI(_BaseURI):
557 implements(IVerifierURI)
559 BASE_STRING='URI:MDMF-Verifier:'
560 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
561 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
563 def __init__(self, storage_index, fingerprint, params=[]):
564 assert len(storage_index) == 16
565 self.storage_index = storage_index
566 self.fingerprint = fingerprint
567 self.extension = params
570 def init_from_human_encoding(cls, uri):
571 mo = cls.HUMAN_RE.search(uri)
573 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
575 params = filter(lambda x: x != '', re.split(SEP, params))
576 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
579 def init_from_string(cls, uri):
580 mo = cls.STRING_RE.search(uri)
582 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
584 params = filter(lambda x: x != '', params.split(":"))
585 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
588 assert isinstance(self.storage_index, str)
589 assert isinstance(self.fingerprint, str)
590 ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
591 base32.b2a(self.fingerprint))
594 ret += ":".join(self.extension)
598 def is_readonly(self):
601 def is_mutable(self):
604 def get_readonly(self):
607 def get_verify_cap(self):
610 def get_extension_params(self):
611 return self.extension
613 class _DirectoryBaseURI(_BaseURI):
614 implements(IURI, IDirnodeURI)
615 def __init__(self, filenode_uri=None):
616 self._filenode_uri = filenode_uri
619 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
622 def init_from_string(cls, uri):
623 mo = cls.BASE_STRING_RE.search(uri)
625 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
626 bits = uri[mo.end():]
627 fn = cls.INNER_URI_CLASS.init_from_string(
628 cls.INNER_URI_CLASS.BASE_STRING+bits)
632 def init_from_human_encoding(cls, uri):
633 mo = cls.BASE_HUMAN_RE.search(uri)
635 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
636 bits = uri[mo.end():]
637 while bits and bits[-1] == '/':
639 fn = cls.INNER_URI_CLASS.init_from_string(
640 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
644 fnuri = self._filenode_uri.to_string()
645 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
647 bits = fnuri[mo.end():]
648 return self.BASE_STRING+bits
651 return self._filenode_uri.to_string().split(':')[2][:5]
654 si = self._filenode_uri.get_storage_index()
657 return base32.b2a(si)[:5]
659 def is_mutable(self):
662 def get_filenode_cap(self):
663 return self._filenode_uri
665 def get_verify_cap(self):
666 return DirectoryURIVerifier(self._filenode_uri.get_verify_cap())
668 def get_storage_index(self):
669 return self._filenode_uri.get_storage_index()
671 class DirectoryURI(_DirectoryBaseURI):
672 implements(IDirectoryURI)
674 BASE_STRING='URI:DIR2:'
675 BASE_STRING_RE=re.compile('^'+BASE_STRING)
676 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
677 INNER_URI_CLASS=WriteableSSKFileURI
679 def __init__(self, filenode_uri=None):
681 assert not filenode_uri.is_readonly()
682 _DirectoryBaseURI.__init__(self, filenode_uri)
684 def is_readonly(self):
687 def get_readonly(self):
688 return ReadonlyDirectoryURI(self._filenode_uri.get_readonly())
691 class ReadonlyDirectoryURI(_DirectoryBaseURI):
692 implements(IReadonlyDirectoryURI)
694 BASE_STRING='URI:DIR2-RO:'
695 BASE_STRING_RE=re.compile('^'+BASE_STRING)
696 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
697 INNER_URI_CLASS=ReadonlySSKFileURI
699 def __init__(self, filenode_uri=None):
701 assert filenode_uri.is_readonly()
702 _DirectoryBaseURI.__init__(self, filenode_uri)
704 def is_readonly(self):
707 def get_readonly(self):
711 class _ImmutableDirectoryBaseURI(_DirectoryBaseURI):
712 def __init__(self, filenode_uri=None):
714 assert isinstance(filenode_uri, self.INNER_URI_CLASS), filenode_uri
715 assert not filenode_uri.is_mutable()
716 _DirectoryBaseURI.__init__(self, filenode_uri)
718 def is_readonly(self):
721 def is_mutable(self):
724 def get_readonly(self):
728 class ImmutableDirectoryURI(_ImmutableDirectoryBaseURI):
729 BASE_STRING='URI:DIR2-CHK:'
730 BASE_STRING_RE=re.compile('^'+BASE_STRING)
731 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK'+SEP)
732 INNER_URI_CLASS=CHKFileURI
734 def get_verify_cap(self):
735 vcap = self._filenode_uri.get_verify_cap()
736 return ImmutableDirectoryURIVerifier(vcap)
739 class LiteralDirectoryURI(_ImmutableDirectoryBaseURI):
740 BASE_STRING='URI:DIR2-LIT:'
741 BASE_STRING_RE=re.compile('^'+BASE_STRING)
742 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-LIT'+SEP)
743 INNER_URI_CLASS=LiteralFileURI
745 def get_verify_cap(self):
746 # LIT caps have no verifier, since they aren't distributed
750 class MDMFDirectoryURI(_DirectoryBaseURI):
751 implements(IDirectoryURI)
753 BASE_STRING='URI:DIR2-MDMF:'
754 BASE_STRING_RE=re.compile('^'+BASE_STRING)
755 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF'+SEP)
756 INNER_URI_CLASS=WritableMDMFFileURI
758 def __init__(self, filenode_uri=None):
760 assert not filenode_uri.is_readonly()
761 _DirectoryBaseURI.__init__(self, filenode_uri)
763 def is_readonly(self):
766 def get_readonly(self):
767 return ReadonlyMDMFDirectoryURI(self._filenode_uri.get_readonly())
769 def get_verify_cap(self):
770 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
773 class ReadonlyMDMFDirectoryURI(_DirectoryBaseURI):
774 implements(IReadonlyDirectoryURI)
776 BASE_STRING='URI:DIR2-MDMF-RO:'
777 BASE_STRING_RE=re.compile('^'+BASE_STRING)
778 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-RO'+SEP)
779 INNER_URI_CLASS=ReadonlyMDMFFileURI
781 def __init__(self, filenode_uri=None):
783 assert filenode_uri.is_readonly()
784 _DirectoryBaseURI.__init__(self, filenode_uri)
786 def is_readonly(self):
789 def get_readonly(self):
792 def get_verify_cap(self):
793 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
795 def wrap_dirnode_cap(filecap):
796 if isinstance(filecap, WriteableSSKFileURI):
797 return DirectoryURI(filecap)
798 if isinstance(filecap, ReadonlySSKFileURI):
799 return ReadonlyDirectoryURI(filecap)
800 if isinstance(filecap, CHKFileURI):
801 return ImmutableDirectoryURI(filecap)
802 if isinstance(filecap, LiteralFileURI):
803 return LiteralDirectoryURI(filecap)
804 if isinstance(filecap, WritableMDMFFileURI):
805 return MDMFDirectoryURI(filecap)
806 if isinstance(filecap, ReadonlyMDMFFileURI):
807 return ReadonlyMDMFDirectoryURI(filecap)
808 assert False, "cannot interpret as a directory cap: %s" % filecap.__class__
810 class MDMFDirectoryURIVerifier(_DirectoryBaseURI):
811 implements(IVerifierURI)
813 BASE_STRING='URI:DIR2-MDMF-Verifier:'
814 BASE_STRING_RE=re.compile('^'+BASE_STRING)
815 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-Verifier'+SEP)
816 INNER_URI_CLASS=MDMFVerifierURI
818 def __init__(self, filenode_uri=None):
820 assert IVerifierURI.providedBy(filenode_uri)
821 self._filenode_uri = filenode_uri
823 def get_filenode_cap(self):
824 return self._filenode_uri
826 def is_mutable(self):
829 class DirectoryURIVerifier(_DirectoryBaseURI):
830 implements(IVerifierURI)
832 BASE_STRING='URI:DIR2-Verifier:'
833 BASE_STRING_RE=re.compile('^'+BASE_STRING)
834 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
835 INNER_URI_CLASS=SSKVerifierURI
837 def __init__(self, filenode_uri=None):
839 assert IVerifierURI.providedBy(filenode_uri)
840 self._filenode_uri = filenode_uri
842 def get_filenode_cap(self):
843 return self._filenode_uri
845 def is_mutable(self):
849 class ImmutableDirectoryURIVerifier(DirectoryURIVerifier):
850 implements(IVerifierURI)
851 BASE_STRING='URI:DIR2-CHK-Verifier:'
852 BASE_STRING_RE=re.compile('^'+BASE_STRING)
853 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK-VERIFIER'+SEP)
854 INNER_URI_CLASS=CHKFileVerifierURI
858 def __init__(self, uri, error=None):
865 def get_readonly(self):
871 def get_verify_cap(self):
875 ALLEGED_READONLY_PREFIX = 'ro.'
876 ALLEGED_IMMUTABLE_PREFIX = 'imm.'
878 def from_string(u, deep_immutable=False, name=u"<unknown name>"):
879 if not isinstance(u, str):
880 raise TypeError("unknown URI type: %s.." % str(u)[:100])
882 # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
883 # on all URIs, even though we would only strictly need to do so for caps of
884 # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
885 # prefix are treated as unknown. This should be revisited when we add the
886 # new cap formats. See <http://allmydata.org/trac/tahoe/ticket/833#comment:31>.
888 can_be_mutable = can_be_writeable = not deep_immutable
889 if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
890 can_be_mutable = can_be_writeable = False
891 s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
892 elif s.startswith(ALLEGED_READONLY_PREFIX):
893 can_be_writeable = False
894 s = s[len(ALLEGED_READONLY_PREFIX):]
899 if s.startswith('URI:CHK:'):
900 return CHKFileURI.init_from_string(s)
901 elif s.startswith('URI:CHK-Verifier:'):
902 return CHKFileVerifierURI.init_from_string(s)
903 elif s.startswith('URI:LIT:'):
904 return LiteralFileURI.init_from_string(s)
905 elif s.startswith('URI:SSK:'):
907 return WriteableSSKFileURI.init_from_string(s)
908 kind = "URI:SSK file writecap"
909 elif s.startswith('URI:SSK-RO:'):
911 return ReadonlySSKFileURI.init_from_string(s)
912 kind = "URI:SSK-RO readcap to a mutable file"
913 elif s.startswith('URI:SSK-Verifier:'):
914 return SSKVerifierURI.init_from_string(s)
915 elif s.startswith('URI:MDMF:'):
916 return WritableMDMFFileURI.init_from_string(s)
917 elif s.startswith('URI:MDMF-RO:'):
918 return ReadonlyMDMFFileURI.init_from_string(s)
919 elif s.startswith('URI:MDMF-Verifier:'):
920 return MDMFVerifierURI.init_from_string(s)
921 elif s.startswith('URI:DIR2:'):
923 return DirectoryURI.init_from_string(s)
924 kind = "URI:DIR2 directory writecap"
925 elif s.startswith('URI:DIR2-RO:'):
927 return ReadonlyDirectoryURI.init_from_string(s)
928 kind = "URI:DIR2-RO readcap to a mutable directory"
929 elif s.startswith('URI:DIR2-Verifier:'):
930 return DirectoryURIVerifier.init_from_string(s)
931 elif s.startswith('URI:DIR2-CHK:'):
932 return ImmutableDirectoryURI.init_from_string(s)
933 elif s.startswith('URI:DIR2-LIT:'):
934 return LiteralDirectoryURI.init_from_string(s)
935 elif s.startswith('URI:DIR2-MDMF:'):
937 return MDMFDirectoryURI.init_from_string(s)
938 kind = "URI:DIR2-MDMF directory writecap"
939 elif s.startswith('URI:DIR2-MDMF-RO:'):
941 return ReadonlyMDMFDirectoryURI.init_from_string(s)
942 kind = "URI:DIR2-MDMF-RO readcap to a mutable directory"
943 elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable:
944 # For testing how future writeable caps would behave in read-only contexts.
945 kind = "x-tahoe-future-test-writeable: testing cap"
946 elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable:
947 # For testing how future mutable readcaps would behave in immutable contexts.
948 kind = "x-tahoe-future-test-mutable: testing cap"
952 # We fell through because a constraint was not met.
953 # Prefer to report the most specific constraint.
954 if not can_be_mutable:
955 error = MustBeDeepImmutableError(kind + " used in an immutable context", name)
957 error = MustBeReadonlyError(kind + " used in a read-only context", name)
959 except BadURIError, e:
962 return UnknownURI(u, error=error)
966 from_string(s, deep_immutable=False)
968 except (TypeError, AssertionError):
971 def is_literal_file_uri(s):
972 if not isinstance(s, str):
974 return (s.startswith('URI:LIT:') or
975 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
976 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
978 def has_uri_prefix(s):
979 if not isinstance(s, str):
981 return (s.startswith("URI:") or
982 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:') or
983 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:'))
986 # These take the same keyword arguments as from_string above.
988 def from_string_dirnode(s, **kwargs):
989 u = from_string(s, **kwargs)
990 assert IDirnodeURI.providedBy(u)
993 registerAdapter(from_string_dirnode, str, IDirnodeURI)
995 def from_string_filenode(s, **kwargs):
996 u = from_string(s, **kwargs)
997 assert IFileURI.providedBy(u)
1000 registerAdapter(from_string_filenode, str, IFileURI)
1002 def from_string_mutable_filenode(s, **kwargs):
1003 u = from_string(s, **kwargs)
1004 assert IMutableFileURI.providedBy(u)
1006 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
1008 def from_string_verifier(s, **kwargs):
1009 u = from_string(s, **kwargs)
1010 assert IVerifierURI.providedBy(u)
1012 registerAdapter(from_string_verifier, str, IVerifierURI)
1015 def pack_extension(data):
1017 for k in sorted(data.keys()):
1019 if isinstance(value, (int, long)):
1020 value = "%d" % value
1021 assert isinstance(value, str), k
1022 assert re.match(r'^[a-zA-Z_\-]+$', k)
1023 pieces.append(k + ':' + hashutil.netstring(value))
1024 uri_extension = ''.join(pieces)
1025 return uri_extension
1027 def unpack_extension(data):
1030 colon = data.index(':')
1032 data = data[colon+1:]
1034 colon = data.index(':')
1035 number = data[:colon]
1036 length = int(number)
1037 data = data[colon+1:]
1039 value = data[:length]
1040 assert data[length] == ','
1041 data = data[length+1:]
1045 # convert certain things to numbers
1046 for intkey in ('size', 'segment_size', 'num_segments',
1047 'needed_shares', 'total_shares'):
1049 d[intkey] = int(d[intkey])
1053 def unpack_extension_readable(data):
1054 unpacked = unpack_extension(data)
1055 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
1056 for k in sorted(unpacked.keys()):
1058 unpacked[k] = base32.b2a(unpacked[k])