MDMF: remove extension fields from caps, tolerate arbitrary ones. Fixes #1526

The filecaps used to be produced with hints for 'k' and segsize, but they
weren't actually used, and doing so had the potential to limit how we change
those filecaps in the future. Also the parsing code had some problems dealing
with other numbers of extensions. Removing the existing fields and making the
parser tolerate (and ignore) extra ones makes MDMF more future-proof.
This commit is contained in:
Brian Warner 2011-10-02 00:35:53 +01:00
parent 5ba0529b87
commit 0716c496c8
9 changed files with 70 additions and 486 deletions

View File

@ -118,17 +118,6 @@ class MutableFileNode:
self._privkey = None self._privkey = None
self._encprivkey = None self._encprivkey = None
# Starting with MDMF caps, we allowed arbitrary extensions in
# caps. If we were initialized with a cap that had extensions,
# we want to remember them so we can tell MutableFileVersions
# about them.
extensions = self._uri.get_extension_params()
if extensions:
extensions = map(int, extensions)
suspected_k, suspected_segsize = extensions
self._downloader_hints['k'] = suspected_k
self._downloader_hints['segsize'] = suspected_segsize
return self return self
def create_with_keys(self, (pubkey, privkey), contents, def create_with_keys(self, (pubkey, privkey), contents,
@ -701,9 +690,6 @@ class MutableFileNode:
def set_downloader_hints(self, hints): def set_downloader_hints(self, hints):
self._downloader_hints = hints self._downloader_hints = hints
extensions = [ hints["k"], hints["segsize"] ]
self._uri.set_extension_params(extensions)
def _did_upload(self, res, size): def _did_upload(self, res, size):
self._most_recent_size = size self._most_recent_size = size

View File

@ -369,8 +369,7 @@ def dump_MDMF_share(m, length, options):
if base32.could_be_base32_encoded(piece): if base32.could_be_base32_encoded(piece):
storage_index = base32.a2b(piece) storage_index = base32.a2b(piece)
fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey) fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey)
hints = [str(k), str(segsize)] u = MDMFVerifierURI(storage_index, fingerprint)
u = MDMFVerifierURI(storage_index, fingerprint, hints)
verify_cap = u.to_string() verify_cap = u.to_string()
print >>out, " verify-cap:", quote_output(verify_cap, quotemarks=False) print >>out, " verify-cap:", quote_output(verify_cap, quotemarks=False)

View File

@ -208,7 +208,6 @@ class FakeMutableFileNode:
data = initial_contents.read(initial_contents.get_size()) data = initial_contents.read(initial_contents.get_size())
data = "".join(data) data = "".join(data)
self.all_contents[self.storage_index] = data self.all_contents[self.storage_index] = data
self.my_uri.set_extension_params([self._k, self._segsize])
return defer.succeed(self) return defer.succeed(self)
def _get_initial_contents(self, contents): def _get_initial_contents(self, contents):
if contents is None: if contents is None:
@ -358,7 +357,6 @@ class FakeMutableFileNode:
new_data = new_contents.read(new_contents.get_size()) new_data = new_contents.read(new_contents.get_size())
new_data = "".join(new_data) new_data = "".join(new_data)
self.all_contents[self.storage_index] = new_data self.all_contents[self.storage_index] = new_data
self.my_uri.set_extension_params([self._k, self._segsize])
return defer.succeed(None) return defer.succeed(None)
def modify(self, modifier): def modify(self, modifier):
# this does not implement FileTooLargeError, but the real one does # this does not implement FileTooLargeError, but the real one does
@ -368,7 +366,6 @@ class FakeMutableFileNode:
old_contents = self.all_contents[self.storage_index] old_contents = self.all_contents[self.storage_index]
new_data = modifier(old_contents, None, True) new_data = modifier(old_contents, None, True)
self.all_contents[self.storage_index] = new_data self.all_contents[self.storage_index] = new_data
self.my_uri.set_extension_params([self._k, self._segsize])
return None return None
# As actually implemented, MutableFilenode and MutableFileVersion # As actually implemented, MutableFilenode and MutableFileVersion

View File

@ -1229,7 +1229,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1) d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1)
def _got_cap((rc, out, err)): def _got_cap((rc, out, err)):
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
self.cap = out self.cap = out.strip()
d.addCallback(_got_cap) d.addCallback(_got_cap)
# Now try to write something to the cap using put. # Now try to write something to the cap using put.
data2 = "data2" * 100000 data2 = "data2" * 100000
@ -1248,13 +1248,11 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
self.failUnlessEqual(out, data2) self.failUnlessEqual(out, data2)
d.addCallback(_got_data) d.addCallback(_got_data)
# Now strip the extension information off of the cap and try # add some extension information to the cap and try to put something
# to put something to it. # to it.
def _make_bare_cap(ignored): def _make_extended_cap(ignored):
cap = self.cap.split(":") self.cap = self.cap + ":Extension-Stuff"
cap = ":".join(cap[:len(cap) - 2]) d.addCallback(_make_extended_cap)
self.cap = cap
d.addCallback(_make_bare_cap)
data3 = "data3" * 100000 data3 = "data3" * 100000
fn3 = os.path.join(self.basedir, "data3") fn3 = os.path.join(self.basedir, "data3")
fileutil.write(fn3, data3) fileutil.write(fn3, data3)
@ -1277,7 +1275,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
d = self.do_cli("put", "--mutable", "--mutable-type=sdmf", fn1) d = self.do_cli("put", "--mutable", "--mutable-type=sdmf", fn1)
def _got_cap((rc, out, err)): def _got_cap((rc, out, err)):
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
self.cap = out self.cap = out.strip()
d.addCallback(_got_cap) d.addCallback(_got_cap)
# Now try to write something to the cap using put. # Now try to write something to the cap using put.
data2 = "data2" * 100000 data2 = "data2" * 100000

View File

@ -41,11 +41,11 @@ class MemAccum:
setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861" setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861"
one_uri = "URI:LIT:n5xgk" # LIT for "one" one_uri = "URI:LIT:n5xgk" # LIT for "one"
mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
mdmf_write_uri = "URI:MDMF:x533rhbm6kiehzl5kj3s44n5ie:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a:1:131072" mdmf_write_uri = "URI:MDMF:x533rhbm6kiehzl5kj3s44n5ie:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a"
empty_litdir_uri = "URI:DIR2-LIT:" empty_litdir_uri = "URI:DIR2-LIT:"
tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT
mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq" mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
mdmf_read_uri = "URI:MDMF-RO:d4cydxselputycfzkw6qgz4zv4:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a:1:131072" mdmf_read_uri = "URI:MDMF-RO:d4cydxselputycfzkw6qgz4zv4:4gif5rhneyd763ouo5qjrgnsoa3bg43xycy4robj2rf3tvmhdl3a"
future_write_uri = "x-tahoe-crazy://I_am_from_the_future." future_write_uri = "x-tahoe-crazy://I_am_from_the_future."
future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future." future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future."
future_nonascii_write_uri = u"x-tahoe-even-more-crazy://I_am_from_the_future_rw_\u263A".encode('utf-8') future_nonascii_write_uri = u"x-tahoe-even-more-crazy://I_am_from_the_future_rw_\u263A".encode('utf-8')

View File

