← Back to team overview

duplicity-team team mailing list archive

[Merge] lp:~mwilck/duplicity/0.7-series into lp:duplicity/0.7-series

 

Martin Wilck has proposed merging lp:~mwilck/duplicity/0.7-series into lp:duplicity/0.7-series.

Requested reviews:
  duplicity-team (duplicity-team)

For more details, see:
https://code.launchpad.net/~mwilck/duplicity/0.7-series/+merge/306333

GPG: enable truly non-interactive operation with gpg2

This patch fixes the IMO unexpected behavior that, when using GnuPG2, a pass phrase dialog always pops up for saving backups. This is particularly annoying when trying to do unattended / fully automatic backups.

The patch changes the behavior of the "--use-agent" option when GnuPG 2 is in use. The man page is updated accordingly. I am not sure if this would be too disruptive a change for the stable series. However I think this is a real improvement, even if some users of duplicity with gpg2 may need to change their command lines.

Successful builds for Trusty and Xenial under https://code.launchpad.net/~mwilck/+recipe/duplicity-mw.

-- 
Your team duplicity-team is requested to review the proposed merge of lp:~mwilck/duplicity/0.7-series into lp:duplicity/0.7-series.
=== modified file 'bin/duplicity.1'
--- bin/duplicity.1	2016-08-22 10:59:00 +0000
+++ bin/duplicity.1	2016-09-21 13:17:18 +0000
@@ -917,9 +917,15 @@
 if needed.
 .br
 .B Note:
-GnuPG 2 and newer ignore this option and will always use a running
-.B gpg-agent
-if no passphrase was delivered.
+Contrary to previous versions of duplicity, this option will also be honored
+by GnuPG 2 and newer versions. If GnuPG 2 is in use, duplicity passes the option
+.I --pinentry-mode=cancel
+to the the gpg process unless
+.I --use-agent
+is specified on the duplicity command line. This has the effect that GnuPG 2
+uses the agent only if
+.I --use-agent
+is given, just like GnuPG 1.
 
 .TP
 .BI "--verbosity " level ", -v" level

=== modified file 'duplicity/gpg.py'
--- duplicity/gpg.py	2015-11-05 15:36:58 +0000
+++ duplicity/gpg.py	2016-09-21 13:17:18 +0000
@@ -87,6 +87,20 @@
         else:
             self.hidden_recipients = []
 
+        self.gpg_major = self.get_gpg_major(globals.gpg_binary)
+
+    _version_re = re.compile(r'^gpg.*\(GnuPG\) (?P<maj>[0-9])\.[0-9]+\.[0-9]+$')
+
+    def get_gpg_major(self, binary):
+        gpg = gpginterface.GnuPG()
+        if binary is not None:
+            gpg.call = binary
+        res = gpg.run(["--version"], create_fhs=["stdout"])
+        line = res.handles["stdout"].readline().rstrip()
+        mtc = self._version_re.search(line)
+        if mtc is not None:
+            return int(mtc.group("maj"), 10)
+        raise GPGError("failed to determine gpg version of %s from %s" % (binary, line))
 
 class GPGFile:
     """
@@ -121,6 +135,10 @@
         gnupg.options.extra_args.append('--no-secmem-warning')
         if globals.use_agent:
             gnupg.options.extra_args.append('--use-agent')
+        elif profile.gpg_major == 2:
+            # This forces gpg2 to ignore the agent.
+            # Necessary to enforce truly non-interactive operation.
+            gnupg.options.extra_args.append('--pinentry-mode=cancel')
         if globals.gpg_options:
             for opt in globals.gpg_options.split():
                 gnupg.options.extra_args.append(opt)