[balsa/gtk3] Pop up key selection dialogue with all keys



commit fc02a5c0307e6eaf6952a3d0221413ebc808809a
Author: Peter Bloomfield <PeterBloomfield bellsouth net>
Date:   Mon May 16 21:03:26 2016 -0400

    Pop up key selection dialogue with all keys
    
        * libbalsa/libbalsa-gpgme-cb.c (lb_gpgme_select_key),
        (key_selection_changed_cb), (sort_iter_cmp_fn):
        * libbalsa/libbalsa-gpgme-cb.h:
        * libbalsa/libbalsa-gpgme.c (get_key_from_name), (get_pubkey),
        (gpgme_add_signer), (gpgme_build_recipients):
        * libbalsa/libbalsa-gpgme.h:

 ChangeLog                    |   15 ++++++
 libbalsa/libbalsa-gpgme-cb.c |  114 ++++++++++++++++++++++++++++++++----------
 libbalsa/libbalsa-gpgme-cb.h |    9 +++-
 libbalsa/libbalsa-gpgme.c    |   90 ++++++++++++++++++++++++++-------
 libbalsa/libbalsa-gpgme.h    |    3 +-
 5 files changed, 184 insertions(+), 47 deletions(-)
---
diff --git a/ChangeLog b/ChangeLog
index 4b56aa1..6931a22 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,20 @@
 2016-05-16  Albrecht Dreß
 
+       With this patch, Balsa will pop up the key selection dialogue
+       containing /all/ keys which may be used for encryption in this
+       case.  The user can either select a key, or cancel the
+       operation.  This basically is the same behaviour as of
+       Thunderbird.
+
+       * libbalsa/libbalsa-gpgme-cb.c (lb_gpgme_select_key),
+       (key_selection_changed_cb), (sort_iter_cmp_fn):
+       * libbalsa/libbalsa-gpgme-cb.h:
+       * libbalsa/libbalsa-gpgme.c (get_key_from_name), (get_pubkey),
+       (gpgme_add_signer), (gpgme_build_recipients):
+       * libbalsa/libbalsa-gpgme.h:
+
+2016-05-16  Albrecht Dreß
+
        * libbalsa/gmime-gpgme-signature.c
        (libbalsa_cert_subject_readable):
        This trivial patch makes sure that the GPG key or S/MIME
diff --git a/libbalsa/libbalsa-gpgme-cb.c b/libbalsa/libbalsa-gpgme-cb.c
index c360be0..327afab 100644
--- a/libbalsa/libbalsa-gpgme-cb.c
+++ b/libbalsa/libbalsa-gpgme-cb.c
@@ -71,6 +71,8 @@ typedef struct {
 
 static void key_selection_changed_cb(GtkTreeSelection * selection,
                                     gpgme_key_t * key);
+static gint sort_iter_cmp_fn(GtkTreeModel *model, GtkTreeIter *a,
+                                        GtkTreeIter *b, gpointer data);
 static gchar *get_passphrase_real(const gchar * uid_hint,
                                  const gchar * passphrase_info,
                                  int prev_was_bad, GtkWindow * parent);
@@ -139,7 +141,7 @@ lb_gpgme_passphrase(void *hook, const gchar * uid_hint,
 
 
 gpgme_key_t
-lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
+lb_gpgme_select_key(const gchar * user_name, lb_key_sel_md_t mode, GList * keys,
                    gpgme_protocol_t protocol, GtkWindow * parent)
 {
     static const gchar *col_titles[] =
@@ -149,13 +151,15 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
     GtkWidget *label;
     GtkWidget *scrolled_window;
     GtkWidget *tree_view;
-    GtkTreeStore *model;
+    GtkListStore *model;
+       GtkTreeSortable *sortable;
     GtkTreeSelection *selection;
     GtkTreeIter iter;
     gint i, last_col;
     gchar *prompt;
     gchar *upcase_name;
     gpgme_key_t use_key = NULL;
+    gint width, height;
 
     /* FIXME: create dialog according to the Gnome HIG */
     dialog = gtk_dialog_new_with_buttons(_("Select key"),
@@ -165,22 +169,38 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
                                          _("_OK"),     GTK_RESPONSE_OK,
                                          _("_Cancel"), GTK_RESPONSE_CANCEL,
                                          NULL);
+    gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL);
+    gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog), GTK_RESPONSE_OK, FALSE);
 #if HAVE_MACOSX_DESKTOP
     libbalsa_macosx_menu_for_parent(dialog, parent);
 #endif
     vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 12);
