1 /* settingsdlg.c - The GNU Privacy Assistant
2    Copyright (C) 2002, Miguel Coca
3    Copyright (C) 2008 g10 Code GmbH.
4 
5    This file is part of GPA.
6 
7    GPA is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    GPA is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, see <http://www.gnu.org/licenses/>.  */
19 
20 
21 /* Violation of GNOME standards: Cancel does not revert a previous
22    apply.  Except for the UI mode, we do not auto-apply or syntax
23    check after focus change.  The rationale for this is that:
24 
25      * gpgconf operations are expensive.
26 
27      * Some fields depend on each other and without a proper framework
28        for doing plausibility checks it is hard to get it right.
29 
30 */
31 
32 
33 #ifdef HAVE_CONFIG_H
34 # include <config.h>
35 #endif
36 
37 /* Required for the IP address check.  */
38 #ifdef HAVE_W32_SYSTEM
39 # include <windows.h>
40 #else /*!HAVE_W32_SYSTEM*/
41 # include <sys/socket.h>
42 # include <netinet/in.h>
43 # include <arpa/inet.h>
44 #endif /*!HAVE_W32_SYSTEM*/
45 
46 
47 #include "gpa.h"
48 #include "gpakeyselector.h"
49 #include "keyserver.h"
50 #include "gtktools.h"
51 #include "confdialog.h"
52 #include "gpg-stuff.h"
53 
54 #include "settingsdlg.h"
55 
56 /* Object and class definition.  */
57 struct _SettingsDlg
58 {
59   GtkDialog parent;
60 
61   gboolean modified;  /* True is there are unsaved changes.  */
62   gboolean gnupg21;   /* True if gnupg 2.1.0 or later is in use.  */
63 
64   /* Data for the user interface frame.  */
65   struct {
66     GtkWidget *frame;
67     GtkToggleButton *advanced_mode;
68     GtkToggleButton *show_advanced_options;
69   } ui;
70 
71   /* Data for the default key frame.  */
72   struct {
73     GtkWidget *frame;
74     GpaKeySelector *list;
75 
76     gpgme_key_t key;  /* The selected key or NULL if none selected.  */
77   } default_key;
78 
79   /* Data for the keyserver frame.  */
80   struct {
81     GtkWidget *frame;
82     GtkComboBox *combo;
83     char *url;         /* Malloced URL or NULL if none selected. */
84   } keyserver;
85 
86   /* Data for the auto-key-locate frame.  */
87   struct {
88     int enabled;  /* True if the AKL feature is enabled.  */
89 
90     GtkWidget *frame;
91     GtkComboBox *methods;
92     GtkWidget *addr_hbox;
93     GtkEntry  *addr_entry;
94 
95     int require_addr; /* A valid addr is required.  */
96 
97     int method_idx;   /* Selected method's index into the akl_table. */
98     char *ip_addr;    /* Malloced server address or NULL.  */
99   } akl;
100 
101 };
102 
103 struct _SettingsDlgClass
104 {
105   GtkDialogClass parent_class;
106 
107 };
108 
109 static GObjectClass *parent_class;
110 
111 /* We only want one instance of the settings dialog at a time.  Thus
112    we use this variable to keep track of a created instance.  */
113 static SettingsDlg *the_settings_dialog;
114 
115 
116 
117 /* Identifiers for our properties. */
118 enum
119   {
120     PROP_0
121   };
122 
123 
124 
125 /* A table with common and useful --auto-key-locate combinations. The
126    first entry "local" is a made-up which indicates that no
127    --auto-key-locate is active.  "Custom" ist used for all settings
128    which can't be mapped to this table and is also the last entry in
129    the table indicating the end of the table. */
130 static struct {
131   const char *list;
132   const char *text;
133 } akl_table[] = {
134   { "l",   N_("Local")},
135 #ifdef ENABLE_KEYSERVER_SUPPORT
136   { "lk",  N_("Local, Keyserver")},
137 #endif
138   { "lp",  N_("Local, PKA")},
139 #ifdef ENABLE_KEYSERVER_SUPPORT
140   { "lpk", N_("Local, PKA, Keyserver")},
141   { "lkp", N_("Local, Keyserver, PKA")},
142 #endif
143   { "lD",  N_("Local, kDNS")},
144 #ifdef ENABLE_KEYSERVER_SUPPORT
145   { "lDk", N_("Local, kDNS, Keyserver")},
146 #endif
147   { "np",  N_("PKA")},
148   { "nD",  N_("kDNS")},
149   { NULL,  N_("Custom")},
150 };
151 
152 
153 
154 static void update_modified (SettingsDlg *dialog, int is_modified);
155 
156 
157 
158 
159 /*
160    User interface section.
161  */
162 
163 /* Update what parts of the dialog are shown.  */
164 void
update_show_advanced_options(SettingsDlg * dialog)165 update_show_advanced_options (SettingsDlg *dialog)
166 {
167   GpaOptions *options = gpa_options_get_instance ();
168 
169   g_return_if_fail (IS_SETTINGS_DLG (dialog));
170 
171   if (gpa_options_get_show_advanced_options (options))
172     {
173 #ifdef ENABLE_KEYSERVER_SUPPORT
174       if (!dialog->gnupg21)
175         gtk_widget_show_all (dialog->keyserver.frame);
176 #endif /*ENABLE_KEYSERVER_SUPPORT*/
177       if (dialog->akl.enabled)
178         gtk_widget_show_all (dialog->akl.frame);
179     }
180   else
181     {
182 #ifdef ENABLE_KEYSERVER_SUPPORT
183       if (!dialog->gnupg21)
184         gtk_widget_hide_all (dialog->keyserver.frame);
185 #endif /*ENABLE_KEYSERVER_SUPPORT*/
186       if (dialog->akl.enabled)
187         gtk_widget_hide_all (dialog->akl.frame);
188     }
189 }
190 
191 
192 static void
advanced_mode_toggled(GtkToggleButton * button,gpointer user_data)193 advanced_mode_toggled (GtkToggleButton *button, gpointer user_data)
194 {
195   gpa_options_set_simplified_ui (gpa_options_get_instance (),
196                                  !gtk_toggle_button_get_active (button));
197 }
198 
199 static void
show_advanced_options_toggled(GtkToggleButton * button,gpointer user_data)200 show_advanced_options_toggled (GtkToggleButton *button, gpointer user_data)
201 {
202   SettingsDlg *dialog = user_data;
203 
204   gpa_options_set_show_advanced_options
205     (gpa_options_get_instance (), gtk_toggle_button_get_active (button));
206   update_show_advanced_options (dialog);
207 }
208 
209 
210 static GtkWidget *
user_interface_mode_frame(SettingsDlg * dialog)211 user_interface_mode_frame (SettingsDlg *dialog)
212 {
213   GtkWidget *frame;
214   GtkWidget *label;
215   GtkWidget *frame_vbox;
216   GtkWidget *button;
217 
218   /* Build UI.  */
219   frame = gtk_frame_new (NULL);
220   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
221   label = gtk_label_new (_("<b>User interface</b>"));
222   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
223   gtk_frame_set_label_widget (GTK_FRAME (frame), label);
224 
225   frame_vbox = gtk_vbox_new (FALSE, 0);
226   gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
227 
228   button = gtk_check_button_new_with_mnemonic (_("Use _advanced mode"));
229   gtk_container_add (GTK_CONTAINER (frame_vbox), button);
230   dialog->ui.advanced_mode = GTK_TOGGLE_BUTTON (button);
231   g_signal_connect (G_OBJECT (button), "toggled",
232                     G_CALLBACK (advanced_mode_toggled), dialog);
233 
234   button = gtk_check_button_new_with_mnemonic (_("Show advanced _options"));
235   gtk_container_add (GTK_CONTAINER (frame_vbox), button);
236   dialog->ui.show_advanced_options = GTK_TOGGLE_BUTTON (button);
237   g_signal_connect (G_OBJECT (button), "toggled",
238                     G_CALLBACK (show_advanced_options_toggled), dialog);
239 
240 
241   return frame;
242 }
243 
244 
245 
246 
247 /*
248    Default key section.
249  */
250 static void
key_selected_cb(SettingsDlg * dialog)251 key_selected_cb (SettingsDlg *dialog)
252 {
253   GList *selected;
254 
255   selected = gpa_key_selector_get_selected_keys (dialog->default_key.list);
256   gpgme_key_unref (dialog->default_key.key);
257   dialog->default_key.key = selected? selected->data : NULL;
258   if (dialog->default_key.key)
259     gpgme_key_ref (dialog->default_key.key);
260 
261   g_list_free (selected);
262   update_modified (dialog, 1);
263 }
264 
265 /* Returns NULL is the default key is valid.  On error the erroneous
266    widget is returned.  */
267 static GtkWidget *
check_default_key(SettingsDlg * dialog)268 check_default_key (SettingsDlg *dialog)
269 {
270   /* Make sure we have all required data.  */
271   key_selected_cb (dialog);
272 
273   /* We require that a default key has been selected.  If not, we ask
274      the user whether to continue. */
275   if (!dialog->default_key.key)
276     {
277       GtkWidget *msg;
278       int resp;
279 
280       msg = gtk_message_dialog_new (GTK_WINDOW(dialog),
281                                     GTK_DIALOG_DESTROY_WITH_PARENT,
282                                     GTK_MESSAGE_QUESTION,
283                                     GTK_BUTTONS_NONE,
284                                     "%s",
285             _("No default key has been selected.  This may lead to problems "
286               "when signing or encrypting.  For example you might later not "
287               "be able to read a mail written by you and encrypted to "
288               "someone else.\n\n"
289               "Please consider creating your own key and select it then."));
290       gtk_dialog_add_buttons (GTK_DIALOG (msg),
291                               _("Continue without a default key"),
292                               GTK_RESPONSE_YES,
293                               _("Let me select a default key"),
294                               GTK_RESPONSE_NO,
295                               NULL);
296       gtk_dialog_set_default_response (GTK_DIALOG (msg), GTK_RESPONSE_NO);
297       resp = gtk_dialog_run (GTK_DIALOG (msg));
298       gtk_widget_destroy (msg);
299       if (resp != GTK_RESPONSE_YES)
300         return GTK_WIDGET (dialog->default_key.list);
301     }
302 
303   return NULL;
304 }
305 
306 
307 static GtkWidget *
default_key_frame(SettingsDlg * dialog)308 default_key_frame (SettingsDlg *dialog)
309 {
310   GtkWidget *frame;
311   GtkWidget *label;
312   GtkWidget *list;
313   GtkWidget *scroller;
314 
315   /* Build UI.  */
316   frame = gtk_frame_new (NULL);
317   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
318   label = gtk_label_new_with_mnemonic (_("<b>Default _key</b>"));
319   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
320   gtk_frame_set_label_widget (GTK_FRAME (frame), label);
321 
322   list = gpa_key_selector_new (TRUE, FALSE);
323   dialog->default_key.list = GPA_KEY_SELECTOR (list);
324   gtk_tree_selection_set_mode (gtk_tree_view_get_selection
325 			       (GTK_TREE_VIEW (list)), GTK_SELECTION_SINGLE);
326   scroller = gtk_scrolled_window_new (NULL, NULL);
327   gtk_scrolled_window_set_policy  (GTK_SCROLLED_WINDOW (scroller),
328 				   GTK_POLICY_AUTOMATIC,
329 				   GTK_POLICY_AUTOMATIC);
330   gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scroller),
331 				       GTK_SHADOW_IN);
332 
333   gtk_widget_set_size_request (scroller, 320, 120);
334   gtk_container_set_border_width (GTK_CONTAINER (scroller), 5);
335   gtk_container_add (GTK_CONTAINER (scroller), list);
336   gtk_container_add (GTK_CONTAINER (frame), scroller);
337 
338   /* Connect signals.  */
339   g_signal_connect_swapped (G_OBJECT (gtk_tree_view_get_selection
340                                       (GTK_TREE_VIEW (list))),
341                             "changed", G_CALLBACK (key_selected_cb), dialog);
342 
343   dialog->default_key.frame = frame;
344 
345   return frame;
346 }
347 
348 
349 
350 /*
351    Default keyserver section.
352 */
353 #ifdef ENABLE_KEYSERVER_SUPPORT
354 static void
keyserver_selected_from_list_cb(SettingsDlg * dialog)355 keyserver_selected_from_list_cb (SettingsDlg *dialog)
356 {
357   char *text;
358 
359   if (dialog->gnupg21)
360     return;
361 
362   text = gtk_combo_box_get_active_text (dialog->keyserver.combo);
363   g_message ("got `%s'", text);
364   xfree (dialog->keyserver.url);
365   dialog->keyserver.url = (text && *text)? text : NULL;
366   update_modified (dialog, 1);
367 }
368 #endif /*ENABLE_KEYSERVER_SUPPORT*/
369 
370 
371 
372 /* Return NULL if the default keyserver is valid.  */
373 #ifdef ENABLE_KEYSERVER_SUPPORT
374 static GtkWidget *
check_default_keyserver(SettingsDlg * dialog)375 check_default_keyserver (SettingsDlg *dialog)
376 {
377   keyserver_spec_t kspec;
378 
379   if (dialog->gnupg21)
380     return NULL; /* GnuPG manages the keyservers.  */
381 
382   keyserver_selected_from_list_cb (dialog);
383 
384   if (!dialog->keyserver.url)
385     return NULL; /* No keyserver is just fine.  */
386 
387   kspec = parse_keyserver_uri (dialog->keyserver.url, 0, NULL, 0);
388   if (!kspec)
389     return GTK_WIDGET (dialog->keyserver.combo);
390   if (!kspec->host
391       || (kspec->scheme && !(!strcmp (kspec->scheme, "hkp")
392                              || !strcmp (kspec->scheme, "http")
393                              || !strcmp (kspec->scheme, "ldap")
394                              || !strcmp (kspec->scheme, "finger")
395                              || !strcmp (kspec->scheme, "mailto"))))
396     {
397       free_keyserver_spec (kspec);
398       return GTK_WIDGET (dialog->keyserver.combo);
399     }
400 
401   free_keyserver_spec (kspec);
402   return NULL;
403 }
404 #endif /*ENABLE_KEYSERVER_SUPPORT*/
405 
406 
407 
408 #ifdef ENABLE_KEYSERVER_SUPPORT
409 static void
append_to_combo(gpointer item,gpointer data)410 append_to_combo (gpointer item, gpointer data)
411 {
412   GtkWidget *combo = data;
413   gchar *text = item;
414 
415   gtk_combo_box_append_text (GTK_COMBO_BOX (combo), text);
416 }
417 #endif /*ENABLE_KEYSERVER_SUPPORT*/
418 
419 
420 #ifdef ENABLE_KEYSERVER_SUPPORT
421 static GtkWidget *
default_keyserver_frame(SettingsDlg * dialog)422 default_keyserver_frame (SettingsDlg *dialog)
423 {
424   GtkWidget *frame;
425   GtkWidget *label;
426   GtkWidget *combo;
427   GList *servers;
428 
429   /* Build UI */
430   frame = gtk_frame_new (NULL);
431   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
432   label = gtk_label_new_with_mnemonic (_("<b>Default key_server</b>"));
433   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
434   gtk_frame_set_label_widget (GTK_FRAME (frame), label);
435 
436   combo = gtk_combo_box_entry_new_text ();
437   gtk_container_set_border_width (GTK_CONTAINER (combo), 5);
438   gtk_container_add (GTK_CONTAINER (frame), combo);
439   /* Set current value.  */
440   servers = keyserver_get_as_glist ();
441   g_list_foreach (servers, append_to_combo, combo);
442   g_list_free (servers);
443   dialog->keyserver.combo = GTK_COMBO_BOX (combo);
444 
445   g_signal_connect_swapped (G_OBJECT (combo),
446                             "changed",
447                             G_CALLBACK (keyserver_selected_from_list_cb),
448                             dialog);
449 
450   dialog->keyserver.frame = frame;
451 
452   return frame;
453 }
454 #endif /*ENABLE_KEYSERVER_SUPPORT*/
455 
456 
457 /*
458    Auto Key Locate section.
459  */
460 
461 static void
akl_method_changed_cb(SettingsDlg * dialog)462 akl_method_changed_cb (SettingsDlg *dialog)
463 {
464   int idx;
465 
466   dialog->akl.require_addr = 0;
467   idx = gtk_combo_box_get_active (dialog->akl.methods);
468   if (idx < 0 || idx > DIM(akl_table))
469     {
470       dialog->akl.method_idx = -1;
471       return;
472     }
473   dialog->akl.method_idx = idx;
474 
475   if (akl_table[idx].list && strchr (akl_table[idx].list, 'D'))
476     dialog->akl.require_addr = 1;
477 
478   update_modified (dialog, 1);
479 }
480 
481 static void
akl_addr_changed_cb(SettingsDlg * dialog)482 akl_addr_changed_cb (SettingsDlg *dialog)
483 {
484   const char *addr;
485   struct in_addr binaddr;
486 
487   xfree (dialog->akl.ip_addr );
488   dialog->akl.ip_addr = NULL;
489 
490   if (!GTK_WIDGET_IS_SENSITIVE (dialog->akl.addr_entry))
491     return;
492 
493   addr = gtk_entry_get_text (dialog->akl.addr_entry);
494   if (!addr || !*addr || !inet_aton (addr, &binaddr) )
495     {
496       g_message ("IP-address is '%s' is NOT valid", addr? addr:"[none]");
497       return;
498     }
499 
500   dialog->akl.ip_addr = xstrdup (addr);
501   g_message ("IP-address is '%s' is valid", addr);
502 
503   update_modified (dialog, 1);
504 }
505 
506 
507 /* Returns NULL is the AKL data is valid.  On error the erroneous
508    widget is returned.  */
509 static GtkWidget *
check_akl(SettingsDlg * dialog)510 check_akl (SettingsDlg *dialog)
511 {
512   /* Make sure we have all required data.  */
513   akl_method_changed_cb (dialog);
514   akl_addr_changed_cb (dialog);
515 
516   if (dialog->akl.method_idx < 0)
517     return GTK_WIDGET (dialog->akl.methods);
518   if (dialog->akl.require_addr && !dialog->akl.ip_addr)
519     return GTK_WIDGET (dialog->akl.addr_entry);
520 
521   return NULL;
522 }
523 
524 
525 static void
parse_akl(SettingsDlg * dialog)526 parse_akl (SettingsDlg *dialog)
527 {
528   akl_t akllist, akl;
529   char *akloption;
530   char list[10];
531   int listidx = 0;
532   int idx;
533 
534   akloption = gpa_load_gpgconf_string ("gpg", "auto-key-locate");
535   if (!akloption)
536     {
537       dialog->akl.require_addr = 0;
538       dialog->akl.method_idx = 0;
539       /* Select the first entry ("Local").  */
540       gtk_combo_box_set_active (dialog->akl.methods, 0);
541       /* Set kDNS server to empty.  */
542       gtk_entry_set_text (dialog->akl.addr_entry, "");
543       return;
544     }
545 
546   /* Well, lets parse it.  */
547   akllist = gpg_parse_auto_key_locate (akloption);
548   g_free (akloption);
549   for (akl = akllist; akl && listidx < sizeof list - 1; akl = akl->next)
550     {
551       keyserver_spec_t k = akl->spec;
552 
553       switch (akl->type )
554         {
555         case AKL_NODEFAULT:
556           list[listidx++] = 'n';
557           break;
558         case AKL_LOCAL:
559           list[listidx++] = 'l';
560           break;
561         case AKL_CERT:
562           list[listidx++] = 'c';
563           break;
564         case AKL_PKA:
565           list[listidx++] = 'p';
566           break;
567         case AKL_LDAP:
568           list[listidx++] = 'L';
569           break;
570         case AKL_KEYSERVER:
571           list[listidx++] = 'k';
572           break;
573         case AKL_SPEC:
574           if ( (k = akl->spec)
575                && k->scheme && !strcmp (k->scheme, "kdns")
576                && !k->auth
577                && k->host
578                && !k->port
579                && k->path && !strcmp (k->path, "/?at=_kdnscert&usevc=1")
580                && !k->opaque
581                && !k->options)
582             {
583               gtk_entry_set_text (dialog->akl.addr_entry, k->host);
584               list[listidx++] = 'D';
585             }
586           else
587             list[listidx++] = '?';
588           break;
589 
590         default:
591           list[listidx++] = '?';
592           break;
593         }
594     }
595   list[listidx] = 0;
596   gpg_release_akl (akllist);
597   g_debug ("akl list: %s", list);
598 
599   for (idx=0; akl_table[idx].list; idx++)
600     if (!strcmp (akl_table[idx].list, list))
601       break;
602   /* (The last entry of the table is the one used for no-match.) */
603   gtk_combo_box_set_active (dialog->akl.methods, idx);
604   dialog->akl.method_idx = idx;
605   /* Enable the kdns Server entry if one is defined.  For ease of
606      implementation we have already put the server address into the
607      entry field even if there might be no match.  */
608   if (akl_table[idx].list && strchr (akl_table[idx].list, 'D'))
609     dialog->akl.require_addr = 1;
610 }
611 
612 
613 
614 static GtkWidget *
auto_key_locate_frame(SettingsDlg * dialog)615 auto_key_locate_frame (SettingsDlg *dialog)
616 {
617   const char *tooltip;
618   GtkWidget *frame;
619   GtkWidget *label;
620   GtkWidget *frame_vbox;
621   GtkWidget *combo;
622   GtkWidget *entry;
623   GtkWidget *hbox;
624   int xpad, ypad;
625   int idx;
626 
627   /* Build UI.  */
628   frame = gtk_frame_new (NULL);
629   gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
630   label = gtk_label_new_with_mnemonic (_("<b>Auto key _locate</b>"));
631   gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
632   gtk_frame_set_label_widget (GTK_FRAME (frame), label);
633 
634   frame_vbox = gtk_vbox_new (FALSE, 0);
635   gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
636 
637   /* The method selection.  */
638   hbox = gtk_hbox_new (FALSE, 0);
639   gtk_container_add (GTK_CONTAINER (frame_vbox), hbox);
640   tooltip = _("The list of methods to locate keys via an email address.\n"
641               "All given methods are used in turn until a matching "
642               "key is found.  The supported methods are:\n"
643               " Local\n"
644               "   - Use the local keyring.\n"
645 #ifdef ENABLE_KEYSERVER_SUPPORT
646               " Keyserver\n"
647               "   - Use the default keyserver.\n"
648 #endif /*ENABLE_KEYSERVER_SUPPORT*/
649               " PKA\n"
650               "   - Use the Public Key Association.\n"
651               " kDNS\n"
652               "   - Use kDNS with the nameserver below.\n"
653               " Custom\n"
654               "   - Configured in the backend dialog.\n");
655   gpa_add_tooltip (hbox, tooltip);
656 
657   label = gtk_label_new (_("Method:"));
658   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
659   xpad += 5;
660   gtk_misc_set_padding (GTK_MISC (label), xpad, ypad);
661   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
662 
663   combo = gtk_combo_box_new_text ();
664   gtk_box_pack_start (GTK_BOX (hbox), combo, FALSE, FALSE, 0);
665 
666   idx=0;
667   do
668     gtk_combo_box_append_text (GTK_COMBO_BOX (combo), _(akl_table[idx].text));
669   while (akl_table[idx++].list);
670 
671   /* The kDNS server.  */
672   hbox = gtk_hbox_new (FALSE, 0);
673   gtk_container_add (GTK_CONTAINER (frame_vbox), hbox);
674   tooltip = _("The IP address of the nameserver used for the kDNS method.");
675   gpa_add_tooltip (hbox, tooltip);
676   label = gtk_label_new (_("kDNS Server:"));
677   gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
678   xpad += 5;
679   gtk_misc_set_padding (GTK_MISC (label), xpad, ypad);
680   gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
681 
682   entry = gtk_entry_new ();
683   gtk_entry_set_max_length (GTK_ENTRY(entry), 3+1+3+1+3+1+3);
684   gtk_entry_set_width_chars (GTK_ENTRY(entry), 3+1+3+1+3+1+3);
685   gtk_box_pack_start (GTK_BOX (hbox), entry, FALSE, FALSE, 0);
686 
687   dialog->akl.addr_hbox = hbox;
688   dialog->akl.addr_entry = GTK_ENTRY (entry);
689   dialog->akl.methods = GTK_COMBO_BOX (combo);
690 
691   g_signal_connect_swapped (G_OBJECT (combo),
692                             "changed",
693                             G_CALLBACK (akl_method_changed_cb), dialog);
694   g_signal_connect_swapped (G_OBJECT (entry),
695                             "changed",
696                             G_CALLBACK (akl_addr_changed_cb), dialog);
697 
698   dialog->akl.frame = frame;
699 
700   return frame;
701 }
702 
703 /* Update the state of the action buttons.  */
704 static void
update_modified(SettingsDlg * dialog,int is_modified)705 update_modified (SettingsDlg *dialog, int is_modified)
706 {
707   int newstate = !!is_modified;
708 
709   dialog->modified = newstate;
710 
711   gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
712                                      GTK_RESPONSE_APPLY, newstate);
713 
714   if (dialog->akl.enabled)
715     gtk_widget_set_sensitive (dialog->akl.addr_hbox, dialog->akl.require_addr);
716 }
717 
718 
719 static void
load_settings(SettingsDlg * dialog)720 load_settings (SettingsDlg *dialog)
721 {
722   GpaOptions *options = gpa_options_get_instance ();
723 
724   g_return_if_fail (IS_SETTINGS_DLG (dialog));
725 
726   /* UI section.  */
727   gtk_toggle_button_set_active
728     (dialog->ui.advanced_mode, !gpa_options_get_simplified_ui (options));
729   gtk_toggle_button_set_active
730     (dialog->ui.show_advanced_options,
731      !!gpa_options_get_show_advanced_options (options));
732 
733   /* Default key section.  */
734   /*gpa_options_get_default_key (options);*/
735   /* FIXME: Need to select the one from the list. */
736 
737 
738   /* Default keyserver section.  */
739 #ifdef ENABLE_KEYSERVER_SUPPORT
740   if (!dialog->gnupg21)
741     {
742       gtk_entry_set_text (GTK_ENTRY
743                           (gtk_bin_get_child
744                            (GTK_BIN (dialog->keyserver.combo))),
745                           gpa_options_get_default_keyserver (options));
746     }
747 #endif /*ENABLE_KEYSERVER_SUPPORT*/
748 
749   /* AKL section. */
750   if (dialog->akl.enabled)
751     parse_akl (dialog);
752 
753 
754   update_modified (dialog, 0);
755 }
756 
757 
758 /* Save all settings, return 0 on success.  */
759 static int
save_settings(SettingsDlg * dialog)760 save_settings (SettingsDlg *dialog)
761 {
762   GtkWidget *errwdg;
763 
764   if (!dialog->modified)
765     return 0;  /* No need to save anything.  */
766 
767   if ((errwdg = check_default_key (dialog)))
768     {
769       gtk_widget_grab_focus (errwdg);
770       return -1;
771     }
772 
773 #ifdef ENABLE_KEYSERVER_SUPPORT
774   if (!dialog->gnupg21)
775     {
776       if ((errwdg = check_default_keyserver (dialog)))
777         {
778           gpa_window_error
779             (_("The URL given for the keyserver is not valid."),
780              GTK_WIDGET (dialog));
781           gtk_widget_grab_focus (errwdg);
782           return -1;
783         }
784     }
785 #endif /*ENABLE_KEYSERVER_SUPPORT*/
786 
787   if ( dialog->akl.enabled && (errwdg = check_akl (dialog)))
788     {
789       gpa_window_error
790         (_("The data given for \"Auto key locate\" is not valid."),
791          GTK_WIDGET (dialog));
792       gtk_widget_grab_focus (errwdg);
793       return -1;
794     }
795 
796   gpa_options_set_default_key (gpa_options_get_instance (),
797  			       dialog->default_key.key);
798 
799 
800 #ifdef ENABLE_KEYSERVER_SUPPORT
801   if (!dialog->gnupg21)
802     gpa_options_set_default_keyserver (gpa_options_get_instance (),
803                                        dialog->keyserver.url);
804 #endif /*ENABLE_KEYSERVER_SUPPORT*/
805 
806   if (!dialog->akl.enabled)
807     ;
808   else if (dialog->akl.method_idx == -1)
809     ; /* oops: none selected.  */
810   else if (!akl_table[dialog->akl.method_idx].list)
811     {
812       /* Custom setting: Do nothing.  */
813     }
814   else
815     {
816       const char *methods = akl_table[dialog->akl.method_idx].list;
817       GString *newakl;
818 
819       newakl = g_string_new ("");
820       for (; *methods; methods++)
821         {
822           if (newakl->len)
823             g_string_append_c (newakl, ',');
824           switch (*methods)
825             {
826             case 'n':
827               g_string_append (newakl, "nodefault");
828               break;
829             case 'l':
830               g_string_append (newakl, "local");
831               break;
832             case 'c':
833               g_string_append (newakl, "cert");
834               break;
835             case 'L':
836               g_string_append (newakl, "ldap");
837               break;
838             case 'k':
839               g_string_append (newakl, "keyserver");
840               break;
841             case 'D':
842               g_string_append_printf (newakl,
843                                       "kdns://%s/?at=_kdnscert&usevc=1",
844                                       dialog->akl.ip_addr ?
845                                       dialog->akl.ip_addr : "");
846               break;
847             default:
848               if (newakl->len)
849                 g_string_truncate (newakl, newakl->len - 1);
850               break;
851             }
852         }
853       gpa_store_gpgconf_string ("gpg", "auto-key-locate", newakl->str);
854       g_string_free (newakl, TRUE);
855     }
856   return 0;
857 }
858 
859 
860 /* Handle the response signal.  */
861 static void
dialog_response(GtkDialog * dialog,gint response)862 dialog_response (GtkDialog *dialog, gint response)
863 {
864   switch (response)
865     {
866     case GTK_RESPONSE_OK:
867       if (save_settings (SETTINGS_DLG (dialog)))
868         return;
869       break;
870 
871     case GTK_RESPONSE_APPLY:
872       if (save_settings (SETTINGS_DLG (dialog)))
873         return;
874       load_settings (SETTINGS_DLG (dialog));
875       return; /* Do not close.  */
876 
877     default:
878       break;
879     }
880 
881   gtk_widget_destroy (GTK_WIDGET(dialog));
882 }
883 
884 
885 /************************************************************
886  ******************   Object Management  ********************
887  ************************************************************/
888 
889 
890 static void
settings_dlg_finalize(GObject * object)891 settings_dlg_finalize (GObject *object)
892 {
893   SettingsDlg *dialog = SETTINGS_DLG (object);
894 
895   gpgme_key_unref (dialog->default_key.key);
896   dialog->default_key.key = NULL;
897 
898   xfree (dialog->keyserver.url);
899   dialog->keyserver.url = NULL;
900 
901   xfree (dialog->akl.ip_addr);
902   dialog->akl.ip_addr = NULL;
903 
904 
905   G_OBJECT_CLASS (parent_class)->finalize (object);
906 }
907 
908 
909 static void
settings_dlg_init(SettingsDlg * dialog)910 settings_dlg_init (SettingsDlg *dialog)
911 {
912   dialog->akl.method_idx = -1;
913   dialog->gnupg21 = is_gpg_version_at_least ("2.1.0");
914 }
915 
916 
917 static GObject*
settings_dlg_constructor(GType type,guint n_construct_properties,GObjectConstructParam * construct_properties)918 settings_dlg_constructor (GType type, guint n_construct_properties,
919                           GObjectConstructParam *construct_properties)
920 {
921   GObject *object;
922   SettingsDlg *dialog;
923   GtkWidget *frame;
924 
925   object = parent_class->constructor (type,
926 				      n_construct_properties,
927 				      construct_properties);
928   dialog = SETTINGS_DLG (object);
929   gpa_window_set_title (GTK_WINDOW (dialog), _("Settings"));
930   gtk_dialog_add_buttons (GTK_DIALOG (dialog),
931                           GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
932                           GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
933                           GTK_STOCK_OK, GTK_RESPONSE_OK,
934                           NULL);
935   gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_OK);
936   gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
937                                            GTK_RESPONSE_OK,
938                                            GTK_RESPONSE_CANCEL,
939                                            GTK_RESPONSE_APPLY,
940                                            -1);
941 
942   /* The UI mode section.  */
943   frame = user_interface_mode_frame (dialog);
944   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame,
945                       FALSE, FALSE, 0);
946 
947   /* The default key section.  */
948   frame = default_key_frame (dialog);
949   gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame);
950 
951   /* The default keyserver section.  Note that there is no keyserver
952      entry if we are using gnupg 2.1.  There we do not have the
953      keyserver helpers anymore and thus the keyservers are to be
954      enabled in the backend preferences. */
955 #ifdef ENABLE_KEYSERVER_SUPPORT
956   if (!dialog->gnupg21)
957     {
958       frame = default_keyserver_frame (dialog);
959       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame,
960                           FALSE, FALSE, 0);
961     }
962 #endif /*ENABLE_KEYSERVER_SUPPORT*/
963 
964   /* The auto key locate section.  */
965   dialog->akl.enabled = dialog->gnupg21;
966   if (dialog->akl.enabled)
967     {
968       frame = auto_key_locate_frame (dialog);
969       gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dialog)->vbox), frame,
970                           FALSE, FALSE, 0);
971     }
972 
973   /* Connect the response signal.  */
974   g_signal_connect (GTK_OBJECT (dialog), "response",
975                     G_CALLBACK (dialog_response), NULL);
976 
977 
978   /* Load values.  */
979   load_settings (dialog);
980 
981   /* Show all windows and hide those we don't want. */
982   gtk_widget_show_all (GTK_WIDGET(dialog));
983   update_show_advanced_options (dialog);
984 
985   return object;
986 }
987 
988 
989 static void
settings_dlg_class_init(SettingsDlgClass * klass)990 settings_dlg_class_init (SettingsDlgClass *klass)
991 {
992   GObjectClass *object_class = G_OBJECT_CLASS (klass);
993 
994   parent_class = g_type_class_peek_parent (klass);
995 
996   object_class->constructor  = settings_dlg_constructor;
997   object_class->finalize     = settings_dlg_finalize;
998 
999 }
1000 
1001 
1002 GType
settings_dlg_get_type(void)1003 settings_dlg_get_type (void)
1004 {
1005   static GType this_type;
1006 
1007   if (!this_type)
1008     {
1009       static const GTypeInfo this_info =
1010 	{
1011 	  sizeof (SettingsDlgClass),
1012 	  (GBaseInitFunc) NULL,
1013 	  (GBaseFinalizeFunc) NULL,
1014 	  (GClassInitFunc) settings_dlg_class_init,
1015 	  NULL, /* class_finalize */
1016 	  NULL, /* class_data */
1017 
1018 	  sizeof (SettingsDlg),
1019 	  0,    /* n_preallocs */
1020 	  (GInstanceInitFunc) settings_dlg_init,
1021 	};
1022 
1023       this_type = g_type_register_static (GTK_TYPE_DIALOG,
1024                                           "SettingsDlg",
1025                                           &this_info, 0);
1026     }
1027 
1028   return this_type;
1029 }
1030 
1031 
1032 
1033 /************************************************************
1034  **********************  Public API  ************************
1035  ************************************************************/
1036 
1037 /* Show the preference dialog and it create it if not yet done.  */
1038 void
settings_dlg_new(GtkWidget * parent)1039 settings_dlg_new (GtkWidget *parent)
1040 {
1041   if (parent)
1042     g_return_if_fail (GTK_IS_WINDOW (parent));
1043 
1044   if (!the_settings_dialog)
1045     {
1046       the_settings_dialog = g_object_new (SETTINGS_DLG_TYPE, NULL);
1047       g_signal_connect (the_settings_dialog,
1048                         "destroy",
1049                         G_CALLBACK (gtk_widget_destroyed),
1050                         &the_settings_dialog);
1051 
1052     }
1053 
1054   if (parent
1055       && (GTK_WINDOW (parent)
1056           != gtk_window_get_transient_for (GTK_WINDOW (the_settings_dialog))))
1057     gtk_window_set_transient_for (GTK_WINDOW (the_settings_dialog),
1058                                   GTK_WINDOW (parent));
1059 
1060   gtk_window_present (GTK_WINDOW (the_settings_dialog));
1061 }
1062 
1063 /* Tell whether the settings dialog is open.  */
1064 gboolean
settings_is_open(void)1065 settings_is_open (void)
1066 {
1067   return !!the_settings_dialog;
1068 }
1069