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"
49 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
50 #define DIMof(type,member) DIM(((type *)0)->member)
63 struct select_keys_s {
72 GtkSortType sort_type;
73 enum col_titles sort_column;
78 static void set_row (GtkCList *clist, GpgmeKey key);
79 static void fill_clist (struct select_keys_s *sk, const char *pattern);
80 static void create_dialog (struct select_keys_s *sk);
81 static void open_dialog (struct select_keys_s *sk);
82 static void close_dialog (struct select_keys_s *sk);
83 static gint delete_event_cb (GtkWidget *widget,
84 GdkEventAny *event, gpointer data);
85 static gboolean key_pressed_cb (GtkWidget *widget,
86 GdkEventKey *event, gpointer data);
87 static void showall_btn_cb (GtkWidget *widget, gpointer data);
88 static void select_btn_cb (GtkWidget *widget, gpointer data);
89 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
90 static void other_btn_cb (GtkWidget *widget, gpointer data);
91 static void sort_keys (struct select_keys_s *sk, enum col_titles column);
92 static void sort_keys_name (GtkWidget *widget, gpointer data);
93 static void sort_keys_email (GtkWidget *widget, gpointer data);
97 update_progress (struct select_keys_s *sk, int running, const char *pattern)
99 static int windmill[] = { '-', '\\', '|', '/' };
103 buf = g_strdup_printf (_("Please select key for `%s'"),
106 buf = g_strdup_printf (_("Collecting info for `%s' ... %c"),
108 windmill[running%DIM(windmill)]);
109 gtk_label_set_text (sk->toplabel, buf);
115 * select_keys_get_recipients:
116 * @recp_names: A list of email addresses
118 * Select a list of recipients from a given list of email addresses.
119 * This may pop up a window to present the user a choice, it will also
120 * check that the recipients key are all valid.
122 * Return value: NULL on error or a list of list of recipients.
125 gpgmegtk_recipient_selection (GSList *recp_names)
127 struct select_keys_s sk;
130 memset (&sk, 0, sizeof sk);
132 err = gpgme_recipients_new (&sk.rset);
134 g_warning ("failed to allocate recipients set: %s",
135 gpgme_strerror (err));
142 sk.pattern = recp_names? recp_names->data:NULL;
143 gtk_clist_clear (sk.clist);
144 fill_clist (&sk, sk.pattern);
145 update_progress (&sk, 0, sk.pattern);
148 recp_names = recp_names->next;
149 } while (sk.okay && recp_names);
154 gpgme_recipients_release (sk.rset);
161 destroy_key (gpointer data)
164 gpgme_key_release (key);
168 set_row (GtkCList *clist, GpgmeKey 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 ( !gpgme_key_get_ulong_attr (key, GPGME_ATTR_CAN_ENCRYPT, NULL, 0 ) )
182 algo_buf = g_strdup_printf ("%lu/%s",
183 gpgme_key_get_ulong_attr (key, GPGME_ATTR_LEN, NULL, 0 ),
184 gpgme_key_get_string_attr (key, GPGME_ATTR_ALGO, NULL, 0 ) );
185 text[COL_ALGO] = algo_buf;
187 s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, NULL, 0);
188 if (strlen (s) == 16)
189 s += 8; /* show only the short keyID */
192 s = gpgme_key_get_string_attr (key, GPGME_ATTR_NAME, NULL, 0);
193 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
194 if (ret_str && by_written) {
199 s = gpgme_key_get_string_attr (key, GPGME_ATTR_EMAIL, NULL, 0);
200 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
201 if (ret_str && by_written) {
206 s = gpgme_key_get_string_attr (key, GPGME_ATTR_VALIDITY, NULL, 0);
207 ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
208 if (ret_str && by_written) {
211 text[COL_VALIDITY] = s;
213 row = gtk_clist_append (clist, (gchar**)text);
216 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
221 fill_clist (struct select_keys_s *sk, const char *pattern)
229 g_return_if_fail (sk);
231 g_return_if_fail (clist);
233 debug_print ("select_keys:fill_clist: pattern `%s'\n", pattern);
235 /*gtk_clist_freeze (select_keys.clist);*/
236 err = gpgme_new (&ctx);
239 sk->select_ctx = ctx;
241 update_progress (sk, ++running, pattern);
242 while (gtk_events_pending ())
243 gtk_main_iteration ();
245 err = gpgme_op_keylist_start (ctx, pattern, 0);
247 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
248 pattern, gpgme_strerror (err));
249 sk->select_ctx = NULL;
252 update_progress (sk, ++running, pattern);
253 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
254 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
255 set_row (clist, key ); key = NULL;
256 update_progress (sk, ++running, pattern);
257 while (gtk_events_pending ())
258 gtk_main_iteration ();
260 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
261 if (err != GPGME_EOF)
262 debug_print ("** gpgme_op_keylist_next failed: %s",
263 gpgme_strerror (err));
264 sk->select_ctx = NULL;
266 /*gtk_clist_thaw (select_keys.clist);*/
271 create_dialog (struct select_keys_s *sk)
274 GtkWidget *vbox, *vbox2, *hbox;
276 GtkWidget *scrolledwin;
279 GtkWidget *select_btn, *cancel_btn, *other_btn;
280 GtkWidget *showall_btn;
281 const char *titles[N_COL_TITLES];
283 g_assert (!sk->window);
284 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
285 gtk_widget_set_size_request (window, 520, 280);
286 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
287 gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
288 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
289 g_signal_connect (G_OBJECT (window), "delete_event",
290 G_CALLBACK (delete_event_cb), sk);
291 g_signal_connect (G_OBJECT (window), "key_press_event",
292 G_CALLBACK (key_pressed_cb), sk);
293 MANAGE_WINDOW_SIGNALS_CONNECT (window);
295 vbox = gtk_vbox_new (FALSE, 8);
296 gtk_container_add (GTK_CONTAINER (window), vbox);
298 hbox = gtk_hbox_new(FALSE, 4);
299 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
300 label = gtk_label_new ( "" );
301 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
303 hbox = gtk_hbox_new (FALSE, 8);
304 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
305 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
307 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
308 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
309 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
310 GTK_POLICY_AUTOMATIC,
311 GTK_POLICY_AUTOMATIC);
313 titles[COL_ALGO] = _("Size");
314 titles[COL_KEYID] = _("Key ID");
315 titles[COL_NAME] = _("Name");
316 titles[COL_EMAIL] = _("Address");
317 titles[COL_VALIDITY] = _("Val");
319 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
320 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
321 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
322 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
323 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
324 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
325 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
326 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
327 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
329 G_CALLBACK(sort_keys_name), sk);
330 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
332 G_CALLBACK(sort_keys_email), sk);
334 hbox = gtk_hbox_new (FALSE, 8);
335 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
337 showall_btn = gtk_button_new_with_label (_(" List all keys "));
338 gtk_widget_show (showall_btn);
339 gtk_box_pack_start (GTK_BOX (hbox), showall_btn, FALSE, FALSE, 0);
341 g_signal_connect(G_OBJECT (showall_btn), "clicked",
342 G_CALLBACK(showall_btn_cb), sk);
344 gtkut_stock_button_set_create (&bbox,
345 &select_btn, _("Select"),
346 &cancel_btn, GTK_STOCK_CANCEL,
347 &other_btn, _("Other"));
348 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
349 gtk_widget_grab_default (select_btn);
351 g_signal_connect (G_OBJECT (select_btn), "clicked",
352 G_CALLBACK (select_btn_cb), sk);
353 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
354 G_CALLBACK (cancel_btn_cb), sk);
355 g_signal_connect (G_OBJECT (other_btn), "clicked",
356 G_CALLBACK (other_btn_cb), sk);
358 vbox2 = gtk_vbox_new (FALSE, 4);
359 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
361 gtk_widget_show_all (window);
364 sk->toplabel = GTK_LABEL (label);
365 sk->clist = GTK_CLIST (clist);
370 open_dialog (struct select_keys_s *sk)
374 manage_window_set_transient (GTK_WINDOW (sk->window));
376 sk->sort_column = N_COL_TITLES; /* use an invalid value */
377 sk->sort_type = GTK_SORT_ASCENDING;
378 gtk_widget_show (sk->window);
383 close_dialog (struct select_keys_s *sk)
385 g_return_if_fail (sk);
386 gtk_widget_destroy (sk->window);
392 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
394 struct select_keys_s *sk = data;
404 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
406 struct select_keys_s *sk = data;
408 g_return_val_if_fail (sk, FALSE);
409 if (event && event->keyval == GDK_Escape) {
418 select_btn_cb (GtkWidget *widget, gpointer data)
420 struct select_keys_s *sk = data;
424 g_return_if_fail (sk);
425 if (!sk->clist->selection) {
426 debug_print ("** nothing selected");
429 row = GPOINTER_TO_INT(sk->clist->selection->data);
430 key = gtk_clist_get_row_data(sk->clist, row);
432 const char *s = gpgme_key_get_string_attr (key,
435 if ( gpgme_key_get_ulong_attr (key, GPGME_ATTR_VALIDITY, NULL, 0 )
436 < GPGME_VALIDITY_FULL ) {
437 debug_print ("** FIXME: we are faking the trust calculation");
439 if (!gpgme_recipients_add_name_with_validity (sk->rset, s,
440 GPGME_VALIDITY_FULL) ) {
449 cancel_btn_cb (GtkWidget *widget, gpointer data)
451 struct select_keys_s *sk = data;
453 g_return_if_fail (sk);
456 gpgme_cancel (sk->select_ctx);
462 other_btn_cb (GtkWidget *widget, gpointer data)
464 struct select_keys_s *sk = data;
467 g_return_if_fail (sk);
468 uid = input_dialog ( _("Add key"),
469 _("Enter another user or key ID:"),
473 fill_clist (sk, uid);
474 update_progress (sk, 0, sk->pattern);
479 showall_btn_cb (GtkWidget *widget, gpointer data)
481 struct select_keys_s *sk = data;
484 g_return_if_fail (sk);
487 fill_clist (sk, uid);
488 update_progress (sk, 0, sk->pattern);
492 cmp_attr (gconstpointer pa, gconstpointer pb, GpgmeAttr attr)
494 GpgmeKey a = ((GtkCListRow *)pa)->data;
495 GpgmeKey b = ((GtkCListRow *)pb)->data;
498 sa = a? gpgme_key_get_string_attr (a, attr, NULL, 0 ) : NULL;
499 sb = b? gpgme_key_get_string_attr (b, attr, NULL, 0 ) : NULL;
504 return g_ascii_strcasecmp(sa, sb);
508 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
510 return cmp_attr (pa, pb, GPGME_ATTR_NAME);
514 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
516 return cmp_attr (pa, pb, GPGME_ATTR_EMAIL);
520 sort_keys ( struct select_keys_s *sk, enum col_titles column)
522 GtkCList *clist = sk->clist;
526 gtk_clist_set_compare_func (clist, cmp_name);
529 gtk_clist_set_compare_func (clist, cmp_email);
535 /* column clicked again: toggle as-/decending */
536 if ( sk->sort_column == column) {
537 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
538 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
541 sk->sort_type = GTK_SORT_ASCENDING;
543 sk->sort_column = column;
544 gtk_clist_set_sort_type (clist, sk->sort_type);
545 gtk_clist_sort (clist);
549 sort_keys_name (GtkWidget *widget, gpointer data)
551 sort_keys ((struct select_keys_s*)data, COL_NAME);
555 sort_keys_email (GtkWidget *widget, gpointer data)
557 sort_keys ((struct select_keys_s*)data, COL_EMAIL);