fix bug 4239, 'Preferences: Text Options Header Display modal is not modal' (sic)
[claws.git] / src / plugins / pgpcore / select-keys.c
1 /* select-keys.c - GTK+ based key selection
2  * Copyright (C) 2001-2016 Werner Koch (dd9jn) and the Claws Mail team
3  *
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 3 of the License, or
7  * (at your option) any later version.
8  *
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.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #ifdef HAVE_CONFIG_H
19 #  include <config.h>
20 #endif
21
22 #ifdef USE_GPGME
23 #include <stdio.h>
24 #include <stdlib.h>
25
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <gtk/gtk.h>
30 #include "select-keys.h"
31 #include "utils.h"
32 #include "gtkutils.h"
33 #include "inputdialog.h"
34 #include "manage_window.h"
35 #include "alertpanel.h"
36
37 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
38 #define DIMof(type,member)   DIM(((type *)0)->member)
39
40
41 enum col_titles { 
42     COL_ALGO,
43     COL_KEYID,
44     COL_NAME,
45     COL_ADDRESS,
46     COL_TRUST,
47     COL_PTR,
48
49     N_COL_TITLES
50 };
51
52 #define COL_ALGO_WIDTH 70
53 #define COL_KEYID_WIDTH 120
54 #define COL_NAME_WIDTH 115
55 #define COL_ADDRESS_WIDTH 140
56 #define COL_TRUST_WIDTH 20
57
58 struct select_keys_s {
59     int okay;
60     GtkWidget *window;
61     GtkLabel *toplabel;
62     GtkWidget *view;
63     const char *pattern;
64     unsigned int num_keys;
65     gpgme_key_t *kset;
66     gpgme_ctx_t select_ctx;
67     gpgme_protocol_t proto;
68     GtkSortType sort_type;
69     enum col_titles sort_column;
70     SelectionResult result;
71 };
72
73
74 static void set_row (GtkListStore *store, gpgme_key_t key, gpgme_protocol_t proto);
75 static gpgme_key_t fill_view (struct select_keys_s *sk, const char *pattern,
76                         gpgme_protocol_t proto);
77 static void create_dialog (struct select_keys_s *sk);
78 static void open_dialog (struct select_keys_s *sk);
79 static void close_dialog (struct select_keys_s *sk);
80 static gint delete_event_cb (GtkWidget *widget,
81                              GdkEventAny *event, gpointer data);
82 static gboolean key_pressed_cb (GtkWidget *widget,
83                                 GdkEventKey *event, gpointer data);
84 static void select_btn_cb (GtkWidget *widget, gpointer data);
85 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
86 static void dont_encrypt_btn_cb (GtkWidget *widget, gpointer data);
87 static void other_btn_cb (GtkWidget *widget, gpointer data);
88
89 static gboolean use_untrusted (gpgme_key_t, gpgme_user_id_t uid, gpgme_protocol_t proto);
90
91 static void
92 update_progress (struct select_keys_s *sk, int running, const char *pattern)
93 {
94     static int windmill[] = { '-', '\\', '|', '/' };
95     char *buf;
96
97     if (!running)
98         buf = g_strdup_printf (_("No exact match for '%s'; please select the key."),
99                                pattern);
100     else 
101         buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
102                                pattern,
103                                windmill[running%DIM(windmill)]);
104     gtk_label_set_text (sk->toplabel, buf);
105     g_free (buf);
106 }
107
108
109 /**
110  * gpgmegtk_recipient_selection:
111  * @recp_names: A list of email addresses
112  * 
113  * Select a list of recipients from a given list of email addresses.
114  * This may pop up a window to present the user a choice, it will also
115  * check that the recipients key are all valid.
116  * 
117  * Return value: NULL on error or a list of list of recipients.
118  **/
119 gpgme_key_t *
120 gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
121                                 gpgme_protocol_t proto)
122 {
123     struct select_keys_s sk;
124     gpgme_key_t key = NULL;
125     memset (&sk, 0, sizeof sk);
126
127     open_dialog (&sk);
128
129     do {
130         sk.pattern = recp_names? recp_names->data:NULL;
131         sk.proto = proto;
132         if (sk.view != NULL) {
133             GtkTreeModel *model =
134                 gtk_tree_view_get_model(GTK_TREE_VIEW(sk.view));
135             gtk_list_store_clear(GTK_LIST_STORE(model));
136         }
137         key = fill_view (&sk, sk.pattern, proto);
138         update_progress (&sk, 0, sk.pattern ? sk.pattern : "NULL");
139         if (!key) {
140                 gtk_widget_show_all (sk.window);
141                 gtk_main ();
142         } else {
143                 gtk_widget_hide (sk.window);
144                 sk.kset = g_realloc(sk.kset,
145                         sizeof(gpgme_key_t) * (sk.num_keys + 1));
146                 gpgme_key_ref(key);
147                 sk.kset[sk.num_keys] = key;
148                 sk.num_keys++;
149                 sk.okay = 1;
150                 sk.result = KEY_SELECTION_OK;
151                 gpgme_release (sk.select_ctx);
152                 sk.select_ctx = NULL;
153                 debug_print("used %s\n", key->uids->email);
154         }
155         key = NULL;
156         if (recp_names)
157             recp_names = recp_names->next;
158     } while (sk.okay && recp_names);
159
160     close_dialog (&sk);
161
162     if (!sk.okay) {
163         g_free(sk.kset);
164         sk.kset = NULL;
165     } else {
166         sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
167         sk.kset[sk.num_keys] = NULL;
168     }
169     if (result)
170             *result = sk.result;
171     return sk.kset;
172
173
174 static void
175 set_row (GtkListStore *store, gpgme_key_t key, gpgme_protocol_t proto)
176 {
177     const gchar *s;
178     gchar *algo_buf, *name, *address;
179     GtkTreeIter iter;
180     gsize by_read = 0, by_written = 0;
181     gchar *ret_str = NULL;
182
183     /* first check whether the key is capable of encryption which is not
184      * the case for revoked, expired or sign-only keys */
185     if (!key->can_encrypt || key->revoked || key->expired || key->disabled)
186         return;
187
188     algo_buf = g_strdup_printf ("%du/%s", 
189          key->subkeys->length,
190          gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
191
192     s = key->uids->name;
193     if (!s || !*s)
194         s = key->uids->uid;
195     if (proto == GPGME_PROTOCOL_CMS) {
196         if (strstr(s, ",CN="))
197                 s = strstr(s, ",CN=")+4;
198         else if (strstr(s, "CN="))
199                 s = strstr(s, "CN=")+3;
200     } 
201     
202     ret_str = NULL;
203     if (!g_utf8_validate(s, -1, NULL))
204             ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
205     if (ret_str && by_written) {
206         s = ret_str;
207     }
208     name = g_strdup(s);
209
210     if (proto == GPGME_PROTOCOL_CMS && (!key->uids->email || !*key->uids->email)) {
211         gpgme_user_id_t uid = key->uids->next;
212         if (uid)
213                 s = uid->email;
214         else
215                 s = key->uids->email;
216     } else {
217         s = key->uids->email;
218     }
219     
220     ret_str = NULL;
221     if (!g_utf8_validate(s, -1, NULL))
222             ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
223     if (ret_str && by_written) {
224         s = ret_str;
225     }
226     address = g_strdup(s);
227
228     switch (key->uids->validity)
229       {
230       case GPGME_VALIDITY_UNDEFINED:
231         s = _("Undefined");
232         break;
233       case GPGME_VALIDITY_NEVER:
234         s = _("Never");
235         break;
236       case GPGME_VALIDITY_MARGINAL:
237         s = _("Marginal");
238         break;
239       case GPGME_VALIDITY_FULL:
240         s = _("Full");
241         break;
242       case GPGME_VALIDITY_ULTIMATE:
243         s = _("Ultimate");
244         break;
245       case GPGME_VALIDITY_UNKNOWN:
246       default:
247         s = _("Unknown");
248         break;
249       }
250
251     gtk_list_store_append(store, &iter);
252     gtk_list_store_set(store, &iter,
253         COL_ALGO, algo_buf,
254         COL_KEYID, key->subkeys->keyid,
255         COL_NAME, name,
256         COL_ADDRESS, address,
257         COL_TRUST, s,
258         COL_PTR, key,
259         -1);
260     gpgme_key_ref(key);
261
262     g_free(name);
263     g_free(address);
264     g_free (algo_buf);
265 }
266
267 static gpgme_key_t 
268 fill_view (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
269 {
270     GtkWidget *view;
271     GtkTreeModel *model;
272     GtkTreeSelection *sel;
273     GtkTreeIter iter;
274     gpgme_ctx_t ctx;
275     gpgme_error_t err;
276     gpgme_key_t key;
277     int running=0;
278     int num_results = 0;
279     gboolean exact_match = FALSE;
280     gpgme_key_t last_key = NULL;
281     gpgme_user_id_t last_uid = NULL;
282
283     cm_return_val_if_fail (sk, NULL);
284
285     view = sk->view;
286     cm_return_val_if_fail (view, NULL);
287     model = gtk_tree_view_get_model(GTK_TREE_VIEW(view));
288
289     debug_print ("select_keys:fill_view:  pattern '%s' proto %d\n", pattern != NULL ? pattern : "NULL", proto);
290
291     err = gpgme_new (&ctx);
292     g_assert (!err);
293
294     gpgme_set_protocol(ctx, proto);
295     sk->select_ctx = ctx;
296
297     update_progress (sk, ++running, pattern);
298     while (gtk_events_pending ())
299         gtk_main_iteration ();
300
301     err = gpgme_op_keylist_start (ctx, pattern, 0);
302     if (err) {
303         debug_print ("** gpgme_op_keylist_start(%s) failed: %s\n",
304                      pattern != NULL ? pattern : "NULL", gpgme_strerror (err));
305         sk->select_ctx = NULL;
306         gpgme_release(ctx);
307         return NULL;
308     }
309     update_progress (sk, ++running, pattern);
310     while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
311         gpgme_user_id_t uid = key->uids;
312         if (!key->can_encrypt || key->revoked || key->expired || key->disabled) {
313                 gpgme_key_unref(key);
314                 continue;
315         }
316         debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
317         set_row (GTK_LIST_STORE(model), key, proto );
318         for (; uid; uid = uid->next) {
319                 gchar *raw_mail = NULL;
320
321                 if (!uid->email)
322                         continue;
323                 if (uid->revoked || uid->invalid)
324                         continue;
325                 raw_mail = g_strdup(uid->email);
326                 extract_address(raw_mail);
327                 if (pattern != NULL && !strcasecmp(pattern, raw_mail)) {
328                         exact_match = TRUE;
329                         last_uid = uid;
330                         g_free(raw_mail);
331                         break;
332                 }
333                 g_free(raw_mail);
334         }
335
336         /* Select the first row */
337         sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
338         if (gtk_tree_model_get_iter_first(model, &iter))
339                 gtk_tree_selection_select_iter(sel, &iter);
340
341         num_results++;
342         if (last_key != NULL)
343                 gpgme_key_unref(last_key);
344         last_key = key;
345         key = NULL;
346         update_progress (sk, ++running, pattern);
347         while (gtk_events_pending ())
348             gtk_main_iteration ();
349     }
350  
351     if (exact_match == TRUE && num_results == 1) {
352             if (last_key->uids->validity < GPGME_VALIDITY_FULL && 
353                 !use_untrusted(last_key, last_uid, proto))
354                     exact_match = FALSE;
355     }
356
357     debug_print ("%% %s:%d:  ready\n", __FILE__ ,__LINE__ );
358     if (gpgme_err_code(err) != GPG_ERR_EOF) {
359         debug_print ("** gpgme_op_keylist_next failed: %s\n",
360                      gpgme_strerror (err));
361         gpgme_op_keylist_end(ctx);
362     }
363     if (!exact_match || num_results != 1) {
364             sk->select_ctx = NULL;
365             gpgme_release (ctx);
366     }
367
368     if (exact_match && num_results == 1)
369             return last_key;
370
371     if (last_key != NULL)
372         gpgme_key_unref(last_key);
373
374     return NULL;
375 }
376
377
378 static void
379 view_row_activated_cb(GtkTreeView *view,
380                 GtkTreePath *path,
381                 GtkTreeViewColumn *column,
382                 gpointer user_data)
383 {
384         select_btn_cb(NULL, user_data);
385 }
386
387
388 static void 
389 create_dialog (struct select_keys_s *sk)
390 {
391     GtkWidget *window;
392     GtkWidget *vbox, *vbox2, *hbox;
393     GtkWidget *bbox;
394     GtkWidget *scrolledwin;
395     GtkWidget *view;
396     GtkWidget *label;
397     GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
398     GtkListStore *store;
399     GtkCellRenderer *rdr;
400     GtkTreeViewColumn *col;
401     GtkTreeSelection *sel;
402     gint i = 0;
403
404     g_assert (!sk->window);
405     window = gtkut_window_new (GTK_WINDOW_TOPLEVEL, "select-keys");
406     gtk_widget_set_size_request (window, 560, 280);
407     gtk_container_set_border_width (GTK_CONTAINER (window), 8);
408     gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
409     gtk_window_set_modal (GTK_WINDOW (window), TRUE);
410     gtk_window_set_type_hint(GTK_WINDOW(window), GDK_WINDOW_TYPE_HINT_DIALOG);
411     g_signal_connect (G_OBJECT (window), "delete_event",
412                       G_CALLBACK (delete_event_cb), sk);
413     g_signal_connect (G_OBJECT (window), "key_press_event",
414                       G_CALLBACK (key_pressed_cb), sk);
415     MANAGE_WINDOW_SIGNALS_CONNECT (window);
416
417     vbox = gtk_vbox_new (FALSE, 8);
418     gtk_container_add (GTK_CONTAINER (window), vbox);
419
420     hbox  = gtk_hbox_new(FALSE, 4);
421     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
422     label = gtk_label_new ( "" );
423     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
424
425     hbox = gtk_hbox_new (FALSE, 8);
426     gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
427     gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
428
429     scrolledwin = gtk_scrolled_window_new (NULL, NULL);
430     gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
431     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
432                                     GTK_POLICY_AUTOMATIC,
433                                     GTK_POLICY_AUTOMATIC);
434
435                 store = gtk_list_store_new(N_COL_TITLES,
436                                 G_TYPE_STRING,
437                                 G_TYPE_STRING,
438                                 G_TYPE_STRING,
439                                 G_TYPE_STRING,
440                                 G_TYPE_STRING,
441                                 G_TYPE_POINTER,
442                                 -1);
443
444                 view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
445                 g_object_unref(store);
446                 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(view), TRUE);
447                 gtk_tree_view_set_reorderable(GTK_TREE_VIEW(view), FALSE);
448                 sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
449                 gtk_tree_selection_set_mode(sel, GTK_SELECTION_BROWSE);
450
451                 rdr = gtk_cell_renderer_text_new();
452                 col = gtk_tree_view_column_new_with_attributes(_("Size"), rdr,
453                                 "markup", COL_ALGO, NULL);
454                 gtk_tree_view_column_set_min_width(col, COL_ALGO_WIDTH);
455                 gtk_tree_view_column_set_sort_column_id(col, i++);
456                 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
457
458                 col = gtk_tree_view_column_new_with_attributes(_("Key ID"), rdr,
459                                 "markup", COL_KEYID, NULL);
460                 gtk_tree_view_column_set_min_width(col, COL_KEYID_WIDTH);
461                 gtk_tree_view_column_set_sort_column_id(col, i++);
462                 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
463
464                 col = gtk_tree_view_column_new_with_attributes(_("Name"), rdr,
465                                 "markup", COL_NAME, NULL);
466                 gtk_tree_view_column_set_min_width(col, COL_NAME_WIDTH);
467                 gtk_tree_view_column_set_sort_column_id(col, i++);
468                 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
469
470                 col = gtk_tree_view_column_new_with_attributes(_("Address"), rdr,
471                                 "markup", COL_ADDRESS, NULL);
472                 gtk_tree_view_column_set_min_width(col, COL_ADDRESS_WIDTH);
473                 gtk_tree_view_column_set_sort_column_id(col, i++);
474                 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
475
476                 col = gtk_tree_view_column_new_with_attributes(_("Trust"), rdr,
477                                 "markup", COL_TRUST, NULL);
478                 gtk_tree_view_column_set_min_width(col, COL_TRUST_WIDTH);
479                 gtk_tree_view_column_set_sort_column_id(col, i++);
480                 gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
481
482                 g_signal_connect(G_OBJECT(view), "row-activated",
483                                 G_CALLBACK(view_row_activated_cb), sk);
484
485     gtk_container_add (GTK_CONTAINER (scrolledwin), view);
486
487     hbox = gtk_hbox_new (FALSE, 8);
488     gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
489
490     /* TRANSLATORS: check that the accelerators in _Select, _Other and
491      * Do_n't encrypt are different than the one in the stock Cancel
492      * button */
493     gtkut_stock_button_set_create (&bbox, 
494                                    &select_btn, _("_Select"),
495                                    &other_btn, _("_Other"),
496                                    &dont_encrypt_btn, _("Do_n't encrypt"));
497     
498     cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
499     gtk_widget_set_can_default(cancel_btn, TRUE);
500     gtk_box_pack_start(GTK_BOX(bbox), cancel_btn, TRUE, TRUE, 0);
501     gtk_widget_show(cancel_btn);
502     gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
503     gtk_widget_grab_default (select_btn);
504
505     g_signal_connect (G_OBJECT (select_btn), "clicked",
506                       G_CALLBACK (select_btn_cb), sk);
507     g_signal_connect (G_OBJECT(cancel_btn), "clicked",
508                       G_CALLBACK (cancel_btn_cb), sk);
509     g_signal_connect (G_OBJECT(dont_encrypt_btn), "clicked",
510                       G_CALLBACK (dont_encrypt_btn_cb), sk);
511     g_signal_connect (G_OBJECT (other_btn), "clicked",
512                       G_CALLBACK (other_btn_cb), sk);
513
514     vbox2 = gtk_vbox_new (FALSE, 4);
515     gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
516
517     sk->window = window;
518     sk->toplabel = GTK_LABEL (label);
519     sk->view  = view;
520 }
521
522
523 /* Function called by gtk_tree_model_foreach() upon dialog close,
524  * which unrefs the gpgme_key_t pointer from each model line */
525 static gboolean
526 close_dialog_foreach_func(GtkTreeModel *model,
527                 GtkTreePath *path,
528                 GtkTreeIter *iter,
529                 gpointer user_data)
530 {
531         gpgme_key_t key;
532
533         gtk_tree_model_get(model, iter, COL_PTR, &key, -1);
534         gpgme_key_unref(key);
535         return FALSE;
536 }
537
538
539 static void
540 open_dialog (struct select_keys_s *sk)
541 {
542     if (!sk->window)
543         create_dialog (sk);
544     manage_window_set_transient (GTK_WINDOW (sk->window));
545     sk->okay = 0;
546     sk->sort_column = N_COL_TITLES; /* use an invalid value */
547     sk->sort_type = GTK_SORT_ASCENDING;
548 }
549
550
551 static void
552 close_dialog (struct select_keys_s *sk)
553 {
554     GtkTreeModel *model;
555     cm_return_if_fail (sk);
556
557     debug_print("pgpcore select-keys dialog closing\n");
558     if (sk->view != NULL) {
559         model = gtk_tree_view_get_model(GTK_TREE_VIEW(sk->view));
560         gtk_tree_model_foreach(model, close_dialog_foreach_func, NULL);
561         gtk_list_store_clear(GTK_LIST_STORE(model));
562     }
563
564     gtk_widget_destroy (sk->window);
565     sk->window = NULL;
566 }
567
568
569 static gint
570 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
571 {
572     struct select_keys_s *sk = data;
573
574     sk->okay = 0;
575     gtk_main_quit ();
576
577     return TRUE;
578 }
579
580
581 static gboolean
582 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
583 {
584     struct select_keys_s *sk = data;
585
586     cm_return_val_if_fail (sk, FALSE);
587     if (event && event->keyval == GDK_KEY_Escape) {
588         sk->okay = 0;
589         gtk_main_quit ();
590     }
591     return FALSE;
592 }
593
594
595 static void 
596 select_btn_cb (GtkWidget *widget, gpointer data)
597 {
598     struct select_keys_s *sk = data;
599     gboolean use_key;
600     gpgme_key_t key;
601
602     cm_return_if_fail (sk);
603
604     key = gtkut_tree_view_get_selected_pointer(
605         GTK_TREE_VIEW(sk->view), COL_PTR,
606                                 NULL, NULL, NULL);
607     if (key) {
608         gpgme_user_id_t uid;
609         for (uid = key->uids; uid; uid = uid->next) {
610                 gchar *raw_mail = NULL;
611
612                 if (!uid->email)
613                         continue;
614                 raw_mail = g_strdup(uid->email);
615                 extract_address(raw_mail);
616                 if (sk->pattern && !strcasecmp(sk->pattern, raw_mail)) {
617                         g_free(raw_mail);
618                         break;
619                 }
620                 g_free(raw_mail);
621         }
622         if (!uid)
623                 uid = key->uids;
624
625         if ( uid->validity < GPGME_VALIDITY_FULL ) {
626             use_key = use_untrusted(key, uid, sk->proto);
627             if (!use_key) {
628                 debug_print ("** Key untrusted, will not encrypt\n");
629                 return;
630             }
631         }
632         sk->kset = g_realloc(sk->kset,
633                 sizeof(gpgme_key_t) * (sk->num_keys + 1));
634         gpgme_key_ref(key);
635         sk->kset[sk->num_keys] = key;
636         sk->num_keys++;
637         sk->okay = 1;
638         sk->result = KEY_SELECTION_OK;
639         gtk_main_quit ();
640     }
641 }
642
643
644 static void 
645 cancel_btn_cb (GtkWidget *widget, gpointer data)
646 {
647     struct select_keys_s *sk = data;
648
649     cm_return_if_fail (sk);
650     sk->okay = 0;
651     sk->result = KEY_SELECTION_CANCEL;
652     if (sk->select_ctx)
653         gpgme_cancel (sk->select_ctx);
654     gtk_main_quit ();
655 }
656
657 static void 
658 dont_encrypt_btn_cb (GtkWidget *widget, gpointer data)
659 {
660     struct select_keys_s *sk = data;
661
662     cm_return_if_fail (sk);
663     sk->okay = 0;
664     sk->result = KEY_SELECTION_DONT;
665     if (sk->select_ctx)
666         gpgme_cancel (sk->select_ctx);
667     gtk_main_quit ();
668 }
669
670 static void
671 other_btn_cb (GtkWidget *widget, gpointer data)
672 {
673     struct select_keys_s *sk = data;
674     char *uid;
675
676     cm_return_if_fail (sk);
677     uid = input_dialog ( _("Add key"),
678                          _("Enter another user or key ID:"),
679                          NULL );
680     if (!uid)
681         return;
682     if (fill_view (sk, uid, sk->proto) != NULL) {
683             gpgme_release(sk->select_ctx);
684             sk->select_ctx = NULL;
685     }
686     update_progress (sk, 0, sk->pattern);
687     g_free (uid);
688 }
689
690
691 static gboolean
692 use_untrusted (gpgme_key_t key, gpgme_user_id_t uid, gpgme_protocol_t proto)
693 {
694     AlertValue aval;
695     gchar *buf = NULL;
696     gchar *title = NULL;
697     if (proto != GPGME_PROTOCOL_OpenPGP)
698         return TRUE;
699
700     title = g_strdup_printf(_("Encrypt to %s <%s>"), uid->name, uid->email);
701     buf = g_strdup_printf(_("This encryption key is not fully trusted.\n"
702                "If you choose to encrypt the message with this key, you don't\n"
703                "know for sure that it will go to the person you mean it to.\n\n"
704                "Key details: ID %s, primary identity %s &lt;%s&gt;\n\n"
705                "Do you trust this key enough to use it anyway?"), 
706                key->subkeys->keyid, key->uids->name, key->uids->email);
707     aval = alertpanel(title, buf,
708              GTK_STOCK_NO, GTK_STOCK_YES, NULL, ALERTFOCUS_FIRST);
709     g_free(buf);
710     g_free(title);
711     if (aval == G_ALERTALTERNATE)
712         return TRUE;
713     else
714         return FALSE;
715 }
716
717 #endif /*USE_GPGME*/