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;
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 other_btn_cb (GtkWidget *widget, gpointer data);
92 static void sort_keys (struct select_keys_s *sk, enum col_titles column);
93 static void sort_keys_name (GtkWidget *widget, gpointer data);
94 static void sort_keys_email (GtkWidget *widget, gpointer data);
96 static gboolean use_untrusted (gpgme_key_t);
99 update_progress (struct select_keys_s *sk, int running, const char *pattern)
101 static int windmill[] = { '-', '\\', '|', '/' };
105 buf = g_strdup_printf (_("Please select key for '%s'"),
108 buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
110 windmill[running%DIM(windmill)]);
111 gtk_label_set_text (sk->toplabel, buf);
117 * gpgmegtk_recipient_selection:
118 * @recp_names: A list of email addresses
120 * Select a list of recipients from a given list of email addresses.
121 * This may pop up a window to present the user a choice, it will also
122 * check that the recipients key are all valid.
124 * Return value: NULL on error or a list of list of recipients.
127 gpgmegtk_recipient_selection (GSList *recp_names)
129 struct select_keys_s sk;
131 memset (&sk, 0, sizeof sk);
136 sk.pattern = recp_names? recp_names->data:NULL;
137 gtk_clist_clear (sk.clist);
138 fill_clist (&sk, sk.pattern);
139 update_progress (&sk, 0, sk.pattern);
142 recp_names = recp_names->next;
143 } while (sk.okay && recp_names);
151 sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
152 sk.kset[sk.num_keys] = NULL;
158 destroy_key (gpointer data)
160 gpgme_key_t key = data;
161 gpgme_key_release (key);
165 set_row (GtkCList *clist, gpgme_key_t key)
168 const char *text[N_COL_TITLES];
171 gssize by_read = 0, by_written = 0;
174 /* first check whether the key is capable of encryption which is not
175 * the case for revoked, expired or sign-only keys */
176 if (!key->can_encrypt)
178 algo_buf = g_strdup_printf ("%du/%s",
179 key->subkeys->length,
180 gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
181 text[COL_ALGO] = algo_buf;
183 s = key->subkeys->keyid;
184 if (strlen (s) == 16)
185 s += 8; /* show only the short keyID */
189 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
190 if (ret_str && by_written) {
195 s = key->uids->email;
196 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
197 if (ret_str && by_written) {
202 switch (key->uids->validity)
204 case GPGME_VALIDITY_UNDEFINED:
207 case GPGME_VALIDITY_NEVER:
210 case GPGME_VALIDITY_MARGINAL:
213 case GPGME_VALIDITY_FULL:
216 case GPGME_VALIDITY_ULTIMATE:
219 case GPGME_VALIDITY_UNKNOWN:
224 text[COL_VALIDITY] = s;
226 row = gtk_clist_append (clist, (gchar**)text);
229 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
233 fill_clist (struct select_keys_s *sk, const char *pattern)
241 g_return_if_fail (sk);
243 g_return_if_fail (clist);
245 debug_print ("select_keys:fill_clist: pattern '%s'\n", pattern);
247 /*gtk_clist_freeze (select_keys.clist);*/
248 err = gpgme_new (&ctx);
251 sk->select_ctx = ctx;
253 update_progress (sk, ++running, pattern);
254 while (gtk_events_pending ())
255 gtk_main_iteration ();
257 err = gpgme_op_keylist_start (ctx, pattern, 0);
259 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
260 pattern, gpgme_strerror (err));
261 sk->select_ctx = NULL;
265 update_progress (sk, ++running, pattern);
266 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
267 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
268 set_row (clist, key ); key = NULL;
269 update_progress (sk, ++running, pattern);
270 while (gtk_events_pending ())
271 gtk_main_iteration ();
273 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
274 if (gpgme_err_code(err) != GPG_ERR_EOF) {
275 debug_print ("** gpgme_op_keylist_next failed: %s",
276 gpgme_strerror (err));
277 gpgme_op_keylist_end(ctx);
279 sk->select_ctx = NULL;
281 /*gtk_clist_thaw (select_keys.clist);*/
286 create_dialog (struct select_keys_s *sk)
289 GtkWidget *vbox, *vbox2, *hbox;
291 GtkWidget *scrolledwin;
294 GtkWidget *select_btn, *cancel_btn, *other_btn;
295 const char *titles[N_COL_TITLES];
297 g_assert (!sk->window);
298 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
299 gtk_widget_set_size_request (window, 520, 280);
300 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
301 gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
302 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
303 g_signal_connect (G_OBJECT (window), "delete_event",
304 G_CALLBACK (delete_event_cb), sk);
305 g_signal_connect (G_OBJECT (window), "key_press_event",
306 G_CALLBACK (key_pressed_cb), sk);
307 MANAGE_WINDOW_SIGNALS_CONNECT (window);
309 vbox = gtk_vbox_new (FALSE, 8);
310 gtk_container_add (GTK_CONTAINER (window), vbox);
312 hbox = gtk_hbox_new(FALSE, 4);
313 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
314 label = gtk_label_new ( "" );
315 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
317 hbox = gtk_hbox_new (FALSE, 8);
318 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
319 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
321 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
322 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
323 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
324 GTK_POLICY_AUTOMATIC,
325 GTK_POLICY_AUTOMATIC);
327 titles[COL_ALGO] = _("Size");
328 titles[COL_KEYID] = _("Key ID");
329 titles[COL_NAME] = _("Name");
330 titles[COL_EMAIL] = _("Address");
331 titles[COL_VALIDITY] = _("Val");
333 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
334 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
335 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
336 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
337 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
338 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
339 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
340 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
341 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
343 G_CALLBACK(sort_keys_name), sk);
344 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
346 G_CALLBACK(sort_keys_email), sk);
348 hbox = gtk_hbox_new (FALSE, 8);
349 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
351 gtkut_stock_button_set_create (&bbox,
352 &select_btn, _("Select"),
353 &cancel_btn, GTK_STOCK_CANCEL,
354 &other_btn, _("Other"));
355 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
356 gtk_widget_grab_default (select_btn);
358 g_signal_connect (G_OBJECT (select_btn), "clicked",
359 G_CALLBACK (select_btn_cb), sk);
360 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
361 G_CALLBACK (cancel_btn_cb), sk);
362 g_signal_connect (G_OBJECT (other_btn), "clicked",
363 G_CALLBACK (other_btn_cb), sk);
365 vbox2 = gtk_vbox_new (FALSE, 4);
366 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
368 gtk_widget_show_all (window);
371 sk->toplabel = GTK_LABEL (label);
372 sk->clist = GTK_CLIST (clist);
377 open_dialog (struct select_keys_s *sk)
381 manage_window_set_transient (GTK_WINDOW (sk->window));
383 sk->sort_column = N_COL_TITLES; /* use an invalid value */
384 sk->sort_type = GTK_SORT_ASCENDING;
385 gtk_widget_show (sk->window);
390 close_dialog (struct select_keys_s *sk)
392 g_return_if_fail (sk);
393 gtk_widget_destroy (sk->window);
399 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
401 struct select_keys_s *sk = data;
411 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
413 struct select_keys_s *sk = data;
415 g_return_val_if_fail (sk, FALSE);
416 if (event && event->keyval == GDK_Escape) {
425 select_btn_cb (GtkWidget *widget, gpointer data)
427 struct select_keys_s *sk = data;
432 g_return_if_fail (sk);
433 if (!sk->clist->selection) {
434 debug_print ("** nothing selected");
437 row = GPOINTER_TO_INT(sk->clist->selection->data);
438 key = gtk_clist_get_row_data(sk->clist, row);
440 if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
441 use_key = use_untrusted(key);
443 debug_print ("** Key untrusted, will not encrypt");
447 sk->kset = g_realloc(sk->kset,
448 sizeof(gpgme_key_t) * (sk->num_keys + 1));
450 sk->kset[sk->num_keys] = key;
459 cancel_btn_cb (GtkWidget *widget, gpointer data)
461 struct select_keys_s *sk = data;
463 g_return_if_fail (sk);
466 gpgme_cancel (sk->select_ctx);
472 other_btn_cb (GtkWidget *widget, gpointer data)
474 struct select_keys_s *sk = data;
477 g_return_if_fail (sk);
478 uid = input_dialog ( _("Add key"),
479 _("Enter another user or key ID:"),
483 fill_clist (sk, uid);
484 update_progress (sk, 0, sk->pattern);
490 use_untrusted (gpgme_key_t key)
496 _("The selected key is not fully trusted.\n"
497 "If you choose to encrypt the message with this key you don't\n"
498 "know for sure that it will go to the person you mean it to.\n"
499 "Do you trust it enough to use it anyway?"),
500 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
501 if (aval == G_ALERTDEFAULT)
509 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
511 gpgme_key_t a = ((GtkCListRow *)pa)->data;
512 gpgme_key_t b = ((GtkCListRow *)pb)->data;
515 sa = a? a->uids->name : NULL;
516 sb = b? b->uids->name : NULL;
521 return g_ascii_strcasecmp(sa, sb);
525 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
527 gpgme_key_t a = ((GtkCListRow *)pa)->data;
528 gpgme_key_t b = ((GtkCListRow *)pb)->data;
531 sa = a? a->uids->email : NULL;
532 sb = b? b->uids->email : NULL;
537 return g_ascii_strcasecmp(sa, sb);
541 sort_keys ( struct select_keys_s *sk, enum col_titles column)
543 GtkCList *clist = sk->clist;
547 gtk_clist_set_compare_func (clist, cmp_name);
550 gtk_clist_set_compare_func (clist, cmp_email);
556 /* column clicked again: toggle as-/decending */
557 if ( sk->sort_column == column) {
558 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
559 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
562 sk->sort_type = GTK_SORT_ASCENDING;
564 sk->sort_column = column;
565 gtk_clist_set_sort_type (clist, sk->sort_type);
566 gtk_clist_sort (clist);
570 sort_keys_name (GtkWidget *widget, gpointer data)
572 sort_keys ((struct select_keys_s*)data, COL_NAME);
576 sort_keys_email (GtkWidget *widget, gpointer data)
578 sort_keys ((struct select_keys_s*)data, COL_EMAIL);