=== modified file 'bin/duplicity'
--- bin/duplicity	2014-01-21 21:04:27 +0000
+++ bin/duplicity	2014-02-05 02:57:13 +0000
@@ -27,7 +27,7 @@
 # Please send mail to me or the mailing list if you find bugs or have
 # any suggestions.
 
-import getpass, gzip, os, sys, time, types
+import gzip, os, sys, time, types
 import traceback, platform, statvfs, resource, re
 import threading
 from datetime import datetime
@@ -37,9 +37,6 @@
 if os.path.exists(os.path.join(pwd, "../duplicity")):
     sys.path.insert(0, os.path.abspath(os.path.join(pwd, "../.")))
 
-import gettext
-gettext.install('duplicity', codeset='utf8')
-
 from duplicity import log
 log.setup()
 
@@ -65,6 +62,13 @@
 # If exit_val is not None, exit with given value at end.
 exit_val = None
 
+def getpass_safe(message):
+    # getpass() in Python 2.x will call str() on our prompt.  So we can't pass
+    # in non-ascii characters.
+    import getpass, locale
+    message = message.encode(locale.getpreferredencoding(), 'replace')
+    return getpass.getpass(message)
+
 def get_passphrase(n, action, for_signing = False):
     """
     Check to make sure passphrase is indeed needed, then get
@@ -160,19 +164,19 @@
                     if use_cache and globals.gpg_profile.signing_passphrase:
                         pass1 = globals.gpg_profile.signing_passphrase
                     else:
-                        pass1 = getpass.getpass(_("GnuPG passphrase for signing key:")+" ")
+                        pass1 = getpass_safe(_("GnuPG passphrase for signing key:")+" ")
                 else:
                     if use_cache and globals.gpg_profile.passphrase:
                         pass1 = globals.gpg_profile.passphrase
                     else:
-                        pass1 = getpass.getpass(_("GnuPG passphrase:")+" ")
+                        pass1 = getpass_safe(_("GnuPG passphrase:")+" ")
 
             if n == 1:
                 pass2 = pass1
             elif for_signing:
-                pass2 = getpass.getpass(_("Retype passphrase for signing key to confirm: "))
+                pass2 = getpass_safe(_("Retype passphrase for signing key to confirm: "))
             else:
-                pass2 = getpass.getpass(_("Retype passphrase to confirm: "))
+                pass2 = getpass_safe(_("Retype passphrase to confirm: "))
 
             if not pass1 == pass2:
                 print _("First and second passphrases do not match!  Please try again.")

=== modified file 'bin/rdiffdir'
--- bin/rdiffdir	2013-12-27 06:39:00 +0000
+++ bin/rdiffdir	2014-02-05 02:57:13 +0000
@@ -27,9 +27,6 @@
 
 import sys, getopt, gzip, os
 
-import gettext
-gettext.install('duplicity', codeset='utf8')
-
 from duplicity import diffdir
 from duplicity import patchdir
 from duplicity import log

=== added directory 'testing/overrides'
=== added file 'testing/overrides/gettext.py'
--- testing/overrides/gettext.py	1970-01-01 00:00:00 +0000
+++ testing/overrides/gettext.py	2014-02-05 02:57:13 +0000
@@ -0,0 +1,34 @@
+# -*- Mode:Python; indent-tabs-mode:nil; tab-width:4; encoding:utf8 -*-
+#
+# Copyright 2014 Michael Terry <mike@mterry.name>
+#
+# This file is part of duplicity.
+#
+# Duplicity is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 2 of the License, or (at your
+# option) any later version.
+#
+# Duplicity is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with duplicity; if not, write to the Free Software Foundation,
+# Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+# This is just a small override to the system gettext.py which allows us to
+# always return a string with fancy unicode characters, which will notify us
+# if we ever get a unicode->ascii translation by accident.
+
+def translation(*args, **kwargs):
+    class Translation:
+        ZWSP = u"​" # ZERO WIDTH SPACE, basically an invisible space separator
+        def install(self, **kwargs):
+            import __builtin__
+            __builtin__.__dict__['_'] = lambda x: x + self.ZWSP
+        def ungettext(self, one, more, n):
+            if n == 1: return one + self.ZWSP
+            else:      return more + self.ZWSP
+    return Translation()

=== modified file 'testing/run-tests'
--- testing/run-tests	2011-11-24 01:49:53 +0000
+++ testing/run-tests	2014-02-05 02:57:13 +0000
@@ -25,9 +25,9 @@
 
 THISDIR=$(pwd)
 export TZ=US/Central
-export LANG=
+export LANG=en_US.UTF-8
 # up for 'duplicity' module and here for 'helper.py'
-export PYTHONPATH="$(dirname $THISDIR):$THISDIR/helpers"
+export PYTHONPATH="$THISDIR/overrides:$(dirname $THISDIR):$THISDIR/helpers"
 export GNUPGHOME="$THISDIR/gnupg"
 export PATH="$(dirname $THISDIR)/bin:$PATH"
 

=== modified file 'testing/tests/finaltest.py'
--- testing/tests/finaltest.py	2012-11-24 19:45:09 +0000
+++ testing/tests/finaltest.py	2014-02-05 02:57:13 +0000
@@ -20,6 +20,7 @@
 # Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 
 import helper
+import pexpect
 import sys, os, unittest
 
 import duplicity.backend
@@ -50,7 +51,8 @@
     """
     Test backup/restore using duplicity binary
     """
-    def run_duplicity(self, arglist, options = [], current_time = None):
+    def run_duplicity(self, arglist, options = [], current_time = None,
+                      passphrase_input = None):
         """Run duplicity binary with given arguments and options"""
         options.append("--archive-dir testfiles/cache")
         cmd_list = ["duplicity"]
@@ -62,22 +64,23 @@
         cmd_list.extend(arglist)
         cmdline = " ".join(cmd_list)
         #print "Running '%s'." % cmdline
-        if not os.environ.has_key('PASSPHRASE'):
+        if passphrase_input is None and not os.environ.has_key('PASSPHRASE'):
             os.environ['PASSPHRASE'] = 'foobar'
-        return_val = os.system(cmdline)
+        (output, return_val) = pexpect.run(cmdline, withexitstatus=True,
+            events={'passphrase.*:': passphrase_input}) 
         if return_val:
             raise CmdError(return_val)
 
-    def backup(self, type, input_dir, options = [], current_time = None):
+    def backup(self, type, input_dir, options = [], **kwargs):
         """Run duplicity backup to default directory"""
         options = options[:]
         if type == "full":
             options.insert(0, 'full')
         args = [input_dir, "'%s'" % backend_url]
-        self.run_duplicity(args, options, current_time)
+        self.run_duplicity(args, options, **kwargs)
 
     def restore(self, file_to_restore = None, time = None, options = [],
-                current_time = None):
+                **kwargs):
         options = options[:] # just nip any mutability problems in bud
         assert not os.system("rm -rf testfiles/restore_out")
         args = ["'%s'" % backend_url, "testfiles/restore_out"]
@@ -85,17 +88,17 @@
             options.extend(['--file-to-restore', file_to_restore])
         if time:
             options.extend(['--restore-time', str(time)])
-        self.run_duplicity(args, options, current_time)
+        self.run_duplicity(args, options, **kwargs)
 
     def verify(self, dirname, file_to_verify = None, time = None, options = [],
-               current_time = None):
+               **kwargs):
         options = ["verify"] + options[:]
         args = ["'%s'" % backend_url, dirname]
         if file_to_verify:
             options.extend(['--file-to-restore', file_to_verify])
         if time:
             options.extend(['--restore-time', str(time)])
-        self.run_duplicity(args, options, current_time)
+        self.run_duplicity(args, options, **kwargs)
 
     def deltmp(self):
         """Delete temporary directories"""
@@ -255,6 +258,12 @@
         assert chain.start_time == 30000, chain.start_time
         assert chain.end_time == 40000, chain.end_time
 
+    def test_piped_password(self):
+        """Make sure that prompting for a password works"""
+        self.backup("full", "testfiles/empty_dir",
+                    passphrase_input="foobar\nfoobar\n")
+        self.restore(passphrase_input="foobar\n")
+
 class FinalTest1(FinalTest, unittest.TestCase):
     def setUp(self):
         assert not os.system("tar xzf testfiles.tar.gz > /dev/null 2>&1")