[balsa/wip/gmime3: 173/197] Crypto performance improvements



commit 53ef712defb8a4244868bcc939259a65389f5cff
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Mon Feb 12 17:59:00 2018 -0500

    Crypto performance improvements
    
        * libbalsa/body.c: calculate protection state without the key
        * libbalsa/gmime-gpgme-signature.[ch]: do not try to load the key when
        creating the signature object, add and rename some signature-only
        related functions from rfc3156.[hc], set 'crypto' log domain
        * libbalsa/libbalsa-gpgme-keys.[ch]: add function to load a public key,
        properly restore the context attributes when listing keys, set 'crypto'
        log domain
        * libbalsa/libbalsa-gpgme-widgets.[ch]: improved function for creating a
        string from the key data (shifted from rfc3156.[hc]), set 'crypto' log
        domain
        * libbalsa/rfc3156.[ch]: refoctoring; shift some signature-only related
        functions to gmime-gpgme-signature.[ch], set 'crypto' log domain
        * src/balsa-message.c: use renamed signature info functions, calculate
        protection state without the key
        * src/balsa-mime-widget-crypto.c: add callback to load the key when the
        user wants to show it, drop condition which cannot be TRUE, use renamed
        signature info functions
        * src/balsa-mime-widget-message.c: use renamed signature info functions
        * src/balsa-print-object-header.c: try to load the key before printing,
        use renamed signature info functions
        * README, configure.ac, meson.build: require gpgme ≥ 1.5.0
        * libbalsa/libbalsa-gpgme.c: set 'crypto' log domain, adjust debug
        messages
        * libbalsa/gmime-application-pkcs7.c, libbalsa/gmime-multipart-crypt.c,
        libbalsa/gmime-part-rfc2440.c, libbalsa/libbalsa-gpgme-cb.c: set
        'crypto' log domain

 ChangeLog                          |   31 ++++++
 README                             |    1 +
 configure.ac                       |    4 +-
 libbalsa/body.c                    |   31 +++---
 libbalsa/gmime-application-pkcs7.c |    6 +
 libbalsa/gmime-gpgme-signature.c   |  131 +++++++++++++++++------
 libbalsa/gmime-gpgme-signature.h   |   18 +++-
 libbalsa/gmime-multipart-crypt.c   |    6 +
 libbalsa/gmime-part-rfc2440.c      |    6 +
 libbalsa/libbalsa-gpgme-cb.c       |    6 +
 libbalsa/libbalsa-gpgme-keys.c     |   75 ++++++++++---
 libbalsa/libbalsa-gpgme-keys.h     |   15 +++
 libbalsa/libbalsa-gpgme-widgets.c  |  208 ++++++++++++++++++++++++++++++++----
 libbalsa/libbalsa-gpgme-widgets.h  |   19 +++-
 libbalsa/libbalsa-gpgme.c          |   30 +++---
 libbalsa/rfc3156.c                 |  183 ++------------------------------
 libbalsa/rfc3156.h                 |    7 --
 meson.build                        |    4 +-
 src/balsa-message.c                |   13 +--
 src/balsa-mime-widget-crypto.c     |   66 ++++++++----
 src/balsa-mime-widget-message.c    |    2 +-
 src/balsa-print-object-header.c    |   14 ++-
 22 files changed, 548 insertions(+), 328 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index fccb175..51d738b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,34 @@
+2018-02-12  Peter Bloomfield  <pbloomfield bellsouth net>
+
+       Crypto performance improvements
+
+       * libbalsa/body.c: calculate protection state without the key
+       * libbalsa/gmime-gpgme-signature.[ch]: do not try to load the key when
+       creating the signature object, add and rename some signature-only
+       related functions from rfc3156.[hc], set 'crypto' log domain
+       * libbalsa/libbalsa-gpgme-keys.[ch]: add function to load a public key,
+       properly restore the context attributes when listing keys, set 'crypto'
+       log domain
+       * libbalsa/libbalsa-gpgme-widgets.[ch]: improved function for creating a
+       string from the key data (shifted from rfc3156.[hc]), set 'crypto' log
+       domain
+       * libbalsa/rfc3156.[ch]: refoctoring; shift some signature-only related
+       functions to gmime-gpgme-signature.[ch], set 'crypto' log domain
+       * src/balsa-message.c: use renamed signature info functions, calculate
+       protection state without the key
+       * src/balsa-mime-widget-crypto.c: add callback to load the key when the
+       user wants to show it, drop condition which cannot be TRUE, use renamed
+       signature info functions
+       * src/balsa-mime-widget-message.c: use renamed signature info functions
+       * src/balsa-print-object-header.c: try to load the key before printing,
+       use renamed signature info functions
+       * README, configure.ac, meson.build: require gpgme ≥ 1.5.0
+       * libbalsa/libbalsa-gpgme.c: set 'crypto' log domain, adjust debug
+       messages
+       * libbalsa/gmime-application-pkcs7.c, libbalsa/gmime-multipart-crypt.c,
+       libbalsa/gmime-part-rfc2440.c, libbalsa/libbalsa-gpgme-cb.c: set
+       'crypto' log domain
+
 2018-02-10  Peter Bloomfield  <pbloomfield bellsouth net>
 
        Clean up generated marshalling files
diff --git a/README b/README
index 0fd5d09..3fef72e 100644
--- a/README
+++ b/README
@@ -59,6 +59,7 @@ The path of the gpgme-config tool must be specified only if it is
 installed in an uncommon place.  See the (rather outdated) web page
 http://home.arcor.de/dralbrecht.dress/balsa/UsingGnuPG.html#FAQ
 for more details.
+Note: requires GpgME version >= 1.5.0
 
 --with-gss[=/usr/kerberos]
        This enables GSSAPI Kerberos based authentication scheme. 
diff --git a/configure.ac b/configure.ac
index 80ea286..6ee9c78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -359,7 +359,7 @@ if test x"$gpgmecfg" != xno ; then
        if test $gpgme_ve -lt 1; then
                gpgmecfg=no
        fi
-       if test \( $gpgme_ve -eq 1 \) -a \( $gpgme_ma -lt 2 \) ; then
+       if test \( $gpgme_ve -eq 1 \) -a \( $gpgme_ma -lt 5 \) ; then
                gpgmecfg=no
        fi
        if test x"$gpgmecfg" != xno ; then
@@ -375,7 +375,7 @@ if test x"$gpgmecfg" != xno ; then
                BALSA_CFLAGS="$BALSA_CFLAGS `$gpgmecfg --cflags`"
                gpgmecfg=yes
        else
-               AC_MSG_WARN([sorry, you need at least gpgme version 1.2.0])
+               AC_MSG_WARN([sorry, you need at least gpgme version 1.5.0])
        fi
 fi
 AM_CONDITIONAL([BUILD_WITH_GPGME], [test $gpgmecfg = "yes"])
