1 /* select-keys.c - GTK+ based key selection
2 * Copyright (C) 2001-2006 Werner Koch (dd9jn) and the Sylpheed-Claws team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtkmain.h>
31 #include <gtk/gtkwidget.h>
32 #include <gtk/gtkwindow.h>
33 #include <gtk/gtkscrolledwindow.h>
34 #include <gtk/gtkvbox.h>
35 #include <gtk/gtkhbox.h>
36 #include <gtk/gtkclist.h>
37 #include <gtk/gtklabel.h>
38 #include <gtk/gtkentry.h>
39 #include <gtk/gtkhbbox.h>
40 #include <gtk/gtkbutton.h>
41 #include <gtk/gtkstock.h>
43 #include "select-keys.h"
46 #include "inputdialog.h"
47 #include "manage_window.h"
48 #include "alertpanel.h"
50 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
51 #define DIMof(type,member) DIM(((type *)0)->member)
64 struct select_keys_s {
70 unsigned int num_keys;
72 gpgme_ctx_t select_ctx;
73 gpgme_protocol_t proto;
74 GtkSortType sort_type;
75 enum col_titles sort_column;
76 SelectionResult result;
80 static void set_row (GtkCList *clist, gpgme_key_t key);
81 static gpgme_key_t fill_clist (struct select_keys_s *sk, const char *pattern,
82 gpgme_protocol_t proto);
83 static void create_dialog (struct select_keys_s *sk);
84 static void open_dialog (struct select_keys_s *sk);
85 static void close_dialog (struct select_keys_s *sk);
86 static gint delete_event_cb (GtkWidget *widget,
87 GdkEventAny *event, gpointer data);
88 static gboolean key_pressed_cb (GtkWidget *widget,
89 GdkEventKey *event, gpointer data);
90 static void select_btn_cb (GtkWidget *widget, gpointer data);
91 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
92 static void dont_encrypt_btn_cb (GtkWidget *widget, gpointer data);
93 static void other_btn_cb (GtkWidget *widget, gpointer data);
94 static void sort_keys (struct select_keys_s *sk, enum col_titles column);
95 static void sort_keys_name (GtkWidget *widget, gpointer data);
96 static void sort_keys_email (GtkWidget *widget, gpointer data);
98 static gboolean use_untrusted (gpgme_key_t);
101 update_progress (struct select_keys_s *sk, int running, const char *pattern)
103 static int windmill[] = { '-', '\\', '|', '/' };
107 buf = g_strdup_printf (_("No exact match for '%s'; please select the key."),
110 buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
112 windmill[running%DIM(windmill)]);
113 gtk_label_set_text (sk->toplabel, buf);
119 * gpgmegtk_recipient_selection:
120 * @recp_names: A list of email addresses
122 * Select a list of recipients from a given list of email addresses.
123 * This may pop up a window to present the user a choice, it will also
124 * check that the recipients key are all valid.
126 * Return value: NULL on error or a list of list of recipients.
129 gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
130 gpgme_protocol_t proto)
132 struct select_keys_s sk;
133 gpgme_key_t key = NULL;
134 memset (&sk, 0, sizeof sk);
139 sk.pattern = recp_names? recp_names->data:NULL;
141 gtk_clist_clear (sk.clist);
142 key = fill_clist (&sk, sk.pattern, proto);
143 update_progress (&sk, 0, sk.pattern);
145 gtk_widget_show_all (sk.window);
148 gtk_widget_hide (sk.window);
149 sk.kset = g_realloc(sk.kset,
150 sizeof(gpgme_key_t) * (sk.num_keys + 1));
152 sk.kset[sk.num_keys] = key;
155 sk.result = KEY_SELECTION_OK;
156 gpgme_release (sk.select_ctx);
157 sk.select_ctx = NULL;
158 printf("used %s\n", key->uids->email);
162 recp_names = recp_names->next;
163 } while (sk.okay && recp_names);
171 sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
172 sk.kset[sk.num_keys] = NULL;
180 destroy_key (gpointer data)
182 gpgme_key_t key = data;
183 gpgme_key_release (key);
187 set_row (GtkCList *clist, gpgme_key_t key)
190 const char *text[N_COL_TITLES];
193 gsize by_read = 0, by_written = 0;
196 /* first check whether the key is capable of encryption which is not
197 * the case for revoked, expired or sign-only keys */
198 if (!key->can_encrypt)
200 algo_buf = g_strdup_printf ("%du/%s",
201 key->subkeys->length,
202 gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
203 text[COL_ALGO] = algo_buf;
205 s = key->subkeys->keyid;
206 if (strlen (s) == 16)
207 s += 8; /* show only the short keyID */
211 if (!s || !strlen(s))
213 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
214 if (ret_str && by_written) {
219 s = key->uids->email;
220 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
221 if (ret_str && by_written) {
226 switch (key->uids->validity)
228 case GPGME_VALIDITY_UNDEFINED:
231 case GPGME_VALIDITY_NEVER:
234 case GPGME_VALIDITY_MARGINAL:
237 case GPGME_VALIDITY_FULL:
240 case GPGME_VALIDITY_ULTIMATE:
243 case GPGME_VALIDITY_UNKNOWN:
248 text[COL_VALIDITY] = s;
250 row = gtk_clist_append (clist, (gchar**)text);
253 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
257 fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
265 gboolean exact_match = FALSE;
266 gpgme_key_t last_key = NULL;
267 g_return_val_if_fail (sk, NULL);
269 g_return_val_if_fail (clist, NULL);
271 debug_print ("select_keys:fill_clist: pattern '%s' proto %d\n", pattern, proto);
273 /*gtk_clist_freeze (select_keys.clist);*/
274 err = gpgme_new (&ctx);
277 gpgme_set_protocol(ctx, proto);
278 sk->select_ctx = ctx;
280 update_progress (sk, ++running, pattern);
281 while (gtk_events_pending ())
282 gtk_main_iteration ();
284 err = gpgme_op_keylist_start (ctx, pattern, 0);
286 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
287 pattern, gpgme_strerror (err));
288 sk->select_ctx = NULL;
292 update_progress (sk, ++running, pattern);
293 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
294 gpgme_user_id_t uid = key->uids;
295 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
296 set_row (clist, key );
297 for (; uid; uid = uid->next) {
298 if (!strcmp(pattern, uid->email)) {
306 update_progress (sk, ++running, pattern);
307 while (gtk_events_pending ())
308 gtk_main_iteration ();
311 if (exact_match == TRUE && num_results == 1) {
312 if (last_key->uids->validity < GPGME_VALIDITY_FULL &&
313 !use_untrusted(last_key))
317 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
318 if (gpgme_err_code(err) != GPG_ERR_EOF) {
319 debug_print ("** gpgme_op_keylist_next failed: %s",
320 gpgme_strerror (err));
321 gpgme_op_keylist_end(ctx);
323 if (!exact_match || num_results != 1) {
324 sk->select_ctx = NULL;
327 /*gtk_clist_thaw (select_keys.clist);*/
328 return (exact_match == TRUE && num_results == 1 ? last_key:NULL);
333 create_dialog (struct select_keys_s *sk)
336 GtkWidget *vbox, *vbox2, *hbox;
338 GtkWidget *scrolledwin;
341 GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
342 const char *titles[N_COL_TITLES];
344 g_assert (!sk->window);
345 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
346 gtk_widget_set_size_request (window, 520, 280);
347 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
348 gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
349 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
350 g_signal_connect (G_OBJECT (window), "delete_event",
351 G_CALLBACK (delete_event_cb), sk);
352 g_signal_connect (G_OBJECT (window), "key_press_event",
353 G_CALLBACK (key_pressed_cb), sk);
354 MANAGE_WINDOW_SIGNALS_CONNECT (window);
356 vbox = gtk_vbox_new (FALSE, 8);
357 gtk_container_add (GTK_CONTAINER (window), vbox);
359 hbox = gtk_hbox_new(FALSE, 4);
360 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
361 label = gtk_label_new ( "" );
362 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
364 hbox = gtk_hbox_new (FALSE, 8);
365 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
366 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
368 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
369 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
370 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
371 GTK_POLICY_AUTOMATIC,
372 GTK_POLICY_AUTOMATIC);
374 titles[COL_ALGO] = _("Size");
375 titles[COL_KEYID] = _("Key ID");
376 titles[COL_NAME] = _("Name");
377 titles[COL_EMAIL] = _("Address");
378 titles[COL_VALIDITY] = _("Val");
380 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
381 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
382 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
383 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
384 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
385 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
386 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
387 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
388 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
390 G_CALLBACK(sort_keys_name), sk);
391 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
393 G_CALLBACK(sort_keys_email), sk);
395 hbox = gtk_hbox_new (FALSE, 8);
396 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
398 gtkut_stock_button_set_create (&bbox,
399 &select_btn, _("Select"),
400 &other_btn, _("Other"),
401 &dont_encrypt_btn, _("Don't encrypt"));
403 cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
404 GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
405 gtk_box_pack_start(GTK_BOX(bbox), cancel_btn, TRUE, TRUE, 0);
406 gtk_widget_show(cancel_btn);
407 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
408 gtk_widget_grab_default (select_btn);
410 g_signal_connect (G_OBJECT (select_btn), "clicked",
411 G_CALLBACK (select_btn_cb), sk);
412 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
413 G_CALLBACK (cancel_btn_cb), sk);
414 g_signal_connect (G_OBJECT(dont_encrypt_btn), "clicked",
415 G_CALLBACK (dont_encrypt_btn_cb), sk);
416 g_signal_connect (G_OBJECT (other_btn), "clicked",
417 G_CALLBACK (other_btn_cb), sk);
419 vbox2 = gtk_vbox_new (FALSE, 4);
420 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
423 sk->toplabel = GTK_LABEL (label);
424 sk->clist = GTK_CLIST (clist);
429 open_dialog (struct select_keys_s *sk)
433 manage_window_set_transient (GTK_WINDOW (sk->window));
435 sk->sort_column = N_COL_TITLES; /* use an invalid value */
436 sk->sort_type = GTK_SORT_ASCENDING;
441 close_dialog (struct select_keys_s *sk)
443 g_return_if_fail (sk);
444 gtk_widget_destroy (sk->window);
450 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
452 struct select_keys_s *sk = data;
462 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
464 struct select_keys_s *sk = data;
466 g_return_val_if_fail (sk, FALSE);
467 if (event && event->keyval == GDK_Escape) {
476 select_btn_cb (GtkWidget *widget, gpointer data)
478 struct select_keys_s *sk = data;
483 g_return_if_fail (sk);
484 if (!sk->clist->selection) {
485 debug_print ("** nothing selected");
488 row = GPOINTER_TO_INT(sk->clist->selection->data);
489 key = gtk_clist_get_row_data(sk->clist, row);
491 if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
492 use_key = use_untrusted(key);
494 debug_print ("** Key untrusted, will not encrypt");
498 sk->kset = g_realloc(sk->kset,
499 sizeof(gpgme_key_t) * (sk->num_keys + 1));
501 sk->kset[sk->num_keys] = key;
504 sk->result = KEY_SELECTION_OK;
511 cancel_btn_cb (GtkWidget *widget, gpointer data)
513 struct select_keys_s *sk = data;
515 g_return_if_fail (sk);
517 sk->result = KEY_SELECTION_CANCEL;
519 gpgme_cancel (sk->select_ctx);
524 dont_encrypt_btn_cb (GtkWidget *widget, gpointer data)
526 struct select_keys_s *sk = data;
528 g_return_if_fail (sk);
530 sk->result = KEY_SELECTION_DONT;
532 gpgme_cancel (sk->select_ctx);
537 other_btn_cb (GtkWidget *widget, gpointer data)
539 struct select_keys_s *sk = data;
542 g_return_if_fail (sk);
543 uid = input_dialog ( _("Add key"),
544 _("Enter another user or key ID:"),
548 if (fill_clist (sk, uid, sk->proto) != NULL) {
549 gpgme_release(sk->select_ctx);
550 sk->select_ctx = NULL;
552 update_progress (sk, 0, sk->pattern);
558 use_untrusted (gpgme_key_t key)
561 gchar *buf = g_strdup_printf(_("The key of '%s' is not fully trusted.\n"
562 "If you choose to encrypt the message with this key you don't\n"
563 "know for sure that it will go to the person you mean it to.\n"
564 "Do you trust it enough to use it anyway?"), key->uids->email);
568 GTK_STOCK_NO, GTK_STOCK_YES, NULL);
570 if (aval == G_ALERTALTERNATE)
578 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
580 gpgme_key_t a = ((GtkCListRow *)pa)->data;
581 gpgme_key_t b = ((GtkCListRow *)pb)->data;
584 sa = a? a->uids->name : NULL;
585 sb = b? b->uids->name : NULL;
590 return g_ascii_strcasecmp(sa, sb);
594 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
596 gpgme_key_t a = ((GtkCListRow *)pa)->data;
597 gpgme_key_t b = ((GtkCListRow *)pb)->data;
600 sa = a? a->uids->email : NULL;
601 sb = b? b->uids->email : NULL;
606 return g_ascii_strcasecmp(sa, sb);
610 sort_keys ( struct select_keys_s *sk, enum col_titles column)
612 GtkCList *clist = sk->clist;
616 gtk_clist_set_compare_func (clist, cmp_name);
619 gtk_clist_set_compare_func (clist, cmp_email);
625 /* column clicked again: toggle as-/decending */
626 if ( sk->sort_column == column) {
627 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
628 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
631 sk->sort_type = GTK_SORT_ASCENDING;
633 sk->sort_column = column;
634 gtk_clist_set_sort_type (clist, sk->sort_type);
635 gtk_clist_sort (clist);
639 sort_keys_name (GtkWidget *widget, gpointer data)
641 sort_keys ((struct select_keys_s*)data, COL_NAME);
645 sort_keys_email (GtkWidget *widget, gpointer data)
647 sort_keys ((struct select_keys_s*)data, COL_EMAIL);