1 /* recipientdlg.c - A dialog to select a mail recipient.
2 Copyright (C) 2008 g10 Code GmbH.
3
4 This file is part of GPA
5
6 GPA is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 3 of the License, or
9 (at your option) any later version.
10
11 GPA is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
14 License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <http://www.gnu.org/licenses/>. */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <gtk/gtk.h>
24
25 #include "gpa.h"
26 #include "i18n.h"
27
28 #include "gtktools.h"
29 #include "selectkeydlg.h"
30 #include "recipientdlg.h"
31
32
33 struct _RecipientDlg
34 {
35 GtkDialog parent;
36
37 GtkWidget *clist_keys;
38 GtkWidget *statushint;
39 GtkWidget *radio_pgp;
40 GtkWidget *radio_x509;
41 GtkWidget *radio_auto;
42
43 GtkWidget *popup_menu;
44
45 /* Flag to disable updates of the status hint. This is actual a
46 counter with updates only allowed if it is zero. */
47 int freeze_update_statushint;
48
49 /* Flag to disable any key selection. This is used while a key
50 selection is active. Implemented as a counter. */
51 int freeze_key_selection;
52
53 /* Set if this dialog has usable key to be passed back to the
54 caller. You need to call update_statushint to set it. */
55 int usable;
56
57 /* The selected protocol. This is also set by update_statushint. */
58 gpgme_protocol_t selected_protocol;
59 };
60
61
62 struct _RecipientDlgClass
63 {
64 GtkDialogClass parent_class;
65
66 };
67
68
69 /* The parent class. */
70 static GObjectClass *parent_class;
71
72
73 /* Indentifiers for our properties. */
74 enum
75 {
76 PROP_0,
77 PROP_WINDOW,
78 PROP_FORCE_ARMOR
79 };
80
81
82 /* For performance reasons we truncate the listing of ambiguous keys
83 at a reasonable value. */
84 #define TRUNCATE_KEYSEARCH_AT 40
85
86
87 /* An object to keep information about keys. */
88 struct keyinfo_s
89 {
90 /* An array with associated key(s) or NULL if none found/selected. */
91 gpgme_key_t *keys;
92
93 /* The allocated size of the KEYS array. This includes the
94 terminating NULL entry. */
95 unsigned int dimof_keys;
96
97 /* If set, indicates that the KEYS array has been truncated. */
98 int truncated:1;
99 };
100
101
102 /* Management information for each recipient. This data is used per
103 recipient. */
104 struct userdata_s
105 {
106 /* The recipient's address. */
107 char *mailbox;
108
109 /* Information about PGP keys. */
110 struct keyinfo_s pgp;
111
112 /* Information about X.509 keys. */
113 struct keyinfo_s x509;
114
115 /* If the user has set this field, no encryption key will be
116 required for the recipient. */
117 int ignore_recipient;
118
119 };
120
121
122 /* Identifiers for the columns of the RECPLIST. */
123 enum
124 {
125 RECPLIST_MAILBOX, /* The rfc822 mailbox to whom a key needs to
126 be associated. */
127 RECPLIST_HAS_PGP, /* A PGP certificate is available. */
128 RECPLIST_HAS_X509, /* An X.509 certificate is available. */
129 RECPLIST_KEYID, /* The key ID of the associated key. */
130
131 RECPLIST_USERDATA, /* Pointer to management information (struct
132 userdata_s *). */
133
134 RECPLIST_N_COLUMNS
135 };
136
137
138
139
140
141 /* Create the main list of this dialog. */
142 static GtkWidget *
recplist_window_new(void)143 recplist_window_new (void)
144 {
145 GtkListStore *store;
146 GtkWidget *list;
147 GtkCellRenderer *renderer;
148 GtkTreeViewColumn *column;
149
150 /* Create a model and associate a view. */
151 store = gtk_list_store_new (RECPLIST_N_COLUMNS,
152 G_TYPE_STRING,
153 G_TYPE_BOOLEAN,
154 G_TYPE_BOOLEAN,
155 G_TYPE_STRING,
156 G_TYPE_POINTER);
157
158 list = gtk_tree_view_new_with_model (GTK_TREE_MODEL (store));
159 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (list), TRUE);
160
161 /* Define the columns. */
162 renderer = gtk_cell_renderer_text_new ();
163 column = gtk_tree_view_column_new_with_attributes
164 (NULL, renderer, "markup", RECPLIST_MAILBOX, NULL);
165 gpa_set_column_title (column, _("Recipient"),
166 _("Shows the recipients of the message."
167 " A key needs to be assigned to each recipient."));
168 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
169
170 renderer = gtk_cell_renderer_toggle_new ();
171 column = gtk_tree_view_column_new_with_attributes
172 (NULL, renderer, "active", RECPLIST_HAS_PGP, NULL);
173 gpa_set_column_title (column, "PGP",
174 _("Checked if at least one matching"
175 " OpenPGP certificate has been found."));
176 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
177
178 renderer = gtk_cell_renderer_toggle_new ();
179 column = gtk_tree_view_column_new_with_attributes
180 (NULL, renderer, "active", RECPLIST_HAS_X509, NULL);
181 gpa_set_column_title (column, "X.509",
182 _("Checked if at least one matching"
183 " X.509 certificate for use with S/MIME"
184 " has been found."));
185 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
186
187 renderer = gtk_cell_renderer_text_new ();
188 column = gtk_tree_view_column_new_with_attributes
189 (NULL, renderer, "text", RECPLIST_KEYID, NULL);
190 gpa_set_column_title (column,
191 _("Key ID"),
192 _("Shows the key ID of the selected key or"
193 " an indication that a key needs to be selected."));
194 gtk_tree_view_append_column (GTK_TREE_VIEW (list), column);
195
196
197 return list;
198 }
199
200
201 /* Get an interator for the selected row. Store it in ITER and
202 returns the mdeol. if nothing is selected NULL is return and ITER
203 is not valid. */
204 static GtkTreeModel *
get_selected_row(RecipientDlg * dialog,GtkTreeIter * iter)205 get_selected_row (RecipientDlg *dialog, GtkTreeIter *iter)
206 {
207 GtkTreeSelection *selection;
208 GtkTreeModel *model;
209
210 g_return_val_if_fail (dialog, NULL);
211
212 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (dialog->clist_keys));
213 if (gtk_tree_selection_count_selected_rows (selection) == 1
214 && gtk_tree_selection_get_selected (selection, &model, iter))
215 return model;
216 return NULL;
217 }
218
219
220 /* Compute and display a new help text for the statushint. */
221 static void
update_statushint(RecipientDlg * dialog)222 update_statushint (RecipientDlg *dialog)
223 {
224 gpgme_protocol_t req_protocol, sel_protocol;
225 GtkTreeModel *model;
226 GtkTreeIter iter;
227 int missing_keys = 0;
228 int ambiguous_pgp_keys = 0;
229 int ambiguous_x509_keys = 0;
230 int n_pgp_keys = 0;
231 int n_x509_keys = 0;
232 int n_keys = 0;
233 const char *hint;
234 int okay = 0;
235
236 if (dialog->freeze_update_statushint)
237 return;
238
239 model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys));
240
241 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp)))
242 req_protocol = GPGME_PROTOCOL_OpenPGP;
243 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
244 (dialog->radio_x509)))
245 req_protocol = GPGME_PROTOCOL_CMS;
246 else
247 req_protocol = GPGME_PROTOCOL_UNKNOWN;
248
249 sel_protocol = GPGME_PROTOCOL_UNKNOWN;
250
251 if (gtk_tree_model_get_iter_first (model, &iter))
252 {
253 do
254 {
255 gboolean has_pgp, has_x509;
256 struct userdata_s *info;
257
258 gtk_tree_model_get (model, &iter,
259 RECPLIST_HAS_PGP, &has_pgp,
260 RECPLIST_HAS_X509, &has_x509,
261 RECPLIST_USERDATA, &info,
262 -1);
263 if (!info)
264 missing_keys++; /* Oops */
265 else if (info->ignore_recipient)
266 ;
267 else if (!info->pgp.keys && !info->x509.keys)
268 missing_keys++;
269 else if ((req_protocol == GPGME_PROTOCOL_OpenPGP && !has_pgp)
270 ||(req_protocol == GPGME_PROTOCOL_CMS && !has_x509))
271 ; /* Not of the requested protocol. */
272 else
273 {
274 n_keys++;
275 if (info->pgp.keys && info->pgp.keys[0])
276 {
277 n_pgp_keys++;
278 if (info->pgp.keys[1])
279 ambiguous_pgp_keys++;
280 }
281 if (info->x509.keys && info->x509.keys[0])
282 {
283 n_x509_keys++;
284 if (info->x509.keys[1])
285 ambiguous_x509_keys++;
286 }
287 }
288 }
289 while (gtk_tree_model_iter_next (model, &iter));
290 }
291
292 if (req_protocol == GPGME_PROTOCOL_UNKNOWN)
293 {
294 /* We select the protocol with the most available keys,
295 preferring PGP. */
296 if (n_pgp_keys >= n_x509_keys)
297 sel_protocol = GPGME_PROTOCOL_OpenPGP;
298 else if (n_x509_keys)
299 sel_protocol = GPGME_PROTOCOL_CMS;
300 }
301 else
302 sel_protocol = req_protocol;
303
304
305 if (missing_keys)
306 hint = _("You need to select a key for each recipient.\n"
307 "To select a key right-click on the respective line.");
308 else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP
309 && ambiguous_pgp_keys)
310 || (sel_protocol == GPGME_PROTOCOL_CMS
311 && ambiguous_x509_keys)
312 || (sel_protocol == GPGME_PROTOCOL_UNKNOWN
313 && (ambiguous_pgp_keys || ambiguous_x509_keys )))
314 hint = _("You need to select exactly one key for each recipient.\n"
315 "To select a key right-click on the respective line.");
316 else if ((sel_protocol == GPGME_PROTOCOL_OpenPGP
317 && n_keys != n_pgp_keys)
318 || (sel_protocol == GPGME_PROTOCOL_CMS
319 && n_keys != n_x509_keys)
320 || (sel_protocol == GPGME_PROTOCOL_UNKNOWN))
321 hint = _("Although you selected keys for all recipients "
322 "a common encryption protocol can't be used. "
323 "Please decide on one protocol by clicking one "
324 "of the above radio buttons.");
325 else if (n_pgp_keys && sel_protocol == GPGME_PROTOCOL_OpenPGP)
326 {
327 hint = _("Using OpenPGP for encryption.");
328 okay = 1;
329 }
330 else if (n_x509_keys && sel_protocol == GPGME_PROTOCOL_CMS)
331 {
332 hint = _("Using S/MIME for encryption.");
333 okay = 1;
334 }
335 else
336 hint = _("No recipients - encryption is not possible");
337
338 gtk_label_set_text (GTK_LABEL (dialog->statushint), hint);
339 gtk_label_set_line_wrap (GTK_LABEL (dialog->statushint), TRUE);
340
341 dialog->usable = okay;
342 dialog->selected_protocol = sel_protocol;
343 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK,
344 okay);
345 }
346
347
348 /* Add KEY to the keyarray of KEYINFO. Ownership of KEY is moved to
349 KEYARRAY. Returns the number of keys in KEYINFO. */
350 static unsigned int
append_key_to_keyinfo(struct keyinfo_s * keyinfo,gpgme_key_t key)351 append_key_to_keyinfo (struct keyinfo_s *keyinfo, gpgme_key_t key)
352 {
353 unsigned int nkeys;
354
355 if (!keyinfo->keys)
356 {
357 keyinfo->dimof_keys = 5; /* Space for 4 keys. */
358 keyinfo->keys = g_new (gpgme_key_t, keyinfo->dimof_keys);
359 keyinfo->keys[0] = NULL;
360 }
361 for (nkeys=0; keyinfo->keys[nkeys]; nkeys++)
362 ;
363 /* Note that we silently skip a KEY of NULL because we can't store
364 a NULL in the array. */
365 if (key)
366 {
367 if (nkeys+1 >= keyinfo->dimof_keys)
368 {
369 keyinfo->dimof_keys += 10;
370 keyinfo->keys = g_renew (gpgme_key_t, keyinfo->keys,
371 keyinfo->dimof_keys);
372 }
373 keyinfo->keys[nkeys++] = key;
374 keyinfo->keys[nkeys] = NULL;
375 }
376 return nkeys;
377 }
378
379
380 /* Clear the content of a keyinfo object. */
381 static void
clear_keyinfo(struct keyinfo_s * keyinfo)382 clear_keyinfo (struct keyinfo_s *keyinfo)
383 {
384 unsigned int nkeys;
385
386 if (keyinfo)
387 {
388 if (keyinfo->keys)
389 {
390 for (nkeys=0; keyinfo->keys[nkeys]; nkeys++)
391 gpgme_key_unref (keyinfo->keys[nkeys]);
392 g_free (keyinfo->keys);
393 keyinfo->keys = NULL;
394 }
395 keyinfo->dimof_keys = 0;
396 keyinfo->truncated = 0;
397 }
398 }
399
400
401 /* Update the row in the list described by by STORE and ITER. The new
402 data shall be taken from INFO. */
403 static void
update_recplist_row(GtkListStore * store,GtkTreeIter * iter,struct userdata_s * info)404 update_recplist_row (GtkListStore *store, GtkTreeIter *iter,
405 struct userdata_s *info)
406 {
407 char *infostr = NULL;
408 char *oldinfostr = NULL;
409 int any_pgp = 0, any_x509 = 0;
410 gpgme_key_t key;
411 char *mailbox;
412 char *oldmailbox;
413
414 if (info->pgp.keys && info->pgp.keys[0])
415 any_pgp = 1;
416 if (info->x509.keys && info->x509.keys[0])
417 any_x509 = 1;
418
419 if (info->ignore_recipient)
420 infostr = NULL;
421 else if (any_pgp && any_x509 && info->pgp.keys[1] && info->x509.keys[1])
422 infostr = g_strdup (_("[Ambiguous keys. Right-click to select]"));
423 else if (any_pgp && info->pgp.keys[1])
424 infostr = g_strdup (_("[Ambiguous PGP key. Right-click to select]"));
425 else if (any_x509 && info->x509.keys[1])
426 infostr = g_strdup (_("[Ambiguous X.509 key. Right-click to select]"));
427 else if (any_pgp && !info->pgp.keys[1])
428 {
429 /* Exactly one key found. */
430 key = info->pgp.keys[0];
431 infostr = gpa_gpgme_key_get_userid (key->uids);
432 }
433 else if (any_x509 && !info->x509.keys[1])
434 {
435 key = info->x509.keys[0];
436 infostr = gpa_gpgme_key_get_userid (key->uids);
437 }
438 else
439 infostr = g_strdup (_("[Right-click to select]"));
440
441
442 mailbox = g_markup_printf_escaped ("<span strikethrough='%s'>%s</span>",
443 info->ignore_recipient? "true":"false",
444 info->mailbox);
445
446 g_print (" mbox=`%s' fmt=`%s'\n", info->mailbox, mailbox);
447 gtk_tree_model_get (GTK_TREE_MODEL (store), iter,
448 RECPLIST_MAILBOX, &oldmailbox,
449 RECPLIST_KEYID, &oldinfostr,
450 -1);
451 gtk_list_store_set (store, iter,
452 RECPLIST_MAILBOX, mailbox,
453 RECPLIST_HAS_PGP, any_pgp,
454 RECPLIST_HAS_X509, any_x509,
455 RECPLIST_KEYID, infostr,
456 -1);
457 g_free (oldmailbox);
458 g_free (mailbox);
459 g_free (infostr);
460 g_free (oldinfostr);
461 }
462
463
464 /* Parse one recipient, this is the working horse of parse_recipeints. */
465 static void
parse_one_recipient(gpgme_ctx_t ctx,GtkListStore * store,GtkTreeIter * iter,struct userdata_s * info)466 parse_one_recipient (gpgme_ctx_t ctx, GtkListStore *store, GtkTreeIter *iter,
467 struct userdata_s *info)
468 {
469 static int have_locate = -1;
470 gpgme_key_t key = NULL;
471 gpgme_keylist_mode_t mode;
472
473 if (have_locate == -1)
474 have_locate = is_gpg_version_at_least ("2.0.10");
475
476 g_return_if_fail (info);
477
478 clear_keyinfo (&info->pgp);
479 gpgme_set_protocol (ctx, GPGME_PROTOCOL_OpenPGP);
480 mode = gpgme_get_keylist_mode (ctx);
481 if (have_locate)
482 gpgme_set_keylist_mode (ctx, (mode | (GPGME_KEYLIST_MODE_LOCAL
483 | GPGME_KEYLIST_MODE_EXTERN)));
484 if (!gpgme_op_keylist_start (ctx, info->mailbox, 0))
485 {
486 while (!gpgme_op_keylist_next (ctx, &key))
487 {
488 if (key->revoked || key->disabled || key->expired
489 || !key->can_encrypt)
490 gpgme_key_unref (key);
491 else if (append_key_to_keyinfo (&info->pgp, key)
492 >= TRUNCATE_KEYSEARCH_AT)
493 {
494 /* Note that the truncation flag is not 100% correct. In
495 case the next iteration would not yield a new key we
496 have not actually truncated the search. */
497 info->pgp.truncated = 1;
498 break;
499 }
500 }
501 }
502 gpgme_op_keylist_end (ctx);
503 gpgme_set_keylist_mode (ctx, mode);
504
505 clear_keyinfo (&info->x509);
506 gpgme_set_protocol (ctx, GPGME_PROTOCOL_CMS);
507 if (!gpgme_op_keylist_start (ctx, info->mailbox, 0))
508 {
509 while (!gpgme_op_keylist_next (ctx, &key))
510 {
511 if (key->revoked || key->disabled || key->expired
512 || !key->can_encrypt)
513 gpgme_key_unref (key);
514 else if (append_key_to_keyinfo (&info->x509,key)
515 >= TRUNCATE_KEYSEARCH_AT)
516 {
517 info->x509.truncated = 1;
518 break;
519 }
520 }
521 }
522 gpgme_op_keylist_end (ctx);
523
524 update_recplist_row (store, iter, info);
525 }
526
527
528 /* Parse the list of recipients, find possible keys and update the
529 store. */
530 static void
parse_recipients(GtkListStore * store)531 parse_recipients (GtkListStore *store)
532 {
533 GtkTreeModel *model;
534 GtkTreeIter iter;
535 gpg_error_t err;
536 gpgme_ctx_t ctx;
537
538 err = gpgme_new (&ctx);
539 if (err)
540 gpa_gpgme_error (err);
541
542
543 model = GTK_TREE_MODEL (store);
544 /* Walk through the list, reading each row. */
545
546 if (gtk_tree_model_get_iter_first (model, &iter))
547 do
548 {
549 struct userdata_s *info;
550
551 gtk_tree_model_get (model, &iter,
552 RECPLIST_USERDATA, &info,
553 -1);
554
555 /* Do something with the data */
556 /*g_print ("parsing mailbox `%s'\n", info? info->mailbox:"(null)");*/
557 parse_one_recipient (ctx, store, &iter,info);
558 }
559 while (gtk_tree_model_iter_next (model, &iter));
560
561 gpgme_release (ctx);
562 }
563
564
565 /* Callback for the row-activated signal. */
566 static void
recplist_row_activated_cb(GtkTreeView * tree_view,GtkTreePath * path,GtkTreeViewColumn * column,gpointer user_data)567 recplist_row_activated_cb (GtkTreeView *tree_view,
568 GtkTreePath *path,
569 GtkTreeViewColumn *column,
570 gpointer user_data)
571 {
572 /*RecipientDlg *dialog = user_data;*/
573 GtkTreeIter iter;
574 GtkTreeModel *model;
575 char *mailbox;
576
577 model = gtk_tree_view_get_model (tree_view);
578 if (!gtk_tree_model_get_iter (model, &iter, path))
579 return;
580
581 gtk_tree_model_get (model, &iter,
582 RECPLIST_MAILBOX, &mailbox,
583 -1);
584 g_free (mailbox);
585 }
586
587
588 static void
recplist_row_changed_cb(GtkTreeModel * model,GtkTreePath * path,GtkTreeIter * changediter,gpointer user_data)589 recplist_row_changed_cb (GtkTreeModel *model, GtkTreePath *path,
590 GtkTreeIter *changediter, gpointer user_data)
591 {
592 RecipientDlg *dialog = user_data;
593
594 g_return_if_fail (dialog);
595 update_statushint (dialog);
596 }
597
598
599 static void
rbutton_toggled_cb(GtkToggleButton * button,gpointer user_data)600 rbutton_toggled_cb (GtkToggleButton *button, gpointer user_data)
601 {
602 RecipientDlg *dialog = user_data;
603 GtkTreeViewColumn *column;
604 int pgp = FALSE;
605 int x509 = FALSE;
606
607 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (dialog->radio_pgp)))
608 {
609 pgp = TRUE;
610 }
611 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
612 (dialog->radio_x509)))
613 {
614 x509 = TRUE;
615 }
616 else
617 {
618 pgp = TRUE;
619 x509 = TRUE;
620 }
621
622 column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys),
623 RECPLIST_HAS_PGP);
624 gtk_tree_view_column_set_visible (column, pgp);
625 column = gtk_tree_view_get_column (GTK_TREE_VIEW (dialog->clist_keys),
626 RECPLIST_HAS_X509);
627 gtk_tree_view_column_set_visible (column, x509);
628 update_statushint (dialog);
629 }
630
631
632
633 /* The select key selection has returned. */
634 static void
select_key_response_cb(SelectKeyDlg * seldlg,int response,void * user_data)635 select_key_response_cb (SelectKeyDlg *seldlg, int response, void *user_data)
636 {
637 RecipientDlg *dialog = user_data;
638 gpgme_key_t key;
639
640 if (response != GTK_RESPONSE_OK)
641 {
642 /* The dialog was canceled */
643 gtk_widget_destroy (GTK_WIDGET (seldlg));
644 dialog->freeze_key_selection--;
645 return;
646 }
647
648 key = select_key_dlg_get_key (seldlg);
649 if (key)
650 {
651 GtkTreeModel *model;
652 GtkTreeIter iter;
653 struct userdata_s *info = NULL;
654 char *uidstr = gpa_gpgme_key_get_userid (key->uids);
655
656 g_free (uidstr);
657 if ((model = get_selected_row (dialog, &iter)))
658 {
659 gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1);
660 if (info)
661 {
662 if (key->protocol == GPGME_PROTOCOL_OpenPGP)
663 {
664 clear_keyinfo (&info->pgp);
665 gpgme_key_ref (key);
666 append_key_to_keyinfo (&info->pgp, key);
667 }
668 else if (key->protocol == GPGME_PROTOCOL_CMS)
669 {
670 clear_keyinfo (&info->x509);
671 gpgme_key_ref (key);
672 append_key_to_keyinfo (&info->x509, key);
673 }
674 update_recplist_row (GTK_LIST_STORE (model), &iter, info);
675 update_statushint (dialog);
676 }
677 }
678 gpgme_key_unref (key);
679 }
680
681 gtk_widget_destroy (GTK_WIDGET (seldlg));
682 dialog->freeze_key_selection--;
683 }
684
685
686
687 static void
do_select_key(RecipientDlg * dialog,gpgme_protocol_t protocol)688 do_select_key (RecipientDlg *dialog, gpgme_protocol_t protocol)
689 {
690 GtkTreeModel *model;
691 GtkTreeIter iter;
692 struct userdata_s *info = NULL;
693 SelectKeyDlg *seldlg;
694
695 if ((model = get_selected_row (dialog, &iter)))
696 {
697 gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1);
698 if (info)
699 {
700 gpgme_key_t *keys;
701
702 if (protocol == GPGME_PROTOCOL_OpenPGP)
703 keys = info->pgp.keys;
704 else if (protocol == GPGME_PROTOCOL_CMS)
705 keys = info->x509.keys;
706 else
707 keys = NULL;
708
709 seldlg = select_key_dlg_new_with_keys (GTK_WIDGET (dialog),
710 protocol,
711 keys,
712 info->mailbox);
713 g_signal_connect (G_OBJECT (seldlg), "response",
714 G_CALLBACK (select_key_response_cb), dialog);
715 gtk_widget_show_all (GTK_WIDGET (seldlg));
716 }
717 }
718 else
719 dialog->freeze_key_selection--;
720 }
721
722
723 static void
recplist_popup_pgp(GtkAction * action,RecipientDlg * dialog)724 recplist_popup_pgp (GtkAction *action, RecipientDlg *dialog)
725 {
726 do_select_key (dialog, GPGME_PROTOCOL_OpenPGP);
727 }
728
729
730 static void
recplist_popup_x509(GtkAction * action,RecipientDlg * dialog)731 recplist_popup_x509 (GtkAction *action, RecipientDlg *dialog)
732 {
733 do_select_key (dialog, GPGME_PROTOCOL_CMS);
734 }
735
736
737 static void
recplist_popup_ignore(GtkAction * action,RecipientDlg * dialog)738 recplist_popup_ignore (GtkAction *action, RecipientDlg *dialog)
739 {
740 GtkTreeModel *model;
741 GtkTreeIter iter;
742 struct userdata_s *info;
743
744 if ((model = get_selected_row (dialog, &iter)))
745 {
746 gtk_tree_model_get (model, &iter, RECPLIST_USERDATA, &info, -1);
747 info->ignore_recipient = !info->ignore_recipient;
748 update_recplist_row (GTK_LIST_STORE (model), &iter, info);
749 update_statushint (dialog);
750 }
751 dialog->freeze_key_selection--;
752 }
753
754
755 /* Left Click on the list auto selects an action. */
756 static void
recplist_default_action(RecipientDlg * dialog)757 recplist_default_action (RecipientDlg *dialog)
758 {
759
760 dialog->freeze_key_selection--;
761 }
762
763
764 static gint
recplist_display_popup_menu(RecipientDlg * dialog,GdkEvent * event,GtkListStore * list)765 recplist_display_popup_menu (RecipientDlg *dialog, GdkEvent *event,
766 GtkListStore *list)
767 {
768 GtkMenu *menu;
769 GdkEventButton *event_button;
770
771 g_return_val_if_fail (dialog, FALSE);
772 g_return_val_if_fail (event, FALSE);
773
774 menu = GTK_MENU (dialog->popup_menu);
775
776 if (event->type == GDK_BUTTON_PRESS)
777 {
778 event_button = (GdkEventButton *) event;
779 if (event_button->button == 1
780 || event_button->button == 3)
781 {
782 GtkTreeSelection *selection;
783 GtkTreePath *path;
784 GtkTreeIter iter;
785
786 selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (list));
787 /* Make sure the clicked key is selected. */
788 if (!dialog->freeze_key_selection
789 && gtk_tree_view_get_path_at_pos (GTK_TREE_VIEW (list),
790 event_button->x,
791 event_button->y,
792 &path, NULL,
793 NULL, NULL))
794 {
795 dialog->freeze_key_selection++;
796 gtk_tree_model_get_iter (gtk_tree_view_get_model
797 (GTK_TREE_VIEW (list)), &iter, path);
798 if (!gtk_tree_selection_iter_is_selected (selection, &iter))
799 {
800 gtk_tree_selection_unselect_all (selection);
801 gtk_tree_selection_select_path (selection, path);
802 }
803 if (event_button->button == 1)
804 recplist_default_action (dialog);
805 else
806 gtk_menu_popup (menu, NULL, NULL, NULL, NULL,
807 event_button->button, event_button->time);
808 }
809 return TRUE;
810 }
811 }
812
813 return FALSE;
814 }
815
816
817 /* Create the popup menu for the recipient list. */
818 static GtkWidget *
recplist_popup_menu_new(GtkWidget * window,RecipientDlg * dialog)819 recplist_popup_menu_new (GtkWidget *window, RecipientDlg *dialog)
820 {
821 static const GtkActionEntry entries[] =
822 {
823 /* Toplevel. */
824 { "SelectPGPKey", NULL, N_("Select _PGP key..."), NULL,
825 NULL, G_CALLBACK (recplist_popup_pgp) },
826 { "SelectCMSKey", NULL, N_("Select _S\\/MIME key..."), NULL,
827 NULL, G_CALLBACK (recplist_popup_x509) },
828 { "ToggleIgnoreFlag", NULL, N_("Toggle _Ignore flag"), NULL,
829 NULL, G_CALLBACK (recplist_popup_ignore) }
830 };
831
832 static const char *ui_description =
833 "<ui>"
834 " <popup name='PopupMenu'>"
835 " <menuitem action='SelectPGPKey'/>"
836 " <menuitem action='SelectCMSKey'/>"
837 " <menuitem action='ToggleIgnoreFlag'/>"
838 " </popup>"
839 "</ui>";
840
841 GtkAccelGroup *accel_group;
842 GtkActionGroup *action_group;
843 GtkUIManager *ui_manager;
844 GError *error;
845
846 action_group = gtk_action_group_new ("MenuActions");
847 gtk_action_group_set_translation_domain (action_group, PACKAGE);
848 gtk_action_group_add_actions (action_group, entries, G_N_ELEMENTS (entries),
849 dialog);
850 ui_manager = gtk_ui_manager_new ();
851 gtk_ui_manager_insert_action_group (ui_manager, action_group, 0);
852 accel_group = gtk_ui_manager_get_accel_group (ui_manager);
853 gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
854 if (! gtk_ui_manager_add_ui_from_string (ui_manager, ui_description,
855 -1, &error))
856 {
857 g_message ("building clipboard menus failed: %s", error->message);
858 g_error_free (error);
859 exit (EXIT_FAILURE);
860 }
861
862 return gtk_ui_manager_get_widget (ui_manager, "/PopupMenu");
863 }
864
865
866
867 /************************************************************
868 ****************** Object Management ********************
869 ************************************************************/
870
871 static void
recipient_dlg_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)872 recipient_dlg_get_property (GObject *object, guint prop_id,
873 GValue *value, GParamSpec *pspec)
874 {
875 RecipientDlg *dialog = RECIPIENT_DLG (object);
876
877 switch (prop_id)
878 {
879 case PROP_WINDOW:
880 g_value_set_object (value,
881 gtk_window_get_transient_for (GTK_WINDOW (dialog)));
882 break;
883 default:
884 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
885 break;
886 }
887 }
888
889
890 static void
recipient_dlg_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)891 recipient_dlg_set_property (GObject *object, guint prop_id,
892 const GValue *value, GParamSpec *pspec)
893 {
894 RecipientDlg *dialog = RECIPIENT_DLG (object);
895
896 switch (prop_id)
897 {
898 case PROP_WINDOW:
899 gtk_window_set_transient_for (GTK_WINDOW (dialog),
900 g_value_get_object (value));
901 break;
902 default:
903 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
904 break;
905 }
906 }
907
908
909 static void
recipient_dlg_finalize(GObject * object)910 recipient_dlg_finalize (GObject *object)
911 {
912 /* Fixme: Release the store. */
913 G_OBJECT_CLASS (parent_class)->finalize (object);
914 }
915
916
917 static void
recipient_dlg_init(RecipientDlg * dialog)918 recipient_dlg_init (RecipientDlg *dialog)
919 {
920 }
921
922
923 static GObject*
recipient_dlg_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)924 recipient_dlg_constructor (GType type, guint n_construct_properties,
925 GObjectConstructParam *construct_properties)
926 {
927 GObject *object;
928 RecipientDlg *dialog;
929 GtkWidget *vbox;
930 GtkWidget *hbox;
931 GtkWidget *widget;
932 GtkWidget *labelKeys;
933 GtkWidget *scrollerKeys;
934 GtkWidget *clistKeys;
935
936 object = parent_class->constructor (type,
937 n_construct_properties,
938 construct_properties);
939 dialog = RECIPIENT_DLG (object);
940
941 gpa_window_set_title (GTK_WINDOW (dialog), _("Select keys for recipients"));
942 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
943 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
944 GTK_STOCK_OK, GTK_RESPONSE_OK,
945 NULL);
946 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
947 GTK_RESPONSE_OK,
948 GTK_RESPONSE_CANCEL,
949 -1);
950 gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
951 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog), GTK_RESPONSE_OK,
952 FALSE);
953 gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
954
955 vbox = GTK_DIALOG (dialog)->vbox;
956 gtk_container_set_border_width (GTK_CONTAINER (vbox), 5);
957
958 labelKeys = gtk_label_new_with_mnemonic (_("_Recipient list"));
959 gtk_misc_set_alignment (GTK_MISC (labelKeys), 0.0, 0.5);
960 gtk_box_pack_start (GTK_BOX (vbox), labelKeys, FALSE, FALSE, 0);
961
962 scrollerKeys = gtk_scrolled_window_new (NULL, NULL);
963 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrollerKeys),
964 GTK_POLICY_AUTOMATIC,
965 GTK_POLICY_AUTOMATIC);
966 gtk_box_pack_start (GTK_BOX (vbox), scrollerKeys, TRUE, TRUE, 0);
967 gtk_widget_set_size_request (scrollerKeys, 400, 200);
968
969 clistKeys = recplist_window_new ();
970 dialog->clist_keys = clistKeys;
971 gtk_container_add (GTK_CONTAINER (scrollerKeys), clistKeys);
972 gtk_label_set_mnemonic_widget (GTK_LABEL (labelKeys), clistKeys);
973
974
975 dialog->popup_menu = recplist_popup_menu_new (dialog->clist_keys, dialog);
976 g_signal_connect_swapped (GTK_OBJECT (dialog->clist_keys),
977 "button_press_event",
978 G_CALLBACK (recplist_display_popup_menu), dialog);
979
980 dialog->radio_pgp = gtk_radio_button_new_with_mnemonic
981 (NULL, _("Use _PGP"));
982 dialog->radio_x509 = gtk_radio_button_new_with_mnemonic
983 (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)),
984 _("Use _X.509"));
985 dialog->radio_auto = gtk_radio_button_new_with_mnemonic
986 (gtk_radio_button_get_group (GTK_RADIO_BUTTON (dialog->radio_pgp)),
987 _("_Auto selection"));
988 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (dialog->radio_auto), TRUE);
989
990 hbox = gtk_hbox_new (FALSE, 0);
991 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
992 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
993 gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_pgp, FALSE, FALSE, 0);
994 gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_x509, FALSE, FALSE, 0);
995 gtk_box_pack_start (GTK_BOX (hbox), dialog->radio_auto, FALSE, FALSE, 0);
996
997 widget = gtk_hseparator_new ();
998 gtk_box_pack_start (GTK_BOX (vbox), widget, FALSE, FALSE, 0);
999
1000 dialog->statushint = gtk_label_new (NULL);
1001 gtk_box_pack_start (GTK_BOX (vbox), dialog->statushint, FALSE, FALSE, 0);
1002
1003
1004 g_signal_connect (G_OBJECT (GTK_TREE_VIEW (dialog->clist_keys)),
1005 "row-activated",
1006 G_CALLBACK (recplist_row_activated_cb), dialog);
1007
1008 g_signal_connect (G_OBJECT (gtk_tree_view_get_model
1009 (GTK_TREE_VIEW (dialog->clist_keys))),
1010 "row-changed",
1011 G_CALLBACK (recplist_row_changed_cb), dialog);
1012
1013 g_signal_connect (G_OBJECT (dialog->radio_pgp),
1014 "toggled",
1015 G_CALLBACK (rbutton_toggled_cb), dialog);
1016 g_signal_connect (G_OBJECT (dialog->radio_x509),
1017 "toggled",
1018 G_CALLBACK (rbutton_toggled_cb), dialog);
1019 g_signal_connect (G_OBJECT (dialog->radio_auto),
1020 "toggled",
1021 G_CALLBACK (rbutton_toggled_cb), dialog);
1022
1023
1024 return object;
1025 }
1026
1027
1028 static void
recipient_dlg_class_init(RecipientDlgClass * klass)1029 recipient_dlg_class_init (RecipientDlgClass *klass)
1030 {
1031 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1032
1033 parent_class = g_type_class_peek_parent (klass);
1034
1035 object_class->constructor = recipient_dlg_constructor;
1036 object_class->finalize = recipient_dlg_finalize;
1037 object_class->set_property = recipient_dlg_set_property;
1038 object_class->get_property = recipient_dlg_get_property;
1039
1040 g_object_class_install_property
1041 (object_class,
1042 PROP_WINDOW,
1043 g_param_spec_object
1044 ("window", "Parent window",
1045 "Parent window", GTK_TYPE_WIDGET,
1046 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
1047
1048 }
1049
1050
1051 GType
recipient_dlg_get_type(void)1052 recipient_dlg_get_type (void)
1053 {
1054 static GType this_type;
1055
1056 if (!this_type)
1057 {
1058 static const GTypeInfo this_info =
1059 {
1060 sizeof (RecipientDlgClass),
1061 (GBaseInitFunc) NULL,
1062 (GBaseFinalizeFunc) NULL,
1063 (GClassInitFunc) recipient_dlg_class_init,
1064 NULL, /* class_finalize */
1065 NULL, /* class_data */
1066
1067 sizeof (RecipientDlg),
1068 0, /* n_preallocs */
1069 (GInstanceInitFunc) recipient_dlg_init,
1070 };
1071
1072 this_type = g_type_register_static (GTK_TYPE_DIALOG,
1073 "RecipientDlg",
1074 &this_info, 0);
1075 }
1076
1077 return this_type;
1078 }
1079
1080
1081
1082 /************************************************************
1083 ********************** Public API ************************
1084 ************************************************************/
1085
1086 RecipientDlg *
recipient_dlg_new(GtkWidget * parent)1087 recipient_dlg_new (GtkWidget *parent)
1088 {
1089 RecipientDlg *dialog;
1090
1091 dialog = g_object_new (RECIPIENT_DLG_TYPE,
1092 "window", parent,
1093 NULL);
1094
1095 return dialog;
1096 }
1097
1098
1099 /* Put RECIPIENTS into the list. PROTOCOL select the default protocol. */
1100 void
recipient_dlg_set_recipients(RecipientDlg * dialog,GSList * recipients,gpgme_protocol_t protocol)1101 recipient_dlg_set_recipients (RecipientDlg *dialog, GSList *recipients,
1102 gpgme_protocol_t protocol)
1103 {
1104 GtkListStore *store;
1105 GSList *recp;
1106 GtkTreeIter iter;
1107 const char *name;
1108 GtkWidget *widget;
1109
1110 g_return_if_fail (dialog);
1111
1112 dialog->freeze_update_statushint++;
1113
1114 if (protocol == GPGME_PROTOCOL_OpenPGP)
1115 widget = dialog->radio_pgp;
1116 else if (protocol == GPGME_PROTOCOL_CMS)
1117 widget = dialog->radio_x509;
1118 else
1119 widget = dialog->radio_auto;
1120 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), TRUE);
1121
1122 if (widget != dialog->radio_auto)
1123 {
1124 gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_pgp), FALSE);
1125 gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_x509), FALSE);
1126 gtk_widget_set_sensitive (GTK_WIDGET (dialog->radio_auto), FALSE);
1127 }
1128
1129 store = GTK_LIST_STORE (gtk_tree_view_get_model
1130 (GTK_TREE_VIEW (dialog->clist_keys)));
1131
1132 gtk_list_store_clear (store);
1133 for (recp = recipients; recp; recp = g_slist_next (recp))
1134 {
1135 name = recp->data;
1136 if (name && *name)
1137 {
1138 struct userdata_s *info = g_malloc0 (sizeof *info);
1139
1140 info->mailbox = g_strdup (name);
1141 gtk_list_store_append (store, &iter);
1142 gtk_list_store_set (store, &iter,
1143 RECPLIST_MAILBOX, g_strdup (""),
1144 RECPLIST_HAS_PGP, FALSE,
1145 RECPLIST_HAS_X509, FALSE,
1146 RECPLIST_KEYID, NULL,
1147 RECPLIST_USERDATA, info,
1148 -1);
1149 }
1150 }
1151
1152 parse_recipients (store);
1153 dialog->freeze_update_statushint--;
1154 update_statushint (dialog);
1155 }
1156
1157
1158 /* Return the selected keys as well as the selected protocol. */
1159 gpgme_key_t *
recipient_dlg_get_keys(RecipientDlg * dialog,gpgme_protocol_t * r_protocol)1160 recipient_dlg_get_keys (RecipientDlg *dialog, gpgme_protocol_t *r_protocol)
1161 {
1162 GtkTreeModel *model;
1163 GtkTreeIter iter;
1164 size_t idx, nkeys;
1165 gpgme_key_t key, *keyarray;
1166 gpgme_protocol_t protocol;
1167
1168 g_return_val_if_fail (dialog, NULL);
1169
1170 if (!dialog->usable)
1171 return NULL; /* No valid keys available. */
1172 protocol = dialog->selected_protocol;
1173
1174 model = gtk_tree_view_get_model (GTK_TREE_VIEW (dialog->clist_keys));
1175
1176 /* Count the number of possible keys. */
1177 nkeys = 0;
1178 if (gtk_tree_model_get_iter_first (model, &iter))
1179 {
1180 do
1181 nkeys++;
1182 while (gtk_tree_model_iter_next (model, &iter));
1183 }
1184 keyarray = g_new (gpgme_key_t, nkeys+1);
1185 idx = 0;
1186 if (gtk_tree_model_get_iter_first (model, &iter))
1187 {
1188 do
1189 {
1190 char *mailbox;
1191 struct userdata_s *info;
1192
1193 if (idx >= nkeys)
1194 {
1195 g_debug ("key list grew unexpectedly\n");
1196 break;
1197 }
1198
1199 gtk_tree_model_get (model, &iter,
1200 RECPLIST_MAILBOX, &mailbox,
1201 RECPLIST_USERDATA, &info,
1202 -1);
1203 if (info && !info->ignore_recipient)
1204 {
1205 if (protocol == GPGME_PROTOCOL_OpenPGP && info->pgp.keys)
1206 key = info->pgp.keys[0];
1207 else if (protocol == GPGME_PROTOCOL_CMS && info->x509.keys)
1208 key = info->x509.keys[0];
1209 else
1210 key = NULL;
1211 if (key)
1212 {
1213 gpgme_key_ref (key);
1214 keyarray[idx++] = key;
1215 }
1216 }
1217 g_free (mailbox);
1218 }
1219 while (gtk_tree_model_iter_next (model, &iter));
1220 }
1221 g_assert (idx < nkeys+1);
1222 keyarray[idx] = NULL;
1223
1224 if (r_protocol)
1225 *r_protocol = protocol;
1226
1227 return keyarray;
1228 }
1229
1230
1231
1232