diff --git a/libbalsa/body.c b/libbalsa/body.c
index 609fcd6..3ab281e 100644
--- a/libbalsa/body.c
+++ b/libbalsa/body.c
@@ -883,22 +883,19 @@ libbalsa_message_body_get_by_id(LibBalsaMessageBody * body,
 LibBalsaMsgProtectState
 libbalsa_message_body_protect_state(LibBalsaMessageBody *body)
 {
-    if (!body || !body->sig_info ||
-       body->sig_info->status == GPG_ERR_NOT_SIGNED ||
-       body->sig_info->status == GPG_ERR_CANCELED)
-       return LIBBALSA_MSG_PROTECT_NONE;
-
-    if (body->sig_info->status == GPG_ERR_NO_ERROR) {
-       /* good signature, check if the validity and trust (OpenPGP only) are
-          at least marginal */
-       if (body->sig_info->validity >= GPGME_VALIDITY_MARGINAL &&
-           (body->sig_info->protocol == GPGME_PROTOCOL_CMS ||
-            body->sig_info->key->owner_trust >= GPGME_VALIDITY_MARGINAL))
-           return LIBBALSA_MSG_PROTECT_SIGN_GOOD;
-       else
-           return LIBBALSA_MSG_PROTECT_SIGN_NOTRUST;
-    }
-    
-    return LIBBALSA_MSG_PROTECT_SIGN_BAD;
+       LibBalsaMsgProtectState state;
+
+       if ((body == NULL) || (body->sig_info == NULL) || (body->sig_info->status == GPG_ERR_NOT_SIGNED) ||
+               (body->sig_info->status == GPG_ERR_CANCELED)) {
+               state = LIBBALSA_MSG_PROTECT_NONE;
+       } else if (body->sig_info->status != GPG_ERR_NO_ERROR) {
+               state = LIBBALSA_MSG_PROTECT_SIGN_BAD;
+       } else if ((body->sig_info->summary & GPGME_SIGSUM_VALID) == GPGME_SIGSUM_VALID) {
+               state = LIBBALSA_MSG_PROTECT_SIGN_GOOD;
+       } else {
+               state = LIBBALSA_MSG_PROTECT_SIGN_NOTRUST;
+       }
+
+       return state;
 }
 #endif
diff --git a/libbalsa/gmime-application-pkcs7.c b/libbalsa/gmime-application-pkcs7.c
index 6d38130..44f8c04 100644
--- a/libbalsa/gmime-application-pkcs7.c
+++ b/libbalsa/gmime-application-pkcs7.c
@@ -30,6 +30,12 @@
 #include <glib/gi18n.h>
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 #define GMIME_PKCS7_ERR_QUARK (g_quark_from_static_string ("gmime-app-pkcs7"))
 
 
diff --git a/libbalsa/gmime-gpgme-signature.c b/libbalsa/gmime-gpgme-signature.c
index 0d49aed..7d86530 100644
--- a/libbalsa/gmime-gpgme-signature.c
+++ b/libbalsa/gmime-gpgme-signature.c
@@ -25,10 +25,19 @@
 #include <gpgme.h>
 #include <string.h>
 #include <glib.h>
+#include <glib/gi18n.h>
 #include "libbalsa-gpgme.h"
 #include "misc.h"
+#include "libbalsa.h"
+#include "libbalsa-gpgme-keys.h"
+#include "libbalsa-gpgme-widgets.h"
 #include "gmime-gpgme-signature.h"
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
 
 /* stuff for the signature status as returned by gpgme as an GObject */
 static GObjectClass *g_mime_gpgme_sigstat_parent_class = NULL;
@@ -36,7 +45,6 @@ static GObjectClass *g_mime_gpgme_sigstat_parent_class = NULL;
 static void g_mime_gpgme_sigstat_class_init(GMimeGpgmeSigstatClass *
                                            klass);
 static void g_mime_gpgme_sigstat_finalize(GMimeGpgmeSigstat * self);
-static void g_mime_gpgme_sigstat_init(GMimeGpgmeSigstat * self);
 
 
 /* GMimeGpgmeSigstat related stuff */
@@ -55,7 +63,7 @@ g_mime_gpgme_sigstat_get_type(void)
            NULL,               /* class_data */
            sizeof(GMimeGpgmeSigstat),  /* instance_size */
            0,                  /* n_preallocs */
-           (GInstanceInitFunc) g_mime_gpgme_sigstat_init,      /* instance_init */
+           NULL,           /* instance_init */
            /* no value_table */
        };
 
@@ -69,11 +77,14 @@ g_mime_gpgme_sigstat_get_type(void)
 
 
 GMimeGpgmeSigstat *
-g_mime_gpgme_sigstat_new(void)
+g_mime_gpgme_sigstat_new(gpgme_ctx_t ctx)
 {
+       GMimeGpgmeSigstat *result;
 
-    return
-       GMIME_GPGME_SIGSTAT(g_object_new(GMIME_TYPE_GPGME_SIGSTAT, NULL));
+       result = GMIME_GPGME_SIGSTAT(g_object_new(GMIME_TYPE_GPGME_SIGSTAT, NULL));
+       result->protocol = gpgme_get_protocol(ctx);
+       result->status = GPG_ERR_NOT_SIGNED;
+    return result;
 }
 
 
@@ -82,37 +93,95 @@ g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t ctx)
 {
     GMimeGpgmeSigstat *sig_stat;
     gpgme_verify_result_t result;
-    gpgme_error_t err;
 
     g_return_val_if_fail(ctx, NULL);
-    if (!(sig_stat = g_mime_gpgme_sigstat_new()))
-       return NULL;
-
-    sig_stat->status = GPG_ERR_NOT_SIGNED;     /* no signature available */
-    sig_stat->protocol = gpgme_get_protocol(ctx);
+    sig_stat = g_mime_gpgme_sigstat_new(ctx);
+
+    /* try to retrieve the result of a verify operation */
+    result = gpgme_op_verify_result(ctx);
+    if ((result != NULL) && (result->signatures != NULL)) {
+        /* there is at least one signature */
+        sig_stat->fingerprint = g_strdup(result->signatures->fpr);
+        sig_stat->sign_time = result->signatures->timestamp;
+        sig_stat->summary = result->signatures->summary;
+        sig_stat->status = gpgme_err_code(result->signatures->status);
+        sig_stat->validity = result->signatures->validity;
+    }
 
-    /* try to retreive the result of a verify operation */
-    if (!(result = gpgme_op_verify_result(ctx))
-       || result->signatures == NULL)
        return sig_stat;
+}
+
+void
+g_mime_gpgme_sigstat_load_key(GMimeGpgmeSigstat *sigstat)
+{
+       g_return_if_fail(GMIME_IS_GPGME_SIGSTAT(sigstat));
 
-    /* there is at least one signature */
-    sig_stat->fingerprint = g_strdup(result->signatures->fpr);
-    sig_stat->sign_time = result->signatures->timestamp;
-    sig_stat->status = gpgme_err_code(result->signatures->status);
-    sig_stat->validity = result->signatures->validity;
+       if ((sigstat->key == NULL) && ((sigstat->summary & GPGME_SIGSUM_KEY_MISSING) == 0)) {
+               gpgme_ctx_t ctx;
 
-    /* try to get the related key */
-    err = gpgme_get_key(ctx, sig_stat->fingerprint, &sig_stat->key, 0);
-    if (err != GPG_ERR_NO_ERROR) {
-       gchar errbuf[4096];             /* should be large enough... */
+               ctx = libbalsa_gpgme_new_with_proto(sigstat->protocol, NULL, NULL, NULL);
+               sigstat->key = libbalsa_gpgme_load_key(ctx, sigstat->fingerprint, NULL);
+               gpgme_release(ctx);
+       }
+}
 
-       gpgme_strerror_r(err, errbuf, sizeof(errbuf));
-       g_message("could not retrieve the key with fingerprint %s: %s: %s",
-               sig_stat->fingerprint, gpgme_strsource(err), errbuf);
+const gchar *
+g_mime_gpgme_sigstat_protocol_name(const GMimeGpgmeSigstat *sigstat)
+{
+       g_return_val_if_fail(GMIME_IS_GPGME_SIGSTAT(sigstat), NULL);
+
+    switch (sigstat->protocol) {
+    case GPGME_PROTOCOL_OpenPGP:
+       return _("PGP signature: ");
+    case GPGME_PROTOCOL_CMS:
+       return _("S/MIME signature: ");
+    default:
+       return _("(unknown protocol) ");
     }
+}
 
-    return sig_stat;
+static inline void
+append_time_t(GString     *str,
+                         const gchar *format,
+                         time_t       when,
+              const gchar *date_string)
+{
+    if (when != (time_t) 0) {
+        gchar *tbuf = libbalsa_date_to_utf8(when, date_string);
+        g_string_append_printf(str, format, tbuf);
+        g_free(tbuf);
+    } else {
+        g_string_append_printf(str, format, _("never"));
+    }
+}
+
+gchar *
+g_mime_gpgme_sigstat_to_gchar(const GMimeGpgmeSigstat *info,
+                                                         gboolean                 full_details,
+                                                         const gchar             *date_string)
+{
+    GString *msg;
+
+    g_return_val_if_fail(GMIME_IS_GPGME_SIGSTAT(info), NULL);
+    g_return_val_if_fail(date_string != NULL, NULL);
+    msg = g_string_new(g_mime_gpgme_sigstat_protocol_name(info));
+    msg = g_string_append(msg, libbalsa_gpgme_sig_stat_to_gchar(info->status));
+    g_string_append_printf(msg, _("\nSignature validity: %s"), libbalsa_gpgme_validity_to_gchar(info-> 
validity));
+    append_time_t(msg, _("\nSigned on: %s"), info->sign_time, date_string);
+    if (info->fingerprint) {
+       g_string_append_printf(msg, _("\nKey fingerprint: %s"), info->fingerprint);
+    }
+
+    /* append key data */
+    if (full_details && (info->key != NULL)) {
+       gchar *key_data;
+
+       key_data = libbalsa_gpgme_key_to_gchar(info->key, info->fingerprint);
+       g_string_append_printf(msg, "\n%s", key_data);
+       g_free(key_data);
+    }
+
+    return g_string_free(msg, FALSE);
 }
 
 
@@ -225,11 +294,3 @@ libbalsa_cert_subject_readable(const gchar *subject)
     libbalsa_utf8_sanitize(&readable_subject, TRUE, NULL);
     return readable_subject;
 }