@ -375,28 +375,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
return d return d
def test_create_from_mdmf_writecap_with_extensions(self):
# Test that the nodemaker is capable of creating an MDMF
# filenode when given a writecap with extension parameters in
# them.
d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)
def _created(n):
self.failUnless(isinstance(n, MutableFileNode))
s = n.get_uri()
# We need to cheat a little and delete the nodemaker's
# cache, otherwise we'll get the same node instance back.
self.failUnlessIn(":3:131073", s)
n2 = self.nodemaker.create_from_cap(s)
self.failUnlessEqual(n2.get_storage_index(), n.get_storage_index())
self.failUnlessEqual(n.get_writekey(), n2.get_writekey())
hints = n2._downloader_hints
self.failUnlessEqual(hints['k'], 3)
self.failUnlessEqual(hints['segsize'], 131073)
d.addCallback(_created)
return d
def test_create_from_mdmf_readcap(self): def test_create_from_mdmf_readcap(self):
d = self.nodemaker.create_mutable_file(version=MDMF_VERSION) d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)
def _created(n): def _created(n):
@ -411,26 +389,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
return d return d
def test_create_from_mdmf_readcap_with_extensions(self):
# We should be able to create an MDMF filenode with the
# extension parameters without it breaking.
d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)
def _created(n):
self.failUnless(isinstance(n, MutableFileNode))
s = n.get_readonly_uri()
self.failUnlessIn(":3:131073", s)
n2 = self.nodemaker.create_from_cap(s)
self.failUnless(isinstance(n2, MutableFileNode))
self.failUnless(n2.is_readonly())
self.failUnlessEqual(n.get_storage_index(), n2.get_storage_index())
hints = n2._downloader_hints
self.failUnlessEqual(hints["k"], 3)
self.failUnlessEqual(hints["segsize"], 131073)
d.addCallback(_created)
return d
def test_internal_version_from_cap(self): def test_internal_version_from_cap(self):
# MutableFileNodes and MutableFileVersions have an internal # MutableFileNodes and MutableFileVersions have an internal
# switch that tells them whether they're dealing with an SDMF or # switch that tells them whether they're dealing with an SDMF or
@ -606,6 +564,9 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
d = self.nodemaker.create_mutable_file(version=MDMF_VERSION) d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)
def _created(node): def _created(node):
self.uri = node.get_uri() self.uri = node.get_uri()
# also confirm that the cap has no extension fields
pieces = self.uri.split(":")
self.failUnlessEqual(len(pieces), 4)
return node.overwrite(MutableData("contents1" * 100000)) return node.overwrite(MutableData("contents1" * 100000))
def _then(ignored): def _then(ignored):
@ -619,37 +580,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
return d return d
def test_create_and_download_from_bare_mdmf_cap(self):
# MDMF caps have extension parameters on them by default. We
# need to make sure that they work without extension parameters.
contents = MutableData("contents" * 100000)
d = self.nodemaker.create_mutable_file(version=MDMF_VERSION,
contents=contents)
def _created(node):
uri = node.get_uri()
self._created = node
self.failUnlessIn(":3:131073", uri)
# Now strip that off the end of the uri, then try creating
# and downloading the node again.
bare_uri = uri.replace(":3:131073", "")
assert ":3:131073" not in bare_uri
return self.nodemaker.create_from_cap(bare_uri)
d.addCallback(_created)
def _created_bare(node):
self.failUnlessEqual(node.get_writekey(),
self._created.get_writekey())
self.failUnlessEqual(node.get_readkey(),
self._created.get_readkey())
self.failUnlessEqual(node.get_storage_index(),
self._created.get_storage_index())
return node.download_best_version()
d.addCallback(_created_bare)
d.addCallback(lambda data:
self.failUnlessEqual(data, "contents" * 100000))
return d
def test_mdmf_write_count(self): def test_mdmf_write_count(self):
# Publishing an MDMF file should only cause one write for each # Publishing an MDMF file should only cause one write for each
# share that is to be published. Otherwise, we introduce # share that is to be published. Otherwise, we introduce
@ -3068,62 +2998,6 @@ class Version(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin, \
return d return d
def test_version_extension_api(self):
# We need to define an API by which an uploader can set the
# extension parameters, and by which a downloader can retrieve
# extensions.
d = self.do_upload_mdmf()
d.addCallback(lambda ign: self.mdmf_node.get_best_mutable_version())
def _got_version(version):
hints = version.get_downloader_hints()
# Should be empty at this point.
self.failUnlessIn("k", hints)
self.failUnlessEqual(hints['k'], 3)
self.failUnlessIn('segsize', hints)
self.failUnlessEqual(hints['segsize'], 131073)
d.addCallback(_got_version)
return d
def test_extensions_from_cap(self):
# If we initialize a mutable file with a cap that has extension
# parameters in it and then grab the extension parameters using
# our API, we should see that they're set correctly.
d = self.do_upload_mdmf()
def _then(ign):
mdmf_uri = self.mdmf_node.get_uri()
new_node = self.nm.create_from_cap(mdmf_uri)
return new_node.get_best_mutable_version()
d.addCallback(_then)
def _got_version(version):
hints = version.get_downloader_hints()
self.failUnlessIn("k", hints)
self.failUnlessEqual(hints["k"], 3)
self.failUnlessIn("segsize", hints)
self.failUnlessEqual(hints["segsize"], 131073)
d.addCallback(_got_version)
return d
def test_extensions_from_upload(self):
# If we create a new mutable file with some contents, we should
# get back an MDMF cap with the right hints in place.
contents = "foo bar baz" * 100000
d = self.nm.create_mutable_file(contents, version=MDMF_VERSION)
def _got_mutable_file(n):
rw_uri = n.get_uri()
expected_k = str(self.c.DEFAULT_ENCODING_PARAMETERS['k'])
self.failUnlessIn(expected_k, rw_uri)
# XXX: Get this more intelligently.
self.failUnlessIn("131073", rw_uri)
ro_uri = n.get_readonly_uri()
self.failUnlessIn(expected_k, ro_uri)
self.failUnlessIn("131073", ro_uri)
d.addCallback(_got_mutable_file)
return d
def test_cap_after_upload(self): def test_cap_after_upload(self):
# If we create a new mutable file and upload things to it, and # If we create a new mutable file and upload things to it, and
# it's an MDMF file, we should get an MDMF cap back from that # it's an MDMF file, we should get an MDMF cap back from that

View File

@ -1,5 +1,5 @@
import re import os, re
from twisted.trial import unittest from twisted.trial import unittest
from allmydata import uri from allmydata import uri
from allmydata.util import hashutil, base32 from allmydata.util import hashutil, base32
@ -430,84 +430,40 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
u4 = u2.get_verify_cap() u4 = u2.get_verify_cap()
self.failUnlessReallyEqual(u4, u2) self.failUnlessReallyEqual(u4, u2)
def test_mdmf_cap_extra_information(self): def test_mdmf_cap_ignore_extensions(self):
# MDMF caps can be arbitrarily extended after the fingerprint # MDMF caps can be arbitrarily extended after the fingerprint and
# and key/storage index fields. # key/storage index fields. tahoe-1.9 is supposed to ignore any
# extensions, and not add any itself.
u1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) u1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
self.failUnlessEqual([], u1.get_extension_params())
cap = u1.to_string() cap = u1.to_string()
# Now let's append some fields. Say, 131073 (the segment size)
# and 3 (the "k" encoding parameter).
expected_extensions = []
for e in ('131073', '3'):
cap += (":%s" % e)
expected_extensions.append(e)
u2 = uri.WriteableMDMFFileURI.init_from_string(cap) cap2 = cap+":I COME FROM THE FUTURE"
u2 = uri.WriteableMDMFFileURI.init_from_string(cap2)
self.failUnlessReallyEqual(self.writekey, u2.writekey) self.failUnlessReallyEqual(self.writekey, u2.writekey)
self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint) self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint)
self.failIf(u2.is_readonly()) self.failIf(u2.is_readonly())
self.failUnless(u2.is_mutable()) self.failUnless(u2.is_mutable())
c2 = u2.to_string() cap3 = cap+":"+os.urandom(40) # parse *that*!
u2n = uri.WriteableMDMFFileURI.init_from_string(c2) u3 = uri.WriteableMDMFFileURI.init_from_string(cap3)
self.failUnlessReallyEqual(u2, u2n) self.failUnlessReallyEqual(self.writekey, u3.writekey)
# We should get the extra back when we ask for it.
self.failUnlessEqual(expected_extensions, u2.get_extension_params())
# These should be preserved through cap attenuation, too.
u3 = u2.get_readonly()
self.failUnlessReallyEqual(self.readkey, u3.readkey)
self.failUnlessReallyEqual(self.fingerprint, u3.fingerprint) self.failUnlessReallyEqual(self.fingerprint, u3.fingerprint)
self.failUnless(u3.is_readonly()) self.failIf(u3.is_readonly())
self.failUnless(u3.is_mutable()) self.failUnless(u3.is_mutable())
self.failUnlessEqual(expected_extensions, u3.get_extension_params())
c3 = u3.to_string() cap4 = u1.get_readonly().to_string()+":ooh scary future stuff"
u3n = uri.ReadonlyMDMFFileURI.init_from_string(c3) u4 = uri.from_string_mutable_filenode(cap4)
self.failUnlessReallyEqual(u3, u3n) self.failUnlessReallyEqual(self.readkey, u4.readkey)
u4 = u3.get_verify_cap()
self.failUnlessReallyEqual(self.storage_index, u4.storage_index)
self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint) self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint)
self.failUnless(u4.is_readonly()) self.failUnless(u4.is_readonly())
self.failIf(u4.is_mutable()) self.failUnless(u4.is_mutable())
c4 = u4.to_string() cap5 = u1.get_verify_cap().to_string()+":spoilers!"
u4n = uri.MDMFVerifierURI.init_from_string(c4) u5 = uri.from_string(cap5)
self.failUnlessReallyEqual(u4n, u4) self.failUnlessReallyEqual(self.storage_index, u5.storage_index)
self.failUnlessReallyEqual(self.fingerprint, u5.fingerprint)
self.failUnlessEqual(expected_extensions, u4.get_extension_params()) self.failUnless(u5.is_readonly())
self.failIf(u5.is_mutable())
def test_sdmf_cap_extra_information(self):
# For interface consistency, we define a method to get
# extensions for SDMF files as well. This method must always
# return no extensions, since SDMF files were not created with
# extensions and cannot be modified to include extensions
# without breaking older clients.
u1 = uri.WriteableSSKFileURI(self.writekey, self.fingerprint)
cap = u1.to_string()
u2 = uri.WriteableSSKFileURI.init_from_string(cap)
self.failUnlessEqual([], u2.get_extension_params())
def test_extension_character_range(self):
# As written now, we shouldn't put things other than numbers in
# the extension fields.
writecap = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint).to_string()
readcap = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint).to_string()
vcap = uri.MDMFVerifierURI(self.storage_index, self.fingerprint).to_string()
self.failUnlessRaises(uri.BadURIError,
uri.WriteableMDMFFileURI.init_from_string,
("%s:invalid" % writecap))
self.failUnlessRaises(uri.BadURIError,
uri.ReadonlyMDMFFileURI.init_from_string,
("%s:invalid" % readcap))
self.failUnlessRaises(uri.BadURIError,
uri.MDMFVerifierURI.init_from_string,
("%s:invalid" % vcap))
def test_mdmf_valid_human_encoding(self): def test_mdmf_valid_human_encoding(self):
@ -517,17 +473,11 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
# test that a valid cap (with and without the traditional # test that a valid cap (with and without the traditional
# separators) is recognized and accepted by the classes. # separators) is recognized and accepted by the classes.
w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
['131073', '3'])
r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
['131073', '3'])
v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
['131073', '3'])
# These will yield six different caps. # These will yield three different caps.
for o in (w1, w2, r1 , r2, v1, v2): for o in (w1, r1, v1):
url = base + o.to_string() url = base + o.to_string()
o1 = o.__class__.init_from_human_encoding(url) o1 = o.__class__.init_from_human_encoding(url)
self.failUnlessReallyEqual(o1, o) self.failUnlessReallyEqual(o1, o)
@ -550,17 +500,11 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
# test that a valid cap (with and without the traditional # test that a valid cap (with and without the traditional
# separators) is recognized and accepted by the classes. # separators) is recognized and accepted by the classes.
w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
['131073', '3'])
r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
['131073', '3'])
v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
['131073', '3'])
# These will yield six different caps. # These will yield three different caps.
for o in (w1, w2, r1 , r2, v1, v2): for o in (w1, r1, v1):
url = base + o.to_string() url = base + o.to_string()
self.failUnlessRaises(uri.BadURIError, self.failUnlessRaises(uri.BadURIError,
o.__class__.init_from_human_encoding, o.__class__.init_from_human_encoding,
@ -572,17 +516,11 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
# test that a valid cap (with and without the traditional # test that a valid cap (with and without the traditional
# separators) is recognized and accepted by the classes. # separators) is recognized and accepted by the classes.
w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint) w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
['131073', '3'])
r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
['131073', '3'])
v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
['131073', '3'])
# These will yield six different caps. # These will yield three different caps.
for o in (w1, w2, r1 , r2, v1, v2): for o in (w1, r1, v1):
# not exhaustive, obviously... # not exhaustive, obviously...
url = base + o.to_string() + "foobarbaz" url = base + o.to_string() + "foobarbaz"
url2 = base + "foobarbaz" + o.to_string() url2 = base + "foobarbaz" + o.to_string()
@ -603,16 +541,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
u3 = uri.from_string_mutable_filenode(cap) u3 = uri.from_string_mutable_filenode(cap)
self.failUnlessEqual(u3, u1) self.failUnlessEqual(u3, u1)
# XXX: We should refactor the extension field into setUp
u1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
['131073', '3'])
cap = u1.to_string()
self.failUnless(uri.is_uri(cap))
u2 = uri.from_string(cap)
self.failUnlessReallyEqual(u1, u2)
u3 = uri.from_string_mutable_filenode(cap)
self.failUnlessEqual(u3, u1)
u1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint) u1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
cap = u1.to_string() cap = u1.to_string()
self.failUnless(uri.is_uri(cap)) self.failUnless(uri.is_uri(cap))
@ -621,15 +549,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
u3 = uri.from_string_mutable_filenode(cap) u3 = uri.from_string_mutable_filenode(cap)
self.failUnlessEqual(u3, u1) self.failUnlessEqual(u3, u1)
u1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
['131073', '3'])
cap = u1.to_string()
self.failUnless(uri.is_uri(cap))
u2 = uri.from_string(cap)
self.failUnlessReallyEqual(u1, u2)
u3 = uri.from_string_mutable_filenode(cap)
self.failUnlessEqual(u3, u1)
u1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint) u1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
cap = u1.to_string() cap = u1.to_string()
self.failUnless(uri.is_uri(cap)) self.failUnless(uri.is_uri(cap))
@ -638,15 +557,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
u3 = uri.from_string_verifier(cap) u3 = uri.from_string_verifier(cap)
self.failUnlessEqual(u3, u1) self.failUnlessEqual(u3, u1)
u1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
['131073', '3'])
cap = u1.to_string()
self.failUnless(uri.is_uri(cap))
u2 = uri.from_string(cap)
self.failUnlessReallyEqual(u1, u2)
u3 = uri.from_string_verifier(cap)
self.failUnlessEqual(u3, u1)
class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase): class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase):
def test_pack(self): def test_pack(self):
@ -816,35 +726,6 @@ class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase):
d3 = uri.from_string(d2.to_string(), deep_immutable=True) d3 = uri.from_string(d2.to_string(), deep_immutable=True)
self.failUnlessIsInstance(d3, uri.UnknownURI) self.failUnlessIsInstance(d3, uri.UnknownURI)
def test_mdmf_with_extensions(self):
writekey = "\x01" * 16
fingerprint = "\x02" * 32
uri1 = uri.WriteableMDMFFileURI(writekey, fingerprint)
d1 = uri.MDMFDirectoryURI(uri1)
d1_uri = d1.to_string()
# Add some extensions, verify that the URI is interpreted
# correctly.
d1_uri += ":3:131073"
uri2 = uri.from_string(d1_uri)
self.failUnlessIsInstance(uri2, uri.MDMFDirectoryURI)
self.failUnless(IURI.providedBy(uri2))
self.failUnless(IDirnodeURI.providedBy(uri2))
self.failUnless(uri1.is_mutable())
self.failIf(uri1.is_readonly())
d2_uri = uri2.to_string()
self.failUnlessIn(":3:131073", d2_uri)
# Now attenuate, verify that the extensions persist
ro_uri = uri2.get_readonly()
self.failUnlessIsInstance(ro_uri, uri.ReadonlyMDMFDirectoryURI)
self.failUnless(ro_uri.is_mutable())
self.failUnless(ro_uri.is_readonly())
self.failUnless(IURI.providedBy(ro_uri))
self.failUnless(IDirnodeURI.providedBy(ro_uri))
ro_uri_str = ro_uri.to_string()
self.failUnlessIn(":3:131073", ro_uri_str)
def test_mdmf_attenuation(self): def test_mdmf_attenuation(self):
writekey = "\x01" * 16 writekey = "\x01" * 16
fingerprint = "\x02" * 32 fingerprint = "\x02" * 32

