Statistics
| Revision:

root / src / select-keys.c @ 1

History | View | Annotate | Download (14.8 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 <gdk/gdkkeysyms.h>
29
#include <gtk/gtkmain.h>
30
#include <gtk/gtkwidget.h>
31
#include <gtk/gtkwindow.h>
32
#include <gtk/gtkscrolledwindow.h>
33
#include <gtk/gtkvbox.h>
34
#include <gtk/gtkhbox.h>
35
#include <gtk/gtkclist.h>
36
#include <gtk/gtklabel.h>
37
#include <gtk/gtkentry.h>
38
#include <gtk/gtkhbbox.h>
39
#include <gtk/gtkbutton.h>
40
#include <gtk/gtksignal.h>
41

    
42
#include "intl.h"
43
#include "select-keys.h"
44
#include "utils.h"
45
#include "gtkutils.h"
46
#include "inputdialog.h"
47
#include "manage_window.h"
48

    
49
#define DIM(v) (sizeof(v)/sizeof((v)[0]))
50
#define DIMof(type,member)   DIM(((type *)0)->member)
51

    
52

    
53
enum col_titles { 
54
    COL_ALGO,
55
    COL_KEYID,
56
    COL_NAME,
57
    COL_EMAIL,
58
    COL_VALIDITY,
59

    
60
    N_COL_TITLES
61
};
62

    
63
struct select_keys_s {
64
    int okay;
65
    GtkWidget *window;
66
    GtkLabel *toplabel;
67
    GtkCList *clist;
68
    const char *pattern;
69
    GpgmeRecipients rset;
70
    GpgmeCtx select_ctx;
71

    
72
    GtkSortType sort_type;
73
    enum col_titles sort_column;
74
    
75
};
76

    
77

    
78
static void set_row (GtkCList *clist, GpgmeKey key);
79
static void fill_clist (struct select_keys_s *sk, const char *pattern);
80
static void create_dialog (struct select_keys_s *sk);
81
static void open_dialog (struct select_keys_s *sk);
82
static void close_dialog (struct select_keys_s *sk);
83
static gint delete_event_cb (GtkWidget *widget,
84
                             GdkEventAny *event, gpointer data);
85
static gboolean key_pressed_cb (GtkWidget *widget,
86
                                GdkEventKey *event, gpointer data);
87
static void select_btn_cb (GtkWidget *widget, gpointer data);
88
static void cancel_btn_cb (GtkWidget *widget, gpointer data);
89
static void other_btn_cb (GtkWidget *widget, gpointer data);
90
static void sort_keys (struct select_keys_s *sk, enum col_titles column);
91
static void sort_keys_name (GtkWidget *widget, gpointer data);
92
static void sort_keys_email (GtkWidget *widget, gpointer data);
93

    
94

    
95
static void
96
update_progress (struct select_keys_s *sk, int running, const char *pattern)
97
{
98
    static int windmill[] = { '-', '\\', '|', '/' };
99
    char *buf;
100

    
101
    if (!running)
102
        buf = g_strdup_printf (_("Please select key for `%s'"), 
103
                               pattern);
104
    else 
105
        buf = g_strdup_printf (_("Collecting info for `%s' ... %c"), 
106
                               pattern,
107
                               windmill[running%DIM(windmill)]);
108
    gtk_label_set_text (sk->toplabel, buf);
109
    g_free (buf);
110
}
111

    
112

    
113
/**
114
 * select_keys_get_recipients:
115
 * @recp_names: A list of email addresses
116
 * 
117
 * Select a list of recipients from a given list of email addresses.
118
 * This may pop up a window to present the user a choice, it will also
119
 * check that the recipients key are all valid.
120
 * 
121
 * Return value: NULL on error or a list of list of recipients.
122
 **/
123
GpgmeRecipients
124
gpgmegtk_recipient_selection (GSList *recp_names)
125
{
126
    struct select_keys_s sk;
127
    GpgmeError err;
128

    
129
    memset (&sk, 0, sizeof sk);
130

    
131
    err = gpgme_recipients_new (&sk.rset);
132
    if (err) {
133
        g_warning ("failed to allocate recipients set: %s",
134
                   gpgme_strerror (err));
135
        return NULL;
136
    }
137

    
138
    open_dialog (&sk);
139

    
140
    do {
141
        sk.pattern = recp_names? recp_names->data:NULL;
142
        gtk_clist_clear (sk.clist);
143
        fill_clist (&sk, sk.pattern);
144
        update_progress (&sk, 0, sk.pattern);
145
        gtk_main ();
146
        if (recp_names)
147
            recp_names = recp_names->next;
148
    } while (sk.okay && recp_names);
149

    
150
    close_dialog (&sk);
151

    
152
    if (!sk.okay) {
153
        gpgme_recipients_release (sk.rset);
154
        sk.rset = NULL;
155
    }
156
    return sk.rset;
157
} 
158

    
159
static void
160
destroy_key (gpointer data)
161
{
162
    GpgmeKey key = data;
163
    gpgme_key_release (key);
164
}
165

    
166
static void
167
set_row (GtkCList *clist, GpgmeKey key)
168
{
169
    const char *s;
170
    const char *text[N_COL_TITLES];
171
    char *algo_buf;
172
    int row;
173

    
174
    /* first check whether the key is capable of encryption which is not
175
     * the case for revoked, expired or sign-only keys */
176
    if ( !gpgme_key_get_ulong_attr (key, GPGME_ATTR_CAN_ENCRYPT, NULL, 0 ) )
177
        return;
178
    
179
    algo_buf = g_strdup_printf ("%lu/%s", 
180
         gpgme_key_get_ulong_attr (key, GPGME_ATTR_LEN, NULL, 0 ),
181
         gpgme_key_get_string_attr (key, GPGME_ATTR_ALGO, NULL, 0 ) );
182
    text[COL_ALGO] = algo_buf;
183

    
184
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_KEYID, NULL, 0);
185
    if (strlen (s) == 16)
186
        s += 8; /* show only the short keyID */
187
    text[COL_KEYID] = s;
188

    
189
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_NAME, NULL, 0);
190
    text[COL_NAME] = s;