-
-static void
-g_mime_gpgme_sigstat_init(GMimeGpgmeSigstat * self)
-{
-    self->status = GPG_ERR_NOT_SIGNED;
-    self->key = NULL;
-    self->fingerprint = NULL;
-}
diff --git a/libbalsa/gmime-gpgme-signature.h b/libbalsa/gmime-gpgme-signature.h
index 06d2d10..7fde9d0 100644
--- a/libbalsa/gmime-gpgme-signature.h
+++ b/libbalsa/gmime-gpgme-signature.h
@@ -42,6 +42,7 @@ struct _GMimeGpgmeSigstat {
 
     /* results form gpgme's verify operation */
     gpgme_protocol_t protocol;
+    gpgme_sigsum_t summary;
     gpgme_error_t status;
     gpgme_validity_t validity;
     gchar *fingerprint;
@@ -57,11 +58,20 @@ struct _GMimeGpgmeSigstatClass {
 
 
 GType g_mime_gpgme_sigstat_get_type(void);
-GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new(void);
-GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t
-                                                          ctx);
+GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new(gpgme_ctx_t ctx)
+       G_GNUC_WARN_UNUSED_RESULT;
+GMimeGpgmeSigstat *g_mime_gpgme_sigstat_new_from_gpgme_ctx(gpgme_ctx_t ctx)
+       G_GNUC_WARN_UNUSED_RESULT;
+void g_mime_gpgme_sigstat_load_key(GMimeGpgmeSigstat *sigstat);
 
-gchar *libbalsa_cert_subject_readable(const gchar *subject);
+const gchar *g_mime_gpgme_sigstat_protocol_name(const GMimeGpgmeSigstat *sigstat);
+gchar *g_mime_gpgme_sigstat_to_gchar(const GMimeGpgmeSigstat *info,
+                                                                        gboolean                 
full_details,
+                                                                        const gchar             *date_string)
+       G_GNUC_WARN_UNUSED_RESULT;
+
+gchar *libbalsa_cert_subject_readable(const gchar *subject)
+       G_GNUC_WARN_UNUSED_RESULT;
 
 
 G_END_DECLS
diff --git a/libbalsa/gmime-multipart-crypt.c b/libbalsa/gmime-multipart-crypt.c
index 3487354..3afd965 100644
--- a/libbalsa/gmime-multipart-crypt.c
+++ b/libbalsa/gmime-multipart-crypt.c
@@ -37,6 +37,12 @@
 #include "libbalsa.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 /**
  * sign_prepare:
  * @mime_part: MIME part
diff --git a/libbalsa/gmime-part-rfc2440.c b/libbalsa/gmime-part-rfc2440.c
index 76a5ef2..586deed 100644
--- a/libbalsa/gmime-part-rfc2440.c
+++ b/libbalsa/gmime-part-rfc2440.c
@@ -29,6 +29,12 @@
 #include "gmime-part-rfc2440.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 #define RFC2440_BUF_LEN    4096
 
 
diff --git a/libbalsa/libbalsa-gpgme-cb.c b/libbalsa/libbalsa-gpgme-cb.c
index 4c18fb1..ce45566 100644
--- a/libbalsa/libbalsa-gpgme-cb.c
+++ b/libbalsa/libbalsa-gpgme-cb.c
@@ -40,6 +40,12 @@
 #include "libbalsa-gpgme-cb.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 /* stuff to get a key fingerprint from a selection list */
 enum {
     GPG_KEY_USER_ID_COLUMN = 0,
diff --git a/libbalsa/libbalsa-gpgme-keys.c b/libbalsa/libbalsa-gpgme-keys.c
index 6025219..f45566e 100644
--- a/libbalsa/libbalsa-gpgme-keys.c
+++ b/libbalsa/libbalsa-gpgme-keys.c
@@ -27,6 +27,12 @@
 #include "libbalsa-gpgme.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 /* key server thread data */
 typedef struct _keyserver_op_t {
        gpgme_ctx_t gpgme_ctx;
@@ -64,19 +70,18 @@ libbalsa_gpgme_list_keys(gpgme_ctx_t   ctx,
                                                 gboolean      on_keyserver,
                                                 GError      **error)
 {
-       gpgme_error_t gpgme_err = GPG_ERR_NO_ERROR;
+       gpgme_error_t gpgme_err;
+       gpgme_keylist_mode_t kl_save;
        gpgme_keylist_mode_t kl_mode;
 
        g_return_val_if_fail((ctx != NULL) && (keys != NULL), FALSE);
 
        /* set key list mode to external if we want to search a remote key server, or to the local key ring */
-       kl_mode = gpgme_get_keylist_mode(ctx);
+       kl_save = gpgme_get_keylist_mode(ctx);
        if (on_keyserver) {
-               kl_mode &= ~GPGME_KEYLIST_MODE_LOCAL;
-               kl_mode |= GPGME_KEYLIST_MODE_EXTERN;
+               kl_mode = (kl_save & ~GPGME_KEYLIST_MODE_LOCAL) | GPGME_KEYLIST_MODE_EXTERN;
        } else {
-               kl_mode &= ~GPGME_KEYLIST_MODE_EXTERN;
-               kl_mode |= GPGME_KEYLIST_MODE_LOCAL;
+               kl_mode = (kl_save & ~GPGME_KEYLIST_MODE_EXTERN) | GPGME_KEYLIST_MODE_LOCAL;
        }
        gpgme_err = gpgme_set_keylist_mode(ctx, kl_mode);
        if (gpgme_err != GPG_ERR_NO_ERROR) {
@@ -127,11 +132,58 @@ libbalsa_gpgme_list_keys(gpgme_ctx_t   ctx,
                        }
                }
        }
+       gpgme_set_keylist_mode(ctx, kl_save);
 
        return (gpgme_err_code(gpgme_err) == GPG_ERR_EOF);
 }
 
 
+gpgme_key_t
+libbalsa_gpgme_load_key(gpgme_ctx_t   ctx,
+                                               const gchar  *fingerprint,
+                                               GError      **error)
+{
+       gpgme_key_t key = NULL;
+       gpgme_error_t gpgme_err;
+       gpgme_keylist_mode_t kl_mode;
+
+       g_return_val_if_fail((ctx != NULL) && (fingerprint != NULL), NULL);
+
+       /* only use the local key ring */
+       kl_mode = gpgme_get_keylist_mode(ctx);
+       gpgme_err = gpgme_set_keylist_mode(ctx, (kl_mode & ~GPGME_KEYLIST_MODE_EXTERN) | 
GPGME_KEYLIST_MODE_LOCAL);
+       if (gpgme_err != GPG_ERR_NO_ERROR) {
+               libbalsa_gpgme_set_error(error, gpgme_err, _("error setting key list mode"));
+       }
+
+       if (gpgme_err == GPG_ERR_NO_ERROR) {
+               gpgme_err = gpgme_op_keylist_start(ctx, fingerprint, 0);
+               if (gpgme_err != GPG_ERR_NO_ERROR) {
+                       libbalsa_gpgme_set_error(error, gpgme_err, _("could not list keys for “%s”"), 
fingerprint);
+               } else {
+                       gpgme_err = gpgme_op_keylist_next(ctx, &key);
+                       if (gpgme_err != GPG_ERR_NO_ERROR) {
+                               libbalsa_gpgme_set_error(error, gpgme_err, _("could not list keys for “%s”"), 
fingerprint);
+                       } else {
+                               gpgme_key_t next_key;
+
+                               /* verify this is the only one */
+                               gpgme_err = gpgme_op_keylist_next(ctx, &next_key);
+                               if (gpgme_err == GPG_ERR_NO_ERROR) {
+                                       libbalsa_gpgme_set_error(error, GPG_ERR_AMBIGUOUS, _("ambiguous keys 
for “%s”"), fingerprint);
+                                       gpgme_key_unref(next_key);
+                                       gpgme_key_unref(key);
+                                       key = NULL;
+                               }
+                       }
+               }
+       }
+       gpgme_set_keylist_mode(ctx, kl_mode);
+
+       return key;
+}
+
+
 /* documentation: see header file */
 gboolean
 libbalsa_gpgme_keyserver_op(const gchar *fingerprint,
@@ -412,16 +464,7 @@ gpgme_import_key(gpgme_ctx_t   ctx,
 
                /* the key has been considered: load the possibly changed key from the local ring, ignoring 
any errors */
                if ((import_result->considered != 0) && (key->subkeys != NULL)) {
-                       gpgme_keylist_mode_t kl_mode;
-
-                       /* ensure local key list mode */
-                       kl_mode = gpgme_get_keylist_mode(ctx);
-                       kl_mode &= ~GPGME_KEYLIST_MODE_EXTERN;
-                       kl_mode |= GPGME_KEYLIST_MODE_LOCAL;
-                       gpgme_err = gpgme_set_keylist_mode(ctx, kl_mode);
-                       if (gpgme_err == GPG_ERR_NO_ERROR) {
-                               (void) gpgme_get_key(ctx, key->subkeys->fpr, imported_key, 0);
-                       }
+                       *imported_key = libbalsa_gpgme_load_key(ctx, key->subkeys->fpr, error);
                }
 
                result = TRUE;
diff --git a/libbalsa/libbalsa-gpgme-keys.h b/libbalsa/libbalsa-gpgme-keys.h
index aee10b8..dcef961 100644
--- a/libbalsa/libbalsa-gpgme-keys.h
+++ b/libbalsa/libbalsa-gpgme-keys.h
@@ -63,6 +63,21 @@ gboolean libbalsa_gpgme_list_keys(gpgme_ctx_t   ctx,
                                                                  gboolean      on_keyserver,
                                                                  GError      **error);
 
+/** \brief Load a key
+ *
+ * \param ctx GpgME context
+ * \param fingerprint key fingerprint to search for
+ * \param error filled with error information on error, may be NULL
+ * \return the key matching the passed fingerprint, or NULL on error
+ *
+ * Return the key matching the passed fingerprint from the local key ring. The function returns NULL if 
either no or more than one
+ * key is available.
+ */
+gpgme_key_t libbalsa_gpgme_load_key(gpgme_ctx_t   ctx,
+                                                                       const gchar  *fingerprint,
+                                                                       GError      **error)
+       G_GNUC_WARN_UNUSED_RESULT;
+
 /** \brief Search the key server for a key
  *
  * \param fingerprint key fingerprint to search for
diff --git a/libbalsa/libbalsa-gpgme-widgets.c b/libbalsa/libbalsa-gpgme-widgets.c
index 87967a6..58747e4 100644
--- a/libbalsa/libbalsa-gpgme-widgets.c
+++ b/libbalsa/libbalsa-gpgme-widgets.c
@@ -2,7 +2,7 @@
 /*
  * Balsa E-Mail Client
  *
- * gpgme -related widgets
+ * gpgme key related widgets and display functions
  * Copyright (C) 2017 Albrecht Dreß <albrecht dress arcor de>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -26,11 +26,25 @@
 #include "rfc3156.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
+
+#define BULLET_STR     "\302\240\342\200\242\302\240"
+
+
 static gchar *create_status_str(gboolean revoked,
                                                                gboolean expired,
                                                                gboolean disabled,
                                                                gboolean invalid)
        G_GNUC_WARN_UNUSED_RESULT;
+static gchar *create_uid_str(const gpgme_user_id_t  uid,
+                                                        gboolean              *warn)
+       G_GNUC_WARN_UNUSED_RESULT;
+
 static gint create_key_grid_row(GtkGrid     *grid,
                                                                gint         row,
                                                                const gchar *key,
@@ -195,6 +209,134 @@ libbalsa_gpgme_key(gpgme_key_t           key,
 }
 
 
+/* documentation: see header file */
+gchar *
+libbalsa_gpgme_key_to_gchar(gpgme_key_t  key,
+                                                       const gchar *fingerprint)
+{
+       GString *result;
+       gchar *status_str;
+
+       g_return_val_if_fail((key != NULL) && (fingerprint != NULL), NULL);
+
+       result = g_string_new(NULL);
+
+       /* print a warning for a bad key status */
+       status_str = create_status_str(key->expired != 0U, key->revoked != 0U, key->disabled != 0U, 
key->invalid != 0U);
+       if (strlen(status_str) > 0U) {
+               g_string_append_printf(result, "%s %s\n", _("Key status:"), status_str);
+       }
+       g_free(status_str);
+
+       /* primary User ID */
+       if (key->uids == NULL) {
+               g_string_append_printf(result, "%s %s", _("User ID:"), _("None"));
+       } else {
+               gchar *uid_str;
+
+               uid_str = create_uid_str(key->uids, NULL);
+               if (key->uids->next != NULL) {
+                       g_string_append_printf(result, "%s %s", _("Primary user ID:"), uid_str);
+               } else {
+                       g_string_append_printf(result, "%s %s", _("User ID:"), uid_str);
+               }
+               g_free(uid_str);
+       }
+
+       /* owner trust is valid for OpenPGP only */
+       if (key->protocol == GPGME_PROTOCOL_OpenPGP) {
+               g_string_append_printf(result, "\n%s %s", _("Key owner trust:"), 
libbalsa_gpgme_validity_to_gchar_short(key->owner_trust));
+       }
+
+       /* add additional UID's (if any) */
+       if ((key->uids != NULL) && (key->uids->next != NULL)) {
+               gpgme_user_id_t uid;
+
+               g_string_append_printf(result, "\n%s", _("Additional User IDs"));
+               for (uid = key->uids->next; uid != NULL; uid = uid->next) {
+                       gchar *uid_str;
+
+                       uid_str = create_uid_str(uid, NULL);
+                       g_string_append_printf(result, "\n" BULLET_STR "%s", uid_str);
+                       g_free(uid_str);
+               }
+       }
+
+       /* add the issuer information for CMS only */
+       if (key->protocol == GPGME_PROTOCOL_CMS) {
+               g_string_append_printf(result, "\n%s", _("Issuer"));
+
+               if (key->issuer_name != NULL) {
+                       gchar *issuer_readable;
+
+                       issuer_readable = libbalsa_cert_subject_readable(key->issuer_name);
+                       g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Name:"), issuer_readable);
+                       g_free(issuer_readable);
+               }
+               if (key->issuer_serial != NULL) {
+                       g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Serial number:"), 
key->issuer_serial);
+               }
+               if (key->chain_id != NULL) {
+                       g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Chain ID:"), 
key->chain_id);
+               }
+       }
+
+       /* subkey information */
+       if (key->subkeys != NULL) {
+               gpgme_subkey_t subkey;
+
+               g_string_append_printf(result, "\n%s", _("Subkey used"));
+               for (subkey = key->subkeys; subkey != NULL; subkey = subkey->next) {
+                       if (strcmp(fingerprint, subkey->fpr) == 0) {
+                               gchar *details_str;
+                               gchar *timebuf;
+
+                               details_str = create_status_str(subkey->expired != 0U, subkey->revoked != 0U, 
subkey->disabled != 0U,
+                                       subkey->invalid != 0U);
+                               if (strlen(details_str) > 0U) {
+                                       g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Status:"), 
details_str);
+                               }
+                               g_free(details_str);
+
+                               g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Fingerprint:"), 
subkey->fpr);
+
+                               details_str = create_subkey_type_str(subkey);
+                               g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Type:"), 
details_str);
+                               g_free(details_str);
+
+                               details_str = create_purpose_str(subkey->can_sign != 0U, subkey->can_encrypt 
!= 0, subkey->can_certify != 0U,
+                                       subkey->can_authenticate != 0U);
+                               if (strlen(details_str) > 0U) {
+                                       g_string_append_printf(result, "\n" BULLET_STR "%s %s", 
_("Capabilities:"), details_str);
+                               }
+                               g_free(details_str);
+
+                               if (subkey->timestamp == -1) {
+                                       timebuf = g_strdup(_("invalid timestamp"));
+                               } else if (subkey->timestamp == 0) {
+                                       timebuf = g_strdup(_("not available"));
+                               } else {
+                                       timebuf = libbalsa_date_to_utf8(subkey->timestamp, "%x %X");
+                               }
+                               g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Created:"), 
timebuf);
+                               g_free(timebuf);
+
+                               if (subkey->expires == 0) {
+                                       timebuf = g_strdup(_("never"));
+                               } else {
+                                       timebuf = libbalsa_date_to_utf8(subkey->expires, "%x %X");
+                               }
+                               g_string_append_printf(result, "\n" BULLET_STR "%s %s", _("Expires:"), 
timebuf);
+                               g_free(timebuf);
+                       }
+               }
+       }
+
+       return g_string_free(result, FALSE);
+}
+
+
+/* documentation: see header file */
 GtkWidget *
 libbalsa_key_dialog(GtkWindow            *parent,
                                    GtkButtonsType                buttons,
@@ -310,6 +452,44 @@ create_status_str(gboolean revoked,
 }
 
 
+/** \brief Create a UID string
+ *
+ * \param uid UID
+ * \param warn filled with TRUE if the UID is revoked or invalid
+ * \return a newly allocated string containing the UID string
+ *
+ * Create a string containing the passed UID subject.  If the UID is revoked or invalid, the status 
information is added in '(…)'.
+ *
+ * \todo Do we want to add more details from the gpgme_user_id_t data?
+ */
+static gchar *
+create_uid_str(const gpgme_user_id_t uid, gboolean *warn)
+{
+       gchar *uid_readable;
+       gchar *uid_status;
+       gchar *result;
+       gboolean do_warn;
+
+       uid_readable = libbalsa_cert_subject_readable(uid->uid);
+       uid_status = create_status_str(uid->revoked != 0U, FALSE, FALSE, uid->invalid != 0U);
+       if (uid_status[0] != '\0') {
+               result = g_strdup_printf("%s (%s)", uid_readable, uid_status);
+               g_free(uid_readable);
+               g_free(uid_status);
+               do_warn = TRUE;
+       } else {
+               result = uid_readable;
+               do_warn = FALSE;
+       }
+
+       if (warn != NULL) {
+               *warn = do_warn;
+       }
+
+       return result;
+}
+
+
 /** \brief Add a grid row
  *
  * \param grid target grid
@@ -349,29 +529,21 @@ create_key_grid_row(GtkGrid     *grid,
  * Create a widget containing the passed UID subject.  If the UID is revoked or invalid, a warning icon is 
prepended, and the
  * status information is added.
  *
+ * \sa create_uid_str()
+ *
  * \todo We might want to show the TOFU data (requires gpgme >= 1.7.0, and GPGME_KEYLIST_MODE_WITH_TOFU in 
key listing options).
  *       Maybe also add an expander?
  */
 static GtkWidget *
 create_key_uid_widget(const gpgme_user_id_t uid)
 {
-       gchar *uid_readable;
-       gchar *uid_status;
+       gchar *buf;
+       gboolean warn;
        GtkWidget *result;
 
-       uid_readable = libbalsa_cert_subject_readable(uid->uid);
-       uid_status = create_status_str(uid->revoked != 0U, FALSE, FALSE, uid->invalid != 0U);
-       if (strlen(uid_status) > 0U) {
-               gchar *buf;
-
-               buf = g_strdup_printf("%s (%s)", uid_readable, uid_status);
-               result = create_key_label_with_warn(buf, TRUE);
-               g_free(buf);
-       } else {
-               result = create_key_label_with_warn(uid_readable, FALSE);
-       }
-       g_free(uid_status);
-       g_free(uid_readable);
+       buf = create_uid_str(uid, &warn);
+       result = create_key_label_with_warn(buf, warn);
+       g_free(buf);
        return result;
 }
 
@@ -525,7 +697,7 @@ create_purpose_str(gboolean can_sign,
  * \return a newly allocated string containing a human-readable description of the subkey type
  *
  * Create a string containing the length of the subkey in bits, the public key algorithm supported by it and 
for ECC algorithms the
- * name of the curve.  Note that the latter is available for Gpgme >= 1.5.0 only.
+ * name of the curve.
  */
 static gchar *
 create_subkey_type_str(gpgme_subkey_t subkey)
@@ -539,11 +711,9 @@ create_subkey_type_str(gpgme_subkey_t subkey)
        if (algo != NULL) {
                g_string_append_printf(type_str, " %s", algo);
        }
-#if GPGME_VERSION_NUMBER >= 0x010500
        if (subkey->curve != NULL) {
                g_string_append_printf(type_str, _(" curve “%s”"), subkey->curve);
        }
-#endif
 
        return g_string_free(type_str, FALSE);
 }
diff --git a/libbalsa/libbalsa-gpgme-widgets.h b/libbalsa/libbalsa-gpgme-widgets.h
index 21723be..0cda361 100644
--- a/libbalsa/libbalsa-gpgme-widgets.h
+++ b/libbalsa/libbalsa-gpgme-widgets.h
@@ -2,7 +2,7 @@
 /*
  * Balsa E-Mail Client
  *
- * gpgme -related widgets
+ * gpgme key related widgets and display functions
  * Copyright (C) 2017 Albrecht Dreß <albrecht dress arcor de>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -48,7 +48,7 @@ typedef enum {
 /** \brief Create a key widget
  *
  * \param key GnuPG or S/MIME key
- * \param fingerprint fingerprint of the subkey which shall be displayed, NULL to display all subkeys
+ * \param fingerprint fingerprint of the subkey which shall be displayed, NULL to display all subkeys with 
certain capabilities
  * \param subkey_capa mask of capabilities for which subkeys shall be included, used only if \em fingerprint 
is NULL
  * \param expanded whether the expanders shall be initially expanded
  * \return a new widget containing details about the key
@@ -64,6 +64,21 @@ GtkWidget *libbalsa_gpgme_key(gpgme_key_t           key,
        G_GNUC_WARN_UNUSED_RESULT;
 
 
+/** \brief Key details as human-readable string
+ *
+ * \param key GnuPG or S/MIME key
+ * \param fingerprint fingerprint of the subkey which shall be printed, <i>must not</i> be NULL
+ * \return a newly allocated string containing the key details
+ *
+ * Create a human-readable multiline string containing the key details, including the details of the subkey 
identified by the
+ * passed fingerprint.  The string is basically a printable version of libbalsa_gpgme_key() for the same key 
and fingerprint, with
+ * the expanders opened.
+ */
+gchar *libbalsa_gpgme_key_to_gchar(gpgme_key_t  key,
+                                                                  const gchar *fingerprint)
+       G_GNUC_WARN_UNUSED_RESULT;
+
+
 /** \brief Create a key message dialogue
  *
  * \param parent transient parent window, may be NULL
diff --git a/libbalsa/libbalsa-gpgme.c b/libbalsa/libbalsa-gpgme.c
index 4acf9da..a093e69 100644
--- a/libbalsa/libbalsa-gpgme.c
+++ b/libbalsa/libbalsa-gpgme.c
@@ -42,6 +42,12 @@
 #include "libbalsa.h"
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 static gboolean gpgme_add_signer(gpgme_ctx_t ctx, const gchar * signer,
                                 GtkWindow * parent, GError ** error);
 static gpgme_key_t *gpgme_build_recipients(gpgme_ctx_t ctx,
@@ -107,7 +113,7 @@ libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
     const gchar *agent_info;
 
     /* initialise the gpgme library */
-    g_message("init gpgme version %s", gpgme_check_version(NULL));
+    g_debug("init gpgme version %s", gpgme_check_version(NULL));
 
 #ifdef ENABLE_NLS
     gpgme_set_locale(NULL, LC_CTYPE, get_utf8_locale(LC_CTYPE));
@@ -117,7 +123,7 @@ libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
     /* dump the available engines */
     if (gpgme_get_engine_info(&e) == GPG_ERR_NO_ERROR) {
        while (e) {
-           g_message("protocol %s: engine %s (home %s, version %s)",
+               g_debug("protocol %s: engine %s (home %s, version %s)",
                      gpgme_get_protocol_name(e->protocol),
                      e->file_name, e->home_dir, e->version);
            e = e->next;
@@ -127,8 +133,8 @@ libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
     /* check for gpg-agent */
     agent_info = g_getenv("GPG_AGENT_INFO");
     if (agent_info) {
-       g_message("gpg-agent found: %s", agent_info);
-       gpgme_passphrase_cb = NULL;
+       g_debug("gpg-agent found: %s", agent_info);
+       gpgme_passphrase_cb = NULL;
     } else {
        gpgme_passphrase_cb = get_passphrase;
     }
@@ -136,19 +142,18 @@ libbalsa_gpgme_init(gpgme_passphrase_cb_t get_passphrase,
     /* verify that the engines we need are there */
     if (gpgme_engine_check_version(GPGME_PROTOCOL_OpenPGP) ==
        GPG_ERR_NO_ERROR) {
-       g_message("OpenPGP protocol supported");
-       has_proto_openpgp = TRUE;
+       g_debug("OpenPGP protocol supported");
+       has_proto_openpgp = TRUE;
     } else {
-       g_message
-           ("OpenPGP protocol not supported, basic crypto will not work!");
-       has_proto_openpgp = FALSE;
+       g_warning("OpenPGP protocol not supported, basic crypto will not work!");
+       has_proto_openpgp = FALSE;
     }
 
     if (gpgme_engine_check_version(GPGME_PROTOCOL_CMS) == GPG_ERR_NO_ERROR) {
-       g_message("CMS (aka S/MIME) protocol supported");
+       g_debug("CMS (aka S/MIME) protocol supported");
        has_proto_cms = TRUE;
     } else {
-       g_message("CMS protocol not supported, S/MIME will not work!");
+       g_warning("CMS protocol not supported, S/MIME will not work!");
        has_proto_cms = FALSE;
     }
 
@@ -341,9 +346,8 @@ libbalsa_gpgme_verify(GMimeStream * content, GMimeStream * sig_plain,
     if (err != GPG_ERR_NO_ERROR) {
        libbalsa_gpgme_set_error(error, err,
                               _("signature verification failed"));
-       result = g_mime_gpgme_sigstat_new();
+       result = g_mime_gpgme_sigstat_new(ctx);
        result->status = err;
-       result->protocol = gpgme_get_protocol(ctx);
     } else
        result = g_mime_gpgme_sigstat_new_from_gpgme_ctx(ctx);
 
diff --git a/libbalsa/rfc3156.c b/libbalsa/rfc3156.c
index 170602c..37c0429 100644
--- a/libbalsa/rfc3156.c
+++ b/libbalsa/rfc3156.c
@@ -45,6 +45,12 @@
 #include <glib/gi18n.h>
 
 
+#ifdef G_LOG_DOMAIN
+#  undef G_LOG_DOMAIN
+#endif
+#define G_LOG_DOMAIN "crypto"
+
+
 /* local prototypes */
 static gboolean gpg_updates_trustdb(void);
 static gboolean have_pub_key_for(gpgme_ctx_t gpgme_ctx,
@@ -646,7 +652,7 @@ libbalsa_gpgme_sig_stat_to_gchar(gpgme_error_t stat)
        gchar errbuf[4096];             /* should be large enough... */
 
        gpgme_strerror_r(stat, errbuf, sizeof(errbuf));
-       g_message("stat %d: %s %s", stat, gpgme_strsource(stat), errbuf);
+       g_debug("%s: stat %d: %s %s", __func__, stat, gpgme_strsource(stat), errbuf);
        return _("An error prevented the signature verification.");
     }
     }
@@ -697,181 +703,6 @@ libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t validity)
 }
 
 
-const gchar *
-libbalsa_gpgme_sig_protocol_name(gpgme_protocol_t protocol)
-{
-    switch (protocol) {
-    case GPGME_PROTOCOL_OpenPGP:
-       return _("PGP signature: ");
-    case GPGME_PROTOCOL_CMS:
-       return _("S/MIME signature: ");
-    default:
-       return _("(unknown protocol) ");
-    }
-}
-
-static inline void
-append_time_t(GString *str, const gchar *format, time_t when,
-              const gchar * date_string)
-{
-    if (when != (time_t) 0) {
-        gchar *tbuf = libbalsa_date_to_utf8(when, date_string);
-        g_string_append_printf(str, format, tbuf);
-        g_free(tbuf);
-    } else {
-        g_string_append_printf(str, format, _("never"));
-    }
-}
-
-gchar *
-libbalsa_signature_info_to_gchar_short(GMimeGpgmeSigstat *info,
-                                                                          const gchar       *date_string)
-{
-    GString *msg;
-    gchar *retval;
-
-    g_return_val_if_fail(info != NULL, NULL);
-    g_return_val_if_fail(date_string != NULL, NULL);
-    msg = g_string_new(libbalsa_gpgme_sig_protocol_name(info->protocol));
-    msg = g_string_append(msg, libbalsa_gpgme_sig_stat_to_gchar(info->status));
-    g_string_append_printf(msg, _("\nSignature validity: %s"), libbalsa_gpgme_validity_to_gchar(info-> 
validity));
-    append_time_t(msg, _("\nSigned on: %s"), info->sign_time, date_string);
-    if (info->fingerprint) {
-       g_string_append_printf(msg, _("\nKey fingerprint: %s"), info->fingerprint);
-    }
-
-    retval = msg->str;
-    g_string_free(msg, FALSE);
-    return retval;
-}
-
-gchar *
-libbalsa_signature_info_to_gchar(GMimeGpgmeSigstat *info,
-                                                                const gchar       *date_string)
-{
-    GString *msg;
-    gchar *retval;
-
-    g_return_val_if_fail(info != NULL, NULL);
-    g_return_val_if_fail(date_string != NULL, NULL);
-    msg = g_string_new(libbalsa_signature_info_to_gchar_short(info, date_string));
-
-    /* add key information */
-    if (info->key != NULL) {
-        gpgme_user_id_t uid;
-        gpgme_subkey_t subkey;
-
-        if (info->protocol == GPGME_PROTOCOL_OpenPGP) {
-               g_string_append_printf(msg, _("\nKey owner trust: %s"), 
libbalsa_gpgme_validity_to_gchar_short(info->key->owner_trust));
-        }
-
-        /* user ID's */
-        if ((uid = info->key->uids)) {
-            gchar *lead_text;
-
-            uid = info->key->uids;
-            if (uid->next) {
-               msg = g_string_append(msg, _("\nUser IDs:"));
-               lead_text = "\n\342\200\242";
-            } else {
-               msg = g_string_append(msg, _("\nUser ID:"));
-               lead_text = "";
-            }
-
-            /* Note: there is no way to determine which user id has been used
-             * to create the signature.  A broken client may even use an
-             * invalid and/or revoked one.  We therefore add all to the
-             * result. */
-            while (uid) {
-               msg = g_string_append(msg, lead_text);
-               if (uid->revoked)
-                   msg = g_string_append(msg, _(" [Revoked]"));
-               if (uid->invalid)
-                   msg = g_string_append(msg, _(" [Invalid]"));
-
-               if (uid->uid && *(uid->uid)) {
-                   gchar *uid_readable =
-                       libbalsa_cert_subject_readable(uid->uid);
-                   g_string_append_printf(msg, " %s", uid_readable);
-                   g_free(uid_readable);
-               } else {
-                   if (uid->name && *(uid->name))
-                       g_string_append_printf(msg, " %s", uid->name);
-                   if (uid->email && *(uid->email))
-                       g_string_append_printf(msg, " <%s>", uid->email);
-                   if (uid->comment && *(uid->comment))
-                       g_string_append_printf(msg, " (%s)", uid->comment);
-               }
-
-               uid = uid->next;
-            }
-        }
-
-        /* subkey */
-        if ((subkey = info->key->subkeys)) {
-            /* find the one which can sign */
-            while (subkey && !subkey->can_sign)
-               subkey = subkey->next;
-
-            if (subkey) {
-               append_time_t(msg, _("\nSubkey created on: %s"),
-                             subkey->timestamp, date_string);
-               append_time_t(msg, _("\nSubkey expires on: %s"),
-                             subkey->expires, date_string);
-               if (subkey->revoked || subkey->expired || subkey->disabled ||
-                   subkey->invalid) {
-       GString * attrs = g_string_new("");
-       int count = 0;
-
-                   if (subkey->revoked) {
-           count++;
-           attrs = g_string_append(attrs, _(" revoked"));
-       }
-                   if (subkey->expired) {
-           if (count++)
-               attrs = g_string_append_c(attrs, ',');
-           attrs = g_string_append(attrs, _(" expired"));
-       }
-                   if (subkey->disabled) {
-           if (count)
-               attrs = g_string_append_c(attrs, ',');
-           attrs = g_string_append(attrs, _(" disabled"));
-       }
-                   if (subkey->invalid) {
-           if (count)
-               attrs = g_string_append_c(attrs, ',');
-           attrs = g_string_append(attrs, _(" invalid"));
-       }
-                   /* ngettext: string begins with a single space, so no space
-                    * after the colon is correct punctuation (in English). */
-       g_string_append_printf(msg, ngettext("\nSubkey attribute:%s",
-                                            "\nSubkey attributes:%s",
-                                            count),
-                              attrs->str);
-       g_string_free(attrs, TRUE);
-    }
-            }
-        }
-
-        if (info->key->issuer_name) {
-            gchar *issuer_name =
-               libbalsa_cert_subject_readable(info->key->issuer_name);
-            g_string_append_printf(msg, _("\nIssuer name: %s"), issuer_name);
-            g_free(issuer_name);
-    }
-        if (info->key->issuer_serial)
-       g_string_append_printf(msg, _("\nIssuer serial number: %s"),
-                                  info->key->issuer_serial);
-        if (info->key->chain_id)
-            g_string_append_printf(msg, _("\nChain ID: %s"), info->key->chain_id);
-    }
-
-    retval = msg->str;
-    g_string_free(msg, FALSE);
-    return retval;
-}
-
-
 /* ==== local stuff ======================================================== */
 
 
diff --git a/libbalsa/rfc3156.h b/libbalsa/rfc3156.h
index dfb641a..6a34d20 100644
--- a/libbalsa/rfc3156.h
+++ b/libbalsa/rfc3156.h
@@ -101,16 +101,9 @@ gpgme_error_t libbalsa_rfc2440_decrypt(GMimePart * part,
                                       GtkWindow * parent);
 
 /* helper functions to convert states to human-readable form */
-const gchar *libbalsa_gpgme_sig_protocol_name(gpgme_protocol_t protocol);
 const gchar *libbalsa_gpgme_sig_stat_to_gchar(gpgme_error_t stat);
 const gchar *libbalsa_gpgme_validity_to_gchar(gpgme_validity_t validity);
 const gchar *libbalsa_gpgme_validity_to_gchar_short(gpgme_validity_t validity);
-gchar *libbalsa_signature_info_to_gchar(GMimeGpgmeSigstat *info,
-                                                                               const gchar       
*date_string)
-       G_GNUC_WARN_UNUSED_RESULT;
-gchar *libbalsa_signature_info_to_gchar_short(GMimeGpgmeSigstat *info,
-                                                                                     const gchar       
*date_string)
-       G_GNUC_WARN_UNUSED_RESULT;
 
 #endif                         /* HAVE_GPGME */
 #endif                         /* __RFC3156_H__ */
diff --git a/meson.build b/meson.build
index fa90ddf..bee39c9 100644
--- a/meson.build
+++ b/meson.build
@@ -249,9 +249,9 @@ endif
 
 if gpgmecfg != 'false'
   gpgmever = run_command(gpgmecfg, '--version').stdout().strip()
-  if gpgmever.version_compare('< 1.2.0')
+  if gpgmever.version_compare('< 1.5.0')
     gpgmecfg   = 'false'
-    message('Sorry, you need at least gpgme version 1.2.0')
+    message('Sorry, you need at least gpgme version 1.5.0')
   else
     if gpgmever.version_compare('< 1.8.0')
       gpgme_libs = run_command(gpgmecfg, '--thread=pthread', '--libs')
diff --git a/src/balsa-message.c b/src/balsa-message.c
index af3150f..5508793 100644
--- a/src/balsa-message.c
+++ b/src/balsa-message.c
@@ -2743,11 +2743,11 @@ get_crypto_content_icon(LibBalsaMessageBody * body, const gchar * content_type,
        g_ascii_strcasecmp(content_type, "application/pkcs7-signature") &&
        g_ascii_strcasecmp(content_type, "application/x-pkcs7-signature"))
        new_title = g_strconcat(*icon_title, "; ",
-                               libbalsa_gpgme_sig_protocol_name(body->sig_info->protocol),
+               g_mime_gpgme_sigstat_protocol_name(body->sig_info),
                                libbalsa_gpgme_sig_stat_to_gchar(body->sig_info->status),
                                NULL);
     else
-       new_title = g_strconcat(libbalsa_gpgme_sig_protocol_name(body->sig_info->protocol),
+       new_title = g_strconcat(g_mime_gpgme_sigstat_protocol_name(body->sig_info),
                                libbalsa_gpgme_sig_stat_to_gchar(body->sig_info->status),
                                NULL);
 
@@ -3089,15 +3089,14 @@ libbalsa_msg_part_2440(LibBalsaMessage * message, LibBalsaMessageBody * body,
     libbalsa_mailbox_unlock_store(body->message->mailbox);
 
     if (body->sig_info && sig_res == GPG_ERR_NO_ERROR) {
-        if (body->sig_info->validity >= GPGME_VALIDITY_MARGINAL &&
-            body->sig_info->key->owner_trust >= GPGME_VALIDITY_MARGINAL)
-            libbalsa_information(LIBBALSA_INFORMATION_DEBUG,
-                                 _("Detected a good signature"));
-        else
+        if ((body->sig_info->summary & GPGME_SIGSUM_VALID) == GPGME_SIGSUM_VALID) {
+               g_debug("%s: detected a good signature", __func__);
+        } else {
             libbalsa_information
                (LIBBALSA_INFORMATION_MESSAGE,
                 _("Detected a good signature with insufficient "
                   "validity/trust"));
+        }
     } else if (sig_res != GPG_ERR_NO_ERROR && sig_res != GPG_ERR_CANCELED)
        libbalsa_information
            (chk_crypto->chk_mode == LB_MAILBOX_CHK_CRYPT_ALWAYS ?
diff --git a/src/balsa-mime-widget-crypto.c b/src/balsa-mime-widget-crypto.c
index 0ce7189..73a15dd 100644
--- a/src/balsa-mime-widget-crypto.c
+++ b/src/balsa-mime-widget-crypto.c
@@ -36,6 +36,7 @@
 static void on_gpg_key_button(GtkWidget *button, const gchar *fingerprint);
 static void on_key_import_button(GtkButton *button, gpointer user_data);
 static gboolean create_import_keys_widget(GtkBox *box, const gchar *key_buf, GError **error);
+static void show_public_key_data(GtkExpander *expander, gpointer user_data);
 
 
 BalsaMimeWidget *
@@ -97,6 +98,7 @@ balsa_mime_widget_signature_widget(LibBalsaMessageBody * mime_body,
                                   const gchar * content_type)
 {
     gchar *infostr;
+    GtkWidget *expander;
     GtkWidget *vbox, *label;
     GtkWidget *signature_widget;
     gchar **lines;
@@ -105,8 +107,7 @@ balsa_mime_widget_signature_widget(LibBalsaMessageBody * mime_body,
        mime_body->sig_info->status == GPG_ERR_NOT_SIGNED)
        return NULL;
 
-    infostr =
-        libbalsa_signature_info_to_gchar_short(mime_body->sig_info, balsa_app.date_string);
+    infostr = g_mime_gpgme_sigstat_to_gchar(mime_body->sig_info, FALSE, balsa_app.date_string);
     if (infostr == NULL) {
         return NULL;
     }
@@ -118,13 +119,6 @@ balsa_mime_widget_signature_widget(LibBalsaMessageBody * mime_body,
     gtk_label_set_selectable(GTK_LABEL(label), TRUE);
     gtk_widget_set_halign(label, GTK_ALIGN_START);
     gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
-    if (mime_body->sig_info->key != NULL) {
-       GtkWidget *key_widget;
-
-       /* show only the subkey which has been used to sign the message */
-       key_widget = libbalsa_gpgme_key(mime_body->sig_info->key, mime_body->sig_info->fingerprint, 0U, 
FALSE);
-        gtk_box_pack_start(GTK_BOX(vbox), key_widget, FALSE, FALSE, 0);
-    }
     if (mime_body->sig_info->protocol == GPGME_PROTOCOL_OpenPGP) {
         GtkWidget *button;
 
@@ -139,19 +133,22 @@ balsa_mime_widget_signature_widget(LibBalsaMessageBody * mime_body,
         gtk_box_pack_start(GTK_BOX(vbox), button, FALSE, FALSE, 0);
     }
 
-    if (lines[1] != NULL) {
-        /* Hack alert: if we omit the box below and use the expander as signature widget
-         * directly, setting the container border width of the container = the expander
-         * causes its sensitive area to shrink to an almost unusable narrow line above
-         * the label... */
-        GtkWidget *expander;
-
-        signature_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
-        expander = gtk_expander_new(lines[0]);
-        gtk_container_add(GTK_CONTAINER(signature_widget), expander);
-        gtk_container_add(GTK_CONTAINER(expander), vbox);
-    } else {
-        signature_widget = vbox;
+    /* Hack alert: if we omit the box below and use the expander as signature widget
+     * directly, setting the container border width of the container = the expander
+     * causes its sensitive area to shrink to an almost unusable narrow line above
+     * the label... */
+    signature_widget = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
+    expander = gtk_expander_new(lines[0]);
+    gtk_container_add(GTK_CONTAINER(signature_widget), expander);
+    gtk_container_add(GTK_CONTAINER(expander), vbox);
+
+    /* add a callback to load the key when the user wants to show the details
+     * Note: do *not* pass mime_body->sig_info to the callback, as it will be replaced when the user 
re-checks the signature or
+     * opens the message again in a separate window */
+    if (((mime_body->sig_info->summary & GPGME_SIGSUM_KEY_MISSING) == 0) &&
+       (mime_body->sig_info->key == NULL)) {
+       g_signal_connect(expander, "activate", (GCallback) show_public_key_data, mime_body);
+       g_object_set_data(G_OBJECT(expander), "vbox", vbox);
     }
     gtk_container_set_border_width(GTK_CONTAINER(signature_widget), BMW_CONTAINER_BORDER);
 
@@ -339,4 +336,29 @@ create_import_keys_widget(GtkBox *box, const gchar *key_buf, GError **error)
        return success;
 }
 
+static void
+show_public_key_data(GtkExpander *expander,
+                     gpointer     user_data)
+{
+       LibBalsaMessageBody *body = (LibBalsaMessageBody *) user_data;
+
+       g_message("%s: %p %p %p", __func__, expander, body, body->sig_info);
+       if (body->sig_info != NULL) {
+               if (body->sig_info->key == NULL) {
+                       g_mime_gpgme_sigstat_load_key(body->sig_info);
+               }
+
+               if ((g_object_get_data(G_OBJECT(expander), "vbox") != NULL) && (body->sig_info->key != NULL)) 
{
+                       GtkWidget *key_widget;
+                       GtkBox *vbox;
+
+                       vbox = GTK_BOX(g_object_steal_data(G_OBJECT(expander), "vbox"));
+                       key_widget = libbalsa_gpgme_key(body->sig_info->key, body->sig_info->fingerprint, 0U, 
FALSE);
+                       gtk_box_pack_start(vbox, key_widget, FALSE, FALSE, 0);
+                       gtk_box_reorder_child(vbox, key_widget, 1U);
+                       gtk_widget_show_all(key_widget);
+               }
+       }
+}
+
 #endif  /* HAVE_GPGME */
diff --git a/src/balsa-mime-widget-message.c b/src/balsa-mime-widget-message.c
index c0037a4..b2b37f2 100644
--- a/src/balsa-mime-widget-message.c
+++ b/src/balsa-mime-widget-message.c
@@ -850,7 +850,7 @@ add_header_sigstate(GtkGrid * grid, GMimeGpgmeSigstat * siginfo)
         GPG_ERR_NO_ERROR ? "<i>%s%s</i>" : "<b><i>%s%s</i></b>";
     msg = g_markup_printf_escaped
         (format,
-         libbalsa_gpgme_sig_protocol_name(siginfo->protocol),
+               g_mime_gpgme_sigstat_protocol_name(siginfo),
          libbalsa_gpgme_sig_stat_to_gchar(siginfo->status));
 
     label = gtk_label_new(NULL);
diff --git a/src/balsa-print-object-header.c b/src/balsa-print-object-header.c
index 352a461..cc7e3e4 100644
--- a/src/balsa-print-object-header.c
+++ b/src/balsa-print-object-header.c
@@ -1,7 +1,7 @@
 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
 /* Balsa E-Mail Client
  * Copyright (C) 1997-2016 Stuart Parmenter and others
- * Written by (C) Albrecht Dre� <albrecht dress arcor de> 2007
+ * Written by (C) Albrecht Dreß <albrecht dress arcor de> 2007
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -232,7 +232,7 @@ balsa_print_object_header_new_real(GList * list,
            GMimeGpgmeSigstat *siginfo = sig_body->parts->next->sig_info;
 
            g_string_append_printf(header_buf, "%s%s\n",
-                                   libbalsa_gpgme_sig_protocol_name(siginfo->protocol),
+               g_mime_gpgme_sigstat_protocol_name(siginfo),
                                    libbalsa_gpgme_sig_stat_to_gchar(siginfo->status));
        }
     }
@@ -387,10 +387,14 @@ balsa_print_object_header_crypto(GList *list, GtkPrintContext * context,
     pango_layout_set_font_description(test_layout, header_font);
     pango_font_description_free(header_font);
 
+    /* check if the key needs to be loaded */
+    if (((body->sig_info->summary & GPGME_SIGSUM_KEY_MISSING) == 0) &&
+       (body->sig_info->key == NULL)) {
+       g_mime_gpgme_sigstat_load_key(body->sig_info);
+    }
+
     /* create a buffer with the signature info */
-    textbuf =
-       libbalsa_signature_info_to_gchar(body->sig_info,
-                                        balsa_app.date_string);
+    textbuf = g_mime_gpgme_sigstat_to_gchar(body->sig_info, TRUE, balsa_app.date_string);
     if (label) {
        gchar *newbuf = g_strconcat(label, "\n", textbuf, NULL);
 


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