2008-07-05 [colin] 3.5.0cvs10
[claws.git] / src / plugins / pgpcore / select-keys.c
1 /* select-keys.c - GTK+ based key selection
2  *      Copyright (C) 2001-2007 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
19 #ifdef HAVE_CONFIG_H
20 #  include <config.h>
21 #endif
22
23 #ifdef USE_GPGME
24 #include <stdio.h>
25 #include <stdlib.h>
26
27 #include <glib.h>
28 #include <glib/gi18n.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <gtk/gtk.h>
31 #include "select-keys.h"
32 #include "utils.h"
33 #include "gtkutils.h"
34 #include "inputdialog.h"
35 #include "manage_window.h"
36 #include "alertpanel.h"
37
38 #define DIM(v) (sizeof(v)/sizeof((v)[0]))
39 #define DIMof(type,member)   DIM(((type *)0)->member)
40
41
42 enum col_titles { 
43     COL_ALGO,
44     COL_KEYID,
45     COL_NAME,
46     COL_EMAIL,
47     COL_VALIDITY,
48
49     N_COL_TITLES
50 };
51
52 struct select_keys_s {
53     int okay;
54     GtkWidget *window;
55     GtkLabel *toplabel;
56     GtkCList *clist;
57     const char *pattern;
58     unsigned int num_keys;
59     gpgme_key_t *kset;
60     gpgme_ctx_t select_ctx;
61     gpgme_protocol_t proto;
62     GtkSortType sort_type;
63     enum col_titles sort_column;
64     SelectionResult result;
65 };
66
67
68 static void set_row (GtkCList *clist, gpgme_key_t key, gpgme_protocol_t proto);
69 static gpgme_key_t fill_clist (struct select_keys_s *sk, const char *pattern,
70                         gpgme_protocol_t proto);
71 static void create_dialog (struct select_keys_s *sk);
72 static void open_dialog (struct select_keys_s *sk);
73 static void close_dialog (struct select_keys_s *sk);
74 static gint delete_event_cb (GtkWidget *widget,
75                              GdkEventAny *event, gpointer data);
76 static gboolean key_pressed_cb (GtkWidget *widget,
77                                 GdkEventKey *event, gpointer data);
78 static void select_btn_cb (GtkWidget *widget, gpointer data);
79 static void cancel_btn_cb (GtkWidget *widget, gpointer data);
80 static void dont_encrypt_btn_cb (GtkWidget *widget, gpointer data);
81 static void other_btn_cb (GtkWidget *widget, gpointer data);
82 static void sort_keys (struct select_keys_s *sk, enum col_titles column);
83 static void sort_keys_name (GtkWidget *widget, gpointer data);
84 static void sort_keys_email (GtkWidget *widget, gpointer data);
85
86 static gboolean use_untrusted (gpgme_key_t, gpgme_protocol_t proto);
87
88 static void
89 update_progress (struct select_keys_s *sk, int running, const char *pattern)
90 {
91     static int windmill[] = { '-', '\\', '|', '/' };
92     char *buf;
93
94     if (!running)
95         buf = g_strdup_printf (_("No exact match for '%s'; please select the key."),
96                                pattern);
97     else 
98         buf = g_strdup_printf (_("Collecting info for '%s' ... %c"),
99                                pattern,
100                                windmill[running%DIM(windmill)]);
101     gtk_label_set_text (sk->toplabel, buf);
102     g_free (buf);
103 }
104
105
106 /**
107  * gpgmegtk_recipient_selection:
108  * @recp_names: A list of email addresses
109  * 
110  * Select a list of recipients from a given list of email addresses.
111  * This may pop up a window to present the user a choice, it will also
112  * check that the recipients key are all valid.
113  * 
114  * Return value: NULL on error or a list of list of recipients.
115  **/
116 gpgme_key_t *
117 gpgmegtk_recipient_selection (GSList *recp_names, SelectionResult *result,
118                                 gpgme_protocol_t proto)
119 {
120     struct select_keys_s sk;
121     gpgme_key_t key = NULL;
122     memset (&sk, 0, sizeof sk);
123
124     open_dialog (&sk);
125
126     do {
127         sk.pattern = recp_names? recp_names->data:NULL;
128         sk.proto = proto;
129         gtk_clist_clear (sk.clist);
130         key = fill_clist (&sk, sk.pattern, proto);
131         update_progress (&sk, 0, sk.pattern);
132         if (!key) {
133                 gtk_widget_show_all (sk.window);
134                 gtk_main ();
135         } else {
136                 gtk_widget_hide (sk.window);
137                 sk.kset = g_realloc(sk.kset,
138                         sizeof(gpgme_key_t) * (sk.num_keys + 1));
139                 gpgme_key_ref(key);
140                 sk.kset[sk.num_keys] = key;
141                 sk.num_keys++;
142                 sk.okay = 1;
143                 sk.result = KEY_SELECTION_OK;
144                 gpgme_release (sk.select_ctx);
145                 sk.select_ctx = NULL;
146                 debug_print("used %s\n", key->uids->email);
147         }
148         key = NULL;
149         if (recp_names)
150             recp_names = recp_names->next;
151     } while (sk.okay && recp_names);
152
153     close_dialog (&sk);
154
155     if (!sk.okay) {
156         g_free(sk.kset);
157         sk.kset = NULL;
158     } else {
159         sk.kset = g_realloc(sk.kset, sizeof(gpgme_key_t) * (sk.num_keys + 1));
160         sk.kset[sk.num_keys] = NULL;
161     }
162     if (result)
163             *result = sk.result;
164     return sk.kset;
165
166
167 static void
168 destroy_key (gpointer data)
169 {
170     gpgme_key_t key = data;
171     gpgme_key_release (key);
172 }
173
174 static void
175 set_row (GtkCList *clist, gpgme_key_t key, gpgme_protocol_t proto)
176 {
177     const char *s;
178     const char *text[N_COL_TITLES];
179     char *algo_buf;
180     int row;
181     gsize by_read = 0, by_written = 0;
182     gchar *ret_str = NULL;
183
184     /* first check whether the key is capable of encryption which is not
185      * the case for revoked, expired or sign-only keys */
186     if (!key->can_encrypt)
187         return;
188     algo_buf = g_strdup_printf ("%du/%s", 
189          key->subkeys->length,
190          gpgme_pubkey_algo_name(key->subkeys->pubkey_algo) );
191     text[COL_ALGO] = algo_buf;
192
193     s = key->subkeys->keyid;
194     if (strlen (s) == 16)
195         s += 8; /* show only the short keyID */
196     text[COL_KEYID] = s;
197
198
199     s = key->uids->name;
200     if (!s || !*s)
201         s = key->uids->uid;
202     if (proto == GPGME_PROTOCOL_CMS) {
203         if (strstr(s, ",CN="))
204                 s = strstr(s, ",CN=")+4;
205         else if (strstr(s, "CN="))
206                 s = strstr(s, "CN=")+3;
207     } 
208     
209     ret_str = NULL;
210     if (!g_utf8_validate(s, -1, NULL))
211             ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
212     if (ret_str && by_written) {
213         s = ret_str;
214     }
215     text[COL_NAME] = s;
216
217     if (proto == GPGME_PROTOCOL_CMS && (!key->uids->email || !*key->uids->email)) {
218         gpgme_user_id_t uid = key->uids->next;
219         if (uid)
220                 s = uid->email;
221         else
222                 s = key->uids->email;
223     } else {
224         s = key->uids->email;
225     }
226     
227     ret_str = NULL;
228     if (!g_utf8_validate(s, -1, NULL))
229             ret_str = g_locale_to_utf8 (s, strlen(s), &by_read, &by_written, NULL);
230     if (ret_str && by_written) {
231         s = ret_str;
232     }
233     text[COL_EMAIL] = s;
234
235     switch (key->uids->validity)
236       {
237       case GPGME_VALIDITY_UNDEFINED:
238         s = "q";
239         break;
240       case GPGME_VALIDITY_NEVER:
241         s = "n";
242         break;
243       case GPGME_VALIDITY_MARGINAL:
244         s = "m";
245         break;
246       case GPGME_VALIDITY_FULL:
247         s = "f";
248         break;
249       case GPGME_VALIDITY_ULTIMATE:
250         s = "u";
251         break;
252       case GPGME_VALIDITY_UNKNOWN:
253       default:
254         s = "?";
255         break;
256       }
257     text[COL_VALIDITY] = s;
258
259     row = gtk_clist_append (clist, (gchar**)text);
260     g_free (algo_buf);
261
262     gtk_clist_set_row_data_full (clist, row, key, destroy_key);
263 }
264
265 static gpgme_key_t 
266 fill_clist (struct select_keys_s *sk, const char *pattern, gpgme_protocol_t proto)
267 {
268     GtkCList *clist;
269     gpgme_ctx_t ctx;
270     gpgme_error_t err;
271     gpgme_key_t key;
272     int running=0;
273     int num_results = 0;
274     gboolean exact_match = FALSE;
275     gpgme_key_t last_key = NULL;
276     g_return_val_if_fail (sk, NULL);
277     clist = sk->clist;
278     g_return_val_if_fail (clist, NULL);
279
280     debug_print ("select_keys:fill_clist:  pattern '%s' proto %d\n", pattern, proto);
281
282     /*gtk_clist_freeze (select_keys.clist);*/
283     err = gpgme_new (&ctx);
284     g_assert (!err);
285
286     gpgme_set_protocol(ctx, proto);
287     sk->select_ctx = ctx;
288
289     update_progress (sk, ++running, pattern);
290     while (gtk_events_pending ())
291         gtk_main_iteration ();
292
293     err = gpgme_op_keylist_start (ctx, pattern, 0);
294     if (err) {
295         debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
296                      pattern, gpgme_strerror (err));
297         sk->select_ctx = NULL;
298         gpgme_release(ctx);
299         return NULL;
300     }
301     update_progress (sk, ++running, pattern);
302     while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
303         gpgme_user_id_t uid = key->uids;
304         if (!key->can_encrypt)
305                 continue;
306         debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
307         set_row (clist, key, proto ); 
308         for (; uid; uid = uid->next) {
309                 gchar *raw_mail = NULL;
310
311                 if (!uid->email)
312                         continue;
313                 raw_mail = g_strdup(uid->email);
314                 extract_address(raw_mail);
315                 if (!strcmp(pattern, raw_mail)) {
316                         exact_match = TRUE;
317                         g_free(raw_mail);
318                         break;
319                 }
320                 g_free(raw_mail);
321         }
322         num_results++;
323         last_key = key;
324         key = NULL;
325         update_progress (sk, ++running, pattern);
326         while (gtk_events_pending ())
327             gtk_main_iteration ();
328     }
329  
330     if (exact_match == TRUE && num_results == 1) {
331             if (last_key->uids->validity < GPGME_VALIDITY_FULL && 
332                 !use_untrusted(last_key, proto))
333                     exact_match = FALSE;
334     }
335
336     debug_print ("%% %s:%d:  ready\n", __FILE__ ,__LINE__ );
337     if (gpgme_err_code(err) != GPG_ERR_EOF) {
338         debug_print ("** gpgme_op_keylist_next failed: %s",
339                      gpgme_strerror (err));
340         gpgme_op_keylist_end(ctx);
341     }
342     if (!exact_match || num_results != 1) {
343             sk->select_ctx = NULL;
344             gpgme_release (ctx);
345     }
346     /*gtk_clist_thaw (select_keys.clist);*/
347     return (exact_match == TRUE && num_results == 1 ? last_key:NULL);
348 }
349
350
351 static void 
352 create_dialog (struct select_keys_s *sk)
353 {
354     GtkWidget *window;
355     GtkWidget *vbox, *vbox2, *hbox;
356     GtkWidget *bbox;
357     GtkWidget *scrolledwin;
358     GtkWidget *clist;
359     GtkWidget *label;
360     GtkWidget *select_btn, *cancel_btn, *dont_encrypt_btn, *other_btn;
361     const char *titles[N_COL_TITLES];
362
363     g_assert (!sk->window);
364     window = gtkut_window_new (GTK_WINDOW_TOPLEVEL, "select-keys");
365     gtk_widget_set_size_request (window, 520, 280);
366     gtk_container_set_border_width (GTK_CONTAINER (window), 8);
367     gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
368     gtk_window_set_modal (GTK_WINDOW (window), TRUE);
369     g_signal_connect (G_OBJECT (window), "delete_event",
370                       G_CALLBACK (delete_event_cb), sk);
371     g_signal_connect (G_OBJECT (window), "key_press_event",
372                       G_CALLBACK (key_pressed_cb), sk);
373     MANAGE_WINDOW_SIGNALS_CONNECT (window);
374
375     vbox = gtk_vbox_new (FALSE, 8);
376     gtk_container_add (GTK_CONTAINER (window), vbox);
377
378     hbox  = gtk_hbox_new(FALSE, 4);
379     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
380     label = gtk_label_new ( "" );
381     gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
382
383     hbox = gtk_hbox_new (FALSE, 8);
384     gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
385     gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
386
387     scrolledwin = gtk_scrolled_window_new (NULL, NULL);
388     gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
389     gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
390                                     GTK_POLICY_AUTOMATIC,
391                                     GTK_POLICY_AUTOMATIC);
392
393     titles[COL_ALGO]     = _("Size");
394     titles[COL_KEYID]    = _("Key ID");
395     titles[COL_NAME]     = _("Name");
396     titles[COL_EMAIL]    = _("Address");
397     titles[COL_VALIDITY] = _("Val");
398
399     clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
400     gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
401     gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO,      72);
402     gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID,     76);
403     gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME,     130);
404     gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL,    130);
405     gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY,  20);
406     gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
407     g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
408                       "clicked",
409                       G_CALLBACK(sort_keys_name), sk);
410     g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
411                       "clicked",
412                       G_CALLBACK(sort_keys_email), sk);
413
414     hbox = gtk_hbox_new (FALSE, 8);
415     gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
416
417     gtkut_stock_button_set_create (&bbox, 
418                                    &select_btn, _("Select"),
419                                    &other_btn, _("Other"),
420                                    &dont_encrypt_btn, _("Don't encrypt"));
421     
422     cancel_btn = gtk_button_new_from_stock(GTK_STOCK_CANCEL);
423     GTK_WIDGET_SET_FLAGS(cancel_btn, GTK_CAN_DEFAULT);
424     gtk_box_pack_start(GTK_BOX(bbox), cancel_btn, TRUE, TRUE, 0);
425     gtk_widget_show(cancel_btn);
426     gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
427     gtk_widget_grab_default (select_btn);
428
429     g_signal_connect (G_OBJECT (select_btn), "clicked",
430                       G_CALLBACK (select_btn_cb), sk);
431     g_signal_connect (G_OBJECT(cancel_btn), "clicked",
432                       G_CALLBACK (cancel_btn_cb), sk);
433     g_signal_connect (G_OBJECT(dont_encrypt_btn), "clicked",
434                       G_CALLBACK (dont_encrypt_btn_cb), sk);
435     g_signal_connect (G_OBJECT (other_btn), "clicked",
436                       G_CALLBACK (other_btn_cb), sk);
437
438     vbox2 = gtk_vbox_new (FALSE, 4);
439     gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
440
441     sk->window = window;
442     sk->toplabel = GTK_LABEL (label);
443     sk->clist  = GTK_CLIST (clist);
444 }
445
446
447 static void
448 open_dialog (struct select_keys_s *sk)
449 {
450     if (!sk->window)
451         create_dialog (sk);
452     manage_window_set_transient (GTK_WINDOW (sk->window));
453     sk->okay = 0;
454     sk->sort_column = N_COL_TITLES; /* use an invalid value */
455     sk->sort_type = GTK_SORT_ASCENDING;
456 }
457
458
459 static void
460 close_dialog (struct select_keys_s *sk)
461 {
462     g_return_if_fail (sk);
463     gtk_widget_destroy (sk->window);
464     sk->window = NULL;
465 }
466
467
468 static gint
469 delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
470 {
471     struct select_keys_s *sk = data;
472
473     sk->okay = 0;
474     gtk_main_quit ();
475
476     return TRUE;
477 }
478
479
480 static gboolean
481 key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
482 {
483     struct select_keys_s *sk = data;
484
485     g_return_val_if_fail (sk, FALSE);
486     if (event && event->keyval == GDK_Escape) {
487         sk->okay = 0;
488         gtk_main_quit ();
489     }
490     return FALSE;
491 }
492
493
494 static void 
495 select_btn_cb (GtkWidget *widget, gpointer data)
496 {
497     struct select_keys_s *sk = data;
498     int row;
499     gboolean use_key;
500     gpgme_key_t key;
501
502     g_return_if_fail (sk);
503     if (!sk->clist->selection) {
504         debug_print ("** nothing selected");
505         return;
506     }
507     row = GPOINTER_TO_INT(sk->clist->selection->data);
508     key = gtk_clist_get_row_data(sk->clist, row);
509     if (key) {
510         if ( key->uids->validity < GPGME_VALIDITY_FULL ) {
511             use_key = use_untrusted(key, sk->proto);
512             if (!use_key) {
513                 debug_print ("** Key untrusted, will not encrypt");
514                 return;
515             }
516         }
517         sk->kset = g_realloc(sk->kset,
518                 sizeof(gpgme_key_t) * (sk->num_keys + 1));
519         gpgme_key_ref(key);
520         sk->kset[sk->num_keys] = key;
521         sk->num_keys++;
522         sk->okay = 1;
523         sk->result = KEY_SELECTION_OK;
524         gtk_main_quit ();
525     }
526 }
527
528
529 static void 
530 cancel_btn_cb (GtkWidget *widget, gpointer data)
531 {
532     struct select_keys_s *sk = data;
533
534     g_return_if_fail (sk);
535     sk->okay = 0;
536     sk->result = KEY_SELECTION_CANCEL;
537     if (sk->select_ctx)
538         gpgme_cancel (sk->select_ctx);
539     gtk_main_quit ();
540 }
541
542 static void 
543 dont_encrypt_btn_cb (GtkWidget *widget, gpointer data)
544 {
545     struct select_keys_s *sk = data;
546
547     g_return_if_fail (sk);
548     sk->okay = 0;
549     sk->result = KEY_SELECTION_DONT;
550     if (sk->select_ctx)
551         gpgme_cancel (sk->select_ctx);
552     gtk_main_quit ();
553 }
554
555 static void
556 other_btn_cb (GtkWidget *widget, gpointer data)
557 {
558     struct select_keys_s *sk = data;
559     char *uid;
560
561     g_return_if_fail (sk);
562     uid = input_dialog ( _("Add key"),
563                          _("Enter another user or key ID:"),
564                          NULL );
565     if (!uid)
566         return;
567     if (fill_clist (sk, uid, sk->proto) != NULL) {
568             gpgme_release(sk->select_ctx);
569             sk->select_ctx = NULL;
570     }
571     update_progress (sk, 0, sk->pattern);
572     g_free (uid);
573 }
574
575
576 static gboolean
577 use_untrusted (gpgme_key_t key, gpgme_protocol_t proto)
578 {
579     AlertValue aval;
580     gchar *buf = NULL;
581     
582     if (proto != GPGME_PROTOCOL_OpenPGP)
583         return TRUE;
584
585     buf = g_strdup_printf(_("The key of '%s' is not fully trusted.\n"
586                "If you choose to encrypt the message with this key you don't\n"
587                "know for sure that it will go to the person you mean it to.\n"
588                "Do you trust it enough to use it anyway?"), key->uids->email);
589     aval = alertpanel
590             (_("Trust key"),
591              buf,
592              GTK_STOCK_NO, GTK_STOCK_YES, NULL);
593     g_free(buf);
594     if (aval == G_ALERTALTERNATE)
595         return TRUE;
596     else
597         return FALSE;
598 }
599
600
601 static gint 
602 cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
603 {
604     gpgme_key_t a = ((GtkCListRow *)pa)->data;
605     gpgme_key_t b = ((GtkCListRow *)pb)->data;
606     const char *sa, *sb;
607     
608     sa = a? a->uids->name : NULL;
609     sb = b? b->uids->name : NULL;
610     if (!sa)
611         return !!sb;
612     if (!sb)
613         return -1;
614     return g_ascii_strcasecmp(sa, sb);
615 }
616
617 static gint 
618 cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
619 {
620     gpgme_key_t a = ((GtkCListRow *)pa)->data;
621     gpgme_key_t b = ((GtkCListRow *)pb)->data;
622     const char *sa, *sb;
623     
624     sa = a? a->uids->email : NULL;
625     sb = b? b->uids->email : NULL;
626     if (!sa)
627         return !!sb;
628     if (!sb)
629         return -1;
630     return g_ascii_strcasecmp(sa, sb);
631 }
632
633 static void
634 sort_keys ( struct select_keys_s *sk, enum col_titles column)
635 {
636     GtkCList *clist = sk->clist;
637
638     switch (column) {
639       case COL_NAME:
640         gtk_clist_set_compare_func (clist, cmp_name);
641         break;
642       case COL_EMAIL:
643         gtk_clist_set_compare_func (clist, cmp_email);
644         break;
645       default:
646         return;
647     }
648
649     /* column clicked again: toggle as-/decending */
650     if ( sk->sort_column == column) {
651         sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
652                         GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
653     }
654     else
655         sk->sort_type = GTK_SORT_ASCENDING;
656
657     sk->sort_column = column;
658     gtk_clist_set_sort_type (clist, sk->sort_type);
659     gtk_clist_sort (clist);
660 }
661
662 static void
663 sort_keys_name (GtkWidget *widget, gpointer data)
664 {
665     sort_keys ((struct select_keys_s*)data, COL_NAME);
666 }
667
668 static void
669 sort_keys_email (GtkWidget *widget, gpointer data)
670 {
671     sort_keys ((struct select_keys_s*)data, COL_EMAIL);
672 }
673
674 #endif /*USE_GPGME*/