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