1 /* select-keys.c - GTK+ based key selection
2 * Copyright (C) 2001 Werner Koch (dd9jn)
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, 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;
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 static void create_dialog (struct select_keys_s *sk);
83 static void open_dialog (struct select_keys_s *sk);
84 static void close_dialog (struct select_keys_s *sk);
85 static gint delete_event_cb (GtkWidget *widget,
86 GdkEventAny *event, gpointer data);
87 static gboolean key_pressed_cb (GtkWidget *widget,
88 GdkEventKey *event, gpointer data);
89 static void select_btn_cb (GtkWidget *widget, gpointer data);
90 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
91 static void dont_encrypt_btn_cb (GtkWidget *widget, gpointer data);
92 static void other_btn_cb (GtkWidget *widget, gpointer data);
93 static void sort_keys (struct select_keys_s *sk, enum col_titles column);
94 static void sort_keys_name (GtkWidget *widget, gpointer data);
95 static void sort_keys_email (GtkWidget *widget, gpointer data);
97 static gboolean use_untrusted (gpgme_key_t);
100 update_progress (struct select_keys_s *sk, int running, const char *pattern)
102 static int windmill[] = { '-', '\\', '|', '/' };
106 buf = g_strdup_printf (_("Please select key for '%s'"),
109 buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
111 windmill[running%DIM(windmill)]);
112 gtk_label_set_text (sk->toplabel, buf);
118 * gpgmegtk_recipient_selection:
119 * @recp_names: A list of email addresses
121 * Select a list of recipients from a given list of email addresses.
122 * This may pop up a window to present the user a choice, it will also
123 * check that the recipients key are all valid.
125 * Return value: NULL on error or a list of list of recipients.
128 gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result)
130 struct select_keys_s sk;
132 memset (&sk, 0, sizeof sk);
137 sk.pattern = recp_names? recp_names->data:NULL;
138 gtk_clist_clear (sk.clist);
139 fill_clist (&sk, sk.pattern);
140 update_progress (&sk, 0, sk.pattern);
143 recp_names = recp_names->next;
144 } while (sk.okay && recp_names);
152 sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
153 sk.kset[sk.num_keys] = NULL;
161 destroy_key (gpointer data)
163 gpgme_key_t key = data;
164 gpgme_key_release (key);
168 set_row (GtkCList *clist, gpgme_key_t key)
171 const char *text[N_COL_TITLES];
174 gssize by_read = 0, by_written = 0;
177 /* first check whether the key is capable of encryption which is not
178 * the case for revoked, expired or sign-only keys */
179 if (!key->can_encrypt)
181 algo_buf = g_strdup_printf ("%du/%s",
182 key->subkeys->length,
183 gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
184 text[COL_ALGO] = algo_buf;
186 s = key->subkeys->keyid;
187 if (strlen (s) == 16)
188 s += 8; /* show only the short keyID */
192 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
193 if (ret_str && by_written) {
198 s = key->uids->email;
199 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
200 if (ret_str && by_written) {
205 switch (key->uids->validity)
207 case GPGME_VALIDITY_UNDEFINED:
210 case GPGME_VALIDITY_NEVER:
213 case GPGME_VALIDITY_MARGINAL:
216 case GPGME_VALIDITY_FULL:
219 case GPGME_VALIDITY_ULTIMATE:
222 case GPGME_VALIDITY_UNKNOWN:
227 text[COL_VALIDITY] = s;
229 row = gtk_clist_append (clist, (gchar**)text);
232 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
236 fill_clist (struct select_keys_s *sk, const char *pattern)
244 g_return_if_fail (sk);
246 g_return_if_fail (clist);
248 debug_print ("select_keys:fill_clist: pattern '%s'\n", pattern);
250 /*gtk_clist_freeze (select_keys.clist);*/
251 err = gpgme_new (&ctx);
254 sk->select_ctx = ctx;
256 update_progress (sk, ++running, pattern);
257 while (gtk_events_pending ())
258 gtk_main_iteration ();
260 err = gpgme_op_keylist_start (ctx, pattern, 0);
262 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
263 pattern, gpgme_strerror (err));
264 sk->select_ctx = NULL;
268 update_progress (sk, ++running, pattern);
269 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
270 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
271 set_row (clist, key ); key = NULL;
272 update_progress (sk, ++running, pattern);
273 while (gtk_events_pending ())
274 gtk_main_iteration ();
276 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
277 if (gpgme_err_code(err) != GPG_ERR_EOF) {
278 debug_print ("** gpgme_op_keylist_next failed: %s",
279 gpgme_strerror (err));
280 gpgme_op_keylist_end(ctx);
282 sk->select_ctx = NULL;
284 /*gtk_clist_thaw (select_keys.clist);*/
289 create_dialog (struct select_keys_s *sk)
292 GtkWidget *vbox, *vbox2, *hbox;
294 GtkWidget *scrolledwin;
297 GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
298 const char *titles[N_COL_TITLES];
300 g_assert (!sk->window);
301 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
302 gtk_widget_set_size_request (window, 520, 280);
303 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
304 gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
305 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
306 g_signal_connect (G_OBJECT (window), "delete_event",
307 G_CALLBACK (delete_event_cb), sk);
308 g_signal_connect (G_OBJECT (window), "key_press_event",
309 G_CALLBACK (key_pressed_cb), sk);
310 MANAGE_WINDOW_SIGNALS_CONNECT (window);
312 vbox = gtk_vbox_new (FALSE, 8);
313 gtk_container_add (GTK_CONTAINER (window), vbox);
315 hbox = gtk_hbox_new(FALSE, 4);
316 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
317 label = gtk_label_new ( "" );
318 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
320 hbox = gtk_hbox_new (FALSE, 8);
321 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
322 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
324 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
325 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
326 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
327 GTK_POLICY_AUTOMATIC,
328 GTK_POLICY_AUTOMATIC);
330 titles[COL_ALGO] = _("Size");
331 titles[COL_KEYID] = _("Key ID");
332 titles[COL_NAME] = _("Name");
333 titles[COL_EMAIL] = _("Address");
334 titles[COL_VALIDITY] = _("Val");
336 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
337 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
338 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
339 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
340 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
341 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
342 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
343 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
344 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
346 G_CALLBACK(sort_keys_name), sk);
347 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
349 G_CALLBACK(sort_keys_email), sk);
351 hbox = gtk_hbox_new (FALSE, 8);
352 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
354 gtkut_stock_button_set_create (&bbox,
355 &select_btn, _("Select"),
356 &cancel_btn, GTK_STOCK_CANCEL,
357 &dont_encrypt_btn, _("Don't encrypt"));
359 other_btn = gtk_button_new_from_stock(_("Other"));
360 GTK_WIDGET_SET_FLAGS(other_btn, GTK_CAN_DEFAULT);
361 gtk_box_pack_start(GTK_BOX(bbox), other_btn, TRUE, TRUE, 0);
362 gtk_widget_show(other_btn);
363 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
364 gtk_widget_grab_default (select_btn);
366 g_signal_connect (G_OBJECT (select_btn), "clicked",
367 G_CALLBACK (select_btn_cb), sk);
368 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
369 G_CALLBACK (cancel_btn_cb), sk);
370 g_signal_connect (G_OBJECT(dont_encrypt_btn), "clicked",
371 G_CALLBACK (dont_encrypt_btn_cb), sk);
372 g_signal_connect (G_OBJECT (other_btn), "clicked",
373 G_CALLBACK (other_btn_cb), sk);
375 vbox2 = gtk_vbox_new (FALSE, 4);
376 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
378 gtk_widget_show_all (window);
381 sk->toplabel = GTK_LABEL (label);
382 sk->clist = GTK_CLIST (clist);
387 open_dialog (struct select_keys_s *sk)
391 manage_window_set_transient (GTK_WINDOW (sk->window));
393 sk->sort_column = N_COL_TITLES; /* use an invalid value */
394 sk->sort_type = GTK_SORT_ASCENDING;
395 gtk_widget_show (sk->window);
400 close_dialog (struct select_keys_s *sk)
402 g_return_if_fail (sk);
403 gtk_widget_destroy (sk->window);
409 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
411 struct select_keys_s *sk = data;
421 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
423 struct select_keys_s *sk = data;
425 g_return_val_if_fail (sk, FALSE);
426 if (event && event->keyval == GDK_Escape) {
435 select_btn_cb (GtkWidget *widget, gpointer data)
437 struct select_keys_s *sk = data;
442 g_return_if_fail (sk);
443 if (!sk->clist->selection) {
444 debug_print ("** nothing selected");
447 row = GPOINTER_TO_INT(sk->clist->selection->data);
448 key = gtk_clist_get_row_data(sk->clist, row);
450 if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
451 use_key = use_untrusted(key);
453 debug_print ("** Key untrusted, will not encrypt");
457 sk->kset = g_realloc(sk->kset,
458 sizeof(gpgme_key_t) * (sk->num_keys + 1));
460 sk->kset[sk->num_keys] = key;
463 sk->result = KEY_SELECTION_OK;
470 cancel_btn_cb (GtkWidget *widget, gpointer data)
472 struct select_keys_s *sk = data;
474 g_return_if_fail (sk);
476 sk->result = KEY_SELECTION_CANCEL;
478 gpgme_cancel (sk->select_ctx);
483 dont_encrypt_btn_cb (GtkWidget *widget, gpointer data)
485 struct select_keys_s *sk = data;
487 g_return_if_fail (sk);
489 sk->result = KEY_SELECTION_DONT;
491 gpgme_cancel (sk->select_ctx);
496 other_btn_cb (GtkWidget *widget, gpointer data)
498 struct select_keys_s *sk = data;
501 g_return_if_fail (sk);
502 uid = input_dialog ( _("Add key"),
503 _("Enter another user or key ID:"),
507 fill_clist (sk, uid);
508 update_progress (sk, 0, sk->pattern);
514 use_untrusted (gpgme_key_t key)
520 _("The selected key is not fully trusted.\n"
521 "If you choose to encrypt the message with this key you don't\n"
522 "know for sure that it will go to the person you mean it to.\n"
523 "Do you trust it enough to use it anyway?"),
524 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
525 if (aval == G_ALERTDEFAULT)
533 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
535 gpgme_key_t a = ((GtkCListRow *)pa)->data;
536 gpgme_key_t b = ((GtkCListRow *)pb)->data;
539 sa = a? a->uids->name : NULL;
540 sb = b? b->uids->name : NULL;
545 return g_ascii_strcasecmp(sa, sb);
549 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
551 gpgme_key_t a = ((GtkCListRow *)pa)->data;
552 gpgme_key_t b = ((GtkCListRow *)pb)->data;
555 sa = a? a->uids->email : NULL;
556 sb = b? b->uids->email : NULL;
561 return g_ascii_strcasecmp(sa, sb);
565 sort_keys ( struct select_keys_s *sk, enum col_titles column)
567 GtkCList *clist = sk->clist;
571 gtk_clist_set_compare_func (clist, cmp_name);
574 gtk_clist_set_compare_func (clist, cmp_email);
580 /* column clicked again: toggle as-/decending */
581 if ( sk->sort_column == column) {
582 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
583 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
586 sk->sort_type = GTK_SORT_ASCENDING;
588 sk->sort_column = column;
589 gtk_clist_set_sort_type (clist, sk->sort_type);
590 gtk_clist_sort (clist);
594 sort_keys_name (GtkWidget *widget, gpointer data)
596 sort_keys ((struct select_keys_s*)data, COL_NAME);
600 sort_keys_email (GtkWidget *widget, gpointer data)
602 sort_keys ((struct select_keys_s*)data, COL_EMAIL);