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