Statistics
| Revision:

root / src / select-keys.c @ 2453

History | View | Annotate | Download (15.5 kB)

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