3 from zope.interface import implements
4 from twisted.python.components import registerAdapter
5 from allmydata import storage
6 from allmydata.util import base62, idlib, hashutil
7 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IVerifierURI, \
8 IMutableFileURI, INewDirectoryURI, IReadonlyNewDirectoryURI
10 # the URI shall be an ascii representation of the file. It shall contain
11 # enough information to retrieve and validate the contents. It shall be
12 # expressed in a limited character set (namely [TODO]).
14 ZBASE32STR_128bits = '(%s{25}%s)' % (idlib.ZBASE32CHAR, idlib.ZBASE32CHAR_3bits)
15 ZBASE32STR_256bits = '(%s{51}%s)' % (idlib.ZBASE32CHAR, idlib.ZBASE32CHAR_1bits)
16 ZBASE62STR_128bits = '(%s{22})' % (base62.ZBASE62CHAR)
21 # URIs (soon to be renamed "caps") are always allowed to come with a leading
22 # 'http://127.0.0.1:8123/uri/' that will be ignored.
23 OPTIONALHTTPLEAD=r'(?:https?://(127.0.0.1|localhost):8123/uri/)?'
28 return hash((self.__class__, self.to_string()))
29 def __cmp__(self, them):
30 if cmp(type(self), type(them)):
31 return cmp(type(self), type(them))
32 if cmp(self.__class__, them.__class__):
33 return cmp(self.__class__, them.__class__)
34 return cmp(self.to_string(), them.to_string())
35 def to_human_encoding(self):
36 return 'http://127.0.0.1:8123/uri/'+self.to_string()
38 class CHKFileURI(_BaseURI):
39 implements(IURI, IFileURI)
41 STRING_RE=re.compile('^URI:CHK:'+ZBASE32STR_128bits+':'+
42 ZBASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
44 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK'+SEP+
45 ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+SEP+NUMBER+
46 SEP+NUMBER+SEP+NUMBER+'$')
48 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
51 self.uri_extension_hash = uri_extension_hash
52 self.needed_shares = needed_shares
53 self.total_shares = total_shares
55 self.storage_index = hashutil.storage_index_hash(self.key)
56 assert len(self.storage_index) == 16
57 self.storage_index = hashutil.storage_index_hash(key)
58 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
61 def init_from_human_encoding(cls, uri):
62 mo = cls.HUMAN_RE.search(uri)
64 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
65 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
68 def init_from_string(cls, uri):
69 mo = cls.STRING_RE.search(uri)
71 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
72 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
75 assert isinstance(self.needed_shares, int)
76 assert isinstance(self.total_shares, int)
77 assert isinstance(self.size, (int,long))
79 return ('URI:CHK:%s:%s:%d:%d:%d' %
81 idlib.b2a(self.uri_extension_hash),
86 def is_readonly(self):
90 def get_readonly(self):
96 def get_verifier(self):
97 return CHKFileVerifierURI(storage_index=self.storage_index,
98 uri_extension_hash=self.uri_extension_hash,
99 needed_shares=self.needed_shares,
100 total_shares=self.total_shares,
103 class CHKFileVerifierURI(_BaseURI):
104 implements(IVerifierURI)
106 STRING_RE=re.compile('^URI:CHK-Verifier:'+ZBASE62STR_128bits+':'+
107 ZBASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
108 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK-Verifier'+SEP+
109 ZBASE62STR_128bits+SEP+ZBASE32STR_256bits+SEP+NUMBER+
110 SEP+NUMBER+SEP+NUMBER)
112 def __init__(self, storage_index, uri_extension_hash,
113 needed_shares, total_shares, size):
114 assert len(storage_index) == 16
115 self.storage_index = storage_index
116 self.uri_extension_hash = uri_extension_hash
117 self.needed_shares = needed_shares
118 self.total_shares = total_shares
122 def init_from_human_encoding(cls, uri):
123 mo = cls.HUMAN_RE.search(uri)
125 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
126 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
129 def init_from_string(cls, uri):
130 mo = cls.STRING_RE.search(uri)
131 assert mo, (uri, cls, cls.STRING_RE)
132 return cls(storage.si_a2b(mo.group(1)), idlib.a2b(mo.group(2)),
133 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
136 assert isinstance(self.needed_shares, int)
137 assert isinstance(self.total_shares, int)
138 assert isinstance(self.size, (int,long))
140 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
141 (storage.si_b2a(self.storage_index),
142 idlib.b2a(self.uri_extension_hash),
148 class LiteralFileURI(_BaseURI):
149 implements(IURI, IFileURI)
151 STRING_RE=re.compile('^URI:LIT:'+idlib.ZBASE32STR_anybytes+'$')
152 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'LIT'+SEP+idlib.ZBASE32STR_anybytes+'$')
154 def __init__(self, data=None):
159 def init_from_human_encoding(cls, uri):
160 mo = cls.HUMAN_RE.search(uri)
162 return cls(idlib.a2b(mo.group(1)))
165 def init_from_string(cls, uri):
166 mo = cls.STRING_RE.search(uri)
168 return cls(idlib.a2b(mo.group(1)))
171 return 'URI:LIT:%s' % idlib.b2a(self.data)
173 def is_readonly(self):
175 def is_mutable(self):
177 def get_readonly(self):
180 def get_verifier(self):
181 # LIT files need no verification, all the data is present in the URI
185 return len(self.data)
187 class WriteableSSKFileURI(_BaseURI):
188 implements(IURI, IMutableFileURI)
190 BASE_STRING='URI:SSK:'
191 STRING_RE=re.compile('^'+BASE_STRING+ZBASE32STR_128bits+':'+
192 ZBASE32STR_256bits+'$')
193 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK'+SEP+
194 ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+'$')
196 def __init__(self, writekey, fingerprint):
197 self.writekey = writekey
198 self.readkey = hashutil.ssk_readkey_hash(writekey)
199 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
200 assert len(self.storage_index) == 16
201 self.fingerprint = fingerprint
204 def init_from_human_encoding(cls, uri):
205 mo = cls.HUMAN_RE.search(uri)
207 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
210 def init_from_string(cls, uri):
211 mo = cls.STRING_RE.search(uri)
212 assert mo, (uri, cls)
213 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
216 assert isinstance(self.writekey, str)
217 assert isinstance(self.fingerprint, str)
218 return 'URI:SSK:%s:%s' % (idlib.b2a(self.writekey),
219 idlib.b2a(self.fingerprint))
222 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
225 return idlib.b2a(self.writekey[:5])
227 def is_readonly(self):
229 def is_mutable(self):
231 def get_readonly(self):
232 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
233 def get_verifier(self):
234 return SSKVerifierURI(self.storage_index, self.fingerprint)
236 class ReadonlySSKFileURI(_BaseURI):
237 implements(IURI, IMutableFileURI)
239 BASE_STRING='URI:SSK-RO:'
240 STRING_RE=re.compile('^URI:SSK-RO:'+ZBASE32STR_128bits+':'+ZBASE32STR_256bits+'$')
241 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+'$')
243 def __init__(self, readkey, fingerprint):
244 self.readkey = readkey
245 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
246 assert len(self.storage_index) == 16
247 self.fingerprint = fingerprint
250 def init_from_human_encoding(cls, uri):
251 mo = cls.HUMAN_RE.search(uri)
253 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
256 def init_from_string(cls, uri):
257 mo = cls.STRING_RE.search(uri)
259 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
262 assert isinstance(self.readkey, str)
263 assert isinstance(self.fingerprint, str)
264 return 'URI:SSK-RO:%s:%s' % (idlib.b2a(self.readkey),
265 idlib.b2a(self.fingerprint))
268 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
271 return idlib.b2a(self.readkey[:5])
273 def is_readonly(self):
275 def is_mutable(self):
277 def get_readonly(self):
279 def get_verifier(self):
280 return SSKVerifierURI(self.storage_index, self.fingerprint)
282 class SSKVerifierURI(_BaseURI):
283 implements(IVerifierURI)
285 BASE_STRING='URI:SSK-Verifier:'
286 STRING_RE=re.compile('^'+BASE_STRING+ZBASE62STR_128bits+':'+ZBASE32STR_256bits+'$')
287 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+ZBASE62STR_128bits+SEP+ZBASE32STR_256bits+'$')
289 def __init__(self, storage_index, fingerprint):
290 assert len(storage_index) == 16
291 self.storage_index = storage_index
292 self.fingerprint = fingerprint
295 def init_from_human_encoding(cls, uri):
296 mo = cls.HUMAN_RE.search(uri)
298 return cls(storage.si_a2b(mo.group(1)), idlib.a2b(mo.group(2)))
301 def init_from_string(cls, uri):
302 mo = cls.STRING_RE.search(uri)
303 assert mo, (uri, cls)
304 return cls(storage.si_a2b(mo.group(1)), idlib.a2b(mo.group(2)))
307 assert isinstance(self.storage_index, str)
308 assert isinstance(self.fingerprint, str)
309 return 'URI:SSK-Verifier:%s:%s' % (storage.si_b2a(self.storage_index),
310 idlib.b2a(self.fingerprint))
312 class _NewDirectoryBaseURI(_BaseURI):
313 implements(IURI, IDirnodeURI)
314 def __init__(self, filenode_uri=None):
315 self._filenode_uri = filenode_uri
318 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
321 def init_from_string(cls, uri):
322 mo = cls.BASE_STRING_RE.search(uri)
323 assert mo, (uri, cls)
324 bits = uri[mo.end():]
325 fn = cls.INNER_URI_CLASS.init_from_string(
326 cls.INNER_URI_CLASS.BASE_STRING+bits)
330 def init_from_human_encoding(cls, uri):
331 mo = cls.BASE_HUMAN_RE.search(uri)
332 assert mo, (uri, cls)
333 bits = uri[mo.end():]
334 while bits and bits[-1] == '/':
336 fn = cls.INNER_URI_CLASS.init_from_string(
337 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
341 fnuri = self._filenode_uri.to_string()
342 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
344 bits = fnuri[mo.end():]
345 return self.BASE_STRING+bits
348 return self._filenode_uri.to_string().split(':')[2][:5]
350 def get_filenode_uri(self):
351 return self._filenode_uri
353 def is_mutable(self):
356 def get_verifier(self):
357 return NewDirectoryURIVerifier(self._filenode_uri.get_verifier())
359 class NewDirectoryURI(_NewDirectoryBaseURI):
360 implements(INewDirectoryURI)
362 BASE_STRING='URI:DIR2:'
363 BASE_STRING_RE=re.compile('^'+BASE_STRING)
364 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
365 INNER_URI_CLASS=WriteableSSKFileURI
367 def __init__(self, filenode_uri=None):
369 assert not filenode_uri.is_readonly()
370 _NewDirectoryBaseURI.__init__(self, filenode_uri)
372 def is_readonly(self):
375 def get_readonly(self):
376 return ReadonlyNewDirectoryURI(self._filenode_uri.get_readonly())
378 class ReadonlyNewDirectoryURI(_NewDirectoryBaseURI):
379 implements(IReadonlyNewDirectoryURI)
381 BASE_STRING='URI:DIR2-RO:'
382 BASE_STRING_RE=re.compile('^'+BASE_STRING)
383 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
384 INNER_URI_CLASS=ReadonlySSKFileURI
386 def __init__(self, filenode_uri=None):
388 assert filenode_uri.is_readonly()
389 _NewDirectoryBaseURI.__init__(self, filenode_uri)
391 def is_readonly(self):
394 def get_readonly(self):
397 class NewDirectoryURIVerifier(_NewDirectoryBaseURI):
398 implements(IVerifierURI)
400 BASE_STRING='URI:DIR2-Verifier:'
401 BASE_STRING_RE=re.compile('^'+BASE_STRING)
402 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
403 INNER_URI_CLASS=SSKVerifierURI
405 def __init__(self, filenode_uri=None):
407 filenode_uri = IVerifierURI(filenode_uri)
408 self._filenode_uri = filenode_uri
410 def get_filenode_uri(self):
411 return self._filenode_uri
416 if s.startswith('URI:CHK:'):
417 return CHKFileURI.init_from_string(s)
418 elif s.startswith('URI:CHK-Verifier:'):
419 return CHKFileVerifierURI.init_from_string(s)
420 elif s.startswith('URI:LIT:'):
421 return LiteralFileURI.init_from_string(s)
422 elif s.startswith('URI:SSK:'):
423 return WriteableSSKFileURI.init_from_string(s)
424 elif s.startswith('URI:SSK-RO:'):
425 return ReadonlySSKFileURI.init_from_string(s)
426 elif s.startswith('URI:SSK-Verifier:'):
427 return SSKVerifierURI.init_from_string(s)
428 elif s.startswith('URI:DIR2:'):
429 return NewDirectoryURI.init_from_string(s)
430 elif s.startswith('URI:DIR2-RO:'):
431 return ReadonlyNewDirectoryURI.init_from_string(s)
432 elif s.startswith('URI:DIR2-Verifier:'):
433 return NewDirectoryURIVerifier.init_from_string(s)
435 raise TypeError("unknown URI type: %s.." % s[:12])
437 registerAdapter(from_string, str, IURI)
443 except (TypeError, AssertionError):
446 def from_string_dirnode(s):
448 assert IDirnodeURI.providedBy(u)
451 registerAdapter(from_string_dirnode, str, IDirnodeURI)
453 def from_string_filenode(s):
455 assert IFileURI.providedBy(u)
458 registerAdapter(from_string_filenode, str, IFileURI)
460 def from_string_mutable_filenode(s):
462 assert IMutableFileURI.providedBy(u)
464 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
466 def from_string_verifier(s):
468 assert IVerifierURI.providedBy(u)
470 registerAdapter(from_string_verifier, str, IVerifierURI)
473 def pack_extension(data):
475 for k in sorted(data.keys()):
477 if isinstance(value, (int, long)):
479 assert isinstance(value, str), k
480 assert re.match(r'^[a-zA-Z_\-]+$', k)
481 pieces.append(k + ':' + hashutil.netstring(value))
482 uri_extension = ''.join(pieces)
485 def unpack_extension(data):
488 colon = data.index(':')
490 data = data[colon+1:]
492 colon = data.index(':')
493 number = data[:colon]
495 data = data[colon+1:]
497 value = data[:length]
498 assert data[length] == ','
499 data = data[length+1:]
503 # convert certain things to numbers
504 for intkey in ('size', 'segment_size', 'num_segments',
505 'needed_shares', 'total_shares'):
507 d[intkey] = int(d[intkey])
511 def unpack_extension_readable(data):
512 unpacked = unpack_extension(data)
513 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
514 for k in sorted(unpacked.keys()):
516 unpacked[k] = idlib.b2a(unpacked[k])