+    gtk_widget_set_vexpand (vbox, TRUE);
     gtk_container_add(GTK_CONTAINER
                      (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
                      vbox);
-    gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
-    if (secret)
-       prompt =
-           g_strdup_printf(_("Select the private key for the signer %s"),
-                           user_name);
-    else
-       prompt = g_strdup_printf(_
-                                ("Select the public key for the recipient %s"),
-                                user_name);
+   gtk_container_set_border_width(GTK_CONTAINER(vbox), 12);
+    switch (mode) {
+       case LB_SELECT_PRIVATE_KEY:
+               prompt =
+                       g_strdup_printf(_("Select the private key for the signer %s"),
+                                                       user_name);
+               break;
+       case LB_SELECT_PUBLIC_KEY_USER:
+               prompt =
+                       g_strdup_printf(_("Select the public key for the recipient %s"),
+                                       user_name);
+               break;
+       case LB_SELECT_PUBLIC_KEY_ANY:
+               prompt =
+                       g_strdup_printf(_("There seems to be no public key for recipient "
+                                 "%s in your key ring.\nIf you are sure that the "
+                                                         "recipient owns a different key, select it from "
+                                                         "the list."), user_name);
+               break;
+       default:
+               g_assert_not_reached();
+       }
     label = gtk_label_new(prompt);
     gtk_widget_set_halign(label, GTK_ALIGN_START);
     g_free(prompt);
@@ -191,17 +211,22 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
                                        (scrolled_window),
                                        GTK_SHADOW_ETCHED_IN);
     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
-                                  GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
+       GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
     gtk_box_pack_start(GTK_BOX(vbox), scrolled_window, TRUE, TRUE, 0);
 
-    model = gtk_tree_store_new(GPG_KEY_NUM_COLUMNS, G_TYPE_STRING,     /* user ID */
+    model = gtk_list_store_new(GPG_KEY_NUM_COLUMNS, G_TYPE_STRING,     /* user ID */
                               G_TYPE_STRING,   /* key ID */
                               G_TYPE_INT,      /* length */
                               G_TYPE_STRING,   /* validity (gpg encrypt only) */
                               G_TYPE_POINTER); /* key */
+    sortable = GTK_TREE_SORTABLE(model);
+    gtk_tree_sortable_set_sort_func(sortable, 0, sort_iter_cmp_fn, NULL, NULL);
+    gtk_tree_sortable_set_sort_column_id(sortable, 0, GTK_SORT_ASCENDING);
 
     tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(model));
     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(tree_view));
+    g_object_set_data(G_OBJECT(selection), "dialog", dialog);
+    g_object_set_data(G_OBJECT(selection), "first", GUINT_TO_POINTER(1));
     gtk_tree_selection_set_mode(selection, GTK_SELECTION_SINGLE);
     g_signal_connect(G_OBJECT(selection), "changed",
                     G_CALLBACK(key_selection_changed_cb), &use_key);
@@ -216,9 +241,11 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
        gboolean uid_found;
 
        /* find the relevant subkey */
-       while (subkey && ((secret && !subkey->can_sign) ||
-                         (!secret && !subkey->can_encrypt)))
+       while (subkey &&
+                  (((mode == LB_SELECT_PRIVATE_KEY) && !subkey->can_sign) ||
+                       ((mode != LB_SELECT_PRIVATE_KEY) && !subkey->can_encrypt))) {
            subkey = subkey->next;
+       }
 
        /* find the relevant uid */
        uid_found = FALSE;
@@ -227,7 +254,9 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
            uid_info = libbalsa_cert_subject_readable(uid->uid);
 
            /* check the email field which may or may not be present */
-           if (uid->email && !g_ascii_strcasecmp(uid->email, user_name))
+           if (uid->email &&
+               ((mode == LB_SELECT_PUBLIC_KEY_ANY) ||
+                !g_ascii_strcasecmp(uid->email, user_name)))
                uid_found = TRUE;
            else {
                /* no email or no match, check the uid */
@@ -242,9 +271,9 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
        }
 
        /* append the element */
