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]+)'
32 # "human-encoded" URIs are allowed to come with a leading
33 # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
34 # Note that nothing in the Tahoe code currently uses the human encoding.
35 OPTIONALHTTPLEAD=r'(?:https?://(?:[^:/]+)(?::%s)?/uri/)?' % NUMBER_IGNORE
40 return self.to_string().__hash__()
42 def __eq__(self, them):
43 if isinstance(them, _BaseURI):
44 return self.to_string() == them.to_string()
48 def __ne__(self, them):
49 if isinstance(them, _BaseURI):
50 return self.to_string() != them.to_string()
54 def to_human_encoding(self):
55 return 'http://127.0.0.1:3456/uri/'+self.to_string()
57 def get_storage_index(self):
58 return self.storage_index
61 class CHKFileURI(_BaseURI):
62 implements(IURI, IImmutableFileURI)
64 BASE_STRING='URI:CHK:'
65 STRING_RE=re.compile('^URI:CHK:'+BASE32STR_128bits+':'+
66 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
68 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK'+SEP+
69 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
70 SEP+NUMBER+SEP+NUMBER+'$')
72 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
75 self.uri_extension_hash = uri_extension_hash
76 self.needed_shares = needed_shares
77 self.total_shares = total_shares
79 self.storage_index = hashutil.storage_index_hash(self.key)
80 if not len(self.storage_index) == 16: # sha256 hash truncated to 128
81 raise BadURIError("storage index must be 16 bytes long")
84 def init_from_human_encoding(cls, uri):
85 mo = cls.HUMAN_RE.search(uri)
87 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
88 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
89 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
92 def init_from_string(cls, uri):
93 mo = cls.STRING_RE.search(uri)
95 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
96 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
97 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
100 assert isinstance(self.needed_shares, int)
101 assert isinstance(self.total_shares, int)
102 assert isinstance(self.size, (int,long))
104 return ('URI:CHK:%s:%s:%d:%d:%d' %
105 (base32.b2a(self.key),
106 base32.b2a(self.uri_extension_hash),
111 def is_readonly(self):
114 def is_mutable(self):
117 def get_readonly(self):
123 def get_verify_cap(self):
124 return CHKFileVerifierURI(storage_index=self.storage_index,
125 uri_extension_hash=self.uri_extension_hash,
126 needed_shares=self.needed_shares,
127 total_shares=self.total_shares,
130 class CHKFileVerifierURI(_BaseURI):
131 implements(IVerifierURI)
133 BASE_STRING='URI:CHK-Verifier:'
134 STRING_RE=re.compile('^URI:CHK-Verifier:'+BASE32STR_128bits+':'+
135 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
136 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK-Verifier'+SEP+
137 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
138 SEP+NUMBER+SEP+NUMBER)
140 def __init__(self, storage_index, uri_extension_hash,
141 needed_shares, total_shares, size):
142 assert len(storage_index) == 16
143 self.storage_index = storage_index
144 self.uri_extension_hash = uri_extension_hash
145 self.needed_shares = needed_shares
146 self.total_shares = total_shares
150 def init_from_human_encoding(cls, uri):
151 mo = cls.HUMAN_RE.search(uri)
153 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
154 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
155 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
158 def init_from_string(cls, uri):
159 mo = cls.STRING_RE.search(uri)
161 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
162 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)),
163 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
166 assert isinstance(self.needed_shares, int)
167 assert isinstance(self.total_shares, int)
168 assert isinstance(self.size, (int,long))
170 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
171 (si_b2a(self.storage_index),
172 base32.b2a(self.uri_extension_hash),
177 def is_readonly(self):
180 def is_mutable(self):
183 def get_readonly(self):
186 def get_verify_cap(self):
190 class LiteralFileURI(_BaseURI):
191 implements(IURI, IImmutableFileURI)
193 BASE_STRING='URI:LIT:'
194 STRING_RE=re.compile('^URI:LIT:'+base32.BASE32STR_anybytes+'$')
195 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'LIT'+SEP+base32.BASE32STR_anybytes+'$')
197 def __init__(self, data=None):
199 assert isinstance(data, str)
203 def init_from_human_encoding(cls, uri):
204 mo = cls.HUMAN_RE.search(uri)
206 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
207 return cls(base32.a2b(mo.group(1)))
210 def init_from_string(cls, uri):
211 mo = cls.STRING_RE.search(uri)
213 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
214 return cls(base32.a2b(mo.group(1)))
217 return 'URI:LIT:%s' % base32.b2a(self.data)
219 def is_readonly(self):
222 def is_mutable(self):
225 def get_readonly(self):
228 def get_storage_index(self):
231 def get_verify_cap(self):
232 # LIT files need no verification, all the data is present in the URI
236 return len(self.data)
239 class WriteableSSKFileURI(_BaseURI):
240 implements(IURI, IMutableFileURI)
242 BASE_STRING='URI:SSK:'
243 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+
244 BASE32STR_256bits+'$')
245 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK'+SEP+
246 BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
248 def __init__(self, writekey, fingerprint):
249 self.writekey = writekey
250 self.readkey = hashutil.ssk_readkey_hash(writekey)
251 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
252 assert len(self.storage_index) == 16
253 self.fingerprint = fingerprint
256 def init_from_human_encoding(cls, uri):
257 mo = cls.HUMAN_RE.search(uri)
259 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
260 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
263 def init_from_string(cls, uri):
264 mo = cls.STRING_RE.search(uri)
266 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
267 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
270 assert isinstance(self.writekey, str)
271 assert isinstance(self.fingerprint, str)
272 return 'URI:SSK:%s:%s' % (base32.b2a(self.writekey),
273 base32.b2a(self.fingerprint))
276 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
279 return base32.b2a(self.writekey[:5])
282 return base32.b2a(self.storage_index)[:5]
284 def is_readonly(self):
287 def is_mutable(self):
290 def get_readonly(self):
291 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
293 def get_verify_cap(self):
294 return SSKVerifierURI(self.storage_index, self.fingerprint)
296 class ReadonlySSKFileURI(_BaseURI):
297 implements(IURI, IMutableFileURI)
299 BASE_STRING='URI:SSK-RO:'
300 STRING_RE=re.compile('^URI:SSK-RO:'+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
301 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
303 def __init__(self, readkey, fingerprint):
304 self.readkey = readkey
305 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
306 assert len(self.storage_index) == 16
307 self.fingerprint = fingerprint
310 def init_from_human_encoding(cls, uri):
311 mo = cls.HUMAN_RE.search(uri)
313 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
314 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
317 def init_from_string(cls, uri):
318 mo = cls.STRING_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 assert isinstance(self.readkey, str)
325 assert isinstance(self.fingerprint, str)
326 return 'URI:SSK-RO:%s:%s' % (base32.b2a(self.readkey),
327 base32.b2a(self.fingerprint))
330 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
333 return base32.b2a(self.readkey[:5])
336 return base32.b2a(self.storage_index)[:5]
338 def is_readonly(self):
341 def is_mutable(self):
344 def get_readonly(self):
347 def get_verify_cap(self):
348 return SSKVerifierURI(self.storage_index, self.fingerprint)
350 class SSKVerifierURI(_BaseURI):
351 implements(IVerifierURI)
353 BASE_STRING='URI:SSK-Verifier:'
354 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
355 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
357 def __init__(self, storage_index, fingerprint):
358 assert len(storage_index) == 16
359 self.storage_index = storage_index
360 self.fingerprint = fingerprint
363 def init_from_human_encoding(cls, uri):
364 mo = cls.HUMAN_RE.search(uri)
366 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
367 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
370 def init_from_string(cls, uri):
371 mo = cls.STRING_RE.search(uri)
373 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
374 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
377 assert isinstance(self.storage_index, str)
378 assert isinstance(self.fingerprint, str)
379 return 'URI:SSK-Verifier:%s:%s' % (si_b2a(self.storage_index),
380 base32.b2a(self.fingerprint))
382 def is_readonly(self):
385 def is_mutable(self):
388 def get_readonly(self):
391 def get_verify_cap(self):
394 class WriteableMDMFFileURI(_BaseURI):
395 implements(IURI, IMutableFileURI)
397 BASE_STRING='URI:MDMF:'
398 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
399 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
401 def __init__(self, writekey, fingerprint):
402 self.writekey = writekey
403 self.readkey = hashutil.ssk_readkey_hash(writekey)
404 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
405 assert len(self.storage_index) == 16
406 self.fingerprint = fingerprint
409 def init_from_human_encoding(cls, uri):
410 mo = cls.HUMAN_RE.search(uri)
412 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
413 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
416 def init_from_string(cls, uri):
417 mo = cls.STRING_RE.search(uri)
419 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
420 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
423 assert isinstance(self.writekey, str)
424 assert isinstance(self.fingerprint, str)
425 ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
426 base32.b2a(self.fingerprint))
430 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
433 return base32.b2a(self.writekey[:5])
436 return base32.b2a(self.storage_index)[:5]
438 def is_readonly(self):
441 def is_mutable(self):
444 def get_readonly(self):
445 return ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
447 def get_verify_cap(self):
448 return MDMFVerifierURI(self.storage_index, self.fingerprint)
450 class ReadonlyMDMFFileURI(_BaseURI):
451 implements(IURI, IMutableFileURI)
453 BASE_STRING='URI:MDMF-RO:'
454 STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
455 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
457 def __init__(self, readkey, fingerprint):
458 self.readkey = readkey
459 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
460 assert len(self.storage_index) == 16
461 self.fingerprint = fingerprint
464 def init_from_human_encoding(cls, uri):
465 mo = cls.HUMAN_RE.search(uri)
467 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
468 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
471 def init_from_string(cls, uri):
472 mo = cls.STRING_RE.search(uri)
474 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
476 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
479 assert isinstance(self.readkey, str)
480 assert isinstance(self.fingerprint, str)
481 ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
482 base32.b2a(self.fingerprint))
486 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
489 return base32.b2a(self.readkey[:5])
492 return base32.b2a(self.storage_index)[:5]
494 def is_readonly(self):
497 def is_mutable(self):
500 def get_readonly(self):
503 def get_verify_cap(self):
504 return MDMFVerifierURI(self.storage_index, self.fingerprint)
506 class MDMFVerifierURI(_BaseURI):
507 implements(IVerifierURI)
509 BASE_STRING='URI:MDMF-Verifier:'
510 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
511 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
513 def __init__(self, storage_index, fingerprint):
514 assert len(storage_index) == 16
515 self.storage_index = storage_index
516 self.fingerprint = fingerprint
519 def init_from_human_encoding(cls, uri):
520 mo = cls.HUMAN_RE.search(uri)
522 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
523 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
526 def init_from_string(cls, uri):
527 mo = cls.STRING_RE.search(uri)
529 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
530 return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
533 assert isinstance(self.storage_index, str)
534 assert isinstance(self.fingerprint, str)
535 ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
536 base32.b2a(self.fingerprint))
539 def is_readonly(self):
542 def is_mutable(self):
545 def get_readonly(self):
548 def get_verify_cap(self):
551 class _DirectoryBaseURI(_BaseURI):
552 implements(IURI, IDirnodeURI)
553 def __init__(self, filenode_uri=None):
554 self._filenode_uri = filenode_uri
557 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
560 def init_from_string(cls, uri):
561 mo = cls.BASE_STRING_RE.search(uri)
563 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
564 bits = uri[mo.end():]
565 fn = cls.INNER_URI_CLASS.init_from_string(
566 cls.INNER_URI_CLASS.BASE_STRING+bits)
570 def init_from_human_encoding(cls, uri):
571 mo = cls.BASE_HUMAN_RE.search(uri)
573 raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
574 bits = uri[mo.end():]
575 while bits and bits[-1] == '/':
577 fn = cls.INNER_URI_CLASS.init_from_string(
578 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
582 fnuri = self._filenode_uri.to_string()
583 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
585 bits = fnuri[mo.end():]
586 return self.BASE_STRING+bits
589 return self._filenode_uri.to_string().split(':')[2][:5]
592 si = self._filenode_uri.get_storage_index()
595 return base32.b2a(si)[:5]
597 def is_mutable(self):
600 def get_filenode_cap(self):
601 return self._filenode_uri
603 def get_verify_cap(self):
604 return DirectoryURIVerifier(self._filenode_uri.get_verify_cap())
606 def get_storage_index(self):
607 return self._filenode_uri.get_storage_index()
609 class DirectoryURI(_DirectoryBaseURI):
610 implements(IDirectoryURI)
612 BASE_STRING='URI:DIR2:'
613 BASE_STRING_RE=re.compile('^'+BASE_STRING)
614 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
615 INNER_URI_CLASS=WriteableSSKFileURI
617 def __init__(self, filenode_uri=None):
619 assert not filenode_uri.is_readonly()
620 _DirectoryBaseURI.__init__(self, filenode_uri)
622 def is_readonly(self):
625 def get_readonly(self):
626 return ReadonlyDirectoryURI(self._filenode_uri.get_readonly())
629 class ReadonlyDirectoryURI(_DirectoryBaseURI):
630 implements(IReadonlyDirectoryURI)
632 BASE_STRING='URI:DIR2-RO:'
633 BASE_STRING_RE=re.compile('^'+BASE_STRING)
634 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
635 INNER_URI_CLASS=ReadonlySSKFileURI
637 def __init__(self, filenode_uri=None):
639 assert filenode_uri.is_readonly()
640 _DirectoryBaseURI.__init__(self, filenode_uri)
642 def is_readonly(self):
645 def get_readonly(self):
649 class _ImmutableDirectoryBaseURI(_DirectoryBaseURI):
650 def __init__(self, filenode_uri=None):
652 assert isinstance(filenode_uri, self.INNER_URI_CLASS), filenode_uri
653 assert not filenode_uri.is_mutable()
654 _DirectoryBaseURI.__init__(self, filenode_uri)
656 def is_readonly(self):
659 def is_mutable(self):
662 def get_readonly(self):
666 class ImmutableDirectoryURI(_ImmutableDirectoryBaseURI):
667 BASE_STRING='URI:DIR2-CHK:'
668 BASE_STRING_RE=re.compile('^'+BASE_STRING)
669 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK'+SEP)
670 INNER_URI_CLASS=CHKFileURI
672 def get_verify_cap(self):
673 vcap = self._filenode_uri.get_verify_cap()
674 return ImmutableDirectoryURIVerifier(vcap)
677 class LiteralDirectoryURI(_ImmutableDirectoryBaseURI):
678 BASE_STRING='URI:DIR2-LIT:'
679 BASE_STRING_RE=re.compile('^'+BASE_STRING)
680 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-LIT'+SEP)
681 INNER_URI_CLASS=LiteralFileURI
683 def get_verify_cap(self):
684 # LIT caps have no verifier, since they aren't distributed
688 class MDMFDirectoryURI(_DirectoryBaseURI):
689 implements(IDirectoryURI)
691 BASE_STRING='URI:DIR2-MDMF:'
692 BASE_STRING_RE=re.compile('^'+BASE_STRING)
693 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF'+SEP)
694 INNER_URI_CLASS=WriteableMDMFFileURI
696 def __init__(self, filenode_uri=None):
698 assert not filenode_uri.is_readonly()
699 _DirectoryBaseURI.__init__(self, filenode_uri)
701 def is_readonly(self):
704 def get_readonly(self):
705 return ReadonlyMDMFDirectoryURI(self._filenode_uri.get_readonly())
707 def get_verify_cap(self):
708 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
711 class ReadonlyMDMFDirectoryURI(_DirectoryBaseURI):
712 implements(IReadonlyDirectoryURI)
714 BASE_STRING='URI:DIR2-MDMF-RO:'
715 BASE_STRING_RE=re.compile('^'+BASE_STRING)
716 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-RO'+SEP)
717 INNER_URI_CLASS=ReadonlyMDMFFileURI
719 def __init__(self, filenode_uri=None):
721 assert filenode_uri.is_readonly()
722 _DirectoryBaseURI.__init__(self, filenode_uri)
724 def is_readonly(self):
727 def get_readonly(self):
730 def get_verify_cap(self):
731 return MDMFDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
733 def wrap_dirnode_cap(filecap):
734 if isinstance(filecap, WriteableSSKFileURI):
735 return DirectoryURI(filecap)
736 if isinstance(filecap, ReadonlySSKFileURI):
737 return ReadonlyDirectoryURI(filecap)
738 if isinstance(filecap, CHKFileURI):
739 return ImmutableDirectoryURI(filecap)
740 if isinstance(filecap, LiteralFileURI):
741 return LiteralDirectoryURI(filecap)
742 if isinstance(filecap, WriteableMDMFFileURI):
743 return MDMFDirectoryURI(filecap)
744 if isinstance(filecap, ReadonlyMDMFFileURI):
745 return ReadonlyMDMFDirectoryURI(filecap)
746 assert False, "cannot interpret as a directory cap: %s" % filecap.__class__
748 class MDMFDirectoryURIVerifier(_DirectoryBaseURI):
749 implements(IVerifierURI)
751 BASE_STRING='URI:DIR2-MDMF-Verifier:'
752 BASE_STRING_RE=re.compile('^'+BASE_STRING)
753 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-MDMF-Verifier'+SEP)
754 INNER_URI_CLASS=MDMFVerifierURI
756 def __init__(self, filenode_uri=None):
758 assert IVerifierURI.providedBy(filenode_uri)
759 self._filenode_uri = filenode_uri
761 def get_filenode_cap(self):
762 return self._filenode_uri
764 def is_mutable(self):
767 def is_readonly(self):
770 def get_readonly(self):
774 class DirectoryURIVerifier(_DirectoryBaseURI):
775 implements(IVerifierURI)
777 BASE_STRING='URI:DIR2-Verifier:'
778 BASE_STRING_RE=re.compile('^'+BASE_STRING)
779 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
780 INNER_URI_CLASS=SSKVerifierURI
782 def __init__(self, filenode_uri=None):
784 assert IVerifierURI.providedBy(filenode_uri)
785 self._filenode_uri = filenode_uri
787 def get_filenode_cap(self):
788 return self._filenode_uri
790 def is_mutable(self):
793 def is_readonly(self):
796 def get_readonly(self):
800 class ImmutableDirectoryURIVerifier(DirectoryURIVerifier):
801 implements(IVerifierURI)
802 BASE_STRING='URI:DIR2-CHK-Verifier:'
803 BASE_STRING_RE=re.compile('^'+BASE_STRING)
804 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-CHK-VERIFIER'+SEP)
805 INNER_URI_CLASS=CHKFileVerifierURI
809 def __init__(self, uri, error=None):
816 def get_readonly(self):
822 def get_verify_cap(self):
826 ALLEGED_READONLY_PREFIX = 'ro.'
827 ALLEGED_IMMUTABLE_PREFIX = 'imm.'
829 def from_string(u, deep_immutable=False, name=u"<unknown name>"):
830 if not isinstance(u, str):
831 raise TypeError("unknown URI type: %s.." % str(u)[:100])
833 # We allow and check ALLEGED_READONLY_PREFIX or ALLEGED_IMMUTABLE_PREFIX
834 # on all URIs, even though we would only strictly need to do so for caps of
835 # new formats (post Tahoe-LAFS 1.6). URIs that are not consistent with their
836 # prefix are treated as unknown. This should be revisited when we add the
837 # new cap formats. See ticket #833 comment:31.
839 can_be_mutable = can_be_writeable = not deep_immutable
840 if s.startswith(ALLEGED_IMMUTABLE_PREFIX):
841 can_be_mutable = can_be_writeable = False
842 s = s[len(ALLEGED_IMMUTABLE_PREFIX):]
843 elif s.startswith(ALLEGED_READONLY_PREFIX):
844 can_be_writeable = False
845 s = s[len(ALLEGED_READONLY_PREFIX):]
850 if s.startswith('URI:CHK:'):
851 return CHKFileURI.init_from_string(s)
852 elif s.startswith('URI:CHK-Verifier:'):
853 return CHKFileVerifierURI.init_from_string(s)
854 elif s.startswith('URI:LIT:'):
855 return LiteralFileURI.init_from_string(s)
856 elif s.startswith('URI:SSK:'):
858 return WriteableSSKFileURI.init_from_string(s)
859 kind = "URI:SSK file writecap"
860 elif s.startswith('URI:SSK-RO:'):
862 return ReadonlySSKFileURI.init_from_string(s)
863 kind = "URI:SSK-RO readcap to a mutable file"
864 elif s.startswith('URI:SSK-Verifier:'):
865 return SSKVerifierURI.init_from_string(s)
866 elif s.startswith('URI:MDMF:'):
868 return WriteableMDMFFileURI.init_from_string(s)
869 kind = "URI:MDMF file writecap"
870 elif s.startswith('URI:MDMF-RO:'):
872 return ReadonlyMDMFFileURI.init_from_string(s)
873 kind = "URI:MDMF-RO readcap to a mutable file"
874 elif s.startswith('URI:MDMF-Verifier:'):
875 return MDMFVerifierURI.init_from_string(s)
876 elif s.startswith('URI:DIR2:'):
878 return DirectoryURI.init_from_string(s)
879 kind = "URI:DIR2 directory writecap"
880 elif s.startswith('URI:DIR2-RO:'):
882 return ReadonlyDirectoryURI.init_from_string(s)
883 kind = "URI:DIR2-RO readcap to a mutable directory"
884 elif s.startswith('URI:DIR2-Verifier:'):
885 return DirectoryURIVerifier.init_from_string(s)
886 elif s.startswith('URI:DIR2-CHK:'):
887 return ImmutableDirectoryURI.init_from_string(s)
888 elif s.startswith('URI:DIR2-CHK-Verifier:'):
889 return ImmutableDirectoryURIVerifier.init_from_string(s)
890 elif s.startswith('URI:DIR2-LIT:'):
891 return LiteralDirectoryURI.init_from_string(s)
892 elif s.startswith('URI:DIR2-MDMF:'):
894 return MDMFDirectoryURI.init_from_string(s)
895 kind = "URI:DIR2-MDMF directory writecap"
896 elif s.startswith('URI:DIR2-MDMF-RO:'):
898 return ReadonlyMDMFDirectoryURI.init_from_string(s)
899 kind = "URI:DIR2-MDMF-RO readcap to a mutable directory"
900 elif s.startswith('URI:DIR2-MDMF-Verifier:'):
901 return MDMFDirectoryURIVerifier.init_from_string(s)
902 elif s.startswith('x-tahoe-future-test-writeable:') and not can_be_writeable:
903 # For testing how future writeable caps would behave in read-only contexts.
904 kind = "x-tahoe-future-test-writeable: testing cap"
905 elif s.startswith('x-tahoe-future-test-mutable:') and not can_be_mutable:
906 # For testing how future mutable readcaps would behave in immutable contexts.
907 kind = "x-tahoe-future-test-mutable: testing cap"
911 # We fell through because a constraint was not met.
912 # Prefer to report the most specific constraint.
913 if not can_be_mutable:
914 error = MustBeDeepImmutableError(kind + " used in an immutable context", name)
916 error = MustBeReadonlyError(kind + " used in a read-only context", name)
918 except BadURIError, e:
921 return UnknownURI(u, error=error)
925 from_string(s, deep_immutable=False)
927 except (TypeError, AssertionError):
930 def is_literal_file_uri(s):
931 if not isinstance(s, str):
933 return (s.startswith('URI:LIT:') or
934 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:LIT:') or
935 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:LIT:'))
937 def has_uri_prefix(s):
938 if not isinstance(s, str):
940 return (s.startswith("URI:") or
941 s.startswith(ALLEGED_READONLY_PREFIX + 'URI:') or
942 s.startswith(ALLEGED_IMMUTABLE_PREFIX + 'URI:'))
945 # These take the same keyword arguments as from_string above.
947 def from_string_dirnode(s, **kwargs):
948 u = from_string(s, **kwargs)
949 assert IDirnodeURI.providedBy(u)
952 registerAdapter(from_string_dirnode, str, IDirnodeURI)
954 def from_string_filenode(s, **kwargs):
955 u = from_string(s, **kwargs)
956 assert IFileURI.providedBy(u)
959 registerAdapter(from_string_filenode, str, IFileURI)
961 def from_string_mutable_filenode(s, **kwargs):
962 u = from_string(s, **kwargs)
963 assert IMutableFileURI.providedBy(u)
965 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
967 def from_string_verifier(s, **kwargs):
968 u = from_string(s, **kwargs)
969 assert IVerifierURI.providedBy(u)
971 registerAdapter(from_string_verifier, str, IVerifierURI)
974 def pack_extension(data):
976 for k in sorted(data.keys()):
978 if isinstance(value, (int, long)):
980 assert isinstance(value, str), k
981 assert re.match(r'^[a-zA-Z_\-]+$', k)
982 pieces.append(k + ':' + hashutil.netstring(value))
983 uri_extension = ''.join(pieces)
986 def unpack_extension(data):
989 colon = data.index(':')
991 data = data[colon+1:]
993 colon = data.index(':')
994 number = data[:colon]
996 data = data[colon+1:]
998 value = data[:length]
999 assert data[length] == ','
1000 data = data[length+1:]
1004 # convert certain things to numbers
1005 for intkey in ('size', 'segment_size', 'num_segments',
1006 'needed_shares', 'total_shares'):
1008 d[intkey] = int(d[intkey])
1012 def unpack_extension_readable(data):
1013 unpacked = unpack_extension(data)
1014 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
1015 for k in sorted(unpacked.keys()):
1017 unpacked[k] = base32.b2a(unpacked[k])