Package: duplicity
Version: 0.5.06-2
Severity: wishlist
Tags: patch

I find myself attempting to perform secure backups that must transit a proxy
server to reach their destination. Generally I use PuTTY for these types of
connections, as it understands how to deal with the proxy (unlike OpenSSH).

Using duplicity and overriding with --scp-command=pscp --sftp-command=psftp
--ssh-options="-load <blah>" *ALMOST* works but dies because psftp does not
understand options on the "ls" command (specifically, "ls -1").

After staring at code for awhile, the path of least resistance seems to be
just adding PuTTY as an additional transport choice for duplicity. Your
python code is much more approachable than trying to extend psftp's "ls"
command. :-)

As a starting point, the attached diff shows how I created a functioning
PuTTY transport definition. (I left out the minor tweaks I made to a few
of the other files to define the --putty-options argument and putty://.)
I'm hoping a better coder could make this less fragile.

Thanks,
    -Scott

-- System Information:
Debian Release: squeeze/sid
  APT prefers testing
  APT policy: (900, 'testing'), (500, 'stable'), (400, 'oldstable'), (50, 
'unstable')
Architecture: i386 (x86_64)

Kernel: Linux 2.6.28-1-amd64 (SMP w/2 CPU cores)
Locale: LANG=C, LC_CTYPE=C (charmap=ANSI_X3.4-1968)
Shell: /bin/sh linked to /bin/bash

Versions of packages duplicity depends on:
ii  gnupg                         1.4.9-4    GNU privacy guard - a free PGP rep
ii  libc6                         2.9-4      GNU C Library: Shared libraries
ii  librsync1                     0.9.7-5    rsync remote-delta algorithm libra
ii  python                        2.5.4-2    An interactive high-level object-o
ii  python-central                0.6.11     register and build utility for Pyt
ii  python-gnupginterface         0.3.2-9    Python interface to GnuPG (GPG)
ii  python-pexpect                2.3-1      Python module for automating inter

duplicity recommends no packages.

Versions of packages duplicity suggests:
pn  ncftp                         <none>     (no description available)
pn  python-boto                   <none>     (no description available)

-- no debconf information
--- sshbackend.py       2009-01-09 09:05:11.000000000 -0500
+++ puttybackend.py     2009-03-20 14:37:04.000000000 -0400
@@ -19,7 +19,7 @@
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
 # The following can be redefined to use different shell commands from
-# ssh or scp or to add more arguments.  However, the replacements must
+# PuTTY or pscp or to add more arguments.  However, the replacements must
 # have the same syntax.  Also these strings will be executed by the
 # shell, so shouldn't have strange characters in them.
 
@@ -33,19 +33,19 @@
 import duplicity.pexpect as pexpect
 from duplicity.errors import *
 
-scp_command = "scp"
-sftp_command = "sftp"
+pscp_command = "pscp"
+psftp_command = "psftp"
 
 # default to batch mode using public-key encryption
-ssh_askpass = False
+putty_askpass = False
 
-# user added ssh options
-ssh_options = ""
+# user added putty options
+putty_options = ""
 
-class SSHBackend(duplicity.backend.Backend):
-    """This backend copies files using scp.  List not supported"""
+class PuTTYBackend(duplicity.backend.Backend):
+    """This backend copies files using pscp.  List not supported"""
     def __init__(self, parsed_url):
-        """scpBackend initializer"""
+        """pscpBackend initializer"""
         duplicity.backend.Backend.__init__(self, parsed_url)
 
         # host string of form [u...@]hostname
@@ -62,22 +62,22 @@
         self.remote_prefix = self.remote_dir + '/'
         # maybe use different ssh port
         if parsed_url.port:
-            self.ssh_options = ssh_options + " -oPort=%s" % parsed_url.port
+            self.putty_options = putty_options + " -P %s" % parsed_url.port
         else:
-            self.ssh_options = ssh_options
+            self.putty_options = putty_options
         # set up password
-        if ssh_askpass:
+        if putty_askpass:
             self.password = self.get_password()
         else:
             self.password = ''
 
-    def run_scp_command(self, commandline):
-        """ Run an scp command, responding to password prompts """
+    def run_pscp_command(self, commandline):
+        """ Run an pscp command, responding to password prompts """
         for n in range(1, globals.num_retries+1):
             log.Log("Running '%s' (attempt #%d)" % (commandline, n), 5)
             child = pexpect.spawn(commandline, timeout = globals.timeout)
             cmdloc = 0
-            if ssh_askpass:
+            if putty_askpass:
                 state = "authorizing"
             else:
                 state = "copying"
@@ -144,8 +144,8 @@
         log.Log("Giving up trying to execute '%s' after %d attempts" % 
(commandline, globals.num_retries), 1)
         raise BackendException("Error running '%s'" % commandline)
 
-    def run_sftp_command(self, commandline, commands):
-        """ Run an sftp command, responding to password prompts, passing 
commands from list """
+    def run_psftp_command(self, commandline, commands):
+        """ Run an psftp command, responding to password prompts, passing 
commands from list """
         for n in range(1, globals.num_retries+1):
             log.Log("Running '%s' (attempt #%d)" % (commandline, n), 5)
             child = pexpect.spawn(commandline, timeout = globals.timeout)
@@ -158,7 +158,7 @@
                                       "(?i)permission denied",
                                       "authenticity",
                                       "(?i)no such file or directory"])
