Sometimes people change their E-Mail address, or add mail aliases, without adding the proper uid's to their public keys. In this case, it is not possible to send them encrypted messages as the key selection code in Balsa completely relies on a key uid which matches the mail address.
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.
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 *);
pgpNQYssuAX5K.pgp
Description: PGP signature
_______________________________________________ balsa-list mailing list [email protected] https://mail.gnome.org/mailman/listinfo/balsa-list
