From 74974b27fe0444388cd31aa4e1058e631d92417b Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Mon, 30 Nov 2009 13:10:09 -0800
Subject: [PATCH] Implement more coherent behavior when copying with
 dircaps/filecaps (closes #761). Patch by Kevan Carstensen.

---
 src/allmydata/scripts/tahoe_cp.py | 13 +++++--
 src/allmydata/test/test_cli.py    | 60 +++++++++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/src/allmydata/scripts/tahoe_cp.py b/src/allmydata/scripts/tahoe_cp.py
index 797f8782..8cf543da 100644
--- a/src/allmydata/scripts/tahoe_cp.py
+++ b/src/allmydata/scripts/tahoe_cp.py
@@ -441,13 +441,13 @@ class Copier:
         self.caps_only = options["caps-only"]
         self.cache = {}
         try:
-            self.try_copy()
+            status = self.try_copy()
+            return status
         except TahoeError, te:
             Failure().printTraceback(self.stderr)
             print >>self.stderr
             te.display(self.stderr)
             return 1
-        return 0
 
     def try_copy(self):
         source_specs = self.options.sources
@@ -498,6 +498,13 @@ class Copier:
             return self.copy_file(source, target)
 
         if isinstance(target, (LocalDirectoryTarget, TahoeDirectoryTarget)):
+            # We're copying to an existing directory -- make sure that we 
+            # have target names for everything
+            for (name, source) in sources:
+                if name is None and isinstance(source, TahoeFileSource):
+                    self.to_stderr(
+                        "error: you must specify a destination filename")
+                    return 1
             return self.copy_to_directory(sources, target)
 
         self.to_stderr("unknown target")
@@ -587,6 +594,8 @@ class Copier:
                 writecap = ascii_or_none(d.get("rw_uri"))
                 readcap = ascii_or_none(d.get("ro_uri"))
                 mutable = d.get("mutable", False) # older nodes don't provide it
+                if source_spec.rfind('/') != -1:
+                    name = source_spec[source_spec.rfind('/')+1:]
                 t = TahoeFileSource(self.nodeurl, mutable, writecap, readcap)
         return name, t
 
diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py
index b88ed4c0..fd12128c 100644
--- a/src/allmydata/test/test_cli.py
+++ b/src/allmydata/test/test_cli.py
@@ -934,6 +934,66 @@ class Cp(GridTestMixin, CLITestMixin, unittest.TestCase):
                                               dn, "tahoe:"))
         return d
 
+    def test_copy_using_filecap(self):
+        self.basedir = "cli/Cp/test_copy_using_filecap"
+        self.set_up_grid()
+        outdir = os.path.join(self.basedir, "outdir")
+        os.mkdir(outdir)
+        self.do_cli("create-alias", "tahoe")
+        fn1 = os.path.join(self.basedir, "Metallica")
+        fn2 = os.path.join(outdir, "Not Metallica")
+        fn3 = os.path.join(outdir, "test2")
+        DATA1 = "puppies" * 10000
+        open(fn1, "wb").write(DATA1)
+        d = self.do_cli("put", fn1)
+        def _put_file((rc, out, err)):
+            self.failUnlessEqual(rc, 0)
+            # keep track of the filecap
+            self.filecap = out.strip()
+        d.addCallback(_put_file)
+        # Let's try copying this to the disk using the filecap
+        #  cp FILECAP filename
+        d.addCallback(lambda res: self.do_cli("cp", self.filecap, fn2))
+        def _copy_file((rc, out, err)):
+            self.failUnlessEqual(rc, 0)
+            results = open(fn2, "r").read()
+            self.failUnlessEqual(results, DATA1)
+        # Test with ./ (see #761)
+        #  cp FILECAP localdir
+        d.addCallback(lambda res: self.do_cli("cp", self.filecap, outdir))
+        def _resp((rc, out, err)):
+            self.failUnlessEqual(rc, 1)
+            self.failUnlessIn("error: you must specify a destination filename",
+                              err)
+        d.addCallback(_resp)
+        # Create a directory, linked at tahoe:test
+        d.addCallback(lambda res: self.do_cli("mkdir", "tahoe:test"))
+        def _get_dir((rc, out, err)):
+            self.failUnlessEqual(rc, 0)
+            self.dircap = out.strip()
+        d.addCallback(_get_dir)
+        # Upload a file to the directory
+        d.addCallback(lambda res:
+            self.do_cli("put", fn1, "tahoe:test/test_file"))
+        d.addCallback(lambda (rc, out, err): self.failUnlessEqual(rc, 0))
+        #  cp DIRCAP/filename localdir
+        d.addCallback(lambda res:
+                      self.do_cli("cp",  self.dircap + "/test_file", outdir))
+        def _get_resp((rc, out, err)):
+            self.failUnlessEqual(rc, 0)
+            results = open(os.path.join(outdir, "test_file"), "r").read()
+            self.failUnlessEqual(results, DATA1)
+        d.addCallback(_get_resp)
+        #  cp -r DIRCAP/filename filename2
+        d.addCallback(lambda res:
+                      self.do_cli("cp",  self.dircap + "/test_file", fn3))
+        def _get_resp2((rc, out, err)):
+            self.failUnlessEqual(rc, 0)
+            results = open(fn3, "r").read()
+            self.failUnlessEqual(results, DATA1)
+        d.addCallback(_get_resp2)
+        return d
+
 class Backup(GridTestMixin, CLITestMixin, StallMixin, unittest.TestCase):
 
     def writeto(self, path, data):
-- 
2.45.2