MDMF: remove extension fields from caps, tolerate arbitrary ones. Fixes #1526
authorBrian Warner <warner@lothar.com>
Sat, 1 Oct 2011 23:35:53 +0000 (00:35 +0100)
committerBrian Warner <warner@lothar.com>
Sat, 1 Oct 2011 23:35:53 +0000 (00:35 +0100)
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.

src/allmydata/mutable/filenode.py
src/allmydata/scripts/debug.py
src/allmydata/test/common.py
src/allmydata/test/test_cli.py
src/allmydata/test/test_dirnode.py
src/allmydata/test/test_mutable.py
src/allmydata/test/test_uri.py
src/allmydata/test/test_web.py
src/allmydata/uri.py

index 02f99b645cc6125080399d8f80106e440e2ffb62..cafe20fe90060f6c8e24204c0d67090d2518f12f 100644 (file)
@@ -118,17 +118,6 @@ class MutableFileNode:
         self._privkey = 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
 
     def create_with_keys(self, (pubkey, privkey), contents,
@@ -701,9 +690,6 @@ class MutableFileNode:
 
     def set_downloader_hints(self, hints):
         self._downloader_hints = hints
-        extensions = [ hints["k"], hints["segsize"] ]
-        self._uri.set_extension_params(extensions)
-
 
     def _did_upload(self, res, size):
         self._most_recent_size = size
index 16e57b33dce3ea2e489e6f4c8b31d599bac15ca6..b482fb9cb37670b96d7e7b814bed8d98123e148f 100644 (file)
@@ -369,8 +369,7 @@ def dump_MDMF_share(m, length, options):
         if base32.could_be_base32_encoded(piece):
             storage_index = base32.a2b(piece)
             fingerprint = hashutil.ssk_pubkey_fingerprint_hash(pubkey)
-            hints = [str(k), str(segsize)]
-            u = MDMFVerifierURI(storage_index, fingerprint, hints)
+            u = MDMFVerifierURI(storage_index, fingerprint)
             verify_cap = u.to_string()
             print >>out, "  verify-cap:", quote_output(verify_cap, quotemarks=False)
 
index 1e29f0d58054aa609e94a5af735dbedd0bc04b77..42890d81412c12d9d233e52a1fce125307057fa5 100644 (file)
@@ -208,7 +208,6 @@ class FakeMutableFileNode:
         data = initial_contents.read(initial_contents.get_size())
         data = "".join(data)
         self.all_contents[self.storage_index] = data
-        self.my_uri.set_extension_params([self._k, self._segsize])
         return defer.succeed(self)
     def _get_initial_contents(self, contents):
         if contents is None:
@@ -358,7 +357,6 @@ class FakeMutableFileNode:
         new_data = new_contents.read(new_contents.get_size())
         new_data = "".join(new_data)
         self.all_contents[self.storage_index] = new_data
-        self.my_uri.set_extension_params([self._k, self._segsize])
         return defer.succeed(None)
     def modify(self, modifier):
         # this does not implement FileTooLargeError, but the real one does
@@ -368,7 +366,6 @@ class FakeMutableFileNode:
         old_contents = self.all_contents[self.storage_index]
         new_data = modifier(old_contents, None, True)
         self.all_contents[self.storage_index] = new_data
-        self.my_uri.set_extension_params([self._k, self._segsize])
         return None
 
     # As actually implemented, MutableFilenode and MutableFileVersion
index 5d6b728512cc1e4a9d7a7df85d5b3dbfe3c2a775..fb6f143afff35b8fe494213176aa265e43701968 100644 (file)
@@ -1229,7 +1229,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
         d = self.do_cli("put", "--mutable", "--mutable-type=mdmf", fn1)
         def _got_cap((rc, out, err)):
             self.failUnlessEqual(rc, 0)
-            self.cap = out
+            self.cap = out.strip()
         d.addCallback(_got_cap)
         # Now try to write something to the cap using put.
         data2 = "data2" * 100000
@@ -1248,13 +1248,11 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
             self.failUnlessEqual(rc, 0)
             self.failUnlessEqual(out, data2)
         d.addCallback(_got_data)
-        # Now strip the extension information off of the cap and try
-        # to put something to it.
-        def _make_bare_cap(ignored):
-            cap = self.cap.split(":")
-            cap = ":".join(cap[:len(cap) - 2])
-            self.cap = cap
-        d.addCallback(_make_bare_cap)
+        # add some extension information to the cap and try to put something
+        # to it.
+        def _make_extended_cap(ignored):
+            self.cap = self.cap + ":Extension-Stuff"
+        d.addCallback(_make_extended_cap)
         data3 = "data3" * 100000
         fn3 = os.path.join(self.basedir, "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)
         def _got_cap((rc, out, err)):
             self.failUnlessEqual(rc, 0)
-            self.cap = out
+            self.cap = out.strip()
         d.addCallback(_got_cap)
         # Now try to write something to the cap using put.
         data2 = "data2" * 100000
index 9f9a5ad75ae4decff5fe0f8cd66b699841eb470f..145419bef375db9a52f857176c9b10ad8611f67c 100644 (file)
@@ -41,11 +41,11 @@ class MemAccum:
 setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861"
 one_uri = "URI:LIT:n5xgk" # LIT for "one"
 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:"
 tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT
 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_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')
index f88cb90e9ceb768028882b7cbe5348a9a7106856..d42a9ab46aeef638fca579fb47a3a9c72980bcf7 100644 (file)
@@ -375,28 +375,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
         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):
         d = self.nodemaker.create_mutable_file(version=MDMF_VERSION)
         def _created(n):
@@ -411,26 +389,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
         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):
         # MutableFileNodes and MutableFileVersions have an internal
         # 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)
         def _created(node):
             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))
         def _then(ignored):
@@ -619,37 +580,6 @@ class Filenode(unittest.TestCase, testutil.ShouldFailMixin):
         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):
         # Publishing an MDMF file should only cause one write for each
         # share that is to be published. Otherwise, we introduce
@@ -3068,62 +2998,6 @@ class Version(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin, \
         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):
         # 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
index 7662c7f8a05175832703dcf5ffc334db5de007ea..c6fb5bc85fb56f909e8df289f969f8a0d2a8f699 100644 (file)
@@ -1,5 +1,5 @@
 
-import re
+import os, re
 from twisted.trial import unittest
 from allmydata import uri
 from allmydata.util import hashutil, base32
@@ -430,84 +430,40 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         u4 = u2.get_verify_cap()
         self.failUnlessReallyEqual(u4, u2)
 
-    def test_mdmf_cap_extra_information(self):
-        # MDMF caps can be arbitrarily extended after the fingerprint
-        # and key/storage index fields. 
+    def test_mdmf_cap_ignore_extensions(self):
+        # MDMF caps can be arbitrarily extended after the fingerprint and
+        # 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)
-        self.failUnlessEqual([], u1.get_extension_params())
-
-        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)
-            self.failUnlessReallyEqual(self.writekey, u2.writekey)
-            self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint)
-            self.failIf(u2.is_readonly())
-            self.failUnless(u2.is_mutable())
-
-            c2 = u2.to_string()
-            u2n = uri.WriteableMDMFFileURI.init_from_string(c2)
-            self.failUnlessReallyEqual(u2, u2n)
-
-            # 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.failUnless(u3.is_readonly())
-            self.failUnless(u3.is_mutable())
-            self.failUnlessEqual(expected_extensions, u3.get_extension_params())
-
-            c3 = u3.to_string()
-            u3n = uri.ReadonlyMDMFFileURI.init_from_string(c3)
-            self.failUnlessReallyEqual(u3, u3n)
-
-            u4 = u3.get_verify_cap()
-            self.failUnlessReallyEqual(self.storage_index, u4.storage_index)
-            self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint)
-            self.failUnless(u4.is_readonly())
-            self.failIf(u4.is_mutable())
-
-            c4 = u4.to_string()
-            u4n = uri.MDMFVerifierURI.init_from_string(c4)
-            self.failUnlessReallyEqual(u4n, u4)
-
-            self.failUnlessEqual(expected_extensions, u4.get_extension_params())
-
-
-    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))
+
+        cap2 = cap+":I COME FROM THE FUTURE"
+        u2 = uri.WriteableMDMFFileURI.init_from_string(cap2)
+        self.failUnlessReallyEqual(self.writekey, u2.writekey)
+        self.failUnlessReallyEqual(self.fingerprint, u2.fingerprint)
+        self.failIf(u2.is_readonly())
+        self.failUnless(u2.is_mutable())
+
+        cap3 = cap+":"+os.urandom(40) # parse *that*!
+        u3 = uri.WriteableMDMFFileURI.init_from_string(cap3)
+        self.failUnlessReallyEqual(self.writekey, u3.writekey)
+        self.failUnlessReallyEqual(self.fingerprint, u3.fingerprint)
+        self.failIf(u3.is_readonly())
+        self.failUnless(u3.is_mutable())
+
+        cap4 = u1.get_readonly().to_string()+":ooh scary future stuff"
+        u4 = uri.from_string_mutable_filenode(cap4)
+        self.failUnlessReallyEqual(self.readkey, u4.readkey)
+        self.failUnlessReallyEqual(self.fingerprint, u4.fingerprint)
+        self.failUnless(u4.is_readonly())
+        self.failUnless(u4.is_mutable())
+
+        cap5 = u1.get_verify_cap().to_string()+":spoilers!"
+        u5 = uri.from_string(cap5)
+        self.failUnlessReallyEqual(self.storage_index, u5.storage_index)
+        self.failUnlessReallyEqual(self.fingerprint, u5.fingerprint)
+        self.failUnless(u5.is_readonly())
+        self.failIf(u5.is_mutable())
 
 
     def test_mdmf_valid_human_encoding(self):
@@ -517,22 +473,16 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         # test that a valid cap (with and without the traditional
         # separators) is recognized and accepted by the classes.
         w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
-        w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
-                                     ['131073', '3'])
         r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
-        r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
-                                     ['131073', '3'])
         v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
-        v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
-                                 ['131073', '3'])
 
-        # These will yield six different caps.
-        for o in (w1, w2, r1 , r2, v1, v2):
+        # These will yield three different caps.
+        for o in (w1, r1, v1):
             url = base + o.to_string()
             o1 = o.__class__.init_from_human_encoding(url)
             self.failUnlessReallyEqual(o1, o)
 
-            # Note that our cap will, by default, have : as separators. 
+            # Note that our cap will, by default, have : as separators.
             # But it's expected that users from, e.g., the WUI, will
             # have %3A as a separator. We need to make sure that the
             # initialization routine handles that, too.
@@ -550,17 +500,11 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         # test that a valid cap (with and without the traditional
         # separators) is recognized and accepted by the classes.
         w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
-        w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
-                                     ['131073', '3'])
         r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
-        r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
-                                     ['131073', '3'])
         v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
-        v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
-                                 ['131073', '3'])
 
-        # These will yield six different caps.
-        for o in (w1, w2, r1 , r2, v1, v2):
+        # These will yield three different caps.
+        for o in (w1, r1, v1):
             url = base + o.to_string()
             self.failUnlessRaises(uri.BadURIError,
                                   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
         # separators) is recognized and accepted by the classes.
         w1 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint)
-        w2 = uri.WriteableMDMFFileURI(self.writekey, self.fingerprint,
-                                     ['131073', '3'])
         r1 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
-        r2 = uri.ReadonlyMDMFFileURI(self.readkey, self.fingerprint,
-                                     ['131073', '3'])
         v1 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint)
-        v2 = uri.MDMFVerifierURI(self.storage_index, self.fingerprint,
-                                 ['131073', '3'])
 
-        # These will yield six different caps.
-        for o in (w1, w2, r1 , r2, v1, v2):
+        # These will yield three different caps.
+        for o in (w1, r1, v1):
             # not exhaustive, obviously...
             url = base + o.to_string() + "foobarbaz"
             url2 = base + "foobarbaz" + o.to_string()
@@ -603,16 +541,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         u3 = uri.from_string_mutable_filenode(cap)
         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)
         cap = u1.to_string()
         self.failUnless(uri.is_uri(cap))
@@ -621,15 +549,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         u3 = uri.from_string_mutable_filenode(cap)
         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)
         cap = u1.to_string()
         self.failUnless(uri.is_uri(cap))
@@ -638,15 +557,6 @@ class Mutable(testutil.ReallyEqualMixin, unittest.TestCase):
         u3 = uri.from_string_verifier(cap)
         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):
     def test_pack(self):
@@ -816,35 +726,6 @@ class Dirnode(testutil.ReallyEqualMixin, unittest.TestCase):
         d3 = uri.from_string(d2.to_string(), deep_immutable=True)
         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):
         writekey = "\x01" * 16
         fingerprint = "\x02" * 32
index 370f09a4b476d014308a61512f16307816e65e30..adf4f71ed9f970da2f9d78985dd0b6f76ae32df3 100644 (file)
@@ -892,22 +892,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     def test_GET_FILE_URI_mdmf_extensions(self):
-        base = "/uri/%s" % urllib.quote("%s:3:131073" % 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)
+        base = "/uri/%s" % urllib.quote("%s:RANDOMSTUFF" % self._quux_txt_uri)
         d = self.GET(base)
         d.addCallback(self.failUnlessIsQuuxDotTxt)
         return d
@@ -949,7 +934,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     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"
         d = self.GET(base)
         d.addCallback(lambda res: self.failUnlessIsQuuxDotTxt(res))
@@ -959,22 +944,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
                                                        res))
         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):
         # We're not allowed to PUT things to a readonly cap.
         base = "/uri/%s" % self._quux_txt_readonly_uri
@@ -1043,7 +1012,7 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         return d
 
     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):
             self.failUnlessIn("mutable file (mdmf)", res)
             self.failUnlessIn(self._quux_txt_uri, res)
@@ -1051,19 +1020,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d.addCallback(_got)
         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):
         # create a directory, put a file in that directory.
         contents, n, filecap = self.makefile(8)
@@ -1288,51 +1244,6 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
         d.addCallback(_got_json, "sdmf")
         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):
         d = self.GET("/uri/%s?t=json" % urllib.quote(self._quux_txt_uri))
         d.addCallback(self.failUnlessIsQuuxJSON)
index 62bf9b9aabd1708bd11dbc2cc3bb708255b15bb0..c500c7b31a72c5f665946adaf86955a94eee5194 100644 (file)
@@ -28,7 +28,6 @@ BASE32STR_256bits = '(%s{51}%s)' % (base32.BASE32CHAR, base32.BASE32CHAR_1bits)
 SEP='(?::|%3A)'
 NUMBER='([0-9]+)'
 NUMBER_IGNORE='(?:[0-9]+)'
-OPTIONAL_EXTENSION_FIELD = '(' + SEP + '[0-9' + SEP + ']+|)'
 
 # "human-encoded" URIs are allowed to come with a leading
 # 'http://127.0.0.1:(8123|3456)/uri/' that will be ignored.
@@ -294,12 +293,6 @@ class WriteableSSKFileURI(_BaseURI):
     def get_verify_cap(self):
         return SSKVerifierURI(self.storage_index, self.fingerprint)
 
-    def get_extension_params(self):
-        return []
-
-    def set_extension_params(self, params):
-        pass
-
 class ReadonlySSKFileURI(_BaseURI):
     implements(IURI, IMutableFileURI)
 
@@ -354,12 +347,6 @@ class ReadonlySSKFileURI(_BaseURI):
     def get_verify_cap(self):
         return SSKVerifierURI(self.storage_index, self.fingerprint)
 
-    def get_extension_params(self):
-        return []
-
-    def set_extension_params(self, params):
-        pass
-
 class SSKVerifierURI(_BaseURI):
     implements(IVerifierURI)
 
@@ -404,53 +391,39 @@ class SSKVerifierURI(_BaseURI):
     def get_verify_cap(self):
         return self
 
-    def get_extension_params(self):
-        return []
-
-    def set_extension_params(self, params):
-        pass
-
 class WriteableMDMFFileURI(_BaseURI):
     implements(IURI, IMutableFileURI)
 
     BASE_STRING='URI:MDMF:'
-    STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
-    HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF'+SEP+BASE32STR_128bits+SEP+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+'(:|$)')
 
-    def __init__(self, writekey, fingerprint, params=[]):
+    def __init__(self, writekey, fingerprint):
         self.writekey = writekey
         self.readkey = hashutil.ssk_readkey_hash(writekey)
         self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
         assert len(self.storage_index) == 16
         self.fingerprint = fingerprint
-        self.extension = params
 
     @classmethod
     def init_from_human_encoding(cls, uri):
         mo = cls.HUMAN_RE.search(uri)
         if not mo:
             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)), params)
+        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     @classmethod
     def init_from_string(cls, uri):
         mo = cls.STRING_RE.search(uri)
         if not mo:
             raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
-        params = mo.group(3)
-        params = filter(lambda x: x != '', params.split(":"))
-        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
+        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     def to_string(self):
         assert isinstance(self.writekey, str)
         assert isinstance(self.fingerprint, str)
         ret = 'URI:MDMF:%s:%s' % (base32.b2a(self.writekey),
                                   base32.b2a(self.fingerprint))
-        if self.extension:
-            ret += ":"
-            ret += ":".join(self.extension)
-
         return ret
 
     def __repr__(self):
@@ -469,40 +442,30 @@ class WriteableMDMFFileURI(_BaseURI):
         return True
 
     def get_readonly(self):
-        return ReadonlyMDMFFileURI(self.readkey, self.fingerprint, self.extension)
+        return ReadonlyMDMFFileURI(self.readkey, self.fingerprint)
 
     def get_verify_cap(self):
-        return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension)
-
-    def get_extension_params(self):
-        return self.extension
-
-    def set_extension_params(self, params):
-        params = map(str, params)
-        self.extension = params
+        return MDMFVerifierURI(self.storage_index, self.fingerprint)
 
 class ReadonlyMDMFFileURI(_BaseURI):
     implements(IURI, IMutableFileURI)
 
     BASE_STRING='URI:MDMF-RO:'
-    STRING_RE=re.compile('^' +BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
-    HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-RO'+SEP+BASE32STR_128bits+SEP+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+'(:|$)')
 
-    def __init__(self, readkey, fingerprint, params=[]):
+    def __init__(self, readkey, fingerprint):
         self.readkey = readkey
         self.storage_index = hashutil.ssk_storage_index_hash(self.readkey)
         assert len(self.storage_index) == 16
         self.fingerprint = fingerprint
-        self.extension = params
 
     @classmethod
     def init_from_human_encoding(cls, uri):
         mo = cls.HUMAN_RE.search(uri)
         if not mo:
             raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
-        params = mo.group(3)
-        params = filter(lambda x: x!= '', re.split(SEP, params))
-        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
+        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     @classmethod
     def init_from_string(cls, uri):
@@ -510,19 +473,13 @@ class ReadonlyMDMFFileURI(_BaseURI):
         if not mo:
             raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
 
-        params = mo.group(3)
-        params = filter(lambda x: x != '', params.split(":"))
-        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
+        return cls(base32.a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     def to_string(self):
         assert isinstance(self.readkey, str)
         assert isinstance(self.fingerprint, str)
         ret = 'URI:MDMF-RO:%s:%s' % (base32.b2a(self.readkey),
                                      base32.b2a(self.fingerprint))
-        if self.extension:
-            ret += ":"
-            ret += ":".join(self.extension)
-
         return ret
 
     def __repr__(self):
@@ -544,55 +501,39 @@ class ReadonlyMDMFFileURI(_BaseURI):
         return self
 
     def get_verify_cap(self):
-        return MDMFVerifierURI(self.storage_index, self.fingerprint, self.extension)
-
-    def get_extension_params(self):
-        return self.extension
-
-    def set_extension_params(self, params):
-        params = map(str, params)
-        self.extension = params
+        return MDMFVerifierURI(self.storage_index, self.fingerprint)
 
 class MDMFVerifierURI(_BaseURI):
     implements(IVerifierURI)
 
     BASE_STRING='URI:MDMF-Verifier:'
-    STRING_RE=re.compile('^'+BASE_STRING+BASE32STR_128bits+':'+BASE32STR_256bits+OPTIONAL_EXTENSION_FIELD+'$')
-    HUMAN_RE=re.compile('^'+OPTIONALHTTPLEAD+'URI'+SEP+'MDMF-Verifier'+SEP+BASE32STR_128bits+SEP+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+'(:|$)')
 
-    def __init__(self, storage_index, fingerprint, params=[]):
+    def __init__(self, storage_index, fingerprint):
         assert len(storage_index) == 16
         self.storage_index = storage_index
         self.fingerprint = fingerprint
-        self.extension = params
 
     @classmethod
     def init_from_human_encoding(cls, uri):
         mo = cls.HUMAN_RE.search(uri)
         if not mo:
             raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
-        params = mo.group(3)
-        params = filter(lambda x: x != '', re.split(SEP, params))
-        return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
+        return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     @classmethod
     def init_from_string(cls, uri):
         mo = cls.STRING_RE.search(uri)
         if not mo:
             raise BadURIError("'%s' doesn't look like a %s cap" % (uri, cls))
-        params = mo.group(3)
-        params = filter(lambda x: x != '', params.split(":"))
-        return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)), params)
+        return cls(si_a2b(mo.group(1)), base32.a2b(mo.group(2)))
 
     def to_string(self):
         assert isinstance(self.storage_index, str)
         assert isinstance(self.fingerprint, str)
         ret = 'URI:MDMF-Verifier:%s:%s' % (si_b2a(self.storage_index),
                                            base32.b2a(self.fingerprint))
-        if self.extension:
-            ret += ':'
-            ret += ":".join(self.extension)
-
         return ret
 
     def is_readonly(self):
@@ -607,9 +548,6 @@ class MDMFVerifierURI(_BaseURI):
     def get_verify_cap(self):
         return self
 
-    def get_extension_params(self):
-        return self.extension
-
 class _DirectoryBaseURI(_BaseURI):
     implements(IURI, IDirnodeURI)
     def __init__(self, filenode_uri=None):