-       if (subkey && uid) {
-           gtk_tree_store_append(GTK_TREE_STORE(model), &iter, NULL);
-           gtk_tree_store_set(GTK_TREE_STORE(model), &iter,
+       if (subkey && uid && uid_info) {
+               gtk_list_store_append(model, &iter);
+               gtk_list_store_set(model, &iter,
                               GPG_KEY_USER_ID_COLUMN, uid_info,
                               GPG_KEY_ID_COLUMN, subkey->keyid,
                               GPG_KEY_LENGTH_COLUMN, subkey->length,
@@ -260,7 +289,7 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
 
     g_object_unref(G_OBJECT(model));
     /* show the validity only if we are asking for a gpg public key */
-    last_col = (protocol == GPGME_PROTOCOL_CMS || secret) ?
+    last_col = (protocol == GPGME_PROTOCOL_CMS || (mode == LB_SELECT_PRIVATE_KEY)) ?
        GPG_KEY_LENGTH_COLUMN : GPG_KEY_VALIDITY_COLUMN;
     for (i = 0; i <= last_col; i++) {
        GtkCellRenderer *renderer;
@@ -272,11 +301,14 @@ lb_gpgme_select_key(const gchar * user_name, gboolean secret, GList * keys,
                                                     renderer, "text", i,
                                                     NULL);
        gtk_tree_view_append_column(GTK_TREE_VIEW(tree_view), column);
-       gtk_tree_view_column_set_resizable(column, TRUE);
+       gtk_tree_view_column_set_resizable(column, (i == 0) ? TRUE : FALSE);
     }
 
     gtk_container_add(GTK_CONTAINER(scrolled_window), tree_view);
-    gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 300);
+
+    /* set window size to 2/3 of the parent */
+    gtk_window_get_size(parent, &width, &height);
+    gtk_window_set_default_size(GTK_WINDOW(dialog), (2 * width) / 3, (2 * height) / 3);
     gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
 
     if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK)
@@ -425,9 +457,37 @@ get_passphrase_idle(gpointer data)
 static void
 key_selection_changed_cb(GtkTreeSelection * selection, gpgme_key_t * key)
 {
-    GtkTreeIter iter;
-    GtkTreeModel *model;
-
-    if (gtk_tree_selection_get_selected(selection, &model, &iter))
-       gtk_tree_model_get(model, &iter, GPG_KEY_PTR_COLUMN, key, -1);
+    GtkDialog *dialog =
+       GTK_DIALOG(g_object_get_data(G_OBJECT(selection), "dialog"));
+
+    if (GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(selection), "first")) != 0) {
+       gtk_tree_selection_unselect_all(selection);
+       g_object_set_data(G_OBJECT(selection), "first", GUINT_TO_POINTER(0));
+    } else {
+        GtkTreeIter iter;
+        GtkTreeModel *model;
+
+        if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+               gtk_tree_model_get(model, &iter, GPG_KEY_PTR_COLUMN, key, -1);
+               gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_OK, TRUE);
+        } else {
+               gtk_dialog_set_response_sensitive(dialog, GTK_RESPONSE_OK, FALSE);
+        }
+    }
 }
