[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#871937: marked as done (stretch-pu: package monkeysign/2.2.3)



Your message dated Sun, 2 Dec 2018 16:47:48 +0100
with message-id <20181202154748.GE20332@tomate.cristau.org>
and subject line Re: Bug#871937: stretch-pu: package monkeysign/2.2.3
has caused the Debian Bug report #871937,
regarding stretch-pu: package monkeysign/2.2.3
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
871937: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=871937
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: stretch
User: release.debian.org@packages.debian.org
Usertags: pu

Hi,

I am working on a new release of Monkeysign, which I'd like to upload
in Debian. If it would be just me, I would tag the current HEAD with
2.2.4, considering the changes are mostly minor and non-disruptive:

angela:monkeysign$ git diff 2.2.3 --stat
 CONTRIBUTING.rst                    |   9 +-
 debian/gbp.conf                     |   2 +-
 doc/usage.rst                       |   4 +
 monkeysign/cli.py                   |  11 +-
 monkeysign/gpg.py                   |  35 ++--
 monkeysign/gtkui.py                 |  81 ++++++---
 monkeysign/tests/files/7B75921E.asc | 331 ++++++++++++++++++++-----------------
 monkeysign/tests/test_gpg.py        |  21 +--
 monkeysign/tests/test_ui.py         | 147 ++++++++++++-----
 monkeysign/ui.py                    | 168 ++++++++++++-------
 po/nl.po                            | 725 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 11 files changed, 1225 insertions(+), 309 deletions(-)

as you can see, more than half of the diff (725 lines) is just a
translation file update. The rest is fixes for tests and critical
bugfixes (although the bugfixes have not been reported directly in the
Debian BTS, but discovered through my own testing).

Attached is the actual diff. Should I upload this as 2.2.4 to unstable
and stable-pu? Or should i minimize this diff to a bare minimum and
release a more targeted 2.2.4 to stable and a 2.3.0 to unstable?

Thanks for the feedback!

A.

-- System Information:
Debian Release: 9.1
  APT prefers stable
  APT policy: (500, 'stable')
Architecture: amd64 (x86_64)

Kernel: Linux 4.9.0-3-amd64 (SMP w/2 CPU cores)
Locale: LANG=fr_CA.UTF-8, LC_CTYPE=fr_CA.UTF-8 (charmap=UTF-8), LANGUAGE=fr_CA.UTF-8 (charmap=UTF-8)
Shell: /bin/sh linked to /bin/dash
Init: systemd (via /run/systemd/system)
diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst
index 6e0e004..cf9e3ea 100644
--- a/CONTRIBUTING.rst
+++ b/CONTRIBUTING.rst
@@ -351,9 +351,12 @@ To renew the keys, try::
 
     mkdir ~/.gpg-tmp
     chmod 700 ~/.gpg-tmp
-    gpg --homedir ~/.gpg-tmp --import 7B75921E.asc
+    gpg --homedir ~/.gpg-tmp --import monkeysign/tests/files/7B75921E.asc
     gpg --homedir ~/.gpg-tmp --refresh-keys 8DC901CE64146C048AD50FBB792152527B75921E
-    gpg --homedir ~/.gpg-tmp --export-options export-minimal --armor --export 8DC901CE64146C048AD50FBB792152527B75921E > 7B75921E.asc
+    gpg --homedir ~/.gpg-tmp --export-options export-minimal --armor --export 8DC901CE64146C048AD50FBB792152527B75921E > monkeysign/tests/files/7B75921E.asc
+
+Once that is done, the ``@skipIfDatePassed`` tests need to be adjusted
+to not be skipped anymore.
 
 It is also possible the key is just expired and there is no replacement.
 In this case the solution is to try and find a similar test case and
@@ -407,10 +410,10 @@ those: ``devscripts``, ``git``, ``git-buildpackage``, ``pip`` and
      monkeysign --version
      monkeysign --test
      monkeyscan
+     dpkg --remove monkeysign
 
 6. build and test Python "wheel"::
 
-     dpkg --remove monkeysign
      python setup.py bdist_wheel
      pip install dist/*.whl
      monkeysign --version
diff --git a/debian/gbp.conf b/debian/gbp.conf
index cb1505f..6513d67 100644
--- a/debian/gbp.conf
+++ b/debian/gbp.conf
@@ -1,3 +1,3 @@
 [DEFAULT]
-debian-branch = 2.2.x
+debian-branch = 2.x
 debian-tag = %(version)s
diff --git a/doc/usage.rst b/doc/usage.rst
index 7a769b1..c825a5a 100644
--- a/doc/usage.rst
+++ b/doc/usage.rst
@@ -141,6 +141,10 @@ examples of known working configurations.
 
     monkeysign --mua "thunderbird -compose to=%(to)s,subject=%(subject)s,body=%(body)s,attachment=%(attach)s" [...]
 
+  .. note:: Thunerbird fails to respect the attachment parameter in
+            versions before 52.1.1, see :bts:`837771` for more
+            details.
+
 * Mutt::
 
     monkeysign --mua "mutt -a %(attach)s -s %(subject)s -i %(body)s %(to)s" [...]
diff --git a/monkeysign/cli.py b/monkeysign/cli.py
index 62901c1..12745ee 100644
--- a/monkeysign/cli.py
+++ b/monkeysign/cli.py
@@ -20,10 +20,13 @@
 import sys
 import os
 import getpass
+import logging
 
 from monkeysign.ui import MonkeysignUi
 import monkeysign.translation
 
+logger = logging.getLogger(__name__)
+
 class MonkeysignCli(MonkeysignUi):
     """sign a key in a safe fashion.
 
@@ -48,11 +51,11 @@ def main(self):
                 os.environ['GPG_TTY'] = os.ttyname(sys.stdin.fileno())
             except OSError as e:
                 if e.errno == errno.ENOTTY:
-                    self.warn(_('cannot find your TTY, GPG may freak out if you do not set the GPG_TTY environment'))
+                    logger.warning(_('cannot find your TTY, GPG may freak out if you do not set the GPG_TTY environment'))
                 else:
                     raise
             else:
-                self.log(_('reset GPG_TTY to %s') % os.environ['GPG_TTY'])
+                logger.info(_('reset GPG_TTY to %s'), os.environ['GPG_TTY'])
 
         # 1. fetch the key into a temporary keyring
         self.find_key()
@@ -60,7 +63,7 @@ def main(self):
         # 2. copy the signing key secrets into the keyring
         self.copy_secrets()
 
-        self.warn(_('Preparing to sign with this key\n\n%s') % self.signing_key)
+        logger.warning(_('Preparing to sign with this key\n\n%s'), self.signing_key)
 
         # 3. for every user id (or all, if -a is specified)
         # 3.1. sign the uid, using gpg-agent
@@ -104,7 +107,7 @@ def choose_uid(self, prompt, key):
             # workaround http://bugs.python.org/issue7768
             pattern = raw_input(prompt.encode(sys.stdout.encoding or locale.getpreferredencoding(True)))
             while not (pattern in allowed_uids or (pattern.isdigit() and int(pattern)-1 in range(0,len(allowed_uids)))):
-                print _('invalid uid')
+                logger.warning(_('invalid uid'))
                 pattern = raw_input(prompt.encode(sys.stdout.encoding or locale.getpreferredencoding(True)))
             if pattern.isdigit():
                 pattern = allowed_uids[int(pattern)-1]
diff --git a/monkeysign/gpg.py b/monkeysign/gpg.py
index 223073c..e40ca59 100644
--- a/monkeysign/gpg.py
+++ b/monkeysign/gpg.py
@@ -70,11 +70,14 @@ class (if you do not want to access your regular keyring but an empty
 import errno
 import os, tempfile, shutil, subprocess, re
 from datetime import datetime
+import logging
 
 from StringIO import StringIO
 
 import monkeysign.translation
 
+logger = logging.getLogger(__name__)
+
 class Context():
     """Python wrapper for GnuPG
 
@@ -105,10 +108,6 @@ class Context():
                 'list-options': 'show-sig-subpackets,show-uid-validity,show-unusable-uids,show-unusable-subkeys,show-keyring,show-sig-expire',
                 }
 
-    # whether to paste output here and there
-    # if not false, needs to be a file descriptor
-    debug = False
-
     def __init__(self):
         self.options = dict(Context.options) # copy
 
@@ -177,15 +176,14 @@ def call_command(self, command, stdin=None):
         we can optionnally watch for a confirmation pattern on the
         statusfd.
         """
+        logger.debug('command: %s', self.build_command(command))
         proc = subprocess.Popen(self.build_command(command),  # nosec
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
                                 stderr=subprocess.PIPE)
         (self.stdout, self.stderr) = proc.communicate(stdin)
         self.returncode = proc.returncode
-        if self.debug:
-            print >>self.debug, 'command:', self.build_command(command)
-            print >>self.debug, 'ret:', self.returncode, 'stdout:', self.stdout, 'stderr:', self.stderr
+        logger.debug('ret: %s stdout: %s stderr: %s', self.returncode, self.stdout, self.stderr)
         return proc.returncode == 0
 
     def seek_pattern(self, fd, pattern):
@@ -204,11 +202,11 @@ def seek_pattern(self, fd, pattern):
         line = fd.readline()
         match = re.search(pattern, line)
         while line and not match:
-            if self.debug: print >>self.debug, "skipped:", line,
+            logger.debug("skipped: %s", line)
             line = fd.readline()
             match = re.search(pattern, line)
         if match:
-            if self.debug: print >>self.debug, "FOUND:", line,
+            logger.debug("FOUND: %s", line)
             return match
         else:
             raise GpgProtocolError(self.returncode, _("could not find pattern '%s' in input, last skipped '%s'") % (pattern, line))
@@ -237,14 +235,16 @@ def expect_pattern(self, fd, pattern):
         ignored = ('[GNUPG:] KEYEXPIRED', '[GNUPG:] SIGEXPIRED', '[GNUPG:] KEY_CONSIDERED', 'gpg: ')
 
         while line and line.startswith(ignored):
-            if self.debug: print >>self.debug, "IGNORED:", line,
+            logger.debug("IGNORED: %s", line)
             line = fd.readline()
 
         match = re.search(pattern, line)
 
-        if self.debug:
-            if match: print >>self.debug, "FOUND:", line,
-            else: print >>self.debug, "SKIPPED:", line,
+        if match:
+            logger.debug("FOUND: %s", line)
+        else:
+            logger.debug("SKIPPED: %s",line)
+
         if not match:
             raise GpgProtocolError(self.returncode, 'expected "%s", found "%s"' % (pattern, line))
         return match
@@ -262,8 +262,7 @@ def write(self, fd, message):
         but really, the pipes are often setup outside of here so the
         fd is hardcoded here
         """
-        if self.debug:
-            print >>self.debug, "WROTE:", message
+        logger.debug("WROTE: %s", message)
         print >>fd, message
 
     def version(self):
@@ -468,8 +467,7 @@ def decrypt_data(self, data):
 
     def del_uid(self, fingerprint, pattern):
         command = self.context.build_command(['edit-key', fingerprint])
-        if self.context.debug:
-            print >>self.context.debug, 'command:', command
+        logger.debug('command: %s', command)
         proc = subprocess.Popen(command,  # nosec
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
@@ -515,8 +513,7 @@ def sign_key(self, pattern, signall = False, local = False):
         # output of --sign-key
         command = self.context.build_command([['sign-key',
                                                'lsign-key'][local], pattern])
-        if self.context.debug:
-            print >>self.context.debug, 'command:', command
+        logger.debug('command: %s', command)
         proc = subprocess.Popen(command,  # nosec
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE,
diff --git a/monkeysign/gtkui.py b/monkeysign/gtkui.py
index ff140df..e23b6be 100644
--- a/monkeysign/gtkui.py
+++ b/monkeysign/gtkui.py
@@ -26,6 +26,8 @@
 import stat
 import subprocess  # nosec
 import webbrowser
+import logging
+import warnings
 
 from glib import GError
 import gobject
@@ -44,6 +46,8 @@
 from monkeysign.msg_exception import errorhandler
 import monkeysign
 
+logger = logging.getLogger(__name__)
+
 
 class MonkeysignScanUi(MonkeysignUi):
     """sign a key in a safe fashion using a webcam to scan for qr-codes
@@ -88,6 +92,10 @@ def main(self):
         self.window = MonkeysignScan()
         self.window.msui = self
 
+        # Add a handler to the root logger that displays warnings and
+        # errors as dialogs
+        logging.getLogger().addHandler(GTKLoggingHandler(self.window))
+
         # XXX: this probably belongs lower in the stack,
         # because we don't want to create a temporary keyring
         # just when we start the graphical UI, but instead
@@ -129,12 +137,14 @@ def abort(self, prompt):
         self.window.resume_capture()
 
     def warn(self, prompt):
-        """display the message but let things go"""
-        md = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT,
-                               gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, prompt)
-        with gtk.gdk.lock:
-            md.run()
-        md.destroy()
+        """display the message but let things go
+
+        DEPRECATED: This method has been deprecated. Use Logger.warning instead.
+        """
+        warning = ("This method is deprecated, and will be removed in a future version. "
+                   "As alternative, use Logger.warning()")
+        warnings.warn(warning, DeprecationWarning)
+        logger.warning(prompt)
 
     def choose_uid(self, prompt, key):
         md = gtk.Dialog(prompt, self.window, gtk.DIALOG_DESTROY_WITH_PARENT,
@@ -159,11 +169,11 @@ def choose_uid(self, prompt, key):
 
         label = None
         if response == gtk.RESPONSE_ACCEPT:
-            self.log(_('okay, signing'))
+            logger.info(_('okay, signing'))
             label = [radio for radio in self.uid_radios.get_group()
                      if radio.get_active()][0].get_label()
         else:
-            self.log(_('user denied signature'))
+            logger.info(_('user denied signature'))
         md.destroy()
         return label
 
@@ -232,7 +242,7 @@ def set_icon(self):
         except GError:
             # misconfigured, ignore
             # XXX: no access to logging, again
-            # self.msui.warn("could not find icon: %s" % e)
+            # logger.warning("could not find icon: %s", e)
             pass
         except pkg_resources.DistributionNotFound:
             # not installed system-wide, ignore
@@ -469,9 +479,15 @@ def import_image(self, widget):
                 if not verified:
                     raise GpgRuntimeError(0, _('cannot find signature for image file'))
             except GpgRuntimeError :
-                self.msui.warn(_("The image provided cannot be verified using a trusted OpenPGP signature.\n\nMake sure the image comes from a trusted source (e.g. your own camera, which you have never left unsurveilled) before signing this!\n\nDO NOT SIGN UNTRUSTED FINGERPRINTS!\n\nTo get rid of this warning, if you really trust this image, use the following command to sign the file\n\n    gpg -s --detach %s\n") % filename)
+                logger.warning(_("The image provided cannot be verified using a trusted OpenPGP signature.\n\n"
+                                 "Make sure the image comes from a trusted source (e.g. your own camera, which "
+                                 "you have never left unsurveilled) before signing this!\n\nDO NOT SIGN UNTRUSTED "
+                                 "FINGERPRINTS!\n\n"
+                                 "To get rid of this warning, if you really trust this image, use the following "
+                                 "command to sign the file\n\n"
+                                 "    gpg -s --detach %s\n"), filename)
             else:
-                self.msui.log(_('image signature verified successfully'))
+                logger.info(_('image signature verified successfully'))
             self.scan_image(filename)
         return
 
@@ -513,7 +529,7 @@ def scan_image(self, filename):
             self.process_scan(symbol.data)
             found = True
         if not found:
-            self.msui.warn(_('no data found in image!'))
+            logger.warning(_('no data found in image!'))
 
     def save_qrcode(self, widget=None):
         """Use a file chooser dialog to enable user to save the current QR
@@ -521,7 +537,7 @@ def save_qrcode(self, widget=None):
         if self.active_key is None:
             # XXX: without this, warn() freezes, go figure
             gtk.gdk.threads_leave()
-            self.msui.warn(_('No identity selected. Select one from the identiy menu or generate a OpenPGP key if none is available.'))
+            logger.warning(_('No identity selected. Select one from the identiy menu or generate a OpenPGP key if none is available.'))
             return
         key = self.active_key
         image = self.make_qrcode(key.fpr)
@@ -547,7 +563,7 @@ def print_op(self, widget=None):
         if self.active_key is None:
             # XXX: without this, warn() freezes, go figure
             gtk.gdk.threads_leave()
-            self.msui.warn(_('No identity selected. Select one from the identiy menu or generate a OpenPGP key if none is available.'))
+            logger.warning(_('No identity selected. Select one from the identiy menu or generate a OpenPGP key if none is available.'))
             return
         keyid = self.active_key.subkeys[0].keyid()
         print_op = gtk.PrintOperation()
@@ -647,7 +663,7 @@ def watch_out_callback(self, pid, condition):
             # this is actually because the key was
             # imported without having to create a dialog
             pass
-        self.msui.log(_('fetching finished'))
+        logger.info(_('fetching finished'))
         if condition == 0:
             # 2. copy the signing key secrets into the keyring
             self.msui.copy_secrets()
@@ -697,7 +713,7 @@ def decoded(self, zbar, data):
     def process_scan(self, data):
         """process zbar-scanned data"""
 
-        self.msui.log(_('zbar captured a frame, looking for 40 character hexadecimal fingerprint in %s') % data)
+        logger.info(_('zbar captured a frame, looking for 40 character hexadecimal fingerprint in %s'), data)
         m = re.search("((?:[0-9A-F]{4}\s*){10})", data, re.IGNORECASE)
 
         if m is not None:
@@ -709,8 +725,8 @@ def process_scan(self, data):
             # interactive but that's ugly as hell - find_key() should
             # take a callback maybe?
             # 1.a) from the local keyring
-            self.msui.log(_('looking for key %s in your keyring')
-                          % self.msui.pattern)
+            logger.info(_('looking for key %s in your keyring'),
+                        self.msui.pattern)
             self.msui.keyring.context.set_option('export-options',
                                                  'export-minimal')
             if self.msui.tmpkeyring.import_data(self.msui.keyring.export_data(self.msui.pattern)):
@@ -728,7 +744,7 @@ def process_scan(self, data):
             if self.msui.options.keyserver is not None:
                 self.msui.tmpkeyring.context.set_option('keyserver', self.msui.options.keyserver)
             command = self.msui.tmpkeyring.context.build_command(['recv-keys', self.msui.pattern])
-            self.msui.log('cmd: ' + str(command))
+            logger.info('cmd: ' + str(command))
             self.dialog = gtk.Dialog(title=_('Please wait'), parent=None, flags=gtk.DIALOG_MODAL, buttons=None)
             self.dialog.add_button('gtk-cancel', gtk.RESPONSE_CANCEL)
             message = gtk.Label(_('Retrieving public key from server...'))
@@ -748,7 +764,7 @@ def process_scan(self, data):
             if self.dialog.run() == gtk.RESPONSE_CANCEL:
                 proc.kill()
         else:
-            self.msui.warn(_('data found in barcode does not match a OpenPGP fingerprint pattern: %s') % data)
+            logger.warning(_('data found in barcode does not match a OpenPGP fingerprint pattern: %s'), data)
             self.resume_capture()
 
     def resume_capture(self):
@@ -810,3 +826,28 @@ def qr_code_right_click_menu(self, event):
             menu.append(new_menu_item)
 
         menu.popup(None, None, None, event.button, event.time)
+
+
+class GTKLoggingHandler(logging.Handler):
+    """
+    Handles log messages and displays a dialog with the message.
+    """
+    def __init__(self, window):
+        self.window = window
+        super(GTKLoggingHandler, self).__init__()
+
+    def emit(self, record):
+        # Set the dialog type for ERROR, CRRITICAL, and WARNING. Don't
+        # show any other levels.
+        if record.levelno is logging.ERROR or record.levelno is logging.CRITICAL:
+            level = gtk.MESSAGE_ERROR
+        elif record.levelno is logging.WARNING:
+            level = gtk.MESSAGE_WARNING
+        else:
+            return
+
+        md = gtk.MessageDialog(self.window, gtk.DIALOG_DESTROY_WITH_PARENT,
+                               level, gtk.BUTTONS_OK, record.msg)
+        with gtk.gdk.lock:
+            md.run()
+        md.destroy()
diff --git a/monkeysign/tests/files/7B75921E.asc b/monkeysign/tests/files/7B75921E.asc
index 78e8091..698461b 100644
--- a/monkeysign/tests/files/7B75921E.asc
+++ b/monkeysign/tests/files/7B75921E.asc
@@ -1,5 +1,4 @@
 -----BEGIN PGP PUBLIC KEY BLOCK-----
-Version: GnuPG v1
 
 mQINBEogKJ4BEADHRk8dXcT3VmnEZQQdiAaNw8pmnoRG2QkoAvv42q9Ua+DRVe/y
 AEUd03EOXbMJl++YKWpVuzSFr7IlZ+/lJHOCqDeSsBD6LKBSx/7uH2EOIDizGwfZ
@@ -25,155 +24,183 @@ talZcOZ1TUmQ4gP941HQBBjp/uDAUlkoa4/HIFxRwBTDnPspkG19HLub6QDs5/AB
 3/55CGS9pBHrU2EsPQ9cLwzb+zfQmJi2vC2IzcVrbwVcTRpAluHo8kUVlgTHpnbw
 XOHrr40FRuKgex7TREBK1OyAn1gYdQUFVhau+SjdcAz9zEVI8aj23Umu4oTVYVOj
 cx2flzZCcdzyG6nzd3JQVWm3gpK3TgWo8eC/hNa7s5aIs7ThTofGXh+d5bUtcZx+
-FbJ5tCVBbnRvaW5lIEJlYXVwcsOpIDxhbmFyY2F0QGRlYmlhbi5vcmc+iQI9BBMB
-CAAnAhsDBQsJCAcDBRUKCQgLBRYCAwEAAh4BAheABQJXPkWABQkPEHO3AAoJEHkh
-UlJ7dZIegzsQALvO3xo2xx84VdZ0Kc1tU9SlLaAZFZttbOvALD4o3x55pyUEaqWH
-4huDRcIRkhFq9FQITT7hj+dXkofR1MSdoUyuS/+XMYTOKBJNFUKT4JcfDr7pa8EV
-sXWpxw/3gaivgi8+fo4niKFCDD0NdTO9c/19QU3Wa/11VxFrZvGS4jGb2eae8stg
-2QGPIB6uobevypXbGNGe9TQ/TILZOlPVLyznf+Tj+9g0iU0oWYCVvj0z3Hrf3w1E
-QX4jx2GSC8KDSM1pdajRkON8qGQgud8URKoqGQRYjHadtpbSY38h++s/NSmWDTPJ
-KRJd4J2VDaYNtH0sQYVhumv+K48DRGQDcc0AHbj2f6NtzCfLLObBPrP0hgn1laPb
-ujyqv/vUVKZCke5SEHDJN+eUSYv1rO1lv+1X4yugle+DyP2Pinls7jxR74L42ST0
-NM33OamQacrMtKtZi3Ql0+x5lkcG0/9TEVlIyc2gZEiFL/9V4XWDbW0Of39vniCh
-IYcxeQGL+369UNr7a3MXE/Z5MPpqVM6KuyPvhWfexwJyvS5CFcb5VX+awx67jodQ
-1PxM6pIESJdfzv7lSwWRuhIzSG4WtfISpzywdrrjOdn9D6BdbY9mVvoUE1lrD3XY
-fDzU8aIveHFcLQ1iqqZMecosqbM0bi2W9zHb36uE5jKNN8qxXyRTMOM7tC5BbnRv
-aW5lIEJlYXVwcsOpIChEZWJpYW4pIDxhbmFyY2F0QGRlYmlhbi5vcmc+iQI2BDAB
-CAAgBQJTznDdGR0gcmVtb3ZpbmcgY29tbWVudCBmaWVsZHMACgkQeSFSUnt1kh5c
-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
-v0PIz18nn90Xm9tI5v73MPrU2/uv
-=5gjM
+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
+lmYt+k8zg+c0UeBd4cJpdds=
+=S2gx
 -----END PGP PUBLIC KEY BLOCK-----
diff --git a/monkeysign/tests/test_gpg.py b/monkeysign/tests/test_gpg.py
index 5ca8472..9c36528 100755
--- a/monkeysign/tests/test_gpg.py
+++ b/monkeysign/tests/test_gpg.py
@@ -26,12 +26,15 @@
 import unittest
 import tempfile
 import re
+import logging
 
 sys.path.insert(0, os.path.dirname(__file__) + '/..')
 
 from monkeysign.gpg import *
 from test_lib import find_test_file, skipUnlessUnicodeLocale, skipIfDatePassed
 
+logger = logging.getLogger(__name__)
+
 class TestContext(unittest.TestCase):
     """Tests for the Context class.
 
@@ -77,16 +80,6 @@ def test_version(self):
         """make sure version() returns something"""
         self.assertTrue(self.gpg.version())
 
-    def test_seek_debug(self):
-        """test if seek actually respects debug"""
-        self.gpg.debug = True # should yield an attribute error, that's fine
-        with self.assertRaises(AttributeError):
-            self.gpg.seek(StringIO('test'), 'test')
-        # now within a keyring?
-        k = TempKeyring()
-        k.context.debug = True
-        with self.assertRaises(AttributeError):
-            k.import_data(open(find_test_file('96F47C6A.asc')).read())
 
 class TestTempKeyring(unittest.TestCase):
     """Test the TempKeyring class."""
@@ -212,9 +205,8 @@ def test_get_keys(self):
 
         @todo we should check the data structure
         """
-        # just a cute display for now
         for fpr, key in self.gpg.get_keys('96F47C6A').iteritems():
-            print key
+            logger.debug(key)
 
     def test_sign_key_wrong_user(self):
         """make sure sign_key with a erroneous local-user fails
@@ -424,8 +416,9 @@ def setUp(self):
 uid:::::::2451063FCBB4D262938687C2D8F6B949B0A3AF01::The Anarcat <anarcat@anarcat.ath.cx>:
 ssb::2048:16:C016FF12EB8D47BB:1110320966::::::::::""")
 
-    def test_print(self):
-        print self.key
+    def test_str(self):
+        """Tests that the __str__ call of OpenPGPKey works for secret keys"""
+        self.assertIn('C9E1 F123 0DBE 47D5 7BAB  3C60 5860 73B3 4023 702F', self.key.__str__())
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/monkeysign/tests/test_ui.py b/monkeysign/tests/test_ui.py
index 6e744c0..c1055ff 100755
--- a/monkeysign/tests/test_ui.py
+++ b/monkeysign/tests/test_ui.py
@@ -29,6 +29,8 @@
 import sys
 import re
 from StringIO import StringIO
+import logging
+import logging.handlers
 import tempfile
 try:
     from unittest.mock import Mock
@@ -64,12 +66,22 @@ def which2(cmd):
 # optimized because called often
 from test_lib import find_test_file, skipIfDatePassed, skipUnlessNetwork
 
+logger = logging.getLogger(__name__)
 
 class CliBaseTest(unittest.TestCase):
     def setUp(self):
         self.argv = sys.argv
         sys.argv = [ 'monkeysign', '--no-mail' ]
 
+        if '--debug' not in self.argv:
+            # Save the logging handlers on the root logger to a variable
+            # and replace them with a NullHandler (the null handler
+            # prevents logging calls from trying to add a stream handler
+            # back). This stops output from the tested functions from
+            # printing to the console and cluttering things up.
+            self.logging_handlers = logging.getLogger().handlers
+            logging.getLogger().handlers = [logging.NullHandler()]
+
     def execute(self):
         '''execute the monkeysign script directly
 
@@ -117,6 +129,10 @@ def write_to_callback(self, stdin, callback):
     def tearDown(self):
         sys.argv = self.argv
 
+        if '--debug' not in self.argv:
+            # Restore the logging handlers we removed in the setup function
+            logging.getLogger().handlers = self.logging_handlers
+
 
 @unittest.skipUnless(Mock, 'mock library missing')
 class CliTestCase(CliBaseTest):
@@ -186,7 +202,7 @@ def test_write_configs(self):
         self.assertTrue(args.local)
 
 
-@skipIfDatePassed('2017-06-01T00:00:00UTC')
+@skipIfDatePassed('2018-04-19T00:00:00UTC')
 class CliTestDialog(CliBaseTest):
     def setUp(self):
         CliBaseTest.setUp(self)
@@ -223,13 +239,14 @@ def callback(self):
         self.write_to_callback("\n\n", callback) # say 'default' twice
 
 
-@skipIfDatePassed('2017-06-01T00:00:00UTC')
+@skipIfDatePassed('2018-04-19T00:00:00UTC')
 class CliTestSpacedFingerprint(CliTestDialog):
     def setUp(self):
         CliTestDialog.setUp(self)
         sys.argv.pop() # remove the uid from parent class
         sys.argv += '8DC9 01CE 6414 6C04 8AD5  0FBB 7921 5252 7B75 921E'.split()
 
+
 class BaseTestCase(unittest.TestCase):
     pattern = None
     args = []
@@ -244,6 +261,7 @@ def setUp(self):
         self.ui.keyring = TempKeyring()
         self.ui.prepare() # needed because we changed the base keyring
 
+
 class BasicTests(BaseTestCase):
     pattern = '7B75921E'
 
@@ -256,18 +274,33 @@ def test_cleanup(self):
         self.assertFalse(os.path.exists(self.homedir))
 
 
-@skipIfDatePassed('2017-06-01T00:00:00UTC')
+@skipIfDatePassed('2018-04-19T00:00:00UTC')
 class SigningTests(BaseTestCase):
     pattern = '7B75921E'
 
     def setUp(self):
         """setup a basic keyring capable of signing a local key"""
         BaseTestCase.setUp(self)
+
+        if not self.ui.options.debug:
+            # Save the logging handlers on the root logger to a variable
+            # and replace them with a NullHandler (the null handler
+            # prevents logging calls from trying to add a stream handler
+            # back). This stops output from the tested functions from
+            # printing to the console and cluttering things up.
+            self.logging_handlers = logging.getLogger().handlers
+            logging.getLogger().handlers = [logging.NullHandler()]
+
         self.assertTrue(self.ui.keyring.import_data(open(find_test_file('7B75921E.asc')).read()))
         self.assertTrue(self.ui.tmpkeyring.import_data(open(find_test_file('96F47C6A.asc')).read()))
         self.assertTrue(self.ui.keyring.import_data(open(find_test_file('96F47C6A.asc')).read()))
         self.assertTrue(self.ui.keyring.import_data(open(find_test_file('96F47C6A-secret.asc')).read()))
 
+    def tearDown(self):
+        if not self.ui.options.debug:
+            # Restore the logging handlers we removed in the setup function
+            logging.getLogger().handlers = self.logging_handlers
+
     def test_find_key(self):
         """test if we can extract the key locally
 
@@ -337,6 +370,22 @@ def test_multiple_secrets(self):
             if 'rev:' in uid:
                 self.assertNotIn('sig:::1:A31E75E4323F39BD', uid)
 
+    def test_multiple_secrets(self):
+        """test if we pick the right key define in gpg.conf"""
+        # configure gpg to use the *first* test key as a default key
+        with open(os.path.join(self.ui.keyring.homedir, 'gpg.conf'), 'w') as f:
+            f.write('default-key 96F47C6A')
+        self.ui.prepare()
+        self.test_copy_secrets()
+        self.ui.keyring.import_data(open(find_test_file('323F39BD.asc')).read())
+        self.ui.keyring.import_data(open(find_test_file('323F39BD-secret.asc')).read())
+        self.test_copy_secrets()
+        self.ui.sign_key()
+        self.ui.tmpkeyring.context.call_command(['list-sigs', '7B75921E'])
+        # this is the secondary test key, it shouldn't have signed this
+        self.assertNotIn('sig:::1:A31E75E4323F39BD:',
+                         self.ui.tmpkeyring.context.stdout)
+
     def test_create_mail_multiple(self):
         """test if exported keys contain the right uid"""
         self.test_sign_key()
@@ -356,42 +405,64 @@ def test_create_mail_multiple(self):
 
     def test_export_key(self):
         """see if we export a properly encrypted key set"""
-        messages = []
-        # collect messages instead of warning the user
-        self.ui.warn = messages.append
-        self.test_sign_key()
-        self.ui.export_key()
-        self.assertIsNone(self.ui.export_key(), 'sends mail?')
-        paste = messages.pop()
-        self.assertNotIn('BEGIN PGP PUBLIC KEY BLOCK', paste,
-                         'message not encrypted')
-        self.assertIn('BEGIN PGP MESSAGE', paste, 'message not encrypted')
-        self.assertNotIn('MIME-Version', paste,
-                         'message to paste has weird MIME headers')
+        # Create a handler to store messages being logged
+        memory_handler = logging.handlers.MemoryHandler(0)
+        # Add this handler to the monkeysign.ui logger
+        monkeysign_ui_logger = logging.getLogger('monkeysign.ui')
+        monkeysign_ui_logger.addHandler(memory_handler)
+        try:
+            self.test_sign_key()
+            self.ui.export_key()
+            self.assertIsNone(self.ui.export_key(), 'sends mail?')
+
+            contains_exported_key = False
+            for record in memory_handler.buffer:
+                if (record.levelname == 'WARNING' and
+                    "here's the encrypted signed public key you can paste in your email client" in record.getMessage()):
+                    contains_exported_key = True
+                    self.assertNotIn('BEGIN PGP PUBLIC KEY BLOCK', record.getMessage(), 'message not encrypted')
+                    self.assertNotIn('MIME-Version', record.getMessage(), 'message to paste has weird MIME headers')
+                    self.assertIn('BEGIN PGP MESSAGE', record.getMessage(), 'message not encrypted')
+                    break
+            self.assertTrue(contains_exported_key, 'no exported key logged')
+        finally:
+            # Remove the handler when we're done.
+            monkeysign_ui_logger.removeHandler(memory_handler)
 
     def test_sendmail(self):
         """see if we can generate a proper commandline to send email"""
         self.test_sign_key()
-        messages = []
-        # collect messages instead of warning the user
-        self.ui.warn = messages.append
-        self.ui.options.nomail = False
-        self.ui.options.user = 'unittests@localhost'
-        self.ui.options.to = 'devnull@localhost'
-        self.ui.options.mta = "dd status=none of='" + \
-                              self.ui.keyring.homedir + "/%(to)s'"
-        self.assertTrue(self.ui.export_key(), 'fails to send mail')
-        filename = '%s/%s' % (self.ui.keyring.homedir, self.ui.options.to)
-        self.assertGreater(os.path.getsize(filename), 0,
-                           'mail properly created')
-        self.assertIn('sent message to %s with dd' % self.ui.options.to,
-                      messages.pop(),
-                      'missing information to user')
-        self.ui.options.to = 'devnull; touch bad'
-        self.assertTrue(self.ui.export_key(),
-                        'fails to send email to weird address')
-        self.assertFalse(os.path.exists('bad'),
-                         'vulnerable to command injection')
+        # Create a handler to store messages being logged
+        memory_handler = logging.handlers.MemoryHandler(0)
+        # Add this handler to the monkeysign.ui logger
+        monkeysign_ui_logger = logging.getLogger('monkeysign.ui')
+        monkeysign_ui_logger.addHandler(memory_handler)
+        try:
+            self.ui.options.nomail = False
+            self.ui.options.user = 'unittests@localhost'
+            self.ui.options.to = 'devnull@localhost'
+            self.ui.options.mta = "dd status=none of='" + \
+                                  self.ui.keyring.homedir + "/%(to)s'"
+            self.assertTrue(self.ui.export_key(), 'fails to send mail')
+            filename = '%s/%s' % (self.ui.keyring.homedir, self.ui.options.to)
+            self.assertGreater(os.path.getsize(filename), 0,
+                               'mail properly created')
+
+            contains_information_for_user = False
+            for record in memory_handler.buffer:
+                if (record.levelname == 'WARNING' and
+                    'sent message to %s with dd' % self.ui.options.to in record.getMessage()):
+                    contains_information_for_user = True
+            self.assertTrue(contains_information_for_user, 'missing information to user')
+
+            self.ui.options.to = 'devnull; touch bad'
+            self.assertTrue(self.ui.export_key(),
+                            'fails to send email to weird address')
+            self.assertFalse(os.path.exists('bad'),
+                             'vulnerable to command injection')
+        finally:
+            # Remove the handler when we're done.
+            monkeysign_ui_logger.removeHandler(memory_handler)
 
     def test_mua(self):
         self.test_sign_key()
@@ -422,7 +493,7 @@ def test_mua(self):
         os.unlink(outputfile)
 
 
-@skipIfDatePassed('2017-06-01T00:00:00UTC')
+@skipIfDatePassed('2018-04-19T00:00:00UTC')
 class EmailFactoryTest(BaseTestCase):
     pattern = '7B75921E'
 
@@ -541,7 +612,3 @@ class KeyserverTests(BaseTestCase):
     def test_find_key(self):
         """this should find the key on the keyservers"""
         self.ui.find_key()
-
-
-if __name__ == '__main__':
-    unittest.main()
diff --git a/monkeysign/ui.py b/monkeysign/ui.py
index b05078a..10fd6bf 100644
--- a/monkeysign/ui.py
+++ b/monkeysign/ui.py
@@ -19,6 +19,7 @@
 # gpg interface
 from monkeysign.gpg import Keyring, TempKeyring, GpgRuntimeError, Context
 import monkeysign.translation
+import warnings
 
 import errno
 # mail functions
@@ -50,7 +51,9 @@
 import socket
 import socks
 import tempfile
+import logging
 
+logger = logging.getLogger(__name__)
 
 class RunTests(argparse._VersionAction):
     '''argparse Action handler to run tests
@@ -66,6 +69,23 @@ def __call__(self, parser, namespace, values, option_string=None):
         sys.argv.remove('--test')
         if '--version' in sys.argv:
             sys.argv.remove('--version')
+
+        # If there aren't already handlers, set the logging level and
+        # format based on the the command line options passed
+        # in. Also, only print the <TIME> - <LEVEL> - <MODULE> prefix
+        # for log messages when debug mode has been requested.
+        #
+        # We're doing this as well as in MonkeysignUi.__init__ because
+        # when running the tests we don't call MonkeysignUi.__init__
+        # until much later.
+        if not logging.getLogger().handlers:
+            if '--debug' in sys.argv or '-d' in sys.argv:
+                logging.getLogger().setLevel(logging.DEBUG)
+                log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+            else:
+                log_format = "%(message)s"
+            logging.basicConfig(format=log_format)
+
         testdir = os.path.join(os.path.dirname(__file__), 'tests')
         suite = unittest.TestLoader().discover(testdir)
         results = unittest.TextTestRunner().run(suite)
@@ -262,7 +282,9 @@ def save_config(self, args, config=None, close=True):
         self.write_config(lines, config)
         if close:
             config.close()
-        # self.ui.log() except that we don't have logging yet
+
+        logger.info("Saving config")
+
         return True
 
     def args_to_config(self, args):
@@ -350,10 +372,26 @@ def sysinfo(cls):
 
     def __init__(self, args = None):
         # the options that determine how we operate, from the parse_args()
-        self.options = {}
+        self.parser = MonkeysignArgumentParser(ui=self)
+        self.options = self.parser.parse_args(args=args)
+
+        # If there aren't already handlers, set the logging level and
+        # format based on the the command line options passed
+        # in. Also, only print the <TIME> - <LEVEL> - <MODULE> prefix
+        # for log messages when debug mode has been requested.
+        if not logging.getLogger().handlers:
+            if self.options.debug:
+                logging.getLogger().setLevel(logging.DEBUG)
+                log_format = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
+            elif self.options.verbose:
+                logging.getLogger().setLevel(logging.INFO)
+                log_format = "%(message)s"
+            else:
+                log_format = "%(message)s"
+            logging.basicConfig(format=log_format)
 
         # the key we are signing, can be a keyid or a uid pattern
-        self.pattern = None
+        self.pattern = self.options.pattern
 
         # the regular keyring we suck secrets and maybe the key to be signed from
         self.keyring = Keyring()
@@ -369,13 +407,7 @@ def __init__(self, args = None):
         # temporary, to keep track of the OpenPGPkey we are signing
         self.signing_key = None
 
-        self.parser = MonkeysignArgumentParser(ui=self)
-        self.options = self.parser.parse_args(args=args)
-        self.pattern = self.options.pattern
-
-        # set a default logging mechanism
-        self.logfile = sys.stderr
-        self.log(_('Initializing UI'))
+        logger.info(_('Initializing UI'))
 
         # create the temporary keyring
         # XXX: i would prefer this to be done outside the constructor
@@ -386,7 +418,7 @@ def __enter__(self):
 
     def __exit__(self, exc_type, exc_value, traceback):
         # this is implicit in the garbage collection, but tell the user anyways
-        self.log(_('deleting the temporary keyring %s') % self.tmpkeyring.homedir)
+        logger.info(_('deleting the temporary keyring %s') % self.tmpkeyring.homedir)
 
         if exc_type is NotImplementedError:
             self.abort(str(exc_value))
@@ -395,9 +427,6 @@ def prepare(self):
         # initialize the temporary keyring directory
         self.tmpkeyring = TempKeyring()
 
-        if self.options.debug:
-            self.tmpkeyring.context.debug = self.logfile
-            self.keyring.context.debug = self.logfile
         if self.options.keyserver is not None:
             self.tmpkeyring.context.set_option('keyserver', self.options.keyserver)
         if self.options.user is not None:
@@ -420,7 +449,7 @@ def prepare(self):
                     m += [str(e)]
                 self.abort(_('cannot open SOCKS proxy %s:%d: %s') % m)
             else:
-                self.log(_('SOCKS proxy %s:%d appears operational') % self.options.socks)
+                logger.info(_('SOCKS proxy %s:%d appears operational'), self.options.socks)
             # this should be --use-tor, but:
             # 1. that is available only in undetermined versions of GPG 2.1.x
             # 2. it is not available in GPG 1.x legacy, which we still support
@@ -430,7 +459,7 @@ def prepare(self):
             self.tmpkeyring.context.set_option('keyserver-options', ks_opts)
         # copy the gpg.conf from the real keyring
         try:
-            self.log(_('copying your gpg.conf in temporary keyring'))
+            logger.info(_('copying your gpg.conf in temporary keyring'))
             shutil.copy(self.keyring.homedir + '/gpg.conf', self.tmpkeyring.homedir)
         except IOError as e:
             # no such file or directory is alright: it means the use
@@ -446,7 +475,7 @@ def prepare(self):
         # cargo-culted from caff, thanks guilhem!
         src = self.keyring.get_agent_socket()
         dst = self.tmpkeyring.get_agent_socket()
-        self.log(_('installing symlinks for sockets from %s to %s') % (src, dst))
+        logger.info(_('installing symlinks for sockets from %s to %s'), src, dst)
         try:
             os.unlink(dst)
         except OSError as e:
@@ -482,12 +511,23 @@ def abort(self, message):
     def warn(self, message):
         """display an warning message
 
+        DEPRECATED: This method has been deprecated. Use Logger.warning instead.
+
 this should not interrupt the flow of the program, but must be visible to the user"""
-        print message.encode('utf-8')
+        warning = ("This method is deprecated, and will be removed in a future version. "
+                   "As alternative, use Logger.warning()")
+        warnings.warn(warning, DeprecationWarning)
+        logger.warning(message)
 
     def log(self, message):
-        """log an informational message if verbose"""
-        if self.options.verbose or self.options.debug: print >>self.logfile, message
+        """log an informational message if verbose
+
+        DEPRECATED: This method has been deprecated. Use Logger.warning instead.
+        """
+        warning = ("This method is deprecated, and will be removed in a future version. "
+                   "As alternative, use Logger.info()")
+        warnings.warn(warning, DeprecationWarning)
+        logger.info(message)
 
     def yes_no(self, prompt, default = True):
         """default UI is not interactive, so we assume yes all the time"""
@@ -508,12 +548,12 @@ def prompt_pass(self, prompt):
     def find_key(self):
         """find the key to be signed somewhere"""
         # 1.b) from the local keyring
-        self.log(_('looking for key %s in your keyring') % self.pattern)
+        logger.info(_('looking for key %s in your keyring'), self.pattern)
         if not self.tmpkeyring.import_data(self.keyring.export_data(self.pattern)):
-            self.log(_('key not in local keyring'))
+            logger.info(_('key not in local keyring'))
 
             # 1.a) if allowed, from the keyservers
-            self.log(_('fetching key %s from keyserver') % self.pattern)
+            logger.info(_('fetching key %s from keyserver'), self.pattern)
 
             if not re.search('^[0-9A-F]*$', self.pattern, re.IGNORECASE): # this is not a keyid
                 # the problem here is that we need to implement --search-keys, and it's a pain
@@ -533,18 +573,32 @@ def copy_secrets(self):
 chose. it could vary based on default-key, for example, or some weird
 ordering.
         """
-        self.log(_('copying your public key to temporary keyring in %s') % self.tmpkeyring.homedir)
-        # detect the proper uid
-        keys = self.keyring.get_keys(self.options.user, True, False)
+        logger.info(_('copying your public key to temporary keyring in %s') % self.tmpkeyring.homedir)
+
+        # detect default key setting
+        default_key = self.options.user
+        try:
+            with open(os.path.join(self.tmpkeyring.homedir,
+                                   'gpg.conf'), 'r') as conf:
+                result = re.search(r'^default-key\s+(.*)$', conf.read(), re.M)
+                if result:
+                    default_key = result.group(1)
+                    logging.info(_('found default-key setting: %s'), default_key)
+        except IOError as e:
+            if e.errno != errno.ENOENT:
+                raise
+        logging.info(_('looking for key %s'), default_key)
 
+        # detect the proper uid
+        keys = self.keyring.get_keys(default_key, True, False)
         for fpr, key in keys.iteritems():
-            self.log(_('found secret key: %s') % key)
+            logger.info(_('found secret key: %s'), key)
             if not key.invalid and not key.disabled and not key.expired and not key.revoked:
                 self.signing_key = key
                 # export public key material associated with all private keys
                 # XXX: we should only do export-minimal here, but passing options down is a pain.
                 if not self.tmpkeyring.import_data(self.keyring.export_data(key.fpr)):
-                    self.warning(_('could not export public key material of private key %s') % key.fpr)
+                    logger.warning(_('could not export public key material of private key %s'), key.fpr)
 
         if self.signing_key is None:
             self.abort(_('could not find public key material for any private key, do you have an OpenPGP key?'))
@@ -556,7 +610,7 @@ def sign_key(self):
 
         # this shouldn't happen unless caller forgot to call copy_secrets
         assert(keys is not None)  # nosec
-        self.log(_('found %d keys matching your request') % len(keys))
+        logger.info(_('found %d keys matching your request'), len(keys))
 
         secret_keys = self.keyring.get_keys(self.options.user, True, False)
 
@@ -564,7 +618,7 @@ def sign_key(self):
             # Make sure the user isn't signing their own key
             for secret_key in secret_keys.values():
                 if keys[key] == secret_key:
-                    self.warn(_('That is your own key, so it is already certified'))
+                    logger.warning(_('That is your own key, so it is already certified'))
                     return False
 
             alluids = self.yes_no(_("""\
@@ -581,7 +635,7 @@ def sign_key(self):
             else:
                 pattern = self.choose_uid(_('Choose the identity to sign'), keys[key])
                 if not pattern:
-                    self.log(_('no identity chosen'))
+                    logger.info(_('no identity chosen'))
                     return False
                 if not self.options.to:
                     self.options.to = pattern
@@ -591,18 +645,18 @@ def sign_key(self):
                 if not self.yes_no(_('Really sign key? [y/N] '), False):
                     continue
                 if not self.tmpkeyring.sign_key(pattern, alluids):
-                    self.warn(_('key signing failed'))
+                    logger.warning(_('key signing failed'))
                 else:
                     self.signed_keys[key] = keys[key]
                 if self.options.local:
-                    self.log(_('making a non-exportable signature'))
+                    logger.info(_('making a non-exportable signature'))
                     self.tmpkeyring.context.set_option('export-options', 'export-minimal')
 
                     # this is inefficient - we could save a copy if we would fetch the key directly
                     if not self.keyring.import_data(self.tmpkeyring.export_data(self.pattern)):
                         self.abort(_('could not import public key back into public keyring, something is wrong'))
                     if not self.keyring.sign_key(pattern, alluids, True):
-                        self.warn(_('local key signing failed'))
+                        logger.warning(_('local key signing failed'))
 
     def export_key(self):
         if self.options.user is not None and '@' in self.options.user:
@@ -611,24 +665,26 @@ def export_key(self):
             from_user = self.signing_key.uidslist[0].uid
 
         if len(self.signed_keys) < 1:
-            self.warn(_('no key signed, nothing to export'))
-        
+            logger.warning(_('no key signed, nothing to export'))
+
+        ret = True
         for fpr, key in self.signed_keys.items():
             if self.chosen_uid is None:
                 for uid in key.uids.values():
                     try:
                         msg = EmailFactory(self.tmpkeyring.export_data(fpr), fpr, uid.uid, from_user, self.options.to)
                     except GpgRuntimeError as e:
-                        self.warn(_('failed to create email: %s') % e)
+                        logger.warning(_('failed to create email: %s'), e)
                         break
-                    return self.sendmail(msg)
+                    ret = ret and self.sendmail(msg)
             else:
                 try:
                     msg = EmailFactory(self.tmpkeyring.export_data(fpr), fpr, self.chosen_uid, from_user, self.options.to)
                 except GpgRuntimeError as e:
-                    self.warn(_('failed to create email: %s') % e)
+                    logger.warning(_('failed to create email: %s'), e)
                     break
-                return self.sendmail(msg)
+                ret = self.sendmail(msg)
+        return ret
 
     def sendmail(self, msg):
             """actually send the email
@@ -659,7 +715,7 @@ def sendmail(self, msg):
                     try:
                         server.starttls()
                     except smtplib.SMTPException:
-                        self.warn(_('SMTP server does not support STARTTLS'))
+                        logger.warning(_('SMTP server does not support STARTTLS'))
                         if self.options.smtpuser:
                             self.abort(_('aborting authentication as credentials would have been sent in clear text'))
                 if self.options.smtpuser:
@@ -669,9 +725,9 @@ def sendmail(self, msg):
                 try:
                     server.sendmail(msg.mailfrom.encode('utf-8'), msg.mailto.encode('utf-8'), msg.as_string().encode('utf-8'))
                 except smtplib.SMTPException as e:
-                    self.warn(_('failed to send email: %s') % e)
+                    logger.warning(_('failed to send email: %s'), e)
                 else:
-                    self.warn(_('sent message through SMTP server %s to %s') % (self.options.smtpserver, msg.mailto))
+                    logger.warning(_('sent message through SMTP server %s to %s') % (self.options.smtpserver, msg.mailto))
                 server.quit()
                 return True
             elif not self.options.nomail:
@@ -696,19 +752,19 @@ def sendmail(self, msg):
                                    for x in shlex.split(self.options.mta)]
                 except ValueError as e:
                     if 'incomplete format' in str(e):
-                        self.warn(_('invalid command (%s) specified to send email: %s')
-                                  % (self.options.mua or self.options.mta, str(e)))
+                        logger.warning(_('invalid command (%s) specified to send email: %s'),
+                                  (self.options.mua or self.options.mta, str(e)))
                         return False
                     else:
                         raise
-                self.log('running command %s' % command)
+                logger.info('running command %s', command)
                 try:
                     p = subprocess.Popen(command,
                                          stdin=subprocess.PIPE,
                                          stdout=subprocess.PIPE,
                                          stderr=subprocess.PIPE)  # nosec
                 except OSError as e:
-                    self.warn(_('cannot find MTA %s, try specifying --mua, --mta or --smtp: %s')
+                    logger.warning(_('cannot find MTA %s, try specifying --mua, --mta or --smtp: %s')
                               % (self.options.mua, repr(e)))
                     return False
 
@@ -721,21 +777,21 @@ def sendmail(self, msg):
                     self.acknowledge('when you have finished writing the email')
                     keyfile.close()  # remove the tmpfile
                 if p.returncode == 0:
-                    self.warn(_('sent message to %(destination)s with %(command)s')
-                              % {'destination': msg.mailto,
-                                 'command': self.options.mua or self.options.mta})
+                    logger.warning(_('sent message to %(destination)s with %(command)s'),
+                                   {'destination': msg.mailto,
+                                    'command': self.options.mua or self.options.mta})
                 else:
-                    self.warn(_('failed sending message to %(destination)s with %(command)s: %(error)s')
-                              % {'destination': msg.mailto,
-                                 'command': self.options.mua or self.options.mta,
-                                 'error': self.mailer_output + self.mailer_error})
+                    logger.warning(_('failed sending message to %(destination)s with %(command)s: %(error)s'),
+                                   {'destination': msg.mailto,
+                                    'command': self.options.mua or self.options.mta,
+                                    'error': self.mailer_output + self.mailer_error})
                 return p.returncode == 0
             else:
                 # okay, no mail, just dump the exported key then
-                self.warn(_("""\
+                logger.warning(_("""\
 not sending email to %s, as requested, here's the encrypted signed public key you can paste in your email client:
 
-%s""") % (msg.mailto, msg.encrypted()))
+%s"""), msg.mailto, msg.encrypted())
 
 
 class EmailFactory:
diff --git a/po/nl.po b/po/nl.po
new file mode 100644
index 0000000..abe9430
--- /dev/null
+++ b/po/nl.po
@@ -0,0 +1,725 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2013-08-14 23:21-0400\n"
+"PO-Revision-Date: 2017-02-11 11:51+0000\n"
+"Last-Translator: Heimen Stoffels <vistausss@outlook.com>\n"
+"Language-Team: Dutch "
+"<https://hosted.weblate.org/projects/monkeysphere/monkeysign/nl/>\n"
+"Language: nl\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=n != 1;\n"
+"X-Generator: Weblate 2.12-dev\n"
+
+#: ../monkeysign/cli.py:26
+msgid ""
+"sign a key in a safe fashion.\n"
+"\n"
+"This command signs a key based on the fingerprint or user id\n"
+"specified on the commandline, encrypt the result and mail it to the\n"
+"user. This leave the choice of publishing the certification to that\n"
+"person and makes sure that person owns the identity signed.\n"
+"\n"
+"This program assumes you have gpg-agent configured to prompt for\n"
+"passwords."
+msgstr ""
+"onderteken een sleutel op een veilige manier.\n"
+"\n"
+"Deze opdracht ondertekent een sleutel gebaseerd op de vingerafdruk of de op "
+"de\n"
+"commandoregel opgegeven gebruikers-ID. Het resultaat wordt daarna "
+"versleuteld en verstuurd\n"
+"naar de gebruiker. Dit geeft u de keuze om het certificaat aan die persoon "
+"te publiceren en\n"
+"ervoor te zorgen dat die persoon ook de ondertekende identiteit bezit.\n"
+"\n"
+"Deze applicatie gaat er vanuit dat u een GPG-agent heeft geconfigureerd "
+"zodat deze kan\n"
+"vragen naar wachtwoorden."
+
+#: ../monkeysign/cli.py:37
+msgid "%prog [options] <keyid>"
+msgstr "%prog [opties] <sleutelid>"
+
+#: ../monkeysign/cli.py:38
+msgid "<keyid>: a GPG fingerprint or key id"
+msgstr "<sleutelid>: een GPG-vingerafdruk of sleutel-ID"
+
+#: ../monkeysign/cli.py:45
+msgid "wrong number of arguments, use -h for full help"
+msgstr "onjuist aantal argumenten; gebruik -h voor hulp"
+
+#: ../monkeysign/cli.py:57
+#, python-format
+msgid "reset GPG_TTY to %s"
+msgstr "GPG_TTY herstellen naar %s"
+
+#: ../monkeysign/cli.py:65
+#, python-format
+msgid ""
+"Preparing to sign with this key\n"
+"\n"
+"%s"
+msgstr ""
+"Bezig met voorbereiden om te ondertekenen met deze sleutel\n"
+"\n"
+"%s"
+
+#: ../monkeysign/cli.py:100
+#, python-format
+msgid " (1-%d or full UID, control-c to abort): "
+msgstr " (1-%d of volledige UID, control-c om af te breken): "
+
+#: ../monkeysign/cli.py:104
+msgid "invalid uid"
+msgstr "ongeldige uid"
+
+#: ../monkeysign/gpg.py:209
+#, python-format
+msgid "could not find pattern '%s' in input, last skipped '%s'"
+msgstr ""
+"het patroon '%s' kan niet worden gevonden in de invoer, laatst overgeslagen "
+"'%s'"
+
+#: ../monkeysign/gpg.py:329
+#, python-format
+msgid "verifying file %s failed: %s."
+msgstr "het verifiëren van %s is mislukt: %s."
+
+#: ../monkeysign/gpg.py:366 ../monkeysign/gpg.py:386
+#, python-format
+msgid "unexpected GPG exit code in list-keys: %d"
+msgstr "onverwachte GPG-afsluitcode in list-keys: %d"
+
+#: ../monkeysign/gpg.py:398
+#, python-format
+msgid "encryption to %s failed: %s."
+msgstr "het versleutelen naar %s is mislukt: %s."
+
+#: ../monkeysign/gpg.py:409
+#, python-format
+msgid "decryption failed: %s"
+msgstr "het ontsleutelen is mislukt: %s"
+
+#: ../monkeysign/gpg.py:471 ../monkeysign/gpg.py:473
+#, python-format
+msgid "cannot sign: %s"
+msgstr "ondertekenen is niet mogelijk: %s"
+
+#: ../monkeysign/gpg.py:483
+msgid "you already signed that key"
+msgstr "u heeft deze sleutel al ondertekend"
+
+#: ../monkeysign/gpg.py:486 ../monkeysign/gpg.py:519
+#, python-format
+msgid "unable to open key for editing: %s"
+msgstr "het openen van de sleutel om deze te bewerken is mislukt: %s"
+
+#: ../monkeysign/gpg.py:494
+msgid "unable to prompt for passphrase, is gpg-agent running?"
+msgstr "het vragen naar een wachtwoord is mislukt. draait gpg-agent?"
+
+#: ../monkeysign/gpg.py:530
+msgid "key is expired, cannot sign"
+msgstr "sleutel is verlopen, ondertekenen is niet mogelijk"
+
+#: ../monkeysign/gpg.py:532
+#, python-format
+msgid "cannot sign, unknown error from gpg: %s"
+msgstr "ondertekenen is niet mogelijk, onbekende fout van gpg: %s"
+
+#: ../monkeysign/gpg.py:537
+msgid "password confirmation failed"
+msgstr "wachtwoordbevestiging is mislukt"
+
+#: ../monkeysign/gpg.py:708
+#, python-format
+msgid "record type '%s' not implemented"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:40
+msgid ""
+"sign a key in a safe fashion using a webcam to scan for qr-codes\n"
+"\n"
+"This command will fire up a graphical interface and turn on the webcam\n"
+"(if available) on this computer. It will also display a qr-code of\n"
+"your main OpenPGP key.\n"
+"\n"
+"The webcam is used to capture an OpenPGP fingerprint represented as a\n"
+"qrcode (or whatever the zbar library can parse) and then go through a\n"
+"signing process.\n"
+"\n"
+"The signature is then encrypted and mailed to the user. This leave the\n"
+"choice of publishing the certification to that person and makes sure\n"
+"that person owns the identity signed.\n"
+"\n"
+"This program assumes you have gpg-agent configure to prompt for\n"
+"passwords.\n"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:120
+msgid "okay, signing"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:123
+msgid "user denied signature"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:152
+msgid "Monkeysign (scan)"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:184
+msgid "_File"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:185
+msgid "Open image..."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:186
+msgid "_Save QR code as..."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:187
+msgid "_Print QR code..."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:189
+msgid "Copy image to clipboard"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:189
+msgid "_Copy QR code"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:190
+msgid "Choose identity"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:190
+msgid "Identity"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:191
+msgid "Select video device to use"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:191
+msgid "Video device"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:192
+msgid "_Quit"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:201
+msgid "Disable video"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:245
+msgid "No video device detected."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:251
+msgid ""
+"This is the output of your webcam, align a qrcode in the image to scan a "
+"fingerprint."
+msgstr ""
+
+#~ msgid "create the QR code display"
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:269
+msgid ""
+"This is a QR-code version of your PGP fingerprint. Scan this with another "
+"monkeysign to transfer your fingerprint."
+msgstr ""
+
+#~ msgid "list the secret keys for selection somewhere"
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:291 ../monkeysign/gtkui.py:292
+msgid "Hide QR code"
+msgstr ""
+
+#~ msgid "When window is resized, regenerate the QR code"
+#~ msgstr ""
+
+#~ msgid "refresh the qrcode when the selected key changes"
+#~ msgstr ""
+
+#~ msgid "draw the qrcode from the key fingerprint"
+#~ msgstr ""
+
+#~ msgid ""
+#~ "callback invoked when a new video device is selected from the\n"
+#~ "                drop-down list.  sets the new device for the zbar widget,\n"
+#~ "                which will eventually cause it to be opened and enabled\n"
+#~ "                "
+#~ msgstr ""
+
+#~ msgid "Given a fingerprint, generate a QR code image with appropriate prefix"
+#~ msgstr ""
+
+#~ msgid "Use a file chooser dialog to import an image containing a QR code"
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:361
+msgid "cannot find signature for image file"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:363
+#, python-format
+msgid ""
+"The image provided cannot be verified using a trusted OpenPGP signature.\n"
+"\n"
+"Make sure the image comes from a trusted source (e.g. your own camera, which "
+"you have never left unsurveilled) before signing this!\n"
+"\n"
+"DO NOT SIGN UNTRUSTED FINGERPRINTS!\n"
+"\n"
+"To get rid of this warning, if you really trust this image, use the "
+"following command to sign the file\n"
+"\n"
+"    gpg -s --detach %s\n"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:365
+msgid "image signature verified successfully"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:370
+msgid "Scan an image for QR codes"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:403
+msgid "data found in image!"
+msgstr ""
+
+#~ msgid ""
+#~ "Use a file chooser dialog to enable user to save the current QR code as a "
+#~ "PNG image file"
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:410 ../monkeysign/gtkui.py:435
+msgid ""
+"No identity selected. Select one from the identiy menu or generate a OpenPGP "
+"key if none is available."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:414
+msgid "Save QR code"
+msgstr ""
+
+#~ msgid "copy the qrcode to the clipboard"
+#~ msgstr ""
+
+#~ msgid "handler for the print QR code menu"
+#~ msgstr ""
+
+#~ msgid "actually print the qr code"
+#~ msgstr ""
+
+#~ msgid "Utility function to convert a PIL image instance to Pixbuf"
+#~ msgstr ""
+
+#~ msgid ""
+#~ "callback invoked for pulsating progressbar\n"
+#~ "                "
+#~ msgstr ""
+
+#~ msgid ""
+#~ "callback invoked when gpg key download is finished\n"
+#~ "                "
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:478
+msgid "fetching finished"
+msgstr ""
+
+#~ msgid ""
+#~ "callback invoked when a barcode is decoded by the zbar widget.\n"
+#~ "                checks for an openpgp fingerprint\n"
+#~ "                "
+#~ msgstr ""
+
+#~ msgid "process zbar-scanned data"
+#~ msgstr ""
+
+#: ../monkeysign/gtkui.py:523
+msgid "zbar captured a frame, looking for 40 character hexadecimal fingerprint"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:534 ../monkeysign/ui.py:229
+#, python-format
+msgid "looking for key %s in your keyring"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:547
+msgid "Please wait"
+msgstr ""
+
+#: ../monkeysign/gtkui.py:549
+msgid "Retrieving public key from server..."
+msgstr ""
+
+#: ../monkeysign/gtkui.py:563
+#, python-format
+msgid "data found in barcode does not match a OpenPGP fingerprint pattern: %s"
+msgstr ""
+
+#~ msgid "restart capture"
+#~ msgstr ""
+
+#~ msgid "close the application"
+#~ msgstr ""
+
+#~ msgid ""
+#~ "User interface abstraction for monkeysign.\n"
+#~ "\n"
+#~ "    This aims to factor out a common pattern to sign keys that is used\n"
+#~ "    regardless of the UI used.\n"
+#~ "\n"
+#~ "    This is mostly geared at console/text-based and X11 interfaces,\n"
+#~ "    but could also be ported to other interfaces (touch-screen/phone\n"
+#~ "    interfaces would be interesting).\n"
+#~ "\n"
+#~ "    The actual process is in main(), which outlines what the\n"
+#~ "    subclasses of this should be doing.\n"
+#~ "\n"
+#~ "    You should have a docstring in derived classes, as it will be\n"
+#~ "    added to the 'usage' output.\n"
+#~ "\n"
+#~ "    You should also set the usage and epilog parameters, see\n"
+#~ "    parse_args().\n"
+#~ "    "
+#~ msgstr ""
+
+#~ msgid "parse the commandline arguments"
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:74
+msgid "show version information and quit"
+msgstr ""
+
+#: ../monkeysign/ui.py:76
+msgid "request debugging information from GPG engine (lots of garbage)"
+msgstr ""
+
+#: ../monkeysign/ui.py:78
+msgid "explain what we do along the way"
+msgstr ""
+
+#: ../monkeysign/ui.py:80
+msgid "do not actually do anything"
+msgstr ""
+
+#: ../monkeysign/ui.py:81
+msgid "user id to sign the key with"
+msgstr ""
+
+#: ../monkeysign/ui.py:82
+msgid "certification level to sign the key with"
+msgstr ""
+
+#: ../monkeysign/ui.py:84
+msgid "import in normal keyring a local certification"
+msgstr ""
+
+#: ../monkeysign/ui.py:86
+msgid "keyserver to fetch keys from"
+msgstr ""
+
+#: ../monkeysign/ui.py:87
+msgid ""
+"SMTP server to use, use a colon to specify the port number if non-standard"
+msgstr ""
+
+#: ../monkeysign/ui.py:88
+msgid "username for the SMTP server (default: no user)"
+msgstr ""
+
+#: ../monkeysign/ui.py:89
+msgid ""
+"password for the SMTP server (default: prompted, if --smtpuser is specified)"
+msgstr ""
+
+#: ../monkeysign/ui.py:91
+msgid "Do not send email at all. (Default is to use sendmail.)"
+msgstr ""
+
+#: ../monkeysign/ui.py:93
+msgid ""
+"Override destination email for testing (default is to use the first uid on "
+"the key or send email to each uid chosen)"
+msgstr ""
+
+#: ../monkeysign/ui.py:136
+msgid "Initializing UI"
+msgstr ""
+
+#: ../monkeysign/ui.py:147
+#, python-format
+msgid "deleting the temporary keyring %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:172
+msgid "copied your gpg.conf in temporary keyring"
+msgstr ""
+
+#~ msgid ""
+#~ "\n"
+#~ "        General process\n"
+#~ "        ===============\n"
+#~ "\n"
+#~ "        1. fetch the key into a temporary keyring\n"
+#~ "        1.a) if allowed (@todo), from the keyservers\n"
+#~ "        1.b) from the local keyring (@todo try that first?)\n"
+#~ "        2. copy the signing key secrets into the keyring\n"
+#~ "        3. for every user id (or all, if -a is specified)\n"
+#~ "        3.1. sign the uid, using gpg-agent\n"
+#~ "        3.2. export and encrypt the signature\n"
+#~ "        3.3. mail the key to the user\n"
+#~ "        3.4. optionnally (-l), create a local signature and import in\n"
+#~ "        local keyring\n"
+#~ "        4. trash the temporary keyring\n"
+#~ "        "
+#~ msgstr ""
+
+#~ msgid "show a message to the user and abort program"
+#~ msgstr ""
+
+#~ msgid ""
+#~ "display an warning message\n"
+#~ "\n"
+#~ "this should not interrupt the flow of the program, but must be visible to "
+#~ "the user"
+#~ msgstr ""
+
+#~ msgid "log an informational message if verbose"
+#~ msgstr ""
+
+#~ msgid "default UI is not interactive, so we assume yes all the time"
+#~ msgstr ""
+
+#~ msgid "find the key to be signed somewhere"
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:231
+msgid "key not in local keyring"
+msgstr ""
+
+#: ../monkeysign/ui.py:234
+#, python-format
+msgid "fetching key %s from keyservers"
+msgstr ""
+
+#: ../monkeysign/ui.py:238
+msgid "please provide a keyid or fingerprint, uids are not supported yet"
+msgstr ""
+
+#: ../monkeysign/ui.py:241
+#, python-format
+msgid "could not find key %s in your keyring or keyservers"
+msgstr ""
+
+#~ msgid ""
+#~ "import secret keys (but only the public part) from your keyring\n"
+#~ "\n"
+#~ "we use --secret-keyring instead of copying the secret key material,\n"
+#~ "but we still need the public part in the temporary keyring for this to\n"
+#~ "work.\n"
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:250
+#, python-format
+msgid "copying your private key to temporary keyring in %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:258
+#, python-format
+msgid "found secret key: %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:264
+msgid "no default secret key found, abort!"
+msgstr ""
+
+#: ../monkeysign/ui.py:265
+#, python-format
+msgid "signing key chosen: %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:269
+msgid "could not find public key material, do you have a GPG key?"
+msgstr ""
+
+#~ msgid "sign the key uids, as specified"
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:276
+#, python-format
+msgid "found %d keys matching your request"
+msgstr ""
+
+#: ../monkeysign/ui.py:279
+#, python-format
+msgid ""
+"Signing the following key\n"
+"\n"
+"%s\n"
+"\n"
+"Sign all identities? [y/N] "
+msgstr ""
+
+#: ../monkeysign/ui.py:291
+msgid "Choose the identity to sign"
+msgstr ""
+
+#: ../monkeysign/ui.py:293
+msgid "no identity chosen"
+msgstr ""
+
+#: ../monkeysign/ui.py:300
+msgid "Really sign key? [y/N] "
+msgstr ""
+
+#: ../monkeysign/ui.py:303
+msgid "key signing failed"
+msgstr ""
+
+#: ../monkeysign/ui.py:307
+msgid "making a non-exportable signature"
+msgstr ""
+
+#: ../monkeysign/ui.py:312
+msgid ""
+"could not import public key back into public keyring, something is wrong"
+msgstr ""
+
+#: ../monkeysign/ui.py:314
+msgid "local key signing failed"
+msgstr ""
+
+#: ../monkeysign/ui.py:322
+msgid "no key signed, nothing to export"
+msgstr ""
+
+#: ../monkeysign/ui.py:330 ../monkeysign/ui.py:337
+#, python-format
+msgid "failed to create email: %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:354
+#, python-format
+msgid "Error connecting to SMTP server %s: %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:356
+#, python-format
+msgid "Unexpected SMTP server error while talking to %s, code: %s (%s)"
+msgstr ""
+
+#: ../monkeysign/ui.py:360
+msgid "SMTP server does not support STARTTLS"
+msgstr ""
+
+#: ../monkeysign/ui.py:361
+msgid "authentication credentials will be sent in clear text"
+msgstr ""
+
+#: ../monkeysign/ui.py:364
+#, python-format
+msgid "enter SMTP password for server %s: "
+msgstr ""
+
+#: ../monkeysign/ui.py:368
+#, python-format
+msgid "sent message through SMTP server %s to %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:374
+#, python-format
+msgid "sent message through sendmail to %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:377
+#, python-format
+msgid ""
+"not sending email to %s, as requested, here's the email message:\n"
+"\n"
+"%s"
+msgstr ""
+
+#~ msgid ""
+#~ "email generator\n"
+#~ "\n"
+#~ "this is a factory, ie. a class generating an object that represents\n"
+#~ "the email and when turned into a string, is the actual\n"
+#~ "mail.\n"
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:392
+msgid "Your signed OpenPGP key"
+msgstr ""
+
+#: ../monkeysign/ui.py:395
+msgid ""
+"\n"
+"Please find attached your signed PGP key. You can import the signed\n"
+"key by running each through `gpg --import`.\n"
+"\n"
+"If you have multiple user ids, each signature was sent in a separate\n"
+"email to each user id.\n"
+"\n"
+"Note that your key was not uploaded to any keyservers. If you want\n"
+"this new signature to be available to others, please upload it\n"
+"yourself.  With GnuPG this can be done using:\n"
+"\n"
+"    gpg --keyserver pool.sks-keyservers.net --send-key <keyid>\n"
+"\n"
+"Regards,\n"
+msgstr ""
+
+#~ msgid ""
+#~ "email constructor\n"
+#~ "\n"
+#~ "we expect to find the following arguments:\n"
+#~ "\n"
+#~ "keydata: the signed public key material\n"
+#~ "keyfpr: the fingerprint of that public key\n"
+#~ "recipient: the recipient to encrypt the mail to\n"
+#~ "mailfrom: who the mail originates from\n"
+#~ "mailto: who to send the mail to (usually similar to recipient, but can be "
+#~ "used to specify specific keyids"
+#~ msgstr ""
+
+#~ msgid "this will remove any UID not matching the 'recipient' set in the class"
+#~ msgstr ""
+
+#~ msgid ""
+#~ "\n"
+#~ "        a multipart/mixed message containing a plain-text message\n"
+#~ "        explaining what this is, and a second part containing PGP data\n"
+#~ "        "
+#~ msgstr ""
+
+#: ../monkeysign/ui.py:494
+#, python-format
+msgid "signed PGP Key %s, uid %s"
+msgstr ""
+
+#: ../monkeysign/ui.py:507
+msgid "This is a multi-part message in PGP/MIME format..."
+msgstr ""
+
+#~ msgid "A non-wrapping formatter for OptionParse."
+#~ msgstr ""

--- End Message ---
--- Begin Message ---
On Sat, Sep 09, 2017 at 03:23:13PM +0200, Julien Cristau wrote:
> Control: tag -1 moreinfo
> 
> On Sat, Aug 12, 2017 at 14:21:11 -0400, Antoine Beaupre wrote:
> 
> > Package: release.debian.org
> > Severity: normal
> > Tags: stretch
> > User: release.debian.org@packages.debian.org
> > Usertags: pu
> > 
> > Hi,
> > 
> > I am working on a new release of Monkeysign, which I'd like to upload
> > in Debian. If it would be just me, I would tag the current HEAD with
> > 2.2.4, considering the changes are mostly minor and non-disruptive:
> > 
> > angela:monkeysign$ git diff 2.2.3 --stat
> >  CONTRIBUTING.rst                    |   9 +-
> >  debian/gbp.conf                     |   2 +-
> >  doc/usage.rst                       |   4 +
> >  monkeysign/cli.py                   |  11 +-
> >  monkeysign/gpg.py                   |  35 ++--
> >  monkeysign/gtkui.py                 |  81 ++++++---
> >  monkeysign/tests/files/7B75921E.asc | 331 ++++++++++++++++++++-----------------
> >  monkeysign/tests/test_gpg.py        |  21 +--
> >  monkeysign/tests/test_ui.py         | 147 ++++++++++++-----
> >  monkeysign/ui.py                    | 168 ++++++++++++-------
> >  po/nl.po                            | 725 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> >  11 files changed, 1225 insertions(+), 309 deletions(-)
> > 
> > as you can see, more than half of the diff (725 lines) is just a
> > translation file update. The rest is fixes for tests and critical
> > bugfixes (although the bugfixes have not been reported directly in the
> > Debian BTS, but discovered through my own testing).
> > 
> > Attached is the actual diff. Should I upload this as 2.2.4 to unstable
> > and stable-pu? Or should i minimize this diff to a bare minimum and
> > release a more targeted 2.2.4 to stable and a 2.3.0 to unstable?
> > 
> There's no such thing as uploading the same package version to unstable
> and proposed-updates.  Please first get the changes in unstable, with
> whatever version number.  Let them sit for a while, and then come back
> with a request for stable, with a description and justification of the
> changes (which I couldn't see here).
> 
That doesn't seem to have happened; closing.

Cheers,
Julien

--- End Message ---

Reply to: