3 from zope.interface import implements
4 from twisted.python.components import registerAdapter
5 from allmydata.util import idlib, hashutil
6 from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IVerifierURI, \
7 IMutableFileURI, INewDirectoryURI, IReadonlyNewDirectoryURI
9 # the URI shall be an ascii representation of the file. It shall contain
10 # enough information to retrieve and validate the contents. It shall be
11 # expressed in a limited character set (namely [TODO]).
13 ZBASE32STR_128bits = '(%s{25}%s)' % (idlib.ZBASE32CHAR, idlib.ZBASE32CHAR_3bits)
14 ZBASE32STR_256bits = '(%s{51}%s)' % (idlib.ZBASE32CHAR, idlib.ZBASE32CHAR_1bits)
19 # URIs (soon to be renamed "caps") are always allowed to come with a leading
20 # 'http://127.0.0.1:8123/uri/' that will be ignored.
21 OPTIONALHTTPLEAD=r'(?:https?://(127.0.0.1|localhost):8123/uri/)?'
26 return hash((self.__class__, self.to_string()))
27 def __cmp__(self, them):
28 if cmp(type(self), type(them)):
29 return cmp(type(self), type(them))
30 if cmp(self.__class__, them.__class__):
31 return cmp(self.__class__, them.__class__)
32 return cmp(self.to_string(), them.to_string())
33 def to_human_encoding(self):
34 return 'http://127.0.0.1:8123/uri/'+self.to_string()
36 class CHKFileURI(_BaseURI):
37 implements(IURI, IFileURI)
39 STRING_RE=re.compile('^URI:CHK:'+ZBASE32STR_128bits+':'+
40 ZBASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER+
42 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK'+SEP+
43 ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+SEP+NUMBER+
44 SEP+NUMBER+SEP+NUMBER+'$')
46 def __init__(self, key, uri_extension_hash, needed_shares, total_shares,
49 self.uri_extension_hash = uri_extension_hash
50 self.needed_shares = needed_shares
51 self.total_shares = total_shares
53 self.storage_index = hashutil.storage_index_hash(self.key)
54 assert len(self.storage_index) == 16
55 self.storage_index = hashutil.storage_index_hash(key)
56 assert len(self.storage_index) == 16 # sha256 hash truncated to 128
59 def init_from_human_encoding(cls, uri):
60 mo = cls.HUMAN_RE.search(uri)
62 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
63 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
66 def init_from_string(cls, uri):
67 mo = cls.STRING_RE.search(uri)
69 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
70 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
73 assert isinstance(self.needed_shares, int)
74 assert isinstance(self.total_shares, int)
75 assert isinstance(self.size, (int,long))
77 return ('URI:CHK:%s:%s:%d:%d:%d' %
79 idlib.b2a(self.uri_extension_hash),
84 def is_readonly(self):
88 def get_readonly(self):
94 def get_verifier(self):
95 return CHKFileVerifierURI(storage_index=self.storage_index,
96 uri_extension_hash=self.uri_extension_hash,
97 needed_shares=self.needed_shares,
98 total_shares=self.total_shares,
101 class CHKFileVerifierURI(_BaseURI):
102 implements(IVerifierURI)
104 STRING_RE=re.compile('^URI:CHK-Verifier:'+ZBASE32STR_128bits+':'+
105 ZBASE32STR_256bits+':'+NUMBER+':'+NUMBER+':'+NUMBER)
106 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'CHK-Verifier'+SEP+
107 ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+SEP+NUMBER+
108 SEP+NUMBER+SEP+NUMBER)
110 def __init__(self, storage_index, uri_extension_hash,
111 needed_shares, total_shares, size):
112 assert len(storage_index) == 16
113 self.storage_index = storage_index
114 self.uri_extension_hash = uri_extension_hash
115 self.needed_shares = needed_shares
116 self.total_shares = total_shares
120 def init_from_human_encoding(cls, uri):
121 mo = cls.HUMAN_RE.search(uri)
123 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
124 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
127 def init_from_string(cls, uri):
128 mo = cls.STRING_RE.search(uri)
130 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)),
131 int(mo.group(3)), int(mo.group(4)), int(mo.group(5)))
134 assert isinstance(self.needed_shares, int)
135 assert isinstance(self.total_shares, int)
136 assert isinstance(self.size, (int,long))
138 return ('URI:CHK-Verifier:%s:%s:%d:%d:%d' %
139 (idlib.b2a(self.storage_index),
140 idlib.b2a(self.uri_extension_hash),
146 class LiteralFileURI(_BaseURI):
147 implements(IURI, IFileURI)
149 STRING_RE=re.compile('^URI:LIT:'+idlib.ZBASE32STR_anybytes+'$')
150 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'LIT'+SEP+idlib.ZBASE32STR_anybytes+'$')
152 def __init__(self, data=None):
157 def init_from_human_encoding(cls, uri):
158 mo = cls.HUMAN_RE.search(uri)
160 return cls(idlib.a2b(mo.group(1)))
163 def init_from_string(cls, uri):
164 mo = cls.STRING_RE.search(uri)
166 return cls(idlib.a2b(mo.group(1)))
169 return 'URI:LIT:%s' % idlib.b2a(self.data)
171 def is_readonly(self):
173 def is_mutable(self):
175 def get_readonly(self):
178 def get_verifier(self):
179 # LIT files need no verification, all the data is present in the URI
183 return len(self.data)
185 class WriteableSSKFileURI(_BaseURI):
186 implements(IURI, IMutableFileURI)
188 BASE_STRING='URI:SSK:'
189 STRING_RE=re.compile('^'+BASE_STRING+ZBASE32STR_128bits+':'+
190 ZBASE32STR_256bits+'$')
191 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK'+SEP+
192 ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+'$')
194 def __init__(self, writekey, fingerprint):
195 self.writekey = writekey
196 self.readkey = hashutil.ssk_readkey_hash(writekey)
197 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
198 assert len(self.storage_index) == 16
199 self.fingerprint = fingerprint
202 def init_from_human_encoding(cls, uri):
203 mo = cls.HUMAN_RE.search(uri)
205 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
208 def init_from_string(cls, uri):
209 mo = cls.STRING_RE.search(uri)
210 assert mo, (uri, cls)
211 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
214 assert isinstance(self.writekey, str)
215 assert isinstance(self.fingerprint, str)
216 return 'URI:SSK:%s:%s' % (idlib.b2a(self.writekey),
217 idlib.b2a(self.fingerprint))
220 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
223 return idlib.b2a(self.writekey[:5])
225 def is_readonly(self):
227 def is_mutable(self):
229 def get_readonly(self):
230 return ReadonlySSKFileURI(self.readkey, self.fingerprint)
231 def get_verifier(self):
232 return SSKVerifierURI(self.storage_index, self.fingerprint)
234 class ReadonlySSKFileURI(_BaseURI):
235 implements(IURI, IMutableFileURI)
237 BASE_STRING='URI:SSK-RO:'
238 STRING_RE=re.compile('^URI:SSK-RO:'+ZBASE32STR_128bits+':'+ZBASE32STR_256bits+'$')
239 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+'$')
241 def __init__(self, readkey, fingerprint):
242 self.readkey = readkey
243 self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
244 assert len(self.storage_index) == 16
245 self.fingerprint = fingerprint
248 def init_from_human_encoding(cls, uri):
249 mo = cls.HUMAN_RE.search(uri)
251 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
254 def init_from_string(cls, uri):
255 mo = cls.STRING_RE.search(uri)
257 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
260 assert isinstance(self.readkey, str)
261 assert isinstance(self.fingerprint, str)
262 return 'URI:SSK-RO:%s:%s' % (idlib.b2a(self.readkey),
263 idlib.b2a(self.fingerprint))
266 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
269 return idlib.b2a(self.readkey[:5])
271 def is_readonly(self):
273 def is_mutable(self):
275 def get_readonly(self):
277 def get_verifier(self):
278 return SSKVerifierURI(self.storage_index, self.fingerprint)
280 class SSKVerifierURI(_BaseURI):
281 implements(IVerifierURI)
283 BASE_STRING='URI:SSK-Verifier:'
284 STRING_RE=re.compile('^'+BASE_STRING+ZBASE32STR_128bits+':'+ZBASE32STR_256bits+'$')
285 HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'SSK-RO'+SEP+ZBASE32STR_128bits+SEP+ZBASE32STR_256bits+'$')
287 def __init__(self, storage_index, fingerprint):
288 assert len(storage_index) == 16
289 self.storage_index = storage_index
290 self.fingerprint = fingerprint
293 def init_from_human_encoding(cls, uri):
294 mo = cls.HUMAN_RE.search(uri)
296 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
299 def init_from_string(cls, uri):
300 mo = cls.STRING_RE.search(uri)
302 return cls(idlib.a2b(mo.group(1)), idlib.a2b(mo.group(2)))
305 assert isinstance(self.storage_index, str)
306 assert isinstance(self.fingerprint, str)
307 return 'URI:SSK-Verifier:%s:%s' % (idlib.b2a(self.storage_index),
308 idlib.b2a(self.fingerprint))
310 class _NewDirectoryBaseURI(_BaseURI):
311 implements(IURI, IDirnodeURI)
312 def __init__(self, filenode_uri=None):
313 self._filenode_uri = filenode_uri
316 return "<%s %s>" % (self.__class__.__name__, self.abbrev())
319 def init_from_string(cls, uri):
320 mo = cls.BASE_STRING_RE.search(uri)
321 assert mo, (uri, cls)
322 bits = uri[mo.end():]
323 fn = cls.INNER_URI_CLASS.init_from_string(
324 cls.INNER_URI_CLASS.BASE_STRING+bits)
328 def init_from_human_encoding(cls, uri):
329 mo = cls.BASE_HUMAN_RE.search(uri)
330 assert mo, (uri, cls)
331 bits = uri[mo.end():]
332 while bits and bits[-1] == '/':
334 fn = cls.INNER_URI_CLASS.init_from_string(
335 cls.INNER_URI_CLASS.BASE_STRING+urllib.unquote(bits))
339 fnuri = self._filenode_uri.to_string()
340 mo = re.match(self.INNER_URI_CLASS.BASE_STRING, fnuri)
342 bits = fnuri[mo.end():]
343 return self.BASE_STRING+bits
346 return self._filenode_uri.to_string().split(':')[2][:5]
348 def get_filenode_uri(self):
349 return self._filenode_uri
351 def is_mutable(self):
354 def get_verifier(self):
355 return NewDirectoryURIVerifier(self._filenode_uri.get_verifier())
357 class NewDirectoryURI(_NewDirectoryBaseURI):
358 implements(INewDirectoryURI)
360 BASE_STRING='URI:DIR2:'
361 BASE_STRING_RE=re.compile('^'+BASE_STRING)
362 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2'+SEP)
363 INNER_URI_CLASS=WriteableSSKFileURI
365 def __init__(self, filenode_uri=None):
367 assert not filenode_uri.is_readonly()
368 _NewDirectoryBaseURI.__init__(self, filenode_uri)
370 def is_readonly(self):
373 def get_readonly(self):
374 return ReadonlyNewDirectoryURI(self._filenode_uri.get_readonly())
376 class ReadonlyNewDirectoryURI(_NewDirectoryBaseURI):
377 implements(IReadonlyNewDirectoryURI)
379 BASE_STRING='URI:DIR2-RO:'
380 BASE_STRING_RE=re.compile('^'+BASE_STRING)
381 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-RO'+SEP)
382 INNER_URI_CLASS=ReadonlySSKFileURI
384 def __init__(self, filenode_uri=None):
386 assert filenode_uri.is_readonly()
387 _NewDirectoryBaseURI.__init__(self, filenode_uri)
389 def is_readonly(self):
392 def get_readonly(self):
395 class NewDirectoryURIVerifier(_NewDirectoryBaseURI):
396 implements(IVerifierURI)
398 BASE_STRING='URI:DIR2-Verifier:'
399 BASE_STRING_RE=re.compile('^'+BASE_STRING)
400 BASE_HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'DIR2-Verifier'+SEP)
401 INNER_URI_CLASS=SSKVerifierURI
403 def __init__(self, filenode_uri=None):
405 filenode_uri = IVerifierURI(filenode_uri)
406 self._filenode_uri = filenode_uri
408 def get_filenode_uri(self):
409 return self._filenode_uri
414 if s.startswith('URI:CHK:'):
415 return CHKFileURI.init_from_string(s)
416 elif s.startswith('URI:CHK-Verifier:'):
417 return CHKFileVerifierURI.init_from_string(s)
418 elif s.startswith('URI:LIT:'):
419 return LiteralFileURI.init_from_string(s)
420 elif s.startswith('URI:SSK:'):
421 return WriteableSSKFileURI.init_from_string(s)
422 elif s.startswith('URI:SSK-RO:'):
423 return ReadonlySSKFileURI.init_from_string(s)
424 elif s.startswith('URI:SSK-Verifier:'):
425 return SSKVerifierURI.init_from_string(s)
426 elif s.startswith('URI:DIR2:'):
427 return NewDirectoryURI.init_from_string(s)
428 elif s.startswith('URI:DIR2-RO:'):
429 return ReadonlyNewDirectoryURI.init_from_string(s)
430 elif s.startswith('URI:DIR2-Verifier:'):
431 return NewDirectoryURIVerifier.init_from_string(s)
433 raise TypeError("unknown URI type: %s.." % s[:12])
435 registerAdapter(from_string, str, IURI)
441 except (TypeError, AssertionError):
444 def from_string_dirnode(s):
446 assert IDirnodeURI.providedBy(u)
449 registerAdapter(from_string_dirnode, str, IDirnodeURI)
451 def from_string_filenode(s):
453 assert IFileURI.providedBy(u)
456 registerAdapter(from_string_filenode, str, IFileURI)
458 def from_string_mutable_filenode(s):
460 assert IMutableFileURI.providedBy(u)
462 registerAdapter(from_string_mutable_filenode, str, IMutableFileURI)
464 def from_string_verifier(s):
466 assert IVerifierURI.providedBy(u)
468 registerAdapter(from_string_verifier, str, IVerifierURI)
471 def pack_extension(data):
473 for k in sorted(data.keys()):
475 if isinstance(value, (int, long)):
477 assert isinstance(value, str), k
478 assert re.match(r'^[a-zA-Z_\-]+$', k)
479 pieces.append(k + ':' + hashutil.netstring(value))
480 uri_extension = ''.join(pieces)
483 def unpack_extension(data):
486 colon = data.index(':')
488 data = data[colon+1:]
490 colon = data.index(':')
491 number = data[:colon]
493 data = data[colon+1:]
495 value = data[:length]
496 assert data[length] == ','
497 data = data[length+1:]
501 # convert certain things to numbers
502 for intkey in ('size', 'segment_size', 'num_segments',
503 'needed_shares', 'total_shares'):
505 d[intkey] = int(d[intkey])
509 def unpack_extension_readable(data):
510 unpacked = unpack_extension(data)
511 unpacked["UEB_hash"] = hashutil.uri_extension_hash(data)
512 for k in sorted(unpacked.keys()):
514 unpacked[k] = idlib.b2a(unpacked[k])