+
+/* compare function for the key list */
+static gint
+sort_iter_cmp_fn(GtkTreeModel *model, GtkTreeIter *a, GtkTreeIter *b,
+                                gpointer data)
+{
+       gchar *name1, *name2;
+       gint result;
+
+       gtk_tree_model_get(model, a, 0, &name1, -1);
+       gtk_tree_model_get(model, b, 0, &name2, -1);
+       result = g_utf8_collate(name1, name2);
+       g_free(name1);
+       g_free(name2);
+       return result;
+ }
diff --git a/libbalsa/libbalsa-gpgme-cb.h b/libbalsa/libbalsa-gpgme-cb.h
index 802ce5f..c16f59e 100644
--- a/libbalsa/libbalsa-gpgme-cb.h
+++ b/libbalsa/libbalsa-gpgme-cb.h
@@ -37,10 +37,17 @@ extern "C" {
 #endif                         /* __cplusplus */
 
 
+typedef enum {
+    LB_SELECT_PRIVATE_KEY = 1,
+    LB_SELECT_PUBLIC_KEY_USER,
+    LB_SELECT_PUBLIC_KEY_ANY
+} lb_key_sel_md_t;
+
+
 gpgme_error_t lb_gpgme_passphrase(void *hook, const gchar * uid_hint,
                                  const gchar * passphrase_info,
                                  int prev_was_bad, int fd);
-gpgme_key_t lb_gpgme_select_key(const gchar * user_name, gboolean secret,
+gpgme_key_t lb_gpgme_select_key(const gchar * user_name, lb_key_sel_md_t mode,
                                GList * keys, gpgme_protocol_t protocol,
                                GtkWindow * parent);
 gboolean lb_gpgme_accept_low_trust_key(const gchar * user_name,
diff --git a/libbalsa/libbalsa-gpgme.c b/libbalsa/libbalsa-gpgme.c
index 1110677..f22cf2c 100644
--- a/libbalsa/libbalsa-gpgme.c
+++ b/libbalsa/libbalsa-gpgme.c
@@ -823,8 +823,9 @@ get_key_from_name(gpgme_ctx_t ctx, const gchar * name, gboolean secret,
     if (g_list_length(keys) > 1) {
        if (select_key_cb)
            key =
-               select_key_cb(name, secret, keys, gpgme_get_protocol(ctx),
-                             parent);
+               select_key_cb(name,
+                                         secret ? LB_SELECT_PRIVATE_KEY : LB_SELECT_PUBLIC_KEY_USER,
+                                         keys, gpgme_get_protocol(ctx), parent);
        else {
            if (error)
                g_set_error(error, GPGME_ERROR_QUARK,
@@ -885,6 +886,57 @@ get_key_from_name(gpgme_ctx_t ctx, const gchar * name, gboolean secret,
 }
 
 
+static gpgme_key_t
+get_pubkey(gpgme_ctx_t ctx, const gchar * name, gboolean accept_all,
+       GtkWindow * parent, GError ** error)
+{
+       GList *keys = NULL;
+       gpgme_key_t key;
+       gpgme_error_t err;
+       time_t now = time(NULL);
+
+       /* let gpgme list keys */
+       if ((err = gpgme_op_keylist_start(ctx, NULL, 0)) != GPG_ERR_NO_ERROR) {
+               gchar *msg = g_strdup_printf(_("could not list keys"));
+
+               g_set_error_from_gpgme(error, err, msg);
+               g_free(msg);
+               return NULL;
+       }
+
+       while ((err = gpgme_op_keylist_next(ctx, &key)) == GPG_ERR_NO_ERROR) {
+               /* check if this key and the relevant subkey are usable */
+               if (check_key(key, 0, now))
+                       keys = g_list_append(keys, key);
+       }
+
+       if (gpg_err_code(err) != GPG_ERR_EOF || !keys) {
+               gchar *msg = g_strdup_printf(_("could not list keys"));
+
+               g_set_error_from_gpgme(error, err, msg);
+               g_free(msg);
+               gpgme_op_keylist_end(ctx);
+               g_list_foreach(keys, (GFunc) gpgme_key_unref, NULL);
+               g_list_free(keys);
+               return NULL;
+       }
+       gpgme_op_keylist_end(ctx);
+
+       /* let the user select a key from the list, even if there is only one */
+       if (select_key_cb)
+               key = select_key_cb(name, LB_SELECT_PUBLIC_KEY_ANY, keys,
+                                                       gpgme_get_protocol(ctx), parent);
+       else
+               key = NULL;
+       if (key) {
+               gpgme_key_ref(key);
+               g_list_foreach(keys, (GFunc) gpgme_key_unref, NULL);
+       }
+       g_list_free(keys);
+       return key;
+}
+
+
 /*
  * Add signer to ctx's list of signers and return TRUE on success or FALSE
  * on error.
@@ -893,19 +945,20 @@ static gboolean
 gpgme_add_signer(gpgme_ctx_t ctx, const gchar * signer, GtkWindow * parent,
                 GError ** error)
 {
-    gpgme_key_t key;
+       gboolean result = FALSE;
+       gpgme_key_t key;
 
     /* note: private (secret) key has never low trust... */
-    if (!
-       (key = get_key_from_name(ctx, signer, TRUE, FALSE, parent, error)))
-       return FALSE;
-
-    /* set the key (the previous operation guaranteed that it exists, no
-     * need 2 check return values...) */
-    gpgme_signers_add(ctx, key);
-    gpgme_key_unref(key);
+       key = get_key_from_name(ctx, signer, TRUE, FALSE, parent, error);
+       if (key != NULL) {
+               /* set the key (the previous operation guaranteed that it exists, no
+                * need 2 check return values...) */
+               gpgme_signers_add(ctx, key);
+               gpgme_key_unref(key);
+               result = TRUE;
+       }
 
-    return TRUE;
+    return result;
 }
 
 
@@ -927,12 +980,13 @@ gpgme_build_recipients(gpgme_ctx_t ctx, GPtrArray * rcpt_list,
        gchar *name = (gchar *) g_ptr_array_index(rcpt_list, num_rcpts);
        gpgme_key_t key;
 
-       if (!
-           (key =
-            get_key_from_name(ctx, name, FALSE, accept_low_trust, parent,
-                              error))) {
-           release_keylist(rcpt);
-           return NULL;
+               key = get_key_from_name(ctx, name, FALSE, accept_low_trust, parent, error);
+               if (key == NULL) {
+                       key = get_pubkey(ctx, name, accept_low_trust, parent, error);
+                       if (key == NULL) {
+                               release_keylist(rcpt);
+                               return NULL;
+                       }
        }
 
        /* set the recipient */
diff --git a/libbalsa/libbalsa-gpgme.h b/libbalsa/libbalsa-gpgme.h
index b58f0a2..3eccf47 100644
--- a/libbalsa/libbalsa-gpgme.h
+++ b/libbalsa/libbalsa-gpgme.h
@@ -31,6 +31,7 @@
 #include <glib.h>
 #include <gtk/gtk.h>
 #include <gmime/gmime.h>
+#include "libbalsa-gpgme-cb.h"
 #include "gmime-gpgme-signature.h"
 
 
@@ -55,7 +56,7 @@ extern "C" {
  * - parent window
  */
 typedef gpgme_key_t(*lbgpgme_select_key_cb) (const gchar *,
-                                                gboolean,
+                                                lb_key_sel_md_t,
                                                 GList *,
                                                 gpgme_protocol_t,
                                                 GtkWindow *);


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