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 void 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 (_("Please select key for '%s'"),
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;
134 memset (&sk, 0, sizeof sk);
139 sk.pattern = recp_names? recp_names->data:NULL;
141 gtk_clist_clear (sk.clist);
142 fill_clist (&sk, sk.pattern, proto);
143 update_progress (&sk, 0, sk.pattern);
146 recp_names = recp_names->next;
147 } while (sk.okay && recp_names);
155 sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
156 sk.kset[sk.num_keys] = NULL;
164 destroy_key (gpointer data)
166 gpgme_key_t key = data;
167 gpgme_key_release (key);
171 set_row (GtkCList *clist, gpgme_key_t key)
174 const char *text[N_COL_TITLES];
177 gsize by_read = 0, by_written = 0;
180 /* first check whether the key is capable of encryption which is not
181 * the case for revoked, expired or sign-only keys */
182 if (!key->can_encrypt)
184 algo_buf = g_strdup_printf ("%du/%s",
185 key->subkeys->length,
186 gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
187 text[COL_ALGO] = algo_buf;
189 s = key->subkeys->keyid;
190 if (strlen (s) == 16)
191 s += 8; /* show only the short keyID */
195 if (!s || !strlen(s))
197 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
198 if (ret_str && by_written) {
203 s = key->uids->email;
204 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
205 if (ret_str && by_written) {
210 switch (key->uids->validity)
212 case GPGME_VALIDITY_UNDEFINED:
215 case GPGME_VALIDITY_NEVER:
218 case GPGME_VALIDITY_MARGINAL:
221 case GPGME_VALIDITY_FULL:
224 case GPGME_VALIDITY_ULTIMATE:
227 case GPGME_VALIDITY_UNKNOWN:
232 text[COL_VALIDITY] = s;
234 row = gtk_clist_append (clist, (gchar**)text);
237 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
241 fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
249 g_return_if_fail (sk);
251 g_return_if_fail (clist);
253 debug_print ("select_keys:fill_clist: pattern '%s' proto %d\n", pattern, proto);
255 /*gtk_clist_freeze (select_keys.clist);*/
256 err = gpgme_new (&ctx);
259 gpgme_set_protocol(ctx, proto);
260 sk->select_ctx = ctx;
262 update_progress (sk, ++running, pattern);
263 while (gtk_events_pending ())
264 gtk_main_iteration ();
266 err = gpgme_op_keylist_start (ctx, pattern, 0);
268 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
269 pattern, gpgme_strerror (err));
270 sk->select_ctx = NULL;
274 update_progress (sk, ++running, pattern);
275 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
276 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
277 set_row (clist, key ); key = NULL;
278 update_progress (sk, ++running, pattern);
279 while (gtk_events_pending ())
280 gtk_main_iteration ();
282 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
283 if (gpgme_err_code(err) != GPG_ERR_EOF) {
284 debug_print ("** gpgme_op_keylist_next failed: %s",
285 gpgme_strerror (err));
286 gpgme_op_keylist_end(ctx);
288 sk->select_ctx = NULL;
290 /*gtk_clist_thaw (select_keys.clist);*/
295 create_dialog (struct select_keys_s *sk)
298 GtkWidget *vbox, *vbox2, *hbox;
300 GtkWidget *scrolledwin;
303 GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
304 const char *titles[N_COL_TITLES];
306 g_assert (!sk->window);
307 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
308 gtk_widget_set_size_request (window, 520, 280);
309 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
310 gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
311 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
312 g_signal_connect (G_OBJECT (window), "delete_event",
313 G_CALLBACK (delete_event_cb), sk);
314 g_signal_connect (G_OBJECT (window), "key_press_event",
315 G_CALLBACK (key_pressed_cb), sk);
316 MANAGE_WINDOW_SIGNALS_CONNECT (window);
318 vbox = gtk_vbox_new (FALSE, 8);
319 gtk_container_add (GTK_CONTAINER (window), vbox);
321 hbox = gtk_hbox_new(FALSE, 4);
322 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
323 label = gtk_label_new ( "" );
324 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
326 hbox = gtk_hbox_new (FALSE, 8);
327 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
328 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
330 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
331 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
332 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
333 GTK_POLICY_AUTOMATIC,
334 GTK_POLICY_AUTOMATIC);
336 titles[COL_ALGO] = _("Size");
337 titles[COL_KEYID] = _("Key ID");
338 titles[COL_NAME] = _("Name");
339 titles[COL_EMAIL] = _("Address");
340 titles[COL_VALIDITY] = _("Val");
342 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
343 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
344 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
345 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
346 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
347 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
348 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
349 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
350 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
352 G_CALLBACK(sort_keys_name), sk);
353 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
355 G_CALLBACK(sort_keys_email), sk);
357 hbox = gtk_hbox_new (FALSE, 8);
358 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
360 gtkut_stock_button_set_create (&bbox,
361 &select_btn, _("Select"),
362 &other_btn, _("Other"),
363 &dont_encrypt_btn, _("Don't encrypt"));
365 cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
366 GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
367 gtk_box_pack_start(GTK_BOX(bbox), cancel_btn, TRUE, TRUE, 0);
368 gtk_widget_show(cancel_btn);
369 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
370 gtk_widget_grab_default (select_btn);
372 g_signal_connect (G_OBJECT (select_btn), "clicked",
373 G_CALLBACK (select_btn_cb), sk);
374 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
375 G_CALLBACK (cancel_btn_cb), sk);
376 g_signal_connect (G_OBJECT(dont_encrypt_btn), "clicked",
377 G_CALLBACK (dont_encrypt_btn_cb), sk);
378 g_signal_connect (G_OBJECT (other_btn), "clicked",
379 G_CALLBACK (other_btn_cb), sk);
381 vbox2 = gtk_vbox_new (FALSE, 4);
382 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
384 gtk_widget_show_all (window);
387 sk->toplabel = GTK_LABEL (label);
388 sk->clist = GTK_CLIST (clist);
393 open_dialog (struct select_keys_s *sk)
397 manage_window_set_transient (GTK_WINDOW (sk->window));
399 sk->sort_column = N_COL_TITLES; /* use an invalid value */
400 sk->sort_type = GTK_SORT_ASCENDING;
401 gtk_widget_show (sk->window);
406 close_dialog (struct select_keys_s *sk)
408 g_return_if_fail (sk);
409 gtk_widget_destroy (sk->window);
415 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
417 struct select_keys_s *sk = data;
427 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
429 struct select_keys_s *sk = data;
431 g_return_val_if_fail (sk, FALSE);
432 if (event && event->keyval == GDK_Escape) {
441 select_btn_cb (GtkWidget *widget, gpointer data)
443 struct select_keys_s *sk = data;
448 g_return_if_fail (sk);
449 if (!sk->clist->selection) {
450 debug_print ("** nothing selected");
453 row = GPOINTER_TO_INT(sk->clist->selection->data);
454 key = gtk_clist_get_row_data(sk->clist, row);
456 if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
457 use_key = use_untrusted(key);
459 debug_print ("** Key untrusted, will not encrypt");
463 sk->kset = g_realloc(sk->kset,
464 sizeof(gpgme_key_t) * (sk->num_keys + 1));
466 sk->kset[sk->num_keys] = key;
469 sk->result = KEY_SELECTION_OK;
476 cancel_btn_cb (GtkWidget *widget, gpointer data)
478 struct select_keys_s *sk = data;
480 g_return_if_fail (sk);
482 sk->result = KEY_SELECTION_CANCEL;
484 gpgme_cancel (sk->select_ctx);
489 dont_encrypt_btn_cb (GtkWidget *widget, gpointer data)
491 struct select_keys_s *sk = data;
493 g_return_if_fail (sk);
495 sk->result = KEY_SELECTION_DONT;
497 gpgme_cancel (sk->select_ctx);
502 other_btn_cb (GtkWidget *widget, gpointer data)
504 struct select_keys_s *sk = data;
507 g_return_if_fail (sk);
508 uid = input_dialog ( _("Add key"),
509 _("Enter another user or key ID:"),
513 fill_clist (sk, uid, sk->proto);
514 update_progress (sk, 0, sk->pattern);
520 use_untrusted (gpgme_key_t key)
526 _("The selected key is not fully trusted.\n"
527 "If you choose to encrypt the message with this key you don't\n"
528 "know for sure that it will go to the person you mean it to.\n"
529 "Do you trust it enough to use it anyway?"),
530 GTK_STOCK_NO, GTK_STOCK_YES, NULL);
531 if (aval == G_ALERTALTERNATE)
539 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
541 gpgme_key_t a = ((GtkCListRow *)pa)->data;
542 gpgme_key_t b = ((GtkCListRow *)pb)->data;
545 sa = a? a->uids->name : NULL;
546 sb = b? b->uids->name : NULL;
551 return g_ascii_strcasecmp(sa, sb);
555 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
557 gpgme_key_t a = ((GtkCListRow *)pa)->data;
558 gpgme_key_t b = ((GtkCListRow *)pb)->data;
561 sa = a? a->uids->email : NULL;
562 sb = b? b->uids->email : NULL;
567 return g_ascii_strcasecmp(sa, sb);
571 sort_keys ( struct select_keys_s *sk, enum col_titles column)
573 GtkCList *clist = sk->clist;
577 gtk_clist_set_compare_func (clist, cmp_name);
580 gtk_clist_set_compare_func (clist, cmp_email);
586 /* column clicked again: toggle as-/decending */
587 if ( sk->sort_column == column) {
588 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
589 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
592 sk->sort_type = GTK_SORT_ASCENDING;
594 sk->sort_column = column;
595 gtk_clist_set_sort_type (clist, sk->sort_type);
596 gtk_clist_sort (clist);
600 sort_keys_name (GtkWidget *widget, gpointer data)
602 sort_keys ((struct select_keys_s*)data, COL_NAME);
606 sort_keys_email (GtkWidget *widget, gpointer data)
608 sort_keys ((struct select_keys_s*)data, COL_EMAIL);