View File

@ -892,22 +892,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d return d
def test_GET_FILE_URI_mdmf_extensions(self): def test_GET_FILE_URI_mdmf_extensions(self):
base = "/uri/%s" % urllib.quote("%s:3:131073" % self._quux_txt_uri) base = "/uri/%s" % urllib.quote("%s:RANDOMSTUFF" % self._quux_txt_uri)
d = self.GET(base)
d.addCallback(self.failUnlessIsQuuxDotTxt)
return d
def test_GET_FILE_URI_mdmf_bare_cap(self):
cap_elements = self._quux_txt_uri.split(":")
# 6 == expected cap length with two extensions.
self.failUnlessEqual(len(cap_elements), 6)
# Now lop off the extension parameters and stitch everything
# back together
quux_uri = ":".join(cap_elements[:len(cap_elements) - 2])
# Now GET that. We should get back quux.
base = "/uri/%s" % urllib.quote(quux_uri)
d = self.GET(base) d = self.GET(base)
d.addCallback(self.failUnlessIsQuuxDotTxt) d.addCallback(self.failUnlessIsQuuxDotTxt)
return d return d
@ -949,7 +934,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d return d
def test_PUT_FILE_URI_mdmf_extensions(self): def test_PUT_FILE_URI_mdmf_extensions(self):
base = "/uri/%s" % urllib.quote("%s:3:131073" % self._quux_txt_uri) base = "/uri/%s" % urllib.quote("%s:EXTENSIONSTUFF" % self._quux_txt_uri)
self._quux_new_contents = "new_contents" self._quux_new_contents = "new_contents"
d = self.GET(base) d = self.GET(base)
d.addCallback(lambda res: self.failUnlessIsQuuxDotTxt(res)) d.addCallback(lambda res: self.failUnlessIsQuuxDotTxt(res))
@ -959,22 +944,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
res)) res))
return d return d
def test_PUT_FILE_URI_mdmf_bare_cap(self):
elements = self._quux_txt_uri.split(":")
self.failUnlessEqual(len(elements), 6)
quux_uri = ":".join(elements[:len(elements) - 2])
base = "/uri/%s" % urllib.quote(quux_uri)
self._quux_new_contents = "new_contents" * 50000
d = self.GET(base)
d.addCallback(self.failUnlessIsQuuxDotTxt)
d.addCallback(lambda ignored: self.PUT(base, self._quux_new_contents))
d.addCallback(lambda ignored: self.GET(base))
d.addCallback(lambda res:
self.failUnlessEqual(res, self._quux_new_contents))
return d
def test_PUT_FILE_URI_mdmf_readonly(self): def test_PUT_FILE_URI_mdmf_readonly(self):
# We're not allowed to PUT things to a readonly cap. # We're not allowed to PUT things to a readonly cap.
base = "/uri/%s" % self._quux_txt_readonly_uri base = "/uri/%s" % self._quux_txt_readonly_uri
@ -1043,7 +1012,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
return d return d
def test_GET_FILEURL_info_mdmf_extensions(self): def test_GET_FILEURL_info_mdmf_extensions(self):
d = self.GET("/uri/%s:3:131073?t=info" % self._quux_txt_uri) d = self.GET("/uri/%s:STUFF?t=info" % self._quux_txt_uri)
def _got(res): def _got(res):
self.failUnlessIn("mutable file (mdmf)", res) self.failUnlessIn("mutable file (mdmf)", res)
self.failUnlessIn(self._quux_txt_uri, res) self.failUnlessIn(self._quux_txt_uri, res)
@ -1051,19 +1020,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
d.addCallback(_got) d.addCallback(_got)
return d return d
def test_GET_FILEURL_info_mdmf_bare_cap(self):
elements = self._quux_txt_uri.split(":")
self.failUnlessEqual(len(elements), 6)
quux_uri = ":".join(elements[:len(elements) - 2])
base = "/uri/%s?t=info" % urllib.quote(quux_uri)
d = self.GET(base)
def _got(res):
self.failUnlessIn("mutable file (mdmf)", res)
self.failUnlessIn(quux_uri, res)
d.addCallback(_got)
return d
def test_PUT_overwrite_only_files(self): def test_PUT_overwrite_only_files(self):
# create a directory, put a file in that directory. # create a directory, put a file in that directory.
contents, n, filecap = self.makefile(8) contents, n, filecap = self.makefile(8)
@ -1288,51 +1244,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
d.addCallback(_got_json, "sdmf") d.addCallback(_got_json, "sdmf")
return d return d
def test_GET_FILEURL_json_mdmf_extensions(self):
# A GET invoked against a URL that includes an MDMF cap with
# extensions should fetch the same JSON information as a GET
# invoked against a bare cap.
self._quux_txt_uri = "%s:3:131073" % self._quux_txt_uri
self._quux_txt_readonly_uri = "%s:3:131073" % self._quux_txt_readonly_uri
d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri))
d.addCallback(self.failUnlessIsQuuxJSON)
return d
def test_GET_FILEURL_json_mdmf_bare_cap(self):
elements = self._quux_txt_uri.split(":")
self.failUnlessEqual(len(elements), 6)
quux_uri = ":".join(elements[:len(elements) - 2])
# so failUnlessIsQuuxJSON will work.
self._quux_txt_uri = quux_uri
# we need to alter the readonly URI in the same way, again so
# failUnlessIsQuuxJSON will work
elements = self._quux_txt_readonly_uri.split(":")
self.failUnlessEqual(len(elements), 6)
quux_ro_uri = ":".join(elements[:len(elements) - 2])
self._quux_txt_readonly_uri = quux_ro_uri
base = "/uri/%s?t=json" % urllib.quote(quux_uri)
d = self.GET(base)
d.addCallback(self.failUnlessIsQuuxJSON)
return d
def test_GET_FILEURL_json_mdmf_bare_readonly_cap(self):
elements = self._quux_txt_readonly_uri.split(":")
self.failUnlessEqual(len(elements), 6)
quux_readonly_uri = ":".join(elements[:len(elements) - 2])
# so failUnlessIsQuuxJSON will work
self._quux_txt_readonly_uri = quux_readonly_uri
base = "/uri/%s?t=json" % quux_readonly_uri
d = self.GET(base)
# XXX: We may need to make a method that knows how to check for
# readonly JSON, or else alter that one so that it knows how to
# do that.
d.addCallback(self.failUnlessIsQuuxJSON, readonly=True)
return d
def test_GET_FILEURL_json_mdmf(self): def test_GET_FILEURL_json_mdmf(self):
d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri)) d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri))
d.addCallback(self.failUnlessIsQuuxJSON) d.addCallback(self.failUnlessIsQuuxJSON)

