3 from zope.interface import implements
4 from twisted.python.components import registerAdapter
5 from allmydata import storage
6 from allmydata.util import base32, hashutil
7 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IImmutableFileURI, \
8 IVerifierURI, 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 BASE32STR_128bits = '(%s{25}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_3bits)
15 BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
19 NUMBER_IGNORE='(?:[0-9]+)'
21 # URIs (soon to be renamed "caps") are always allowed to come with a leading
22 # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
23 OPTIONALHTTPLEAD=r'(?:https?://(?:[^:/]+)(?::%s)?/uri/)?' % NUMBER_IGNORE
28 return self.to_string().__hash__()
29 def __eq__(self, them):
30 if isinstance(them, _BaseURI):
31 return self.to_string() == them.to_string()
34 def __ne__(self, them):
35 if isinstance(them, _BaseURI):
36 return self.to_string() != them.to_string()
39 def to_human_encoding(self):
40 return 'http://127.0.0.1:3456/uri/'+self.to_string()
42 def get_storage_index(self):
43 return self.storage_index
45 class CHKFileURI(_BaseURI):
46 implements(IURI, IImmutableFileURI)
48 STRING_RE=re.compile('^URI:CHK:'+BASE32STR_128bits+':'+
49 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
51 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK'+SEP+
52 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
53 SEP+NUMBER+SEP+NUMBER+'$')
55 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
58 self.uri_extension_hash = uri_extension_hash
59 self.needed_shares = needed_shares
60 self.total_shares = total_shares
62 self.storage_index = hashutil.storage_index_hash(self.key)
63 assert len(self.storage_index) == 16
64 self.storage_index = hashutil.storage_index_hash(key)
65 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
68 def init_from_human_encoding(cls, uri):
69 mo = cls.HUMAN_RE.search(uri)
71 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
72 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
75 def init_from_string(cls, uri):
76 mo = cls.STRING_RE.search(uri)
78 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
79 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
82 assert isinstance(self.needed_shares, int)
83 assert isinstance(self.total_shares, int)
84 assert isinstance(self.size, (int,long))
86 return ('URI:CHK:%s:%s:%d:%d:%d' %
87 (base32.b2a(self.key),
88 base32.b2a(self.uri_extension_hash),
93 def is_readonly(self):
97 def get_readonly(self):
103 def get_verify_cap(self):
104 return CHKFileVerifierURI(storage_index=self.storage_index,
105 uri_extension_hash=self.uri_extension_hash,
106 needed_shares=self.needed_shares,
107 total_shares=self.total_shares,
110 class CHKFileVerifierURI(_BaseURI):
111 implements(IVerifierURI)
113 STRING_RE=re.compile('^URI:CHK-Verifier:'+BASE32STR_128bits+':'+
114 BASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
115 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK-Verifier'+SEP+
116 BASE32STR_128bits+SEP+BASE32STR_256bits+SEP+NUMBER+
117 SEP+NUMBER+SEP+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_human_encoding(cls, uri):
130 mo = cls.HUMAN_RE.search(uri)
132 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)),
133 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
136 def init_from_string(cls, uri):
137 mo = cls.STRING_RE.search(uri)
138 assert mo, (uri, cls, cls.STRING_RE)
139 return cls(storage.si_a2b(mo.group(1)), base32.a2b(mo.group(2)),
140 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
143 assert isinstance(self.needed_shares, int)
144 assert isinstance(self.total_shares, int)
145 assert isinstance(self.size, (int,long))
147 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
148 (storage.si_b2a(self.storage_index),
149 base32.b2a(self.uri_extension_hash),
155 class LiteralFileURI(_BaseURI):
156 implements(IURI, IImmutableFileURI)
158 STRING_RE=re.compile('^URI:LIT:'+base32.BASE32STR_anybytes+'$')
159 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'LIT'+SEP+base32.BASE32STR_anybytes+'$')
161 def __init__(self, data=None):
166 def init_from_human_encoding(cls, uri):
167 mo = cls.HUMAN_RE.search(uri)
169 return cls(base32.a2b(mo.group(1)))
172 def init_from_string(cls, uri):
173 mo = cls.STRING_RE.search(uri)
175 return cls(base32.a2b(mo.group(1)))
178 return 'URI:LIT:%s' % base32.b2a(self.data)
180 def is_readonly(self):
182 def is_mutable(self):
184 def get_readonly(self):
186 def get_storage_index(self):
189 def get_verify_cap(self):
190 # LIT files need no verification, all the data is present in the URI
194 return len(self.data)
196 class WriteableSSKFileURI(_BaseURI):
197 implements(IURI, IMutableFileURI)
199 BASE_STRING='URI:SSK:'
200 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+
201 BASE32STR_256bits+'$')
202 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK'+SEP+
203 BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
205 def __init__(self, writekey, fingerprint):
206 self.writekey = writekey
207 self.readkey = hashutil.ssk_readkey_hash(writekey)
208 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
209 assert len(self.storage_index) == 16
210 self.fingerprint = fingerprint
213 def init_from_human_encoding(cls, uri):
214 mo = cls.HUMAN_RE.search(uri)
216 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
219 def init_from_string(cls, uri):
220 mo = cls.STRING_RE.search(uri)
221 assert mo, (uri, cls)
222 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
225 assert isinstance(self.writekey, str)
226 assert isinstance(self.fingerprint, str)
227 return 'URI:SSK:%s:%s' % (base32.b2a(self.writekey),
228 base32.b2a(self.fingerprint))
231 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
234 return base32.b2a(self.writekey[:5])
236 return base32.b2a(self.storage_index)[:5]
238 def is_readonly(self):
240 def is_mutable(self):
242 def get_readonly(self):
243 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
244 def get_verify_cap(self):
245 return SSKVerifierURI(self.storage_index, self.fingerprint)
247 class ReadonlySSKFileURI(_BaseURI):
248 implements(IURI, IMutableFileURI)
250 BASE_STRING='URI:SSK-RO:'
251 STRING_RE=re.compile('^URI:SSK-RO:'+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
252 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
254 def __init__(self, readkey, fingerprint):
255 self.readkey = readkey
256 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
257 assert len(self.storage_index) == 16
258 self.fingerprint = fingerprint
261 def init_from_human_encoding(cls, uri):
262 mo = cls.HUMAN_RE.search(uri)
264 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
267 def init_from_string(cls, uri):
268 mo = cls.STRING_RE.search(uri)
270 return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
273 assert isinstance(self.readkey, str)
274 assert isinstance(self.fingerprint, str)
275 return 'URI:SSK-RO:%s:%s' % (base32.b2a(self.readkey),
276 base32.b2a(self.fingerprint))
279 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
282 return base32.b2a(self.readkey[:5])
284 return base32.b2a(self.storage_index)[:5]
286 def is_readonly(self):
288 def is_mutable(self):
290 def get_readonly(self):
292 def get_verify_cap(self):
293 return SSKVerifierURI(self.storage_index, self.fingerprint)
295 class SSKVerifierURI(_BaseURI):
296 implements(IVerifierURI)
298 BASE_STRING='URI:SSK-Verifier:'
299 STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'$')
300 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'$')
302 def __init__(self, storage_index, fingerprint):
303 assert len(storage_index) == 16
304 self.storage_index = storage_index
305 self.fingerprint = fingerprint
308 def init_from_human_encoding(cls, uri):
309 mo = cls.HUMAN_RE.search(uri)
311 return cls(storage.si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
314 def init_from_string(cls, uri):
315 mo = cls.STRING_RE.search(uri)
316 assert mo, (uri, cls)
317 return cls(storage.si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
320 assert isinstance(self.storage_index, str)
321 assert isinstance(self.fingerprint, str)
322 return 'URI:SSK-Verifier:%s:%s' % (storage.si_b2a(self.storage_index),
323 base32.b2a(self.fingerprint))
325 class _NewDirectoryBaseURI(_BaseURI):
326 implements(IURI, IDirnodeURI)
327 def __init__(self, filenode_uri=None):
328 self._filenode_uri = filenode_uri
331 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
334 def init_from_string(cls, uri):
335 mo = cls.BASE_STRING_RE.search(uri)
336 assert mo, (uri, cls)
337 bits = uri[mo.end():]
338 fn = cls.INNER_URI_CLASS.init_from_string(
339 cls.INNER_URI_CLASS.BASE_STRING+bits)
343 def init_from_human_encoding(cls, uri):
344 mo = cls.BASE_HUMAN_RE.search(uri)
345 assert mo, (uri, cls)
346 bits = uri[mo.end():]
347 while bits and bits[-1] == '/':
349 fn = cls.INNER_URI_CLASS.init_from_string(
350 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
354 fnuri = self._filenode_uri.to_string()
355 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
357 bits = fnuri[mo.end():]
358 return self.BASE_STRING+bits
361 return self._filenode_uri.to_string().split(':')[2][:5]
363 return base32.b2a(self._filenode_uri.storage_index)[:5]
365 def get_filenode_uri(self):
366 return self._filenode_uri
368 def is_mutable(self):
371 def get_verify_cap(self):
372 return NewDirectoryURIVerifier(self._filenode_uri.get_verify_cap())
374 def get_storage_index(self):
375 return self._filenode_uri.get_storage_index()
377 class NewDirectoryURI(_NewDirectoryBaseURI):
378 implements(INewDirectoryURI)
380 BASE_STRING='URI:DIR2:'
381 BASE_STRING_RE=re.compile('^'+BASE_STRING)
382 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
383 INNER_URI_CLASS=WriteableSSKFileURI
385 def __init__(self, filenode_uri=None):
387 assert not filenode_uri.is_readonly()
388 _NewDirectoryBaseURI.__init__(self, filenode_uri)
390 def is_readonly(self):
393 def get_readonly(self):
394 return ReadonlyNewDirectoryURI(self._filenode_uri.get_readonly())
396 class ReadonlyNewDirectoryURI(_NewDirectoryBaseURI):
397 implements(IReadonlyNewDirectoryURI)
399 BASE_STRING='URI:DIR2-RO:'
400 BASE_STRING_RE=re.compile('^'+BASE_STRING)
401 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
402 INNER_URI_CLASS=ReadonlySSKFileURI
404 def __init__(self, filenode_uri=None):
406 assert filenode_uri.is_readonly()
407 _NewDirectoryBaseURI.__init__(self, filenode_uri)
409 def is_readonly(self):
412 def get_readonly(self):
415 class NewDirectoryURIVerifier(_NewDirectoryBaseURI):
416 implements(IVerifierURI)
418 BASE_STRING='URI:DIR2-Verifier:'
419 BASE_STRING_RE=re.compile('^'+BASE_STRING)
420 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
421 INNER_URI_CLASS=SSKVerifierURI
423 def __init__(self, filenode_uri=None):
425 filenode_uri = IVerifierURI(filenode_uri)
426 self._filenode_uri = filenode_uri
428 def get_filenode_uri(self):
429 return self._filenode_uri
434 if s.startswith('URI:CHK:'):
435 return CHKFileURI.init_from_string(s)
436 elif s.startswith('URI:CHK-Verifier:'):
437 return CHKFileVerifierURI.init_from_string(s)
438 elif s.startswith('URI:LIT:'):
439 return LiteralFileURI.init_from_string(s)
440 elif s.startswith('URI:SSK:'):
441 return WriteableSSKFileURI.init_from_string(s)
442 elif s.startswith('URI:SSK-RO:'):
443 return ReadonlySSKFileURI.init_from_string(s)
444 elif s.startswith('URI:SSK-Verifier:'):
445 return SSKVerifierURI.init_from_string(s)
446 elif s.startswith('URI:DIR2:'):
447 return NewDirectoryURI.init_from_string(s)
448 elif s.startswith('URI:DIR2-RO:'):
449 return ReadonlyNewDirectoryURI.init_from_string(s)
450 elif s.startswith('URI:DIR2-Verifier:'):
451 return NewDirectoryURIVerifier.init_from_string(s)
453 raise TypeError("unknown URI type: %s.." % s[:12])
455 registerAdapter(from_string, str, IURI)
461 except (TypeError, AssertionError):
464 def from_string_dirnode(s):
466 assert IDirnodeURI.providedBy(u)
469 registerAdapter(from_string_dirnode, str, IDirnodeURI)
471 def from_string_filenode(s):
473 assert IFileURI.providedBy(u)
476 registerAdapter(from_string_filenode, str, IFileURI)
478 def from_string_mutable_filenode(s):
480 assert IMutableFileURI.providedBy(u)
482 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
484 def from_string_verifier(s):
486 assert IVerifierURI.providedBy(u)
488 registerAdapter(from_string_verifier, str, IVerifierURI)
491 def pack_extension(data):
493 for k in sorted(data.keys()):
495 if isinstance(value, (int, long)):
497 assert isinstance(value, str), k
498 assert re.match(r'^[a-zA-Z_\-]+$', k)
499 pieces.append(k + ':' + hashutil.netstring(value))
500 uri_extension = ''.join(pieces)
503 def unpack_extension(data):
506 colon = data.index(':')
508 data = data[colon+1:]
510 colon = data.index(':')
511 number = data[:colon]
513 data = data[colon+1:]
515 value = data[:length]
516 assert data[length] == ','
517 data = data[length+1:]
521 # convert certain things to numbers
522 for intkey in ('size', 'segment_size', 'num_segments',
523 'needed_shares', 'total_shares'):
525 d[intkey] = int(d[intkey])
529 def unpack_extension_readable(data):
530 unpacked = unpack_extension(data)
531 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
532 for k in sorted(unpacked.keys()):
534 unpacked[k] = base32.b2a(unpacked[k])