summaryrefslogtreecommitdiff
path: root/src/select-keys.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/select-keys.c')
-rw-r--r--src/select-keys.c567
1 files changed, 567 insertions, 0 deletions
diff --git a/src/select-keys.c b/src/select-keys.c
new file mode 100644
index 0000000..36c8edc
--- /dev/null
+++ b/src/select-keys.c
@@ -0,0 +1,567 @@
1/* select-keys.c - GTK+ based key selection
2 * Copyright (C) 2001 Werner Koch (dd9jn)
3 * Copyright (C) 1999-2014 Hiroyuki Yamamoto
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18 */
19
20#ifdef HAVE_CONFIG_H
21# include <config.h>
22#endif
23
24#ifdef USE_GPGME
25#include <stdio.h>
26#include <stdlib.h>
27
28#include <glib.h>
29#include <glib/gi18n.h>
30#include <gdk/gdkkeysyms.h>
31#include <gtk/gtk.h>
32
33#include "select-keys.h"
34#include "utils.h"
35#include "gtkutils.h"
36#include "inputdialog.h"
37#include "manage_window.h"
38#include "alertpanel.h"
39
40#define DIM(v) (sizeof(v)/sizeof((v)[0]))
41#define DIMof(type,member) DIM(((type *)0)->member)
42
43
44enum col_titles {
45 COL_ALGO,
46 COL_KEYID,
47 COL_NAME,
48 COL_EMAIL,
49 COL_VALIDITY,
50
51 N_COL_TITLES
52};
53
54struct select_keys_s {
55 int okay;
56 GtkWidget *window;
57 GtkLabel *toplabel;
58 GtkCList *clist;
59 const char *pattern;
60 unsigned int num_keys;
61 gpgme_key_t *kset;
62 gpgme_ctx_t select_ctx;
63
64 GtkSortType sort_type;
65 enum col_titles sort_column;
66
67};
68
69
70static void set_row (GtkCList *clist, gpgme_key_t key);
71static void fill_clist (struct select_keys_s *sk, const char *pattern);
72static void create_dialog (struct select_keys_s *sk);
73static void open_dialog (struct select_keys_s *sk);
74static void close_dialog (struct select_keys_s *sk);
75static gint delete_event_cb (GtkWidget *widget,
76 GdkEventAny *event, gpointer data);
77static gboolean key_pressed_cb (GtkWidget *widget,
78 GdkEventKey *event, gpointer data);
79static void select_btn_cb (GtkWidget *widget, gpointer data);
80static void cancel_btn_cb (GtkWidget *widget, gpointer data);
81static void other_btn_cb (GtkWidget *widget, gpointer data);
82static void sort_keys (struct select_keys_s *sk, enum col_titles column);
83static void sort_keys_name (GtkWidget *widget, gpointer data);
84static void sort_keys_email (GtkWidget *widget, gpointer data);
85
86static gboolean use_untrusted (gpgme_key_t);
87
88static void
89update_progress (struct select_keys_s *sk, int running, const char *pattern)
90{
91 static int windmill[] = { '-', '\\', '|', '/' };
92 char *buf;
93
94 if (!pattern)
95 pattern = "";
96 if (!running)
97 buf = g_strdup_printf (_("Please select key for \"%s\""),
98 pattern);
99 else
100 buf = g_strdup_printf (_("Collecting info for \"%s\" ... %c"),
101 pattern,
102 windmill[running%DIM(windmill)]);
103 gtk_label_set_text (sk->toplabel, buf);
104 g_free (buf);
105}
106
107
108/**
109 * gpgmegtk_recipient_selection:
110 * @recp_names: A list of email addresses
111 *
112 * Select a list of recipients from a given list of email addresses.
113 * This may pop up a window to present the user a choice, it will also
114 * check that the recipients key are all valid.
115 *
116 * Return value: NULL on error or a list of list of recipients.
117 **/
118gpgme_key_t *
119gpgmegtk_recipient_selection (GSList *recp_names)
120{
121 struct select_keys_s sk;
122
123 memset (&sk, 0, sizeof sk);
124
125 open_dialog (&sk);
126
127 do {
128 sk.pattern = recp_names ? recp_names->data : NULL;
129 gtk_clist_clear (sk.clist);
130 fill_clist (&sk, sk.pattern);
131 update_progress (&sk, 0, sk.pattern);
132 gtk_main ();
133 if (recp_names)
134 recp_names = recp_names->next;
135 } while (sk.okay && recp_names);
136
137 close_dialog (&sk);
138
139 if (!sk.okay) {
140 g_free(sk.kset);
141 sk.kset = NULL;
142 } else {
143 sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
144 sk.kset[sk.num_keys] = NULL;
145 }
146 return sk.kset;
147}
148
149static void
150destroy_key (gpointer data)
151{
152 gpgme_key_t key = data;
153 gpgme_key_release (key);
154}
155
156static void
157set_row (GtkCList *clist, gpgme_key_t key)
158{
159 const char *s;
160 const char *text[N_COL_TITLES];
161 char *algo_buf;
162 int row;
163
164 /* first check whether the key is capable of encryption which is not
165 * the case for revoked, expired or sign-only keys */
166 if (!key->can_encrypt)
167 return;
168 algo_buf = g_strdup_printf ("%du/%s",
169 key->subkeys->length,
170 gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
171 text[COL_ALGO] = algo_buf;
172
173 s = key->subkeys->keyid;
174 if (strlen (s) == 16)
175 s += 8; /* show only the short keyID */
176 text[COL_KEYID] = s;
177
178 s = key->uids->name;
179 text[COL_NAME] = s;
180
181 s = key->uids->email;
182 text[COL_EMAIL] = s;
183
184 switch (key->uids->validity)
185 {
186 case GPGME_VALIDITY_UNDEFINED:
187 s = "q";
188 break;
189 case GPGME_VALIDITY_NEVER:
190 s = "n";
191 break;
192 case GPGME_VALIDITY_MARGINAL:
193 s = "m";
194 break;
195 case GPGME_VALIDITY_FULL:
196 s = "f";
197 break;
198 case GPGME_VALIDITY_ULTIMATE:
199 s = "u";
200 break;
201 case GPGME_VALIDITY_UNKNOWN:
202 default:
203 s = "?";
204 break;
205 }
206 text[COL_VALIDITY] = s;
207
208 row = gtk_clist_append (clist, (gchar**)text);
209 g_free (algo_buf);
210
211 gtk_clist_set_row_data_full (clist, row, key, destroy_key);
212}
213
214static void
215fill_clist (struct select_keys_s *sk, const char *pattern)
216{
217 GtkCList *clist;
218 gpgme_ctx_t ctx;
219 gpgme_error_t err;
220 gpgme_key_t key;
221 int running=0;
222
223 g_return_if_fail (sk);
224 clist = sk->clist;
225 g_return_if_fail (clist);
226
227 debug_print ("select_keys:fill_clist: pattern '%s'\n",
228 pattern ? pattern : "");
229
230 /*gtk_clist_freeze (select_keys.clist);*/
231 err = gpgme_new (&ctx);
232 g_assert (!err);
233
234 sk->select_ctx = ctx;
235
236 update_progress (sk, ++running, pattern);
237 while (gtk_events_pending ())
238 gtk_main_iteration ();
239
240 err = gpgme_op_keylist_start (ctx, pattern, 0);
241 if (err) {
242 debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
243 pattern ? pattern : "", gpgme_strerror (err));
244 sk->select_ctx = NULL;
245 gpgme_release(ctx);
246 return;
247 }
248 update_progress (sk, ++running, pattern);
249 while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
250 debug_print ("%% %s:%d: insert\n", __FILE__ ,__LINE__ );
251 set_row (clist, key ); key = NULL;
252 update_progress (sk, ++running, pattern);
253 while (gtk_events_pending ())
254 gtk_main_iteration ();
255 }
256 debug_print ("%% %s:%d: ready\n", __FILE__ ,__LINE__ );
257 if (gpgme_err_code(err) != GPG_ERR_EOF) {
258 debug_print ("** gpgme_op_keylist_next failed: %s",
259 gpgme_strerror (err));
260 gpgme_op_keylist_end(ctx);
261 }
262 sk->select_ctx = NULL;
263 gpgme_release (ctx);
264 /*gtk_clist_thaw (select_keys.clist);*/
265}
266
267
268static void
269create_dialog (struct select_keys_s *sk)
270{
271 GtkWidget *window;
272 GtkWidget *vbox, *vbox2, *hbox;
273 GtkWidget *bbox;
274 GtkWidget *scrolledwin;
275 GtkWidget *clist;
276 GtkWidget *label;
277 GtkWidget *select_btn, *cancel_btn, *other_btn;
278 const char *titles[N_COL_TITLES];
279
280 g_assert (!sk->window);
281 window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
282 gtk_widget_set_size_request (window, 520, 280);
283 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
284 gtk_window_set_title (GTK_WINDOW (window), _("Select encryption keys"));
285 gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
286 gtk_window_set_modal (GTK_WINDOW (window), TRUE);
287 g_signal_connect (G_OBJECT (window), "delete_event",
288 G_CALLBACK (delete_event_cb), sk);
289 g_signal_connect (G_OBJECT (window), "key_press_event",
290 G_CALLBACK (key_pressed_cb), sk);
291 MANAGE_WINDOW_SIGNALS_CONNECT (window);
292
293 vbox = gtk_vbox_new (FALSE, 8);
294 gtk_container_add (GTK_CONTAINER (window), vbox);
295
296 hbox = gtk_hbox_new(FALSE, 4);
297 gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
298 label = gtk_label_new ( "" );
299 gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
300
301 hbox = gtk_hbox_new (FALSE, 8);
302 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
303 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
304
305 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
306 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
307 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
308 GTK_POLICY_AUTOMATIC,
309 GTK_POLICY_AUTOMATIC);
310
311 titles[COL_ALGO] = _("Size");
312 titles[COL_KEYID] = _("Key ID");
313 titles[COL_NAME] = _("Name");
314 titles[COL_EMAIL] = _("Address");
315 titles[COL_VALIDITY] = _("Val");
316
317 clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
318 gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
319 gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO, 72);
320 gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID, 76);
321 gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME, 130);
322 gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL, 130);
323 gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY, 20);
324 gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
325 gtkut_clist_set_redraw (GTK_CLIST(clist));
326 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
327 "clicked",
328 G_CALLBACK(sort_keys_name), sk);
329 g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
330 "clicked",
331 G_CALLBACK(sort_keys_email), sk);
332
333 hbox = gtk_hbox_new (FALSE, 8);
334 gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
335
336 gtkut_stock_button_set_create (&bbox,
337 &select_btn, _("Select"),
338 &cancel_btn, GTK_STOCK_CANCEL,
339 &other_btn, _("Other"));
340 gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
341 gtk_widget_grab_default (select_btn);
342 gtk_widget_grab_focus (select_btn);
343
344 g_signal_connect (G_OBJECT (select_btn), "clicked",
345 G_CALLBACK (select_btn_cb), sk);
346 g_signal_connect (G_OBJECT(cancel_btn), "clicked",
347 G_CALLBACK (cancel_btn_cb), sk);
348 g_signal_connect (G_OBJECT (other_btn), "clicked",
349 G_CALLBACK (other_btn_cb), sk);
350
351 vbox2 = gtk_vbox_new (FALSE, 4);
352 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
353
354 gtk_widget_show_all (window);
355
356 sk->window = window;
357 sk->toplabel = GTK_LABEL (label);
358 sk->clist = GTK_CLIST (clist);
359}
360
361
362static void
363open_dialog (struct select_keys_s *sk)
364{
365 if (!sk->window)
366 create_dialog (sk);
367 manage_window_set_transient (GTK_WINDOW (sk->window));
368 sk->okay = 0;
369 sk->sort_column = N_COL_TITLES; /* use an invalid value */
370 sk->sort_type = GTK_SORT_ASCENDING;
371 gtk_widget_show (sk->window);
372}
373
374
375static void
376close_dialog (struct select_keys_s *sk)
377{
378 g_return_if_fail (sk);
379 gtk_widget_destroy (sk->window);
380 sk->window = NULL;
381}
382
383
384static gint
385delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
386{
387 struct select_keys_s *sk = data;
388
389 sk->okay = 0;
390 gtk_main_quit ();
391
392 return TRUE;
393}
394
395
396static gboolean
397key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
398{
399 struct select_keys_s *sk = data;
400
401 g_return_val_if_fail (sk, FALSE);
402 if (event && event->keyval == GDK_Escape) {
403 sk->okay = 0;
404 gtk_main_quit ();
405 }
406 return FALSE;
407}
408
409
410static void
411select_btn_cb (GtkWidget *widget, gpointer data)
412{
413 struct select_keys_s *sk = data;
414 int row;
415 gboolean use_key;
416 gpgme_key_t key;
417
418 g_return_if_fail (sk);
419 if (!sk->clist->selection) {
420 debug_print ("** nothing selected");
421 return;
422 }
423 row = GPOINTER_TO_INT(sk->clist->selection->data);
424 key = gtk_clist_get_row_data(sk->clist, row);
425 if (key) {
426 if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
427 use_key = use_untrusted(key);
428 if (!use_key) {
429 debug_print ("** Key untrusted, will not encrypt");
430 return;
431 }
432 }
433 sk->kset = g_realloc(sk->kset,
434 sizeof(gpgme_key_t) * (sk->num_keys + 1));
435 gpgme_key_ref(key);
436 sk->kset[sk->num_keys] = key;
437 sk->num_keys++;
438 sk->okay = 1;
439 gtk_main_quit ();
440 }
441}
442
443
444static void
445cancel_btn_cb (GtkWidget *widget, gpointer data)
446{
447 struct select_keys_s *sk = data;
448
449 g_return_if_fail (sk);
450 sk->okay = 0;
451 if (sk->select_ctx)
452 gpgme_cancel (sk->select_ctx);
453 gtk_main_quit ();
454}
455
456
457static void
458other_btn_cb (GtkWidget *widget, gpointer data)
459{
460 struct select_keys_s *sk = data;
461 char *uid;
462
463 g_return_if_fail (sk);
464 uid = input_dialog ( _("Add key"),
465 _("Enter another user or key ID:"),
466 NULL );
467 if (!uid)
468 return;
469 fill_clist (sk, uid);
470 update_progress (sk, 0, sk->pattern);
471 g_free (uid);
472}
473
474
475static gboolean
476use_untrusted (gpgme_key_t key)
477{
478 AlertValue aval;
479
480 aval = alertpanel
481 (_("Trust key"),
482 _("The selected key is not fully trusted.\n"
483 "If you choose to encrypt the message with this key you don't\n"
484 "know for sure that it will go to the person you mean it to.\n"
485 "Do you trust it enough to use it anyway?"),
486 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
487 if (aval == G_ALERTDEFAULT)
488 return TRUE;
489 else
490 return FALSE;
491}
492
493
494static gint
495cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
496{
497 gpgme_key_t a = ((GtkCListRow *)pa)->data;
498 gpgme_key_t b = ((GtkCListRow *)pb)->data;
499 const char *sa, *sb;
500
501 sa = a? a->uids->name : NULL;
502 sb = b? b->uids->name : NULL;
503 if (!sa)
504 return !!sb;
505 if (!sb)
506 return -1;
507 return g_ascii_strcasecmp(sa, sb);
508}
509
510static gint
511cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
512{
513 gpgme_key_t a = ((GtkCListRow *)pa)->data;
514 gpgme_key_t b = ((GtkCListRow *)pb)->data;
515 const char *sa, *sb;
516
517 sa = a? a->uids->email : NULL;
518 sb = b? b->uids->email : NULL;
519 if (!sa)
520 return !!sb;
521 if (!sb)
522 return -1;
523 return g_ascii_strcasecmp(sa, sb);
524}
525
526static void
527sort_keys ( struct select_keys_s *sk, enum col_titles column)
528{
529 GtkCList *clist = sk->clist;
530
531 switch (column) {
532 case COL_NAME:
533 gtk_clist_set_compare_func (clist, cmp_name);
534 break;
535 case COL_EMAIL:
536 gtk_clist_set_compare_func (clist, cmp_email);
537 break;
538 default:
539 return;
540 }
541
542 /* column clicked again: toggle as-/decending */
543 if ( sk->sort_column == column) {
544 sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
545 GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
546 }
547 else
548 sk->sort_type = GTK_SORT_ASCENDING;
549
550 sk->sort_column = column;
551 gtk_clist_set_sort_type (clist, sk->sort_type);
552 gtk_clist_sort (clist);
553}
554
555static void
556sort_keys_name (GtkWidget *widget, gpointer data)
557{
558 sort_keys ((struct select_keys_s*)data, COL_NAME);
559}
560
561static void
562sort_keys_email (GtkWidget *widget, gpointer data)
563{
564 sort_keys ((struct select_keys_s*)data, COL_EMAIL);
565}
566
567#endif /*USE_GPGME*/