View File

@ -28,7 +28,6 @@ BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
SEP='(?::|%3A)' SEP='(?::|%3A)'
NUMBER='([0-9]+)' NUMBER='([0-9]+)'
NUMBER_IGNORE='(?:[0-9]+)' NUMBER_IGNORE='(?:[0-9]+)'
OPTIONAL_EXTENSION_FIELD = '(' + SEP + '[0-9' + SEP + ']+|)'
# "human-encoded" URIs are allowed to come with a leading # "human-encoded" URIs are allowed to come with a leading
# 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored. # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
@ -294,12 +293,6 @@ class WriteableSSKFileURI(_BaseURI):
def get_verify_cap(self): def get_verify_cap(self):
return SSKVerifierURI(self.storage_index, self.fingerprint) return SSKVerifierURI(self.storage_index, self.fingerprint)
def get_extension_params(self):
return []
def set_extension_params(self, params):
pass
class ReadonlySSKFileURI(_BaseURI): class ReadonlySSKFileURI(_BaseURI):
implements(IURI, IMutableFileURI) implements(IURI, IMutableFileURI)
@ -354,12 +347,6 @@ class ReadonlySSKFileURI(_BaseURI):
def get_verify_cap(self): def get_verify_cap(self):
return SSKVerifierURI(self.storage_index, self.fingerprint) return SSKVerifierURI(self.storage_index, self.fingerprint)
def get_extension_params(self):
return []
def set_extension_params(self, params):
pass
class SSKVerifierURI(_BaseURI): class SSKVerifierURI(_BaseURI):
implements(IVerifierURI) implements(IVerifierURI)
@ -404,53 +391,39 @@ class SSKVerifierURI(_BaseURI):
def get_verify_cap(self): def get_verify_cap(self):
return self return self
def get_extension_params(self):
return []
def set_extension_params(self, params):
pass
class WriteableMDMFFileURI(_BaseURI): class WriteableMDMFFileURI(_BaseURI):
implements(IURI, IMutableFileURI) implements(IURI, IMutableFileURI)
BASE_STRING='URI:MDMF:' BASE_STRING='URI:MDMF:'
STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
def __init__(self, writekey, fingerprint, params=[]): def __init__(self, writekey, fingerprint):
self.writekey = writekey self.writekey = writekey
self.readkey = hashutil.ssk_readkey_hash(writekey) self.readkey = hashutil.ssk_readkey_hash(writekey)
self.storage_index = hashutil.ssk_storage_index_hash(self.readkey) self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
assert len(self.storage_index) == 16 assert len(self.storage_index) == 16
self.fingerprint = fingerprint self.fingerprint = fingerprint
self.extension = params
@classmethod @classmethod
def init_from_human_encoding(cls, uri): def init_from_human_encoding(cls, uri):
mo = cls.HUMAN_RE.search(uri) mo = cls.HUMAN_RE.search(uri)
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = filter(lambda x: x != '', re.split(SEP, mo.group(3))) return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
@classmethod @classmethod
def init_from_string(cls, uri): def init_from_string(cls, uri):
mo = cls.STRING_RE.search(uri) mo = cls.STRING_RE.search(uri)
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = mo.group(3) return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
params = filter(lambda x: x != '', params.split(":"))
return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
def to_string(self): def to_string(self):
assert isinstance(self.writekey, str) assert isinstance(self.writekey, str)
assert isinstance(self.fingerprint, str) assert isinstance(self.fingerprint, str)
ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey), ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
base32.b2a(self.fingerprint)) base32.b2a(self.fingerprint))
if self.extension:
ret += ":"
ret += ":".join(self.extension)
return ret return ret
def __repr__(self): def __repr__(self):
@ -469,40 +442,30 @@ class WriteableMDMFFileURI(_BaseURI):
return True return True
def get_readonly(self): def get_readonly(self):
return ReadonlyMDMFFileURI(self.readkey, self.fingerprint, self.extension) return ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
def get_verify_cap(self): def get_verify_cap(self):
return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension) return MDMFVerifierURI(self.storage_index, self.fingerprint)
def get_extension_params(self):
return self.extension
def set_extension_params(self, params):
params = map(str, params)
self.extension = params
class ReadonlyMDMFFileURI(_BaseURI): class ReadonlyMDMFFileURI(_BaseURI):
implements(IURI, IMutableFileURI) implements(IURI, IMutableFileURI)
BASE_STRING='URI:MDMF-RO:' BASE_STRING='URI:MDMF-RO:'
STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
def __init__(self, readkey, fingerprint, params=[]): def __init__(self, readkey, fingerprint):
self.readkey = readkey self.readkey = readkey
self.storage_index = hashutil.ssk_storage_index_hash(self.readkey) self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
assert len(self.storage_index) == 16 assert len(self.storage_index) == 16
self.fingerprint = fingerprint self.fingerprint = fingerprint
self.extension = params
@classmethod @classmethod
def init_from_human_encoding(cls, uri): def init_from_human_encoding(cls, uri):
mo = cls.HUMAN_RE.search(uri) mo = cls.HUMAN_RE.search(uri)
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = mo.group(3) return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
params = filter(lambda x: x!= '', re.split(SEP, params))
return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
@classmethod @classmethod
def init_from_string(cls, uri): def init_from_string(cls, uri):
@ -510,19 +473,13 @@ class ReadonlyMDMFFileURI(_BaseURI):
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = mo.group(3) return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
params = filter(lambda x: x != '', params.split(":"))
return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
def to_string(self): def to_string(self):
assert isinstance(self.readkey, str) assert isinstance(self.readkey, str)
assert isinstance(self.fingerprint, str) assert isinstance(self.fingerprint, str)
ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey), ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
base32.b2a(self.fingerprint)) base32.b2a(self.fingerprint))
if self.extension:
ret += ":"
ret += ":".join(self.extension)
return ret return ret
def __repr__(self): def __repr__(self):
@ -544,55 +501,39 @@ class ReadonlyMDMFFileURI(_BaseURI):
return self return self
def get_verify_cap(self): def get_verify_cap(self):
return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension) return MDMFVerifierURI(self.storage_index, self.fingerprint)
def get_extension_params(self):
return self.extension
def set_extension_params(self, params):
params = map(str, params)
self.extension = params
class MDMFVerifierURI(_BaseURI): class MDMFVerifierURI(_BaseURI):
implements(IVerifierURI) implements(IVerifierURI)
BASE_STRING='URI:MDMF-Verifier:' BASE_STRING='URI:MDMF-Verifier:'
STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+'(:|$)')
HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$') HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+BASE32STR_256bits+'(:|$)')
def __init__(self, storage_index, fingerprint, params=[]): def __init__(self, storage_index, fingerprint):
assert len(storage_index) == 16 assert len(storage_index) == 16
self.storage_index = storage_index self.storage_index = storage_index
self.fingerprint = fingerprint self.fingerprint = fingerprint
self.extension = params
@classmethod @classmethod
def init_from_human_encoding(cls, uri): def init_from_human_encoding(cls, uri):
mo = cls.HUMAN_RE.search(uri) mo = cls.HUMAN_RE.search(uri)
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = mo.group(3) return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
params = filter(lambda x: x != '', re.split(SEP, params))
return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
@classmethod @classmethod
def init_from_string(cls, uri): def init_from_string(cls, uri):
mo = cls.STRING_RE.search(uri) mo = cls.STRING_RE.search(uri)
if not mo: if not mo:
raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls)) raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
params = mo.group(3) return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
params = filter(lambda x: x != '', params.split(":"))
return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
def to_string(self): def to_string(self):
assert isinstance(self.storage_index, str) assert isinstance(self.storage_index, str)
assert isinstance(self.fingerprint, str) assert isinstance(self.fingerprint, str)
ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index), ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
base32.b2a(self.fingerprint)) base32.b2a(self.fingerprint))
if self.extension:
ret += ':'
ret += ":".join(self.extension)
return ret return ret
def is_readonly(self): def is_readonly(self):
@ -607,9 +548,6 @@ class MDMFVerifierURI(_BaseURI):
def get_verify_cap(self): def get_verify_cap(self):
return self return self
def get_extension_params(self):
return self.extension
class _DirectoryBaseURI(_BaseURI): class _DirectoryBaseURI(_BaseURI):
implements(IURI, IDirnodeURI) implements(IURI, IDirnodeURI)
def __init__(self, filenode_uri=None): def __init__(self, filenode_uri=None):