-                log.Log("State = sftp, Before = '%s'" % 
(child.before.strip()), 9)
+                log.Log("State = psftp, Before = '%s'" % 
(child.before.strip()), 9)
                 if match == 0:
                     break
                 elif match == 1:
@@ -167,7 +167,7 @@
                 if match == 2:
                     if cmdloc < len(commands):
                         command = commands[cmdloc]
-                        log.Log("sftp command: '%s'" % (command,), 5)
+                        log.Log("psftp command: '%s'" % (command,), 5)
                         child.sendline(command)
                         cmdloc += 1
                     else:
@@ -194,19 +194,19 @@
         raise BackendException("Error running '%s'" % commandline)
 
     def put(self, source_path, remote_filename = None):
-        """Use scp to copy source_dir/filename to remote computer"""
+        """Use pscp to copy source_dir/filename to remote computer"""
         if not remote_filename: remote_filename = source_path.get_filename()
         commandline = "%s %s %s %s:%s%s" % \
-            (scp_command, self.ssh_options, source_path.name, self.host_string,
+            (pscp_command, self.putty_options, source_path.name, 
self.host_string,
              self.remote_prefix, remote_filename)
-        self.run_scp_command(commandline)
+        self.run_pscp_command(commandline)
 
     def get(self, remote_filename, local_path):
-        """Use scp to get a remote file"""
+        """Use pscp to get a remote file"""
         commandline = "%s %s %s:%s%s %s" % \
-            (scp_command, self.ssh_options, self.host_string, 
self.remote_prefix,
+            (pscp_command, self.putty_options, self.host_string, 
self.remote_prefix,
              remote_filename, local_path.name)
-        self.run_scp_command(commandline)
+        self.run_pscp_command(commandline)
         local_path.setdata()
         if not local_path.exists():
             raise BackendException("File %s not found locally after get "
@@ -214,7 +214,7 @@
 
     def list(self):
         """
-        List files available for scp
+        List files available for pscp
 
         Note that this command can get confused when dealing with
         files with newlines in them, as the embedded newlines cannot
@@ -222,24 +222,27 @@
         """
         commands = ["mkdir %s" % (self.remote_dir,),
                     "cd %s" % (self.remote_dir,),
-                    "ls -1"]
-        commandline = ("%s %s %s" % (sftp_command,
-                                     self.ssh_options,
+                    "ls"]
+        commandline = ("%s %s %s" % (psftp_command,
+                                     self.putty_options,
                                      self.host_string))
 
-        l = self.run_sftp_command(commandline, commands).split('\n')[1:]
-
-        return filter(lambda x: x, map(string.strip, l))
+        l = self.run_psftp_command(commandline, commands).split('\n')[1:]
+        crud = []
+        for fn in l[1:]:
+            if len(fn) > 56 and fn[56] != ".":
+                crud.append(string.strip(fn[56:]))
+        return crud
 
     def delete(self, filename_list):
         """
-        Runs sftp rm to delete files.  Files must not require quoting.
+        Runs psftp rm to delete files.  Files must not require quoting.
         """
         commands = ["cd %s" % (self.remote_dir,)]
         for fn in filename_list:
             commands.append("rm %s" % fn)
-        commandline = ("%s %s %s" % (sftp_command, self.ssh_options, 
self.host_string))
-        self.run_sftp_command(commandline, commands)
+        commandline = ("%s %s %s" % (psftp_command, self.putty_options, 
self.host_string))
+        self.run_psftp_command(commandline, commands)
 
-duplicity.backend.register_backend("ssh", SSHBackend)
-duplicity.backend.register_backend("scp", SSHBackend)
+duplicity.backend.register_backend("putty", PuTTYBackend)
+duplicity.backend.register_backend("pscp", PuTTYBackend)

Reply via email to