1 /* confdialog.c - GPGME based configuration dialog for GPA.
2 Copyright (C) 2007, 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 <stdio.h>
24 #include <stdlib.h>
25 #include <assert.h>
26 #include <string.h>
27 #include <errno.h>
28
29 #include <gpgme.h>
30 #include <glib.h>
31 #include <gtk/gtk.h>
32
33 #include "i18n.h"
34 #include "gpgmetools.h"
35 #include "gtktools.h"
36 #include "options.h"
37 #include "gpa.h"
38
39 /* Violation of GNOME standards: Cancel does not revert previous
40 apply. We do not auto-apply or syntax check after focus
41 change. */
42
43
44 /* Internal public interface. */
45 static void hide_backend_config (void);
46
47
48 /* Some global variables. */
49
50 /* Custom response IDs. */
51 #define CUSTOM_RESPONSE_RESET 1
52
53
54 /* The configuration dialog. */
55 static GtkWidget *dialog;
56
57 /* The notebook with one page for each component. */
58 static gpgme_ctx_t dialog_ctx;
59
60 /* The notebook with one page for each component. */
61 static GtkWidget *dialog_notebook;
62
63 /* The current configuration. */
64 static gpgme_conf_comp_t dialog_conf;
65
66 /* If we modified something in the current tab. */
67 static int dialog_tab_modified;
68
69 /* The expert level. */
70 static gpgme_conf_level_t dialog_level;
71
72
73 /* We define the following behaviour for options:
74
75 An option of alt-type NONE gets a check button, and if it has the
76 LIST flag set, also a spin button for a repeat count. The spin
77 button is only active if the button is checked.
78
79 An option of a different alt-type does not get a check box button
80 but a combo box and a text entry field. If the option does have a
81 default or default description, the combo box contains an entry for
82 "Use default value", which sets the text entry field to that
83 default (and makes it insensitive?). Otherwise the combo box
84 contains an entry for "Option not active" which makes the entry
85 field insensitive. If the option has the OPTIONAl flag set, the
86 combo box contains an entry for "Use default argument" which sets
87 the text entry field to the no arg value or description, if any
88 (and makes it insensitive?). In any case, the combo box contains
89 an entry for "Use custom value" which makes the text entry field
90 sensitive and editable.
91
92 Options with the LIST flag set could get a more sophisticated
93 dialog where you have "Add" and "Remove" buttons, or a multi-line
94 entry field. Currently, having the LIST flag and OPTIONAL flag at
95 the same time creates an ambiguity, and entering commas in values
96 is not supported. */
97 typedef enum
98 {
99 OPTION_SIMPLE,
100 OPTION_SPIN,
101 OPTION_ENTRY,
102 OPTION_OPT_ENTRY
103 } option_type_t;
104
105 typedef enum
106 {
107 COMBO_UNDEF = -1,
108 COMBO_DEFAULT = 0,
109 COMBO_CUSTOM = 1,
110 COMBO_NO_ARG = 2
111 } option_combo_t;
112
113
114 /* This structure combines an option with its GUI elements. */
115 typedef struct option_widget_s
116 {
117 /* The option. */
118 gpgme_conf_opt_t option;
119
120 /* We remember the type of the option to make life easier on us. */
121 option_type_t type;
122
123 /* The check button (or, in the case of COMBO being not NULL, the
124 label) widget. */
125 GtkWidget *check;
126
127 /* The entry or spin button widget. */
128 GtkWidget *widget;
129
130 /* The current user setting in string representation. This defaults
131 to the last input by the user, followed by the current setting,
132 followed by the default. */
133 char *saved_value;
134
135 /* This helps to know about state changes. */
136 int old_combo_box_state;
137
138 /* The combo box, if any. Only used for OPTION_ENTRY and
139 OPTION_OPT_ENTRY. The combo has
140 COMBO_DEFAULT = 0: Use default value or Do not use option.
141 COMBO_CUSTOM = 1: Use custom value
142 COMBO_NO_ARG = 2: Use default argument (for OPTION_OPT_ENTRY). */
143 GtkWidget *combo;
144 } *option_widget_t;
145
146
147 #if 0
148 /* Not needed anymore, as all stock actions work on all tabs.
149 However, might come in handy at some point. */
150
151 static gpgme_conf_comp_t
152 dialog_current_comp (void)
153 {
154 gpgme_conf_comp_t comp = dialog_conf;
155 int page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog_notebook));
156 const char *label;
157
158 /* Iterate over all options in the current tab, and reset them. */
159
160 label = gtk_notebook_get_tab_label_text
161 (GTK_NOTEBOOK (dialog_notebook),
162 gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook), page));
163
164 while (comp)
165 {
166 if (! strcmp (label, comp->description))
167 break;
168 comp = comp->next;
169 }
170
171 assert (comp);
172 return comp;
173 }
174 #endif
175
176
177 /* Convert an argument value to a string representation. Lists are
178 converted to comma-separated values, empty strings in lists are
179 surrounded by double-quotes. */
180 static char *
arg_to_str(gpgme_conf_arg_t arg,gpgme_conf_type_t type)181 arg_to_str (gpgme_conf_arg_t arg, gpgme_conf_type_t type)
182 {
183 static char *result;
184 char *new_result = NULL;
185
186 if (result)
187 {
188 g_free (result);
189 result = NULL;
190 }
191
192 while (arg)
193 {
194 if (result)
195 {
196 new_result = g_strdup_printf ("%s,", result);
197 g_free (result);
198 result = new_result;
199 }
200 else
201 result = g_strdup ("");
202
203 if (!arg->no_arg)
204 {
205 switch (type)
206 {
207 case GPGME_CONF_NONE:
208 case GPGME_CONF_UINT32:
209 new_result = g_strdup_printf ("%s%u", result, arg->value.uint32);
210 g_free (result);
211 result = new_result;
212 break;
213
214 case GPGME_CONF_INT32:
215 new_result = g_strdup_printf ("%s%i", result, arg->value.int32);
216 g_free (result);
217 result = new_result;
218 break;
219
220 case GPGME_CONF_STRING:
221 case GPGME_CONF_PATHNAME:
222 case GPGME_CONF_LDAP_SERVER:
223 new_result = g_strdup_printf ("%s%s", result, arg->value.string);
224 g_free (result);
225 result = new_result;
226 break;
227
228 default:
229 assert (!"Not supported.");
230 break;
231 }
232 }
233 arg = arg->next;
234 }
235 return result;
236 }
237
238
239 /* Extract the argument from a widget. */
240 static gpgme_conf_arg_t
option_widget_to_arg(option_widget_t opt)241 option_widget_to_arg (option_widget_t opt)
242 {
243 gpgme_error_t err;
244 gpgme_conf_opt_t option = opt->option;
245 gpgme_conf_arg_t arg = NULL;
246
247 if (opt->type == OPTION_SIMPLE || opt->type == OPTION_SPIN)
248 {
249 int active;
250 unsigned int count = 1;
251
252 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (opt->check));
253 if (!active)
254 return NULL;
255
256 if (opt->type == OPTION_SPIN)
257 count = gtk_spin_button_get_value (GTK_SPIN_BUTTON (opt->widget));
258
259 err = gpgme_conf_arg_new (&arg, option->alt_type, &count);
260 if (err)
261 gpa_gpgme_error (err);
262
263 return arg;
264 }
265 else
266 {
267 int combo;
268 gpgme_conf_arg_t *argp;
269 char *val;
270 char *valp;
271
272 combo = gtk_combo_box_get_active (GTK_COMBO_BOX (opt->combo));
273 if (combo == COMBO_DEFAULT)
274 return NULL;
275
276 if (combo == COMBO_NO_ARG)
277 {
278 /* A single no-arg option. */
279 err = gpgme_conf_arg_new (&arg, option->alt_type, NULL);
280 if (err)
281 gpa_gpgme_error (err);
282
283 return arg;
284 }
285
286 argp = &arg;
287
288 val = g_strdup (gtk_entry_get_text (GTK_ENTRY (opt->widget)));
289 valp = val;
290 do
291 {
292 while (*valp == ' ' || *valp == '\t')
293 valp++;
294
295 if (*valp == ',' || *valp == '\0')
296 {
297 if (option->flags & GPGME_CONF_OPTIONAL)
298 err = gpgme_conf_arg_new (argp, option->alt_type, NULL);
299 else
300 /* This seems a useful default to make things simpler
301 for the user, as the OPTIONAL flag is not much
302 used. */
303 err = gpgme_conf_arg_new (argp, option->alt_type, "");
304
305 if (err)
306 gpa_gpgme_error (err);
307
308 argp = &(*argp)->next;
309
310 /* Skip comma. */
311 if (*valp)
312 valp++;
313 }
314 else
315 {
316 char *str = valp;
317 char *strend = valp;
318 int done = 0;
319 int in_quote = 0;
320
321 /* Remove quoting. */
322 do
323 {
324 if (in_quote)
325 {
326 if (*valp == '\0')
327 done = 1;
328 else if (*valp == '"')
329 {
330 in_quote = 0;
331 valp++;
332 }
333 else
334 *(strend++) = *(valp++);
335 }
336 else
337 {
338 if (*valp == ',' || *valp == '\0')
339 done = 1;
340 else if (*valp == '"')
341 {
342 in_quote = 1;
343 valp++;
344 }
345 else
346 *(strend++) = *(valp++);
347 }
348 }
349 while (! done);
350
351 if (*valp == ',')
352 valp++;
353
354 /* Find end of the string. */
355 while (strend > str && (*strend == ' ' || *strend == '\t'))
356 strend--;
357
358 *strend = '\0';
359
360 switch (option->alt_type)
361 {
362 case GPGME_CONF_NONE:
363 case GPGME_CONF_UINT32:
364 {
365 unsigned int nr;
366 nr = strtoul (str, NULL, 0);
367 err = gpgme_conf_arg_new (argp, option->alt_type, &nr);
368 if (err)
369 gpa_gpgme_error (err);
370
371 argp = &(*argp)->next;
372 }
373 break;
374
375 case GPGME_CONF_INT32:
376 {
377 int nr;
378 nr = strtoul (str, NULL, 0);
379 err = gpgme_conf_arg_new (argp, option->alt_type, &nr);
380 if (err)
381 gpa_gpgme_error (err);
382
383 argp = &(*argp)->next;
384 }
385 break;
386
387 case GPGME_CONF_STRING:
388 case GPGME_CONF_LDAP_SERVER:
389 case GPGME_CONF_PATHNAME:
390 {
391 err = gpgme_conf_arg_new (argp, option->alt_type, str);
392 if (err)
393 gpa_gpgme_error (err);
394
395 argp = &(*argp)->next;
396 }
397 break;
398
399 default:
400 assert (!"Not supported.");
401 break;
402 }
403 }
404 }
405 while (*valp);
406
407 g_free (val);
408 return arg;
409 }
410 }
411
412
413 /* Compare two arguments and returns true if they are equal. */
414 static int
args_are_equal(gpgme_conf_arg_t arg1,gpgme_conf_arg_t arg2,gpgme_conf_type_t type)415 args_are_equal (gpgme_conf_arg_t arg1, gpgme_conf_arg_t arg2,
416 gpgme_conf_type_t type)
417 {
418 while (arg1 && arg2)
419 {
420 if ((!arg1->no_arg ^ !arg2->no_arg))
421 return 0;
422 if (!arg1->no_arg)
423 {
424 switch (type)
425 {
426 case GPGME_CONF_NONE:
427 case GPGME_CONF_UINT32:
428 if (arg1->value.uint32 != arg2->value.uint32)
429 return 0;
430 break;
431
432 case GPGME_CONF_INT32:
433 if (arg1->value.int32 != arg2->value.int32)
434 return 0;
435 break;
436
437 case GPGME_CONF_STRING:
438 case GPGME_CONF_LDAP_SERVER:
439 case GPGME_CONF_PATHNAME:
440 if (strcmp (arg1->value.string, arg2->value.string))
441 return 0;
442 break;
443
444 default:
445 assert (!"Not supported.");
446 break;
447 }
448 }
449
450 arg1 = arg1->next;
451 arg2 = arg2->next;
452 }
453 if (arg1 || arg2)
454 return 0;
455 return 1;
456 }
457
458
459 /* Commit all the changes in component COMP. */
460 static void
save_options(gpgme_conf_comp_t comp)461 save_options (gpgme_conf_comp_t comp)
462 {
463 gpgme_conf_opt_t option = comp->options;
464 gpgme_error_t err;
465 int changed;
466
467 changed = 0;
468 while (option)
469 {
470 option_widget_t opt = option->user_data;
471 gpgme_conf_arg_t arg;
472
473 /* Exclude group headers etc. */
474 if (!opt)
475 {
476 option = option->next;
477 continue;
478 }
479
480 arg = option_widget_to_arg (opt);
481 if (! args_are_equal (arg, option->value, option->alt_type))
482 {
483 err = gpgme_conf_opt_change (option, 0, arg);
484 if (err)
485 gpa_gpgme_error (err);
486 changed++;
487
488 /* FIXME: Disable this. */
489 g_debug ("Changing component %s, option %s",
490 comp->name, option->name);
491 }
492
493 option = option->next;
494 }
495
496 if (changed)
497 {
498 err = gpgme_op_conf_save (dialog_ctx, comp);
499 if (err)
500 gpa_gpgme_warning (err);
501 }
502 }
503
504
505 static void
save_all_options(void)506 save_all_options (void)
507 {
508 gpgme_conf_comp_t comp;
509
510 /* Save all tabs. */
511 comp = dialog_conf;
512 while (comp)
513 {
514 save_options (comp);
515 comp = comp->next;
516 }
517 }
518
519
520 /* Update user-changes to the option widget OPT. */
521 static void
update_option(option_widget_t opt)522 update_option (option_widget_t opt)
523 {
524 gpgme_conf_opt_t option;
525
526 option = opt->option;
527
528 if (opt->type == OPTION_SPIN)
529 {
530 int active;
531
532 active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (opt->check));
533
534 gtk_widget_set_sensitive (opt->widget, active);
535 }
536 else if (opt->type == OPTION_ENTRY || opt->type == OPTION_OPT_ENTRY)
537 {
538 int combo;
539 GtkWidget *entry = opt->widget;
540
541 combo = gtk_combo_box_get_active (GTK_COMBO_BOX (opt->combo));
542
543 if (opt->old_combo_box_state == COMBO_UNDEF)
544 {
545 /* Initialization. */
546 if (option->value)
547 {
548 /* A single no-arg argument can be represented directly
549 in the combo box. Otherwise, we use a custom
550 argument and comma-separated lists. */
551 if (option->value->no_arg && !option->value->next)
552 combo = COMBO_NO_ARG;
553 else
554 combo = COMBO_CUSTOM;
555 }
556 else
557 combo = COMBO_DEFAULT;
558 gtk_combo_box_set_active (GTK_COMBO_BOX (opt->combo), combo);
559 }
560 else if (combo == opt->old_combo_box_state)
561 return;
562 else if (opt->old_combo_box_state == COMBO_CUSTOM)
563 {
564 if (opt->saved_value)
565 g_free (opt->saved_value);
566 opt->saved_value = g_strdup
567 (gtk_entry_get_text (GTK_ENTRY (opt->widget)));
568 }
569
570 if (combo == COMBO_DEFAULT)
571 {
572 if (option->default_value)
573 gtk_entry_set_text (GTK_ENTRY (entry),
574 arg_to_str (option->default_value,
575 option->alt_type));
576 else if (option->default_description)
577 gtk_entry_set_text (GTK_ENTRY (entry),
578 option->default_description);
579 else
580 gtk_entry_set_text (GTK_ENTRY (entry), "");
581
582 gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
583 gtk_widget_set_sensitive (entry, FALSE);
584 }
585 else if (combo == COMBO_NO_ARG)
586 {
587 if (option->no_arg_value)
588 gtk_entry_set_text (GTK_ENTRY (entry),
589 arg_to_str (option->no_arg_value,
590 option->alt_type));
591 else if (option->no_arg_description)
592 gtk_entry_set_text (GTK_ENTRY (entry),
593 option->no_arg_description);
594 else
595 gtk_entry_set_text (GTK_ENTRY (entry), "");
596
597 gtk_editable_set_editable (GTK_EDITABLE (entry), FALSE);
598 gtk_widget_set_sensitive (entry, FALSE);
599 /* FIXME: Change focus. */
600 }
601 else if (combo == COMBO_CUSTOM)
602 {
603 if (opt->saved_value)
604 gtk_entry_set_text (GTK_ENTRY (entry), opt->saved_value);
605 else if (option->value)
606 gtk_entry_set_text (GTK_ENTRY (entry),
607 arg_to_str (option->value,
608 option->alt_type));
609 else if (option->default_value)
610 {
611 gtk_entry_set_text (GTK_ENTRY (entry),
612 arg_to_str (option->default_value,
613 option->alt_type));
614 /* A default (rather than current) value is selected
615 initially. */
616 gtk_editable_select_region (GTK_EDITABLE (entry), 0, -1);
617 }
618 else
619 gtk_entry_set_text (GTK_ENTRY (entry), "");
620
621 gtk_editable_set_editable (GTK_EDITABLE (entry), TRUE);
622 gtk_widget_set_sensitive (entry, TRUE);
623 /* FIXME: Change focus. */
624 }
625
626 opt->old_combo_box_state = combo;
627 }
628 }
629
630
631 /* Update the tab when it is modified (if modified is true) or reset
632 it (else). */
633 static void
update_modified(int modified)634 update_modified (int modified)
635 {
636 if (modified)
637 {
638 /* Run with every button click, so keep it short. */
639 if (!dialog_tab_modified)
640 {
641 dialog_tab_modified = 1;
642 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
643 GTK_RESPONSE_APPLY, TRUE);
644 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
645 CUSTOM_RESPONSE_RESET, TRUE);
646 }
647 }
648 else
649 {
650 /* This is also called at initialization time. */
651
652 dialog_tab_modified = 0;
653 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
654 GTK_RESPONSE_APPLY, FALSE);
655 gtk_dialog_set_response_sensitive (GTK_DIALOG (dialog),
656 CUSTOM_RESPONSE_RESET, FALSE);
657 }
658 }
659
660
661 static void
update_modified_cb(void * dummy)662 update_modified_cb (void *dummy)
663 {
664 update_modified (1);
665 }
666
667
668 /* Update a toggled checkbox button. */
669 static void
option_checkbutton_toggled(GtkToggleButton * button,gpointer data)670 option_checkbutton_toggled (GtkToggleButton *button, gpointer data)
671 {
672 option_widget_t opt = data;
673
674 update_option (opt);
675 update_modified (1);
676 }
677
678
679 /* Update a toggled combo box. */
680 static void
option_combobox_changed(GtkComboBox * combo,gpointer data)681 option_combobox_changed (GtkComboBox *combo, gpointer data)
682 {
683 option_widget_t opt = data;
684
685 update_option (opt);
686 update_modified (1);
687 }
688
689
690 static void create_dialog_tabs (void);
691
692
693 /* Handle stock response "apply". */
694 static void
dialog_response_apply(GtkDialog * dummy)695 dialog_response_apply (GtkDialog *dummy)
696 {
697 save_all_options ();
698
699 /* Reload configuration. */
700 create_dialog_tabs ();
701 }
702
703
704 /* Soft reset which reuses the configuration at last load. */
705 static void
reset_options(gpgme_conf_comp_t comp)706 reset_options (gpgme_conf_comp_t comp)
707 {
708 gpgme_conf_opt_t option;
709
710 option = comp->options;
711
712 while (option)
713 {
714 option_widget_t opt = option->user_data;
715
716 if (!opt)
717 {
718 /* Exclude group headers etc. */
719 option = option->next;
720 continue;
721 }
722
723 if (opt->type == OPTION_SIMPLE || opt->type == OPTION_SPIN)
724 {
725 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (opt->check),
726 !!option->value);
727 }
728 else if (opt->type == OPTION_ENTRY || opt->type == OPTION_OPT_ENTRY)
729 {
730 /* Force re-initialization. */
731 opt->old_combo_box_state = COMBO_UNDEF;
732 update_option (opt);
733 }
734
735 option = option->next;
736 }
737 }
738
739
740 static void
reset_all_options(void)741 reset_all_options (void)
742 {
743 gpgme_conf_comp_t comp;
744
745 /* Save all tabs. */
746 comp = dialog_conf;
747 while (comp)
748 {
749 reset_options (comp);
750 comp = comp->next;
751 }
752 }
753
754
755 /* Handle stock response "reset". */
756 static void
dialog_response_reset(GtkDialog * dummy)757 dialog_response_reset (GtkDialog *dummy)
758 {
759 #if 0
760 /* Allow to use the reset button also as a refresh button. Note:
761 This is kind of awkward, because the reset button is only
762 sensitive if local modifications exist. Currently, we require a
763 dialog cancel/reopen cycle or an expert level change for a "hard"
764 reset. */
765 create_dialog_tabs ();
766 #else
767 /* Soft reset, using last loaded configuration. Much faster, and
768 consistent with sensitivity of Reset button. */
769 reset_all_options ();
770 update_modified (0);
771 #endif
772 }
773
774
775 /* Handle stock responses like OK, apply and cancel. */
776 static int
dialog_response(GtkDialog * dlg,gint response,gpointer data)777 dialog_response (GtkDialog *dlg, gint response, gpointer data)
778 {
779 switch (response)
780 {
781 case GTK_RESPONSE_ACCEPT:
782 case GTK_RESPONSE_YES:
783 save_all_options ();
784 hide_backend_config ();
785 break;
786
787 case GTK_RESPONSE_CANCEL:
788 case GTK_RESPONSE_CLOSE:
789 case GTK_RESPONSE_REJECT:
790 case GTK_RESPONSE_NO:
791 case GTK_RESPONSE_DELETE_EVENT:
792 hide_backend_config ();
793 break;
794
795 case GTK_RESPONSE_APPLY:
796 dialog_response_apply (dlg);
797 break;
798
799 case CUSTOM_RESPONSE_RESET:
800 dialog_response_reset (dlg);
801 break;
802
803 default:
804 g_warning ("unhandled response: %i", response);
805 /* Do whatever is the default. */
806 return FALSE;
807 }
808
809 /* Don't do anything. We handled it all. */
810 return TRUE;
811 }
812
813
814 /* Determine the width of a checkbox. We use this to align labels
815 with and without checkboxes vertically. This approach is not
816 proper, as Gtk+ can not automatically adjust the padding when the
817 theme switches, but it is simple. */
818 static gint
get_checkbox_width(void)819 get_checkbox_width (void)
820 {
821 GtkWidget *checkbox;
822 GtkRequisition checkbox_size;
823
824 /* We do not save the result, as it may change with a theme change.
825 In this case we will at least refresh eventually. */
826
827 checkbox = gtk_check_button_new ();
828 gtk_widget_size_request (checkbox, &checkbox_size);
829 gtk_widget_destroy (checkbox);
830
831 return checkbox_size.width;
832 }
833
834
835 /* A callback to be used with gtk_container_foreach, which removes each
836 widget unconditionally. */
837 static void
remove_from_container(GtkWidget * widget,gpointer data)838 remove_from_container (GtkWidget *widget, gpointer data)
839 {
840 GtkWidget *container = data;
841
842 gtk_container_remove (GTK_CONTAINER (container), widget);
843 }
844
845
846 /* Return true iff the component COMP has any displayed options. */
847 static gboolean
comp_has_options(gpgme_conf_comp_t comp)848 comp_has_options (gpgme_conf_comp_t comp)
849 {
850 gpgme_conf_opt_t option;
851 gboolean has_options;
852
853 /* Skip over all components that do not have any options. This can
854 happen for example with old installed versions of components, or
855 if there are only options with a higher expert level. */
856 has_options = FALSE;
857 option = comp->options;
858 while (option)
859 {
860 if (option->level <= dialog_level)
861 {
862 has_options = TRUE;
863 break;
864 }
865 option = option->next;
866 }
867
868 return has_options;
869 }
870
871
872 /* Return true iff the group GROUP has any displayed options. If the
873 result is FALSE, also set NEXT_GROUP to the start of the next
874 group, or NULL if there is no more group. */
875 static gboolean
group_has_options(gpgme_conf_opt_t option,gpgme_conf_opt_t * next_group)876 group_has_options (gpgme_conf_opt_t option, gpgme_conf_opt_t *next_group)
877 {
878 gboolean has_options;
879
880 /* Skip the group header. */
881 option = option->next;
882
883 has_options = FALSE;
884 while (option && ! (option->flags & GPGME_CONF_GROUP))
885 {
886 if (option->level <= dialog_level)
887 {
888 has_options = TRUE;
889 break;
890 }
891 option = option->next;
892 }
893
894 if (! has_options && next_group)
895 *next_group = option;
896
897 return has_options;
898 }
899
900
901 static void
create_dialog_tabs_2(gpgme_conf_comp_t old_conf,gpgme_conf_comp_t new_conf)902 create_dialog_tabs_2 (gpgme_conf_comp_t old_conf, gpgme_conf_comp_t new_conf)
903 {
904 gpgme_conf_comp_t comp;
905 gpgme_conf_comp_t comp_alt;
906 int reset;
907 int page_nr;
908 gint checkbox_width = get_checkbox_width ();
909 /* We keep size groups for the field elements across all tabs for
910 visual consistency. */
911 GtkSizeGroup *size_group[2];
912
913 size_group[0] = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
914 size_group[1] = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL);
915
916 /* In most cases, the new components are the same as the old
917 components. We catch this special case here, and if it is true,
918 we reuse the existing tabs in the notebook below. Otherwise, we
919 reset everything. */
920 reset = 0;
921 comp = old_conf;
922 comp_alt = new_conf;
923 while (comp && comp_alt)
924 {
925 /* A mismatch is found if either the description changes, or if
926 a component changes from no options to some options or vice
927 verse (as we suppress generating tabs for components without
928 options below). */
929
930 if (strcmp (comp->description, comp_alt->description)
931 || comp_has_options (comp) != comp_has_options (comp_alt))
932 break;
933
934 comp = comp->next;
935 comp_alt = comp_alt->next;
936 }
937 if (comp || comp_alt)
938 reset = 1;
939
940 if (reset)
941 {
942 gtk_widget_hide (dialog_notebook);
943
944 /* Remove the current tabs. */
945 gtk_container_foreach (GTK_CONTAINER (dialog_notebook),
946 &remove_from_container, dialog_notebook);
947 }
948
949 comp = new_conf;
950 page_nr = 0;
951
952 while (comp)
953 {
954 GtkWidget *label;
955 GtkWidget *page;
956 gpgme_conf_opt_t option;
957 /* For each group in the component, we keep track of a frame,
958 and the table inside the frame. */
959 GtkWidget *frame = NULL;
960 GtkWidget *frame_vbox = NULL;
961
962 /* Skip over all components that do not have any options. This
963 can happen for example with old installed versions of
964 components, or if there are only options with a higher expert
965 level. */
966 if (! comp_has_options (comp))
967 {
968 comp = comp->next;
969 continue;
970 }
971
972 if (reset)
973 {
974 char *description;
975 /* FIXME: Might need to put pages into scrolled panes if
976 there are too many. */
977 page = gtk_vbox_new (FALSE, 0);
978 gtk_container_add (GTK_CONTAINER (dialog_notebook), page);
979
980 description = xstrdup (comp->description);
981 percent_unescape (description, 0);
982 label = gtk_label_new (description);
983 xfree (description);
984 gtk_notebook_set_tab_label
985 (GTK_NOTEBOOK (dialog_notebook),
986 gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook),
987 page_nr),
988 label);
989 }
990 else
991 {
992 page = gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook),
993 page_nr);
994
995 gtk_container_foreach (GTK_CONTAINER (page),
996 &remove_from_container, page);
997 }
998
999 option = comp->options;
1000 /* All ungrouped options come first. We put them in a frame
1001 titled "Main", just so we have a consistent layout. */
1002 if (option && ! (option->flags & GPGME_CONF_GROUP))
1003 {
1004 frame = gtk_frame_new (NULL);
1005 gtk_box_pack_start (GTK_BOX (page), frame, TRUE, TRUE, 0);
1006 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
1007
1008 label = gtk_label_new (_("<b>Main</b>"));
1009 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1010 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
1011
1012 frame_vbox = gtk_vbox_new (FALSE, 0);
1013 gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
1014 }
1015
1016 while (option)
1017 {
1018 if (option->flags & GPGME_CONF_GROUP)
1019 {
1020 char *name;
1021 const char *title;
1022
1023 if (! group_has_options (option, &option))
1024 continue;
1025
1026 frame = gtk_frame_new (NULL);
1027 gtk_box_pack_start (GTK_BOX (page), frame, FALSE, FALSE, 0);
1028 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_NONE);
1029
1030 /* For i18n reasons we use the description. It might be
1031 better to add a new field to privide a localized
1032 version of the Group name. Maybe the argname can be
1033 used for it. AFAICS, we would only need to prefix
1034 the description with the group name and gpgconf would
1035 instantly privide that. */
1036 title = (option->argname && *option->argname)?
1037 option->argname : option->description;
1038 name = g_strdup_printf ("<b>%s</b>", title);
1039 percent_unescape (name, 0);
1040 label = gtk_label_new (name);
1041 xfree (name);
1042
1043 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1044 gtk_frame_set_label_widget (GTK_FRAME (frame), label);
1045
1046 frame_vbox = gtk_vbox_new (FALSE, 0);
1047 gtk_container_add (GTK_CONTAINER (frame), frame_vbox);
1048 }
1049 else if (option->level <= dialog_level)
1050 {
1051 GtkWidget *vbox;
1052 GtkWidget *hbox;
1053 option_widget_t opt;
1054 GtkWidget *widget;
1055 GtkWidget *entry;
1056
1057 opt = g_new0 (struct option_widget_s, 1);
1058 opt->option = option;
1059 option->user_data = opt;
1060
1061 /* Add the entry fields for the option, depending on its
1062 type and flags. */
1063 if (option->alt_type == GPGME_CONF_NONE
1064 && !(option->flags & GPGME_CONF_LIST))
1065 opt->type = OPTION_SIMPLE;
1066 else if (option->alt_type == GPGME_CONF_NONE
1067 && (option->flags & GPGME_CONF_LIST))
1068 opt->type = OPTION_SPIN;
1069 else if (option->flags & GPGME_CONF_OPTIONAL
1070 && !(option->flags & GPGME_CONF_LIST))
1071 opt->type = OPTION_OPT_ENTRY;
1072 else
1073 opt->type = OPTION_ENTRY;
1074
1075 /* We create a vbox for the option, in case we want to
1076 support multi-hbox setups for options. Currently we
1077 only add one hbox to that vbox though. */
1078
1079 vbox = gtk_vbox_new (FALSE, 0);
1080 gtk_box_pack_start (GTK_BOX (frame_vbox), vbox, TRUE, TRUE, 0);
1081
1082 hbox = gtk_hbox_new (FALSE, 0);
1083 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
1084
1085 /* Add the entry fields for the option, depending on its
1086 type and flags. */
1087 if (opt->type == OPTION_SIMPLE)
1088 {
1089 widget = gtk_check_button_new_with_label (option->name);
1090 opt->check = widget;
1091 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1092
1093 /* Add the checkbox label to the size group. */
1094 gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
1095 widget);
1096 if (option->value)
1097 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
1098 TRUE);
1099 }
1100 else if (opt->type == OPTION_SPIN)
1101 {
1102 widget = gtk_check_button_new_with_label (option->name);
1103 opt->check = widget;
1104 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1105
1106 /* Add the checkbox label to the size group. */
1107 gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
1108 widget);
1109 if (option->value)
1110 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget),
1111 TRUE);
1112
1113 /* For list options without arguments, we use a
1114 simple spin button to capture the repeat
1115 count. */
1116 widget = gtk_spin_button_new_with_range (1, 100, 1);
1117 opt->widget = widget;
1118
1119 if (option->value)
1120 gtk_spin_button_set_value (GTK_SPIN_BUTTON (widget),
1121 option->value->value.count);
1122
1123 g_signal_connect ((gpointer) widget, "value-changed",
1124 G_CALLBACK (update_modified_cb), NULL);
1125
1126 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1127 }
1128 else
1129 {
1130 GtkWidget *align;
1131 guint pt, pb, pl, pr;
1132
1133 align = gtk_alignment_new (0, 0.5, 0, 0);
1134 gtk_alignment_get_padding (GTK_ALIGNMENT (align),
1135 &pt, &pb, &pl, &pr);
1136 gtk_alignment_set_padding (GTK_ALIGNMENT (align), pt, pb,
1137 pl + checkbox_width, pr);
1138 gtk_box_pack_start (GTK_BOX (hbox), align, FALSE, FALSE, 0);
1139
1140 widget = gtk_label_new (option->name);
1141 opt->check = widget;
1142 gtk_container_add (GTK_CONTAINER (align), widget);
1143
1144 /* Add the checkbox label to the size group. */
1145 gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[0]),
1146 align);
1147
1148 widget = gtk_combo_box_new_text ();
1149 gtk_box_pack_start (GTK_BOX (hbox), widget, FALSE, FALSE, 0);
1150 gtk_size_group_add_widget (GTK_SIZE_GROUP (size_group[1]),
1151 widget);
1152 opt->combo = widget;
1153
1154 entry = gtk_entry_new ();
1155 gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
1156
1157 opt->widget = entry;
1158
1159 g_signal_connect ((gpointer) entry, "changed",
1160 G_CALLBACK (update_modified_cb), NULL);
1161
1162 if (option->flags & GPGME_CONF_DEFAULT
1163 || option->flags & GPGME_CONF_DEFAULT_DESC)
1164 gtk_combo_box_append_text
1165 (GTK_COMBO_BOX (widget),
1166 (option->flags & GPGME_CONF_LIST) ?
1167 _("Use default values") : _("Use default value"));
1168 else
1169 gtk_combo_box_append_text
1170 (GTK_COMBO_BOX (widget), _("Do not use option"));
1171
1172 gtk_combo_box_append_text
1173 (GTK_COMBO_BOX (widget),
1174 (option->flags & GPGME_CONF_LIST) ?
1175 _("Use custom values") : _("Use custom value"));
1176
1177 if (opt->type == OPTION_OPT_ENTRY)
1178 gtk_combo_box_append_text
1179 (GTK_COMBO_BOX (widget), _("Use default argument"));
1180 }
1181
1182 /* Force update. */
1183 opt->old_combo_box_state = COMBO_UNDEF;
1184 update_option (opt);
1185
1186 if (opt->combo)
1187 g_signal_connect ((gpointer) opt->combo, "changed",
1188 G_CALLBACK (option_combobox_changed),
1189 opt);
1190 else
1191 g_signal_connect ((gpointer) opt->check, "toggled",
1192 G_CALLBACK (option_checkbutton_toggled),
1193 opt);
1194
1195 /* Grey out options which can not be changed at all. */
1196 if (option->flags & GPGME_CONF_NO_CHANGE)
1197 gtk_widget_set_sensitive (vbox, FALSE);
1198
1199 #if GTK_CHECK_VERSION (2, 12, 0)
1200 /* Add a tooltip description. */
1201 if (option->description)
1202 {
1203 char *description = xstrdup (option->description);
1204
1205 percent_unescape (description, 0);
1206 gtk_widget_set_tooltip_text (vbox, description);
1207 xfree (description);
1208 }
1209 #endif
1210 }
1211 option = option->next;
1212 }
1213 page_nr++;
1214 comp = comp->next;
1215 }
1216
1217 /* Release our references to the size groups. */
1218 g_object_unref (size_group[0]);
1219 g_object_unref (size_group[1]);
1220
1221 gtk_widget_show_all (dialog_notebook);
1222
1223 update_modified (0);
1224 }
1225
1226
1227 static void
create_dialog_tabs(void)1228 create_dialog_tabs (void)
1229 {
1230 gpgme_error_t err;
1231 gpgme_conf_comp_t new_conf;
1232 int page;
1233 int nr_pages;
1234 char *current_tab = NULL;
1235
1236 if (dialog_notebook)
1237 {
1238 /* Remember the current tab by its label. */
1239 page = gtk_notebook_get_current_page (GTK_NOTEBOOK (dialog_notebook));
1240 if (page >= 0)
1241 current_tab = strdup (gtk_notebook_get_tab_label_text
1242 (GTK_NOTEBOOK (dialog_notebook),
1243 gtk_notebook_get_nth_page
1244 (GTK_NOTEBOOK (dialog_notebook), page)));
1245 }
1246
1247 err = gpgme_op_conf_load (dialog_ctx, &new_conf);
1248 if (err)
1249 {
1250 gpa_gpgme_warning (err);
1251 return;
1252 }
1253
1254 create_dialog_tabs_2 (dialog_conf, new_conf);
1255 gpgme_conf_release (dialog_conf);
1256 dialog_conf = new_conf;
1257
1258 if (current_tab)
1259 {
1260 /* Rediscover the current tab. */
1261 page = 0;
1262 nr_pages = gtk_notebook_get_n_pages (GTK_NOTEBOOK (dialog_notebook));
1263 while (page < nr_pages)
1264 {
1265 const char *label = gtk_notebook_get_tab_label_text
1266 (GTK_NOTEBOOK (dialog_notebook),
1267 gtk_notebook_get_nth_page (GTK_NOTEBOOK (dialog_notebook), page));
1268 if (! strcmp (label, current_tab))
1269 break;
1270 page++;
1271 }
1272 if (page < nr_pages)
1273 gtk_notebook_set_current_page (GTK_NOTEBOOK (dialog_notebook), page);
1274
1275 free (current_tab);
1276 }
1277 }
1278
1279
1280 static void
dialog_level_chooser_cb(GtkComboBox * level_chooser,gpointer * data)1281 dialog_level_chooser_cb (GtkComboBox *level_chooser, gpointer *data)
1282 {
1283 gpgme_conf_level_t level;
1284
1285 level = gtk_combo_box_get_active (GTK_COMBO_BOX (level_chooser));
1286
1287 if (level == dialog_level)
1288 return;
1289
1290 if (dialog_tab_modified)
1291 {
1292 GtkWidget *window;
1293 GtkWidget *hbox;
1294 GtkWidget *labelMessage;
1295 GtkWidget *pixmap;
1296 gint result;
1297
1298 window = gtk_dialog_new_with_buttons
1299 (_("GPA Message"), (GtkWindow *) dialog, GTK_DIALOG_MODAL,
1300 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
1301 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
1302
1303 gtk_container_set_border_width (GTK_CONTAINER (window), 5);
1304 gtk_dialog_set_default_response (GTK_DIALOG (window),
1305 GTK_RESPONSE_CANCEL);
1306
1307 hbox = gtk_hbox_new (FALSE, 0);
1308 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1309 gtk_box_pack_start_defaults (GTK_BOX (GTK_DIALOG (window)->vbox), hbox);
1310 pixmap = gtk_image_new_from_stock (GTK_STOCK_DIALOG_INFO,
1311 GTK_ICON_SIZE_DIALOG);
1312 gtk_box_pack_start (GTK_BOX (hbox), pixmap, TRUE, FALSE, 10);
1313 labelMessage = gtk_label_new (_("There are unapplied changes by you. "
1314 "Changing the expert setting will apply "
1315 "those changes. Do you want to "
1316 "continue?"));
1317 gtk_label_set_line_wrap (GTK_LABEL (labelMessage), TRUE);
1318 gtk_box_pack_start (GTK_BOX (hbox), labelMessage, TRUE, FALSE, 10);
1319
1320 gtk_widget_show_all (window);
1321 result = gtk_dialog_run (GTK_DIALOG (window));
1322 gtk_widget_destroy (window);
1323
1324 if (result != GTK_RESPONSE_APPLY)
1325 {
1326 gtk_combo_box_set_active (GTK_COMBO_BOX (level_chooser),
1327 dialog_level);
1328 return;
1329 }
1330 }
1331
1332 save_all_options ();
1333
1334 /* Note: We know intimately that this matches GPGME_CONF_BASIC,
1335 GPGME_CONF_ADVANCED and GPGME_CONF_BEGINNER. */
1336 dialog_level = level;
1337 create_dialog_tabs ();
1338 }
1339
1340
1341 /* Return a new dialog widget. */
1342 static GtkDialog *
create_dialog(void)1343 create_dialog (void)
1344 {
1345 gpgme_error_t err;
1346 GtkWidget *dialog_vbox;
1347 GtkWidget *hbox;
1348 GtkWidget *level_chooser;
1349 GtkWidget *label;
1350 gint xpad;
1351 gint ypad;
1352
1353 /* Check error. */
1354 err = gpgme_new (&dialog_ctx);
1355 if (err)
1356 gpa_gpgme_error (err);
1357
1358 dialog = gtk_dialog_new_with_buttons (_("Crypto Backend Configuration"),
1359 NULL /* transient parent */,
1360 0,
1361 NULL);
1362 gtk_dialog_add_buttons (GTK_DIALOG (dialog),
1363 GTK_STOCK_APPLY, GTK_RESPONSE_APPLY,
1364 _("Reset"), CUSTOM_RESPONSE_RESET,
1365 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
1366 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1367 NULL );
1368 gtk_dialog_set_alternative_button_order (GTK_DIALOG (dialog),
1369 GTK_RESPONSE_ACCEPT,
1370 GTK_RESPONSE_CANCEL,
1371 CUSTOM_RESPONSE_RESET,
1372 GTK_RESPONSE_APPLY,
1373 -1);
1374
1375 g_signal_connect ((gpointer) dialog, "response",
1376 G_CALLBACK (dialog_response), NULL);
1377
1378 dialog_vbox = GTK_DIALOG (dialog)->vbox;
1379 /* gtk_box_set_spacing (GTK_CONTAINER (dialog_vbox), 5); */
1380
1381 hbox = gtk_hbox_new (FALSE, 0);
1382
1383 label = gtk_label_new (_("Configure the tools of the GnuPG system."));
1384 gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
1385 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1386
1387 label = gtk_label_new (_("Level:"));
1388 gtk_misc_get_padding (GTK_MISC (label), &xpad, &ypad);
1389 xpad += 5;
1390 gtk_misc_set_padding (GTK_MISC (label), xpad, ypad);
1391 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 0);
1392
1393 level_chooser = gtk_combo_box_new_text ();
1394 /* Note: We know intimately that this matches GPGME_CONF_BASIC,
1395 GPGME_CONF_ADVANCED and GPGME_CONF_BEGINNER. */
1396 gtk_combo_box_append_text (GTK_COMBO_BOX (level_chooser), _("Basic"));
1397 gtk_combo_box_append_text (GTK_COMBO_BOX (level_chooser), _("Advanced"));
1398 gtk_combo_box_append_text (GTK_COMBO_BOX (level_chooser), _("Expert"));
1399 g_signal_connect ((gpointer) level_chooser, "changed",
1400 G_CALLBACK (dialog_level_chooser_cb), NULL);
1401
1402 gtk_box_pack_start (GTK_BOX (hbox), level_chooser, FALSE, FALSE, 0);
1403 gtk_box_pack_start (GTK_BOX (dialog_vbox), hbox, FALSE, FALSE, 0);
1404 gtk_container_set_border_width (GTK_CONTAINER (hbox), 5);
1405 if (gpa_options_get_simplified_ui (gpa_options_get_instance ()))
1406 dialog_level = GPGME_CONF_BASIC;
1407 else
1408 dialog_level = GPGME_CONF_ADVANCED;
1409 gtk_combo_box_set_active (GTK_COMBO_BOX (level_chooser), dialog_level);
1410
1411 dialog_notebook = gtk_notebook_new ();
1412 gtk_container_set_border_width (GTK_CONTAINER (dialog_notebook), 5);
1413 gtk_box_pack_start (GTK_BOX (dialog_vbox), dialog_notebook, TRUE, TRUE, 0);
1414
1415 /* This should also be run on show after hide. */
1416 dialog_tab_modified = 0;
1417 create_dialog_tabs ();
1418
1419 return GTK_DIALOG (dialog);
1420 }
1421
1422
1423 GtkWidget *
gpa_backend_config_dialog_new(void)1424 gpa_backend_config_dialog_new (void)
1425 {
1426 assert (! dialog);
1427
1428 create_dialog ();
1429
1430 return dialog;
1431 }
1432
1433
1434 static void
hide_backend_config(void)1435 hide_backend_config (void)
1436 {
1437 gtk_widget_destroy (dialog);
1438 dialog = NULL;
1439 dialog_notebook = NULL;
1440
1441 gpgme_conf_release (dialog_conf);
1442 dialog_conf = NULL;
1443
1444 gpgme_release (dialog_ctx);
1445 dialog_ctx = NULL;
1446 }
1447
1448
1449
1450 /* Load the value of option NAME of component CNAME from the backend.
1451 If none is configured, return NULL. Caller must g_free the
1452 returned value. */
1453 char *
gpa_load_gpgconf_string(const char * cname,const char * name)1454 gpa_load_gpgconf_string (const char *cname, const char *name)
1455 {
1456 gpg_error_t err;
1457 gpgme_ctx_t ctx;
1458 gpgme_conf_comp_t conf_list, conf;
1459 gpgme_conf_opt_t opt;
1460 char *retval = NULL;
1461
1462 err = gpgme_new (&ctx);
1463 if (err)
1464 {
1465 gpa_gpgme_error (err);
1466 return NULL;
1467 }
1468
1469 err = gpgme_op_conf_load (ctx, &conf_list);
1470 if (err)
1471 {
1472 gpa_gpgme_warning (err);
1473 gpgme_release (ctx);
1474 return NULL;
1475 }
1476
1477 for (conf = conf_list; conf; conf = conf->next)
1478 {
1479 if ( !strcmp (conf->name, cname) )
1480 {
1481 for (opt = conf->options; opt; opt = opt->next)
1482 if ( !(opt->flags & GPGME_CONF_GROUP)
1483 && !strcmp (opt->name, name))
1484 {
1485 if (opt->value && opt->alt_type == GPGME_CONF_STRING)
1486 retval = g_strdup (opt->value->value.string);
1487 break;
1488 }
1489 break;
1490 }
1491 }
1492
1493 gpgme_conf_release (conf_list);
1494 gpgme_release (ctx);
1495 return retval;
1496 }
1497
1498
1499 /* Set the option NAME in component "CNAME" to VALUE. The option
1500 needs to be of type string. */
1501 void
gpa_store_gpgconf_string(const char * cname,const char * name,const char * value)1502 gpa_store_gpgconf_string (const char *cname,
1503 const char *name, const char *value)
1504 {
1505 gpg_error_t err;
1506 gpgme_ctx_t ctx;
1507 gpgme_conf_comp_t conf_list, conf;
1508 gpgme_conf_opt_t opt;
1509 gpgme_conf_arg_t arg;
1510
1511 err = gpgme_conf_arg_new (&arg, GPGME_CONF_STRING, (char*)value);
1512 if (err)
1513 {
1514 gpa_gpgme_warning (err);
1515 return;
1516 }
1517
1518 err = gpgme_new (&ctx);
1519 if (err)
1520 {
1521 gpa_gpgme_error (err);
1522 return;
1523 }
1524
1525 err = gpgme_op_conf_load (ctx, &conf_list);
1526 if (err)
1527 {
1528 gpa_gpgme_error (err);
1529 gpgme_release (ctx);
1530 return;
1531 }
1532
1533 for (conf = conf_list; conf; conf = conf->next)
1534 {
1535 if ( !strcmp (conf->name, cname) )
1536 {
1537 for (opt = conf->options; opt; opt = opt->next)
1538 if ( !(opt->flags & GPGME_CONF_GROUP)
1539 && !strcmp (opt->name, name))
1540 {
1541 if (opt->alt_type == GPGME_CONF_STRING
1542 && !args_are_equal (arg, opt->value, opt->alt_type))
1543 {
1544 err = gpgme_conf_opt_change (opt, 0, arg);
1545 if (err)
1546 gpa_gpgme_error (err);
1547 else
1548 {
1549 err = gpgme_op_conf_save (ctx, conf);
1550 if (err)
1551 gpa_gpgme_error (err);
1552 }
1553 }
1554 break;
1555 }
1556 break;
1557 }
1558 }
1559
1560 gpgme_conf_release (conf_list);
1561 gpgme_release (ctx);
1562 }
1563
1564
1565 /* Read the configured keyserver from the backend. If none is
1566 configured, return NULL. Caller must g_free the returned value. */
1567 char *
gpa_load_configured_keyserver(void)1568 gpa_load_configured_keyserver (void)
1569 {
1570 #ifdef ENABLE_KEYSERVER_SUPPORT
1571 return gpa_load_gpgconf_string ("gpg", "keyserver");
1572 #else
1573 return NULL;
1574 #endif
1575 }
1576
1577 /* Save the configured keyserver from the backend. If none is
1578 configured, return NULL. Caller must g_free the returned value. */
1579 void
gpa_store_configured_keyserver(const char * value)1580 gpa_store_configured_keyserver (const char *value)
1581 {
1582 #ifdef ENABLE_KEYSERVER_SUPPORT
1583 gpa_store_gpgconf_string ("gpg", "keyserver", value);
1584 #endif
1585 }
1586
1587
1588 /* Ask the user whether to configure GnuPG to use a keyserver. Return
1589 NULL if it could or shall not be configured or the name of the
1590 keyserver which needs to be g_freed. */
1591 char *
gpa_configure_keyserver(GtkWidget * parent)1592 gpa_configure_keyserver (GtkWidget *parent)
1593 {
1594 #ifdef ENABLE_KEYSERVER_SUPPORT
1595 GtkWidget *msgbox;
1596 char *keyserver;
1597
1598 msgbox = gtk_message_dialog_new
1599 (GTK_WINDOW(parent), GTK_DIALOG_MODAL,
1600 GTK_MESSAGE_WARNING, GTK_BUTTONS_NONE,
1601 "%s\n\n%s",
1602 _("A keyserver has not been configured."),
1603 _("Configure backend to use a keyserver?"));
1604 gtk_dialog_add_buttons (GTK_DIALOG (msgbox),
1605 _("_Yes"), GTK_RESPONSE_YES,
1606 _("_No"), GTK_RESPONSE_NO, NULL);
1607 if (gtk_dialog_run (GTK_DIALOG (msgbox)) != GTK_RESPONSE_YES)
1608 {
1609 gtk_widget_destroy (msgbox);
1610 return NULL;
1611 }
1612 gtk_widget_destroy (msgbox);
1613 gpa_store_configured_keyserver ("hkp://keys.gnupg.net");
1614 keyserver = gpa_load_configured_keyserver ();
1615 if (!keyserver)
1616 {
1617 gpa_show_warn
1618 (parent, NULL, _("Configuring the backend to use a keyserver failed"));
1619 return NULL;
1620 }
1621 return keyserver;
1622 #else
1623 (void)parent;
1624 return NULL
1625 #endif
1626 }
1627