191

    
192
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_EMAIL, NULL, 0);
193
    text[COL_EMAIL] = s;
194

    
195
    s = gpgme_key_get_string_attr (key, GPGME_ATTR_VALIDITY, NULL, 0);
196
    text[COL_VALIDITY] = s;
197

    
198
    row = gtk_clist_append (clist, (gchar**)text);
199
    g_free (algo_buf);
200

    
201
    gtk_clist_set_row_data_full (clist, row, key, destroy_key);
202
}
203

    
204

    
205
static void 
206
fill_clist (struct select_keys_s *sk, const char *pattern)
207
{
208
    GtkCList *clist;
209
    GpgmeCtx ctx;
210
    GpgmeError err;
211
    GpgmeKey key;
212
    int running=0;
213

    
214
    g_return_if_fail (sk);
215
    clist = sk->clist;
216
    g_return_if_fail (clist);
217

    
218
    debug_print ("select_keys:fill_clist:  pattern `%s'\n", pattern);
219

    
220
    /*gtk_clist_freeze (select_keys.clist);*/
221
    err = gpgme_new (&ctx);
222
    g_assert (!err);
223

    
224
    sk->select_ctx = ctx;
225

    
226
    update_progress (sk, ++running, pattern);
227
    while (gtk_events_pending ())
228
        gtk_main_iteration ();
229

    
230
    err = gpgme_op_keylist_start (ctx, pattern, 0);
231
    if (err) {
232
        debug_print ("** gpgme_op_keylist_start(%s) failed: %s",
233
                     pattern, gpgme_strerror (err));
234
        sk->select_ctx = NULL;
235
        return;
236
    }
237
    update_progress (sk, ++running, pattern);
238
    while ( !(err = gpgme_op_keylist_next ( ctx, &key )) ) {
239
        debug_print ("%% %s:%d:  insert\n", __FILE__ ,__LINE__ );
240
        set_row (clist, key ); key = NULL;
241
        update_progress (sk, ++running, pattern);
242
        while (gtk_events_pending ())
243
            gtk_main_iteration ();
244
    }
245
    debug_print ("%% %s:%d:  ready\n", __FILE__ ,__LINE__ );
246
    if (err != GPGME_EOF)
247
        debug_print ("** gpgme_op_keylist_next failed: %s",
248
                     gpgme_strerror (err));
249
    sk->select_ctx = NULL;
250
    gpgme_release (ctx);
251
    /*gtk_clist_thaw (select_keys.clist);*/
252
}
253

    
254

    
255
static void 
256
create_dialog (struct select_keys_s *sk)
257
{
258
    GtkWidget *window;
259
    GtkWidget *vbox, *vbox2, *hbox;
260
    GtkWidget *bbox;
261
    GtkWidget *scrolledwin;
262
    GtkWidget *clist;
263
    GtkWidget *label;
264
    GtkWidget *select_btn, *cancel_btn, *other_btn;
265
    const char *titles[N_COL_TITLES];
266

    
267
    g_assert (!sk->window);
268
    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
269
    gtk_widget_set_size_request (window, 520, 280);
270
    gtk_container_set_border_width (GTK_CONTAINER (window), 8);
271
    gtk_window_set_title (GTK_WINDOW (window), _("Select Keys"));
272
    gtk_window_set_modal (GTK_WINDOW (window), TRUE);
273
    g_signal_connect (G_OBJECT (window), "delete_event",
274
                      G_CALLBACK (delete_event_cb), sk);
275
    g_signal_connect (G_OBJECT (window), "key_press_event",
276
                      G_CALLBACK (key_pressed_cb), sk);
277
    MANAGE_WINDOW_SIGNALS_CONNECT (window);
278

    
279
    vbox = gtk_vbox_new (FALSE, 8);
280
    gtk_container_add (GTK_CONTAINER (window), vbox);
281

    
282
    hbox  = gtk_hbox_new(FALSE, 4);
283
    gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
284
    label = gtk_label_new ( "" );
285
    gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 0);
286

    
287
    hbox = gtk_hbox_new (FALSE, 8);
288
    gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
289
    gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
290

    
291
    scrolledwin = gtk_scrolled_window_new (NULL, NULL);
292
    gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
293
    gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
294
                                    GTK_POLICY_AUTOMATIC,
295
                                    GTK_POLICY_AUTOMATIC);
296

    
297
    titles[COL_ALGO]     = _("Size");
298
    titles[COL_KEYID]    = _("Key ID");
299
    titles[COL_NAME]     = _("Name");
300
    titles[COL_EMAIL]    = _("Address");
301
    titles[COL_VALIDITY] = _("Val");
302

    
303
    clist = gtk_clist_new_with_titles (N_COL_TITLES, (char**)titles);
304
    gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
305
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_ALGO,      72);
306
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_KEYID,     76);
307
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME,     130);
308
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_EMAIL,    130);
309
    gtk_clist_set_column_width (GTK_CLIST(clist), COL_VALIDITY,  20);
310
    gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
311
    g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_NAME].button),
312
                      "clicked",
313
                      G_CALLBACK(sort_keys_name), sk);
314
    g_signal_connect (G_OBJECT(GTK_CLIST(clist)->column[COL_EMAIL].button),
315
                      "clicked",
316
                      G_CALLBACK(sort_keys_email), sk);
317

    
318
    hbox = gtk_hbox_new (FALSE, 8);
319
    gtk_box_pack_end (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
320

    
321
    gtkut_button_set_create (&bbox, 
322
                             &select_btn, _("Select"),
323
                             &cancel_btn, _("Cancel"),
324
                             &other_btn,  _("Other"));
325
    gtk_box_pack_end (GTK_BOX (hbox), bbox, FALSE, FALSE, 0);
326
    gtk_widget_grab_default (select_btn);
327

    
328
    g_signal_connect (G_OBJECT (select_btn), "clicked",
329
                      G_CALLBACK (select_btn_cb), sk);
330
    g_signal_connect (G_OBJECT(cancel_btn), "clicked",
331
                      G_CALLBACK (cancel_btn_cb), sk);
332
    g_signal_connect (G_OBJECT (other_btn), "clicked",
333
                      G_CALLBACK (other_btn_cb), sk);
334

    
335
    vbox2 = gtk_vbox_new (FALSE, 4);
336
    gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
337

    
338
    gtk_widget_show_all (window);
339

    
340
    sk->window = window;
341
    sk->toplabel = GTK_LABEL (label);
342
    sk->clist  = GTK_CLIST (clist);
343
}
344

    
345

    
346
static void
347
open_dialog (struct select_keys_s *sk)
348
{
349
    if (!sk->window)
350
        create_dialog (sk);
351
    manage_window_set_transient (GTK_WINDOW (sk->window));
352
    sk->okay = 0;
353
    sk->sort_column = N_COL_TITLES; /* use an invalid value */
354
    sk->sort_type = GTK_SORT_ASCENDING;
355
    gtk_widget_show (sk->window);
356
}
357

    
358

    
359
static void
360
close_dialog (struct select_keys_s *sk)
361
{
362
    g_return_if_fail (sk);
363
    gtk_widget_destroy (sk->window);
364
    sk->window = NULL;
365
}
366

    
367

    
368
static gint
369
delete_event_cb (GtkWidget *widget, GdkEventAny *event, gpointer data)
370
{
371
    struct select_keys_s *sk = data;
372

    
373
    sk->okay = 0;
374
    gtk_main_quit ();
375

    
376
    return TRUE;
377
}
378

    
379

    
380
static gboolean
381
key_pressed_cb (GtkWidget *widget, GdkEventKey *event, gpointer data)
382
{
383
    struct select_keys_s *sk = data;
384

    
385
    g_return_val_if_fail (sk, FALSE);
386
    if (event && event->keyval == GDK_Escape) {
387
        sk->okay = 0;
388
        gtk_main_quit ();
389
    }
390
    return FALSE;
391
}
392

    
393

    
394
static void 
395
select_btn_cb (GtkWidget *widget, gpointer data)
396
{
397
    struct select_keys_s *sk = data;
398
    int row;
399
    GpgmeKey key;
400

    
401
    g_return_if_fail (sk);
402
    if (!sk->clist->selection) {
403
        debug_print ("** nothing selected");
404
        return;
405
    }
406
    row = GPOINTER_TO_INT(sk->clist->selection->data);
407
    key = gtk_clist_get_row_data(sk->clist, row);
408
    if (key) {
409
        const char *s = gpgme_key_get_string_attr (key,
410
                                                   GPGME_ATTR_FPR,
411
                                                   NULL, 0 );
412
        if ( gpgme_key_get_ulong_attr (key, GPGME_ATTR_VALIDITY, NULL, 0 )
413
             < GPGME_VALIDITY_FULL ) {
414
            debug_print ("** FIXME: we are faking the trust calculation");
415
        }
416
        if (!gpgme_recipients_add_name_with_validity (sk->rset, s,
417
                                                      GPGME_VALIDITY_FULL) ) {
418
            sk->okay = 1;
419
            gtk_main_quit ();
420
        }
421
    }
422
}
423

    
424

    
425
static void 
426
cancel_btn_cb (GtkWidget *widget, gpointer data)
427
{
428
    struct select_keys_s *sk = data;
429

    
430
    g_return_if_fail (sk);
431
    sk->okay = 0;
432
    if (sk->select_ctx)
433
        gpgme_cancel (sk->select_ctx);
434
    gtk_main_quit ();
435
}
436

    
437

    
438
static void
439
other_btn_cb (GtkWidget *widget, gpointer data)
440
{
441
    struct select_keys_s *sk = data;
442
    char *uid;
443

    
444
    g_return_if_fail (sk);
445
    uid = input_dialog ( _("Add key"),
446
                         _("Enter another user or key ID:"),
447
                         NULL );
448
    if (!uid)
449
        return;
450
    fill_clist (sk, uid);
451
    update_progress (sk, 0, sk->pattern);
452
    g_free (uid);
453
}
454

    
455

    
456
static gint 
457
cmp_attr (gconstpointer pa, gconstpointer pb, GpgmeAttr attr)
458
{
459
    GpgmeKey a = ((GtkCListRow *)pa)->data;
460
    GpgmeKey b = ((GtkCListRow *)pb)->data;
461
    const char *sa, *sb;
462
    
463
    sa = a? gpgme_key_get_string_attr (a, attr, NULL, 0 ) : NULL;
464
    sb = b? gpgme_key_get_string_attr (b, attr, NULL, 0 ) : NULL;
465
    if (!sa)
466
        return !!sb;
467
    if (!sb)
468
        return -1;
469
    return strcasecmp(sa, sb);
470
}
471

    
472
static gint 
473
cmp_name (GtkCList *clist, gconstpointer pa, gconstpointer pb)
474
{
475
    return cmp_attr (pa, pb, GPGME_ATTR_NAME);
476
}
477

    
478
static gint 
479
cmp_email (GtkCList *clist, gconstpointer pa, gconstpointer pb)
480
{
481
    return cmp_attr (pa, pb, GPGME_ATTR_EMAIL);
482
}
483

    
484
static void
485
sort_keys ( struct select_keys_s *sk, enum col_titles column)
486
{
487
    GtkCList *clist = sk->clist;
488

    
489
    switch (column) {
490
      case COL_NAME:
491
        gtk_clist_set_compare_func (clist, cmp_name);
492
        break;
493
      case COL_EMAIL:
494
        gtk_clist_set_compare_func (clist, cmp_email);
495
        break;
496
      default:
497
        return;
498
    }
499

    
500
    /* column clicked again: toggle as-/decending */
501
    if ( sk->sort_column == column) {
502
        sk->sort_type = sk->sort_type == GTK_SORT_ASCENDING ?
503
                        GTK_SORT_DESCENDING : GTK_SORT_ASCENDING;
504
    }
505
    else
506
        sk->sort_type = GTK_SORT_ASCENDING;
507

    
508
    sk->sort_column = column;
509
    gtk_clist_set_sort_type (clist, sk->sort_type);
510
    gtk_clist_sort (clist);
511
}
512

    
513
static void
514
sort_keys_name (GtkWidget *widget, gpointer data)
515
{
516
    sort_keys ((struct select_keys_s*)data, COL_NAME);
517
}
518

    
519
static void
520
sort_keys_email (GtkWidget *widget, gpointer data)
521
{
522
    sort_keys ((struct select_keys_s*)data, COL_EMAIL);
523
}
524

    
525
#endif /*USE_GPGME*/