1 /* -*-mode:c; c-style:k&r; c-basic-offset:4; -*- */
2 /* Balsa E-Mail Client
3 * Copyright (C) 1997-2013 Stuart Parmenter and others,
4 * See the file AUTHORS for a list.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public 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, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
19 * 02111-1307, USA.
20 */
21
22 #if defined(HAVE_CONFIG_H) && HAVE_CONFIG_H
23 # include "config.h"
24 #endif /* HAVE_CONFIG_H */
25 #include "identity.h"
26
27 #include "rfc3156.h"
28 #include "libbalsa.h"
29 #include "information.h"
30 #include "libbalsa-conf.h"
31 #include <glib/gi18n.h>
32 #include "misc.h"
33
34 #if HAVE_MACOSX_DESKTOP
35 # include "macosx-helpers.h"
36 #endif
37
38 #if ENABLE_ESMTP
39 #include <string.h>
40 #include "smtp-server.h"
41 #endif /* ENABLE_ESMTP */
42
43 /*
44 * The class.
45 */
46
47 static GObjectClass* parent_class;
48
49 /* Forward references. */
50 static void libbalsa_identity_class_init(LibBalsaIdentityClass* klass);
51 static void libbalsa_identity_init(LibBalsaIdentity* ident);
52 static void libbalsa_identity_finalize(GObject* object);
53
54 GType
libbalsa_identity_get_type()55 libbalsa_identity_get_type()
56 {
57 static GType libbalsa_identity_type = 0;
58
59 if (!libbalsa_identity_type) {
60 static const GTypeInfo libbalsa_identity_info = {
61 sizeof(LibBalsaIdentityClass),
62 NULL, /* base_init */
63 NULL, /* base_finalize */
64 (GClassInitFunc) libbalsa_identity_class_init,
65 NULL, /* class_finalize */
66 NULL, /* class_data */
67 sizeof(LibBalsaIdentity),
68 0, /* n_preallocs */
69 (GInstanceInitFunc) libbalsa_identity_init,
70 };
71
72 libbalsa_identity_type =
73 g_type_register_static(G_TYPE_OBJECT,
74 "LibBalsaIdentity",
75 &libbalsa_identity_info, 0);
76 }
77
78 return libbalsa_identity_type;
79 }
80
81 static void
libbalsa_identity_class_init(LibBalsaIdentityClass * klass)82 libbalsa_identity_class_init(LibBalsaIdentityClass* klass)
83 {
84 GObjectClass* object_class;
85
86 parent_class = g_type_class_peek_parent(klass);
87
88 object_class = G_OBJECT_CLASS(klass);
89 object_class->finalize = libbalsa_identity_finalize;
90 }
91
92 /*
93 * Instance inititialization function: set defaults for new objects.
94 */
95 static void
libbalsa_identity_init(LibBalsaIdentity * ident)96 libbalsa_identity_init(LibBalsaIdentity* ident)
97 {
98 ident->identity_name = NULL;
99 ident->ia = NULL;
100 ident->replyto = NULL;
101 ident->domain = NULL;
102 ident->bcc = NULL;
103 ident->reply_string = g_strdup(_("Re:"));
104 ident->forward_string = g_strdup(_("Fwd:"));
105 ident->send_mp_alternative = FALSE;
106 ident->signature_path = NULL;
107 ident->sig_executable = FALSE;
108 ident->sig_sending = TRUE;
109 ident->sig_whenforward = TRUE;
110 ident->sig_whenreply = TRUE;
111 ident->sig_separator = TRUE;
112 ident->sig_prepend = FALSE;
113 ident->gpg_sign = FALSE;
114 ident->gpg_encrypt = FALSE;
115 ident->always_trust = FALSE;
116 ident->warn_send_plain = TRUE;
117 ident->crypt_protocol = LIBBALSA_PROTECT_OPENPGP;
118 ident->force_key_id = NULL;
119 ident->request_mdn = FALSE;
120 /*
121 ident->face = NULL;
122 ident->x_face = NULL;
123 */
124 }
125
126 /*
127 * Destroy the object, freeing all the values in the process.
128 */
129 static void
libbalsa_identity_finalize(GObject * object)130 libbalsa_identity_finalize(GObject * object)
131 {
132 LibBalsaIdentity *ident = LIBBALSA_IDENTITY(object);
133
134 if (ident->ia)
135 g_object_unref(ident->ia);
136 g_free(ident->identity_name);
137 g_free(ident->replyto);
138 g_free(ident->domain);
139 g_free(ident->bcc);
140 g_free(ident->reply_string);
141 g_free(ident->forward_string);
142 g_free(ident->signature_path);
143 #if ENABLE_ESMTP
144 if (ident->smtp_server)
145 g_object_unref(ident->smtp_server);
146 #endif /* ENABLE_ESMTP */
147 g_free(ident->face);
148 g_free(ident->x_face);
149 g_free(ident->force_key_id);
150
151 G_OBJECT_CLASS(parent_class)->finalize(object);
152 }
153
154 /*
155 * Public methods.
156 */
157
158 /*
159 * Create a new object with the default identity name. Does not add
160 * it to the list of identities for the application.
161 */
162 GObject*
libbalsa_identity_new(void)163 libbalsa_identity_new(void)
164 {
165 return libbalsa_identity_new_with_name(_("New Identity"));
166 }
167
168
169 /*
170 * Create a new object with the specified identity name. Does not add
171 * it to the list of identities for the application.
172 */
173 GObject*
libbalsa_identity_new_with_name(const gchar * ident_name)174 libbalsa_identity_new_with_name(const gchar* ident_name)
175 {
176 LibBalsaIdentity* ident;
177
178 ident = g_object_new(LIBBALSA_TYPE_IDENTITY, NULL);
179 libbalsa_identity_set_identity_name(ident, ident_name);
180
181 return G_OBJECT(ident);
182 }
183
184
185 void
libbalsa_identity_set_identity_name(LibBalsaIdentity * ident,const gchar * name)186 libbalsa_identity_set_identity_name(LibBalsaIdentity* ident, const gchar* name)
187 {
188 g_return_if_fail(ident != NULL);
189
190 g_free(ident->identity_name);
191 ident->identity_name = g_strdup(name);
192 }
193
194
195 void
libbalsa_identity_set_address(LibBalsaIdentity * ident,InternetAddress * ia)196 libbalsa_identity_set_address(LibBalsaIdentity * ident,
197 InternetAddress * ia)
198 {
199 g_return_if_fail(ident != NULL);
200
201 if (ident->ia)
202 g_object_unref(ident->ia);
203 ident->ia = ia;
204 }
205
206
207 void
libbalsa_identity_set_replyto(LibBalsaIdentity * ident,const gchar * address)208 libbalsa_identity_set_replyto(LibBalsaIdentity* ident, const gchar* address)
209 {
210 g_return_if_fail(ident != NULL);
211
212 g_free(ident->replyto);
213 ident->replyto = g_strdup(address);
214 }
215
216
217 void
libbalsa_identity_set_domain(LibBalsaIdentity * ident,const gchar * dom)218 libbalsa_identity_set_domain(LibBalsaIdentity* ident, const gchar* dom)
219 {
220 g_return_if_fail(ident != NULL);
221
222 g_free(ident->domain);
223 ident->domain = g_strdup(dom);
224 }
225
226
227 void
libbalsa_identity_set_bcc(LibBalsaIdentity * ident,const gchar * bcc)228 libbalsa_identity_set_bcc(LibBalsaIdentity* ident, const gchar* bcc)
229 {
230 g_return_if_fail(ident != NULL);
231
232 g_free(ident->bcc);
233 ident->bcc = g_strdup(bcc);
234 }
235
236
237 void
libbalsa_identity_set_reply_string(LibBalsaIdentity * ident,const gchar * reply)238 libbalsa_identity_set_reply_string(LibBalsaIdentity* ident, const gchar* reply)
239 {
240 g_return_if_fail(ident != NULL);
241
242 g_free(ident->reply_string);
243 ident->reply_string = g_strdup(reply);
244 }
245
246
247 void
libbalsa_identity_set_forward_string(LibBalsaIdentity * ident,const gchar * forward)248 libbalsa_identity_set_forward_string(LibBalsaIdentity* ident, const gchar* forward)
249 {
250 g_return_if_fail(ident != NULL);
251
252 g_free(ident->forward_string);
253 ident->forward_string = g_strdup(forward);
254 }
255
256
257 void
libbalsa_identity_set_send_mp_alternative(LibBalsaIdentity * ident,gboolean send_mp_alternative)258 libbalsa_identity_set_send_mp_alternative(LibBalsaIdentity* ident, gboolean send_mp_alternative)
259 {
260 g_return_if_fail(ident != NULL);
261 ident->send_mp_alternative = send_mp_alternative;
262 }
263
264
265 void
libbalsa_identity_set_signature_path(LibBalsaIdentity * ident,const gchar * path)266 libbalsa_identity_set_signature_path(LibBalsaIdentity* ident, const gchar* path)
267 {
268 g_return_if_fail(ident != NULL);
269
270 g_free(ident->signature_path);
271 ident->signature_path = g_strdup(path);
272 }
273
274
275 void
libbalsa_identity_set_sig_executable(LibBalsaIdentity * ident,gboolean sig_executable)276 libbalsa_identity_set_sig_executable(LibBalsaIdentity* ident, gboolean sig_executable)
277 {
278 g_return_if_fail(ident != NULL);
279 ident->sig_executable = sig_executable;
280 }
281
282
283 void
libbalsa_identity_set_sig_sending(LibBalsaIdentity * ident,gboolean sig_sending)284 libbalsa_identity_set_sig_sending(LibBalsaIdentity* ident, gboolean sig_sending)
285 {
286 g_return_if_fail(ident != NULL);
287 ident->sig_sending = sig_sending;
288 }
289
290
291 void
libbalsa_identity_set_sig_whenforward(LibBalsaIdentity * ident,gboolean forward)292 libbalsa_identity_set_sig_whenforward(LibBalsaIdentity* ident, gboolean forward)
293 {
294 g_return_if_fail(ident != NULL);
295 ident->sig_whenforward = forward;
296 }
297
298
299 void
libbalsa_identity_set_sig_whenreply(LibBalsaIdentity * ident,gboolean reply)300 libbalsa_identity_set_sig_whenreply(LibBalsaIdentity* ident, gboolean reply)
301 {
302 g_return_if_fail(ident != NULL);
303 ident->sig_whenreply = reply;
304 }
305
306
307 void
libbalsa_identity_set_sig_separator(LibBalsaIdentity * ident,gboolean separator)308 libbalsa_identity_set_sig_separator(LibBalsaIdentity* ident, gboolean separator)
309 {
310 g_return_if_fail(ident != NULL);
311 ident->sig_separator = separator;
312 }
313
314
315 void
libbalsa_identity_set_sig_prepend(LibBalsaIdentity * ident,gboolean prepend)316 libbalsa_identity_set_sig_prepend(LibBalsaIdentity* ident, gboolean prepend)
317 {
318 g_return_if_fail(ident != NULL);
319 ident->sig_prepend = prepend;
320 }
321
322 /** Returns a signature for given identity, adding a signature prefix
323 if needed. parent can be NULL. */
324 gchar*
libbalsa_identity_get_signature(LibBalsaIdentity * identity,GtkWindow * parent)325 libbalsa_identity_get_signature(LibBalsaIdentity* identity, GtkWindow *parent)
326 {
327 gchar *ret = NULL, *path;
328
329 if (identity->signature_path == NULL ||
330 *identity->signature_path == '\0')
331 return NULL;
332
333 path = libbalsa_expand_path(identity->signature_path);
334 if(identity->sig_executable){
335 FILE *fp;
336
337 /* signature is executable */
338 fp = popen(path,"r");
339 if (fp) {
340 libbalsa_readfile_nostat(fp, &ret);
341 pclose(fp);
342 } else
343 libbalsa_information_parented
344 (parent, LIBBALSA_INFORMATION_ERROR,
345 _("Error executing signature generator %s"),
346 identity->signature_path);
347 } else {
348 FILE *fp;
349
350 /* sign is normal file */
351 fp = fopen(path, "r");
352 if (fp) {
353 libbalsa_readfile_nostat(fp, &ret);
354 fclose(fp);
355 } else
356 libbalsa_information_parented(parent, LIBBALSA_INFORMATION_ERROR,
357 _("Cannot open signature file '%s' "
358 "for reading"),
359 identity->signature_path);
360 }
361 if(!ret)
362 libbalsa_information_parented(parent, LIBBALSA_INFORMATION_ERROR,
363 _("Error reading signature from %s"), path);
364 else {
365 if(!libbalsa_utf8_sanitize(&ret, FALSE, NULL))
366 libbalsa_information_parented
367 (parent, LIBBALSA_INFORMATION_ERROR,
368 _("Signature in %s is not a UTF-8 text."),
369 identity->signature_path);
370 }
371 g_free(path);
372
373 if(ret == NULL) return NULL;
374
375 /* Prepend the separator if needed... */
376
377 if (identity->sig_separator
378 && strncmp(ret, "--\n", 3)
379 && strncmp(ret, "-- \n", 4)) {
380 gchar *sig_tmp = g_strconcat("\n-- \n", ret, NULL);
381 g_free(ret);
382 ret = sig_tmp;
383 } else {
384 gchar *sig_tmp = g_strconcat("\n", ret, NULL);
385 g_free(ret);
386 ret = sig_tmp;
387 }
388 return ret;
389 }
390
391 #if ENABLE_ESMTP
392 void
libbalsa_identity_set_smtp_server(LibBalsaIdentity * ident,LibBalsaSmtpServer * smtp_server)393 libbalsa_identity_set_smtp_server(LibBalsaIdentity * ident,
394 LibBalsaSmtpServer * smtp_server)
395 {
396 g_return_if_fail(ident != NULL);
397
398 if (ident->smtp_server)
399 g_object_unref(ident->smtp_server);
400 ident->smtp_server = smtp_server;
401 if (smtp_server)
402 g_object_ref(smtp_server);
403 }
404 #endif /* ENABLE_ESMTP */
405
406
407 /* Used by both dialogs: */
408
409 /* Widget padding: */
410 static const guint padding = 12;
411
412 /* Forward references: */
413 static void identity_list_update_real(GtkTreeView * tree,
414 GList * identities,
415 LibBalsaIdentity * default_id);
416 static GtkWidget *libbalsa_identity_tree(GCallback toggled_cb,
417 gpointer toggled_data,
418 gchar * toggled_title);
419
420 /*
421 * The Select Identity dialog; called from compose window.
422 */
423
424 /* Info passed to callbacks: */
425 struct SelectDialogInfo_ {
426 LibBalsaIdentityCallback update;
427 gpointer data;
428 GtkWidget *tree;
429 GtkWidget *dialog;
430 GtkWindow *parent;
431 guint idle_handler_id;
432 };
433 typedef struct SelectDialogInfo_ SelectDialogInfo;
434
435 /* Forward references: */
436 static void sd_destroy_notify(SelectDialogInfo * sdi);
437 static void sd_response_cb(GtkWidget * dialog, gint response,
438 SelectDialogInfo * sdi);
439 static void sd_idle_add_response_ok(SelectDialogInfo * sdi);
440 static gboolean sd_response_ok(SelectDialogInfo * sdi);
441
442 /* Tree columns: */
443 enum {
444 DEFAULT_COLUMN,
445 NAME_COLUMN,
446 IDENT_COLUMN,
447 N_COLUMNS
448 };
449
450 /*
451 * Public method: create and show the dialog.
452 */
453 #define LIBBALSA_IDENTITY_SELECT_DIALOG_KEY "libbalsa-identity-select-dialog"
454 void
libbalsa_identity_select_dialog(GtkWindow * parent,const gchar * prompt,GList * identities,LibBalsaIdentity * initial_id,LibBalsaIdentityCallback update,gpointer data)455 libbalsa_identity_select_dialog(GtkWindow * parent,
456 const gchar * prompt,
457 GList * identities,
458 LibBalsaIdentity * initial_id,
459 LibBalsaIdentityCallback update,
460 gpointer data)
461 {
462 GtkWidget *dialog;
463 GtkWidget *tree;
464 SelectDialogInfo *sdi;
465 GtkWidget *frame;
466
467 /* Show only one dialog at a time. */
468 sdi = g_object_get_data(G_OBJECT(parent),
469 LIBBALSA_IDENTITY_SELECT_DIALOG_KEY);
470 if (sdi) {
471 gtk_window_present(GTK_WINDOW(sdi->dialog));
472 return;
473 }
474
475 sdi = g_new(SelectDialogInfo, 1);
476 sdi->parent = parent;
477 g_object_set_data_full(G_OBJECT(parent),
478 LIBBALSA_IDENTITY_SELECT_DIALOG_KEY,
479 sdi, (GDestroyNotify) sd_destroy_notify);
480 sdi->update = update;
481 sdi->data = data;
482 sdi->idle_handler_id = 0;
483 sdi->dialog = dialog =
484 gtk_dialog_new_with_buttons(prompt, parent,
485 GTK_DIALOG_DESTROY_WITH_PARENT,
486 GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
487 GTK_STOCK_OK, GTK_RESPONSE_OK,
488 NULL);
489 #if HAVE_MACOSX_DESKTOP
490 libbalsa_macosx_menu_for_parent(dialog, parent);
491 #endif
492
493 g_signal_connect(dialog, "response",
494 G_CALLBACK(sd_response_cb), sdi);
495 gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_OK);
496
497 sdi->tree = tree =
498 libbalsa_identity_tree(G_CALLBACK(sd_idle_add_response_ok), sdi,
499 _("Current"));
500 g_signal_connect_swapped(tree, "row-activated",
501 G_CALLBACK(sd_idle_add_response_ok), sdi);
502 identity_list_update_real(GTK_TREE_VIEW(tree), identities, initial_id);
503
504 frame = gtk_frame_new(NULL);
505 gtk_box_pack_start(GTK_BOX
506 (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
507 frame, TRUE, TRUE, 0);
508 gtk_container_add(GTK_CONTAINER(frame), tree);
509 gtk_container_set_border_width(GTK_CONTAINER(frame), padding);
510
511 gtk_widget_show_all(dialog);
512 gtk_widget_grab_focus(tree);
513 }
514
515 /* GDestroyNotify for sdi. */
516 static void
sd_destroy_notify(SelectDialogInfo * sdi)517 sd_destroy_notify(SelectDialogInfo * sdi)
518 {
519 if (sdi->idle_handler_id) {
520 g_source_remove(sdi->idle_handler_id);
521 sdi->idle_handler_id = 0;
522 }
523 g_free(sdi);
524 }
525
526 /* Callback for the dialog's "response" signal. */
527 static void
sd_response_cb(GtkWidget * dialog,gint response,SelectDialogInfo * sdi)528 sd_response_cb(GtkWidget * dialog, gint response, SelectDialogInfo * sdi)
529 {
530 if (response == GTK_RESPONSE_OK) {
531 GtkTreeSelection *selection =
532 gtk_tree_view_get_selection(GTK_TREE_VIEW(sdi->tree));
533 GtkTreeModel *model;
534 GtkTreeIter iter;
535
536 if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
537 LibBalsaIdentity *identity;
538
539 gtk_tree_model_get(model, &iter, IDENT_COLUMN, &identity, -1);
540 sdi->update(sdi->data, identity);
541 }
542 }
543
544 /* Clear the data set on the parent window, so we know that the
545 * dialog was destroyed. This will also trigger the GDestroyNotify
546 * function, sd_destroy_notify.
547 */
548 g_object_set_data(G_OBJECT(sdi->parent),
549 LIBBALSA_IDENTITY_SELECT_DIALOG_KEY,
550 NULL);
551
552 gtk_widget_destroy(dialog);
553 }
554
555 /* Helper for adding idles. */
556 static void
sd_idle_add_response_ok(SelectDialogInfo * sdi)557 sd_idle_add_response_ok(SelectDialogInfo * sdi)
558 {
559 if (!sdi->idle_handler_id)
560 sdi->idle_handler_id =
561 g_idle_add((GSourceFunc) sd_response_ok, sdi);
562 }
563
564 /* Idle handler for sending the OK response to the dialog. */
565 static gboolean
sd_response_ok(SelectDialogInfo * sdi)566 sd_response_ok(SelectDialogInfo * sdi)
567 {
568 gdk_threads_enter();
569 if (sdi->idle_handler_id) {
570 sdi->idle_handler_id = 0;
571 gtk_dialog_response(GTK_DIALOG(sdi->dialog), GTK_RESPONSE_OK);
572 }
573 gdk_threads_leave();
574 return FALSE;
575 }
576
577 /*
578 * The Manage Identities dialog; called from main window.
579 */
580
581 #define ELEMENTS(x) (sizeof (x) / sizeof (x[0]))
582 typedef struct _IdentityDeleteInfo IdentityDeleteInfo;
583
584 /* button actions */
585 static gboolean close_cb(GObject * dialog);
586 static void new_ident_cb(GtkTreeView * tree, GObject * dialog);
587 static void delete_ident_cb(GtkTreeView * tree, GtkWidget * dialog);
588 static void delete_ident_response(GtkWidget * confirm, gint response,
589 IdentityDeleteInfo * di);
590 static void help_ident_cb(GtkWidget * widget);
591
592 static void set_default_ident_cb(GtkTreeView * tree, GtkTreePath * path,
593 GtkTreeViewColumn * column,
594 gpointer data);
595 static void config_frame_button_select_cb(GtkTreeSelection * selection,
596 GtkDialog * dialog);
597
598 static void ident_dialog_add_checkbutton(GtkWidget *, gint, GtkDialog *,
599 const gchar *, const gchar *,
600 gboolean sensitive);
601 static void ident_dialog_add_check_and_entry(GtkWidget *, gint, GtkDialog *,
602 const gchar *, const gchar *);
603 static void ident_dialog_add_entry(GtkWidget *, gint, GtkDialog *,
604 const gchar *, const gchar *);
605 typedef enum LibBalsaIdentityPathType_ {
606 LBI_PATH_TYPE_FACE,
607 LBI_PATH_TYPE_XFACE
608 } LibBalsaIdentityPathType;
609 static void ident_dialog_add_file_chooser_button(GtkWidget * grid,
610 gint row,
611 GtkDialog * dialog,
612 LibBalsaIdentityPathType
613 type);
614 static void ident_dialog_add_boxes(GtkWidget * grid, gint row,
615 GtkDialog * dialog, const gchar * key1,
616 const gchar * key2);
617 #if ENABLE_ESMTP
618 #endif /* ENABLE_ESMTP */
619 static gchar *ident_dialog_get_text(GObject *, const gchar *);
620 static gboolean ident_dialog_get_bool(GObject *, const gchar *);
621 static gchar *ident_dialog_get_path(GObject * dialog, const gchar * key);
622 static gboolean ident_dialog_update(GObject *);
623 static void config_dialog_select(GtkTreeSelection * selection,
624 GtkDialog * dialog);
625
626 static void display_frame_update(GObject * dialog, LibBalsaIdentity* ident);
627 static void display_frame_set_field(GObject * dialog, const gchar* key,
628 const gchar* value);
629 static void display_frame_set_boolean(GObject * dialog, const gchar* key,
630 gboolean value);
631 static void display_frame_set_path(GObject * dialog, const gchar * key,
632 const gchar * value, gboolean use_chooser);
633
634
635 static void identity_list_update(GtkTreeView * tree);
636 static gboolean select_identity(GtkTreeView * tree,
637 LibBalsaIdentity * identity);
638 static LibBalsaIdentity *get_selected_identity(GtkTreeView * tree);
639 static void set_identity_name_in_tree(GtkTreeView * tree,
640 LibBalsaIdentity * identity,
641 const gchar * name);
642 static void md_response_cb(GtkWidget * dialog, gint response,
643 GtkTreeView * tree);
644 static void md_name_changed(GtkEntry * name, GtkTreeView * tree);
645
646 static void ident_dialog_add_gpg_menu(GtkWidget * grid, gint row,
647 GtkDialog * dialog,
648 const gchar * label_name,
649 const gchar * menu_key);
650 static void add_show_menu(const char *label, gpointer data,
651 GtkWidget * menu);
652 static void ident_dialog_free_values(GPtrArray * values);
653
654 static void display_frame_set_gpg_mode(GObject * dialog,
655 const gchar * key, gint * value);
656
657 #if ENABLE_ESMTP
658 static void ident_dialog_add_smtp_menu(GtkWidget * grid, gint row,
659 GtkDialog * dialog,
660 const gchar * label_name,
661 const gchar * menu_key,
662 GSList * smtp_servers);
663 static void display_frame_set_server(GObject * dialog,
664 const gchar * key,
665 LibBalsaSmtpServer * smtp_server);
666 #endif /* ENABLE_ESMTP */
667
668 static gpointer ident_dialog_get_value(GObject * dialog,
669 const gchar * key);
670
671 /* Callback for the "toggled" signal of the "Default" column. */
672 static void
toggle_cb(GObject * dialog,gchar * path)673 toggle_cb(GObject * dialog, gchar * path)
674 {
675 GtkTreeView *tree = g_object_get_data(dialog, "tree");
676 GtkTreeModel *model = gtk_tree_view_get_model(tree);
677 GtkTreeIter iter;
678
679 /* Save any changes to current identity; if it's not valid, just
680 * return. */
681 if (!ident_dialog_update(dialog))
682 return;
683
684 if (gtk_tree_model_get_iter_from_string(model, &iter, path)) {
685 LibBalsaIdentity *identity, **default_id;
686
687 gtk_tree_model_get(model, &iter, IDENT_COLUMN, &identity, -1);
688 default_id = g_object_get_data(G_OBJECT(tree), "default-id");
689 *default_id = identity;
690 identity_list_update(tree);
691 }
692 }
693
694 /*
695 * Common code for making a GtkTreeView list of identities:
696 *
697 * toggled_cb callback for the "toggled" signal of the boolean
698 * column;
699 * toggled_data user_data for the callback;
700 * toggled_title title for the boolean column.
701 */
702 static GtkWidget *
libbalsa_identity_tree(GCallback toggled_cb,gpointer toggled_data,gchar * toggled_title)703 libbalsa_identity_tree(GCallback toggled_cb, gpointer toggled_data,
704 gchar * toggled_title)
705 {
706 GtkListStore *store;
707 GtkWidget *tree;
708 GtkCellRenderer *renderer;
709 GtkTreeViewColumn *column;
710
711 store = gtk_list_store_new(N_COLUMNS,
712 G_TYPE_BOOLEAN,
713 G_TYPE_STRING,
714 G_TYPE_POINTER);
715
716 tree = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
717 g_object_unref(store);
718
719 renderer = gtk_cell_renderer_toggle_new();
720 g_signal_connect_swapped(renderer, "toggled",
721 toggled_cb, toggled_data);
722 column =
723 gtk_tree_view_column_new_with_attributes(toggled_title, renderer,
724 "radio", DEFAULT_COLUMN,
725 NULL);
726 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
727
728 renderer = gtk_cell_renderer_text_new();
729 column = gtk_tree_view_column_new_with_attributes("Name", renderer,
730 "text", NAME_COLUMN,
731 NULL);
732 gtk_tree_view_append_column(GTK_TREE_VIEW(tree), column);
733
734 return tree;
735 }
736
737 /*
738 * Create and return a frame containing a list of the identities in
739 * the application and a number of buttons to edit, create, and delete
740 * identities. Also provides a way to set the default identity.
741 */
742 static GtkWidget*
libbalsa_identity_config_frame(GList ** identities,LibBalsaIdentity ** defid,GtkWidget * dialog,void (* cb)(gpointer),gpointer data)743 libbalsa_identity_config_frame(GList** identities,
744 LibBalsaIdentity** defid, GtkWidget * dialog,
745 void (*cb)(gpointer), gpointer data)
746 {
747 GtkWidget* config_frame = gtk_frame_new(NULL);
748 GtkWidget *tree;
749
750 gtk_container_set_border_width(GTK_CONTAINER(config_frame), 0);
751
752 tree = libbalsa_identity_tree(G_CALLBACK(toggle_cb), dialog,
753 _("Default"));
754 g_signal_connect(tree, "row-activated",
755 G_CALLBACK(set_default_ident_cb), NULL);
756 g_object_set_data(G_OBJECT(tree), "identities", identities);
757 g_object_set_data(G_OBJECT(tree), "default-id", defid);
758 g_object_set_data(G_OBJECT(tree), "callback", cb);
759 g_object_set_data(G_OBJECT(tree), "cb-data", data);
760
761 gtk_container_add(GTK_CONTAINER(config_frame), tree);
762
763 identity_list_update(GTK_TREE_VIEW(tree));
764
765 return config_frame;
766 }
767
768 static gint
compare_identities(LibBalsaIdentity * id1,LibBalsaIdentity * id2)769 compare_identities(LibBalsaIdentity *id1, LibBalsaIdentity *id2)
770 {
771 return g_ascii_strcasecmp(id1->identity_name, id2->identity_name);
772 }
773
774 /* identity_list_update:
775 * Update the list of identities in the config frame, displaying the
776 * available identities in the application, and which is default.
777 */
778 static void
identity_list_update(GtkTreeView * tree)779 identity_list_update(GtkTreeView * tree)
780 {
781 GList **identities =
782 g_object_get_data(G_OBJECT(tree), "identities");
783 LibBalsaIdentity **default_id =
784 g_object_get_data(G_OBJECT(tree), "default-id");
785
786 identity_list_update_real(tree, *identities, *default_id);
787 }
788
789 static void
identity_list_update_real(GtkTreeView * tree,GList * identities,LibBalsaIdentity * default_id)790 identity_list_update_real(GtkTreeView * tree,
791 GList * identities,
792 LibBalsaIdentity * default_id)
793 {
794 GtkListStore *store = GTK_LIST_STORE(gtk_tree_view_get_model(tree));
795 GList *sorted, *list;
796 LibBalsaIdentity *current;
797 GtkTreeIter iter;
798
799 current = get_selected_identity(tree);
800
801 gtk_list_store_clear(store);
802
803 sorted = g_list_sort(g_list_copy(identities),
804 (GCompareFunc) compare_identities);
805 for (list = sorted; list; list = g_list_next(list)) {
806 LibBalsaIdentity* ident = LIBBALSA_IDENTITY(list->data);
807 gtk_list_store_append(store, &iter);
808 gtk_list_store_set(store, &iter,
809 DEFAULT_COLUMN, ident == default_id,
810 NAME_COLUMN, ident->identity_name,
811 IDENT_COLUMN, ident,
812 -1);
813 }
814 g_list_free(sorted);
815
816 if (!select_identity(tree, current))
817 select_identity(tree, default_id);
818 }
819
820 static gboolean
select_identity(GtkTreeView * tree,LibBalsaIdentity * identity)821 select_identity(GtkTreeView * tree, LibBalsaIdentity * identity)
822 {
823 GtkTreeModel *model = gtk_tree_view_get_model(tree);
824 GtkTreeIter iter;
825 gboolean valid;
826
827 for (valid = gtk_tree_model_get_iter_first(model, &iter);
828 valid;
829 valid = gtk_tree_model_iter_next(model, &iter)) {
830 LibBalsaIdentity *tmp;
831
832 gtk_tree_model_get(model, &iter, IDENT_COLUMN, &tmp, -1);
833 if (identity == tmp) {
834 GtkTreePath *path = gtk_tree_model_get_path(model, &iter);
835 gtk_tree_view_set_cursor(tree, path, NULL, FALSE);
836 gtk_tree_path_free(path);
837
838 return TRUE;
839 }
840 }
841
842 return FALSE;
843 }
844
845 static LibBalsaIdentity *
get_selected_identity(GtkTreeView * tree)846 get_selected_identity(GtkTreeView * tree)
847 {
848 GtkTreeSelection *select = gtk_tree_view_get_selection(tree);
849 GtkTreeModel *model = gtk_tree_view_get_model(tree);
850 GtkTreeIter iter;
851 LibBalsaIdentity *identity = NULL;
852
853 if (gtk_tree_selection_get_selected(select, &model, &iter))
854 gtk_tree_model_get(model, &iter, IDENT_COLUMN, &identity, -1);
855
856 return identity;
857 }
858
859 enum {
860 IDENTITY_RESPONSE_HELP = GTK_RESPONSE_HELP,
861 IDENTITY_RESPONSE_CLOSE,
862 IDENTITY_RESPONSE_NEW,
863 IDENTITY_RESPONSE_REMOVE
864 };
865
866 /* callback for the "changed" signal */
867 static void
config_frame_button_select_cb(GtkTreeSelection * selection,GtkDialog * dialog)868 config_frame_button_select_cb(GtkTreeSelection * selection,
869 GtkDialog * dialog)
870 {
871 config_dialog_select(selection, dialog);
872 }
873
874 /*
875 * Callback for the close button.
876 * Call ident_dialog_update to save any changes, and close the dialog if
877 * OK.
878 */
879 static gboolean
close_cb(GObject * dialog)880 close_cb(GObject * dialog)
881 {
882 return ident_dialog_update(dialog);
883 }
884
885 /*
886 * Create a new identity
887 */
888 static void
new_ident_cb(GtkTreeView * tree,GObject * dialog)889 new_ident_cb(GtkTreeView * tree, GObject * dialog)
890 {
891 LibBalsaIdentity *ident;
892 GList **identities;
893 GtkWidget *name_entry;
894 void (*cb)(gpointer) = g_object_get_data(G_OBJECT(tree), "callback");
895 gpointer data = g_object_get_data(G_OBJECT(tree), "cb-data");
896
897 /* Save any changes to current identity; if it's not valid, just
898 * return. */
899 if (!ident_dialog_update(dialog))
900 return;
901
902 ident = LIBBALSA_IDENTITY(libbalsa_identity_new());
903 identities = g_object_get_data(G_OBJECT(tree), "identities");
904 *identities = g_list_append(*identities, ident);
905 identity_list_update(tree);
906 /* select just added identity */
907 select_identity(tree, ident);
908
909 name_entry = g_object_get_data(dialog, "identity-name");
910 gtk_widget_grab_focus(name_entry);
911 cb(data);
912 }
913
914
915 /*
916 * Helper: append a notebook page containing a table with the requested
917 * number of rows
918 */
919 static GtkWidget*
append_ident_notebook_page(GtkNotebook * notebook,guint rows,const gchar * tab_label,const gchar * footnote)920 append_ident_notebook_page(GtkNotebook *notebook, guint rows,
921 const gchar * tab_label,
922 const gchar * footnote)
923 {
924 GtkWidget *vbox;
925 GtkWidget *grid;
926
927 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
928 grid = libbalsa_create_grid();
929 gtk_container_set_border_width(GTK_CONTAINER(grid), padding);
930 gtk_box_pack_start(GTK_BOX(vbox), grid, FALSE, FALSE, 0);
931 if (footnote) {
932 GtkWidget *label;
933
934 label = gtk_label_new(footnote);
935 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
936 gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);
937 }
938 gtk_notebook_append_page(notebook, vbox, gtk_label_new(tab_label));
939
940 return grid;
941 }
942
943
944 /*
945 * Put the required GtkEntries, Labels, and Checkbuttons in the dialog
946 * for creating/editing identities.
947 */
948 struct {
949 const gchar *mnemonic;
950 const gchar *path_key;
951 const gchar *box_key;
952 const gchar *basename;
953 const gchar *info;
954 } static const path_info[] = {
955 /* Translators: please do not translate Face. */
956 {N_("_Face Path"),
957 "identity-facepath",
958 "identity-facebox",
959 ".face",
960 "Face"},
961 /* Translators: please do not translate Face. */
962 {N_("_X-Face Path"),
963 "identity-xfacepath",
964 "identity-xfacebox",
965 ".xface",
966 "X-Face"}
967 };
968
969 #define LIBBALSA_IDENTITY_CHECK "libbalsa-identity-check"
970 static void md_sig_path_changed_cb(GtkToggleButton * sig_button,
971 GObject * dialog);
972
973 #if ENABLE_ESMTP
974 static GtkWidget*
setup_ident_frame(GtkDialog * dialog,gboolean createp,gpointer tree,GSList * smtp_servers)975 setup_ident_frame(GtkDialog * dialog, gboolean createp, gpointer tree,
976 GSList * smtp_servers)
977 #else /* ENABLE_ESMTP */
978 static GtkWidget*
979 setup_ident_frame(GtkDialog * dialog, gboolean createp, gpointer tree)
980 #endif /* ENABLE_ESMTP */
981 {
982 GtkNotebook *notebook = GTK_NOTEBOOK(gtk_notebook_new());
983 GtkWidget *grid;
984 gint row;
985 GObject *name;
986 gpointer path;
987 gchar *footnote;
988
989 /* create the "General" tab */
990 grid = append_ident_notebook_page(notebook, 5, _("General"), NULL);
991 row = 0;
992 ident_dialog_add_entry(grid, row++, dialog, _("_Identity name:"),
993 "identity-name");
994 ident_dialog_add_entry(grid, row++, dialog, _("_Full name:"),
995 "identity-fullname");
996 ident_dialog_add_entry(grid, row++, dialog, _("_Mailing address:"),
997 "identity-address");
998 ident_dialog_add_entry(grid, row++, dialog, _("Reply _to:"),
999 "identity-replyto");
1000 ident_dialog_add_entry(grid, row++, dialog, _("_Domain:"),
1001 "identity-domain");
1002
1003 /* create the "Messages" tab */
1004 grid = append_ident_notebook_page(notebook, 9, _("Messages"), NULL);
1005 row = 0;
1006 ident_dialog_add_entry(grid, row++, dialog, _("_Bcc:"),
1007 "identity-bcc");
1008 ident_dialog_add_entry(grid, row++, dialog, _("Reply _string:"),
1009 "identity-replystring");
1010 ident_dialog_add_entry(grid, row++, dialog, _("F_orward string:"),
1011 "identity-forwardstring");
1012 ident_dialog_add_checkbutton(grid, row++, dialog,
1013 _("send messages in both plain text and _HTML format"),
1014 "identity-sendmpalternative", TRUE);
1015 ident_dialog_add_checkbutton(grid, row++, dialog,
1016 _("request _Message Disposition Notification by default"),
1017 "identity-requestmdn", TRUE);
1018 ident_dialog_add_file_chooser_button(grid, row++, dialog,
1019 LBI_PATH_TYPE_FACE);
1020 ident_dialog_add_file_chooser_button(grid, row++, dialog,
1021 LBI_PATH_TYPE_XFACE);
1022 ident_dialog_add_boxes(grid, row++, dialog,
1023 path_info[LBI_PATH_TYPE_FACE].box_key,
1024 path_info[LBI_PATH_TYPE_XFACE].box_key);
1025 #if ENABLE_ESMTP
1026 ident_dialog_add_smtp_menu(grid, row++, dialog, _("SMT_P server:"),
1027 "identity-smtp-server", smtp_servers);
1028 #endif /* ENABLE_ESMTP */
1029
1030 /* create the "Signature" tab */
1031 grid = append_ident_notebook_page(notebook, 7, _("Signature"), NULL);
1032 row = 0;
1033 ident_dialog_add_check_and_entry(grid, row++, dialog,
1034 _("Signature _path"),
1035 "identity-sigpath");
1036 ident_dialog_add_checkbutton(grid, row++, dialog,
1037 _("_Execute signature"),
1038 "identity-sigexecutable", FALSE);
1039 ident_dialog_add_checkbutton(grid, row++, dialog,
1040 _("Incl_ude signature"),
1041 "identity-sigappend", FALSE);
1042 ident_dialog_add_checkbutton(grid, row++, dialog,
1043 _("Include signature when for_warding"),
1044 "identity-whenforward", FALSE);
1045 ident_dialog_add_checkbutton(grid, row++, dialog,
1046 _("Include signature when rep_lying"),
1047 "identity-whenreply", FALSE);
1048 ident_dialog_add_checkbutton(grid, row++, dialog,
1049 _("_Add signature separator"),
1050 "identity-sigseparator", FALSE);
1051 ident_dialog_add_checkbutton(grid, row++, dialog,
1052 _("Prepend si_gnature"),
1053 "identity-sigprepend", FALSE);
1054
1055 #ifdef HAVE_GPGME
1056 footnote = NULL;
1057 #else
1058 footnote = _("Signing and encrypting messages are possible "
1059 "only if Balsa is built with cryptographic support.");
1060 #endif
1061 /* create the "Security" tab */
1062 grid =
1063 append_ident_notebook_page(notebook, 5, _("Security"), footnote);
1064 row = 0;
1065 ident_dialog_add_checkbutton(grid, row++, dialog,
1066 _("sign messages by default"),
1067 "identity-gpgsign", TRUE);
1068 ident_dialog_add_checkbutton(grid, row++, dialog,
1069 _("encrypt messages by default"),
1070 "identity-gpgencrypt", TRUE);
1071 ident_dialog_add_gpg_menu(grid, row++, dialog,
1072 _("default protocol"),
1073 "identity-crypt-protocol");
1074 ident_dialog_add_checkbutton(grid, row++, dialog,
1075 _("always trust GnuPG keys when encrypting"),
1076 "identity-trust-always", TRUE);
1077 ident_dialog_add_checkbutton(grid, row++, dialog,
1078 _("remind me if messages can be encrypted"),
1079 "identity-warn-send-plain", TRUE);
1080 ident_dialog_add_entry(grid, row++, dialog,
1081 _("use secret key with this id for signing "
1082 "(leave empty for automatic selection)"),
1083 "identity-keyid");
1084 #ifndef HAVE_GPGME
1085 gtk_widget_set_sensitive(grid, FALSE);
1086 #endif
1087
1088 name = g_object_get_data(G_OBJECT(dialog), "identity-name");
1089 g_signal_connect(name, "changed",
1090 G_CALLBACK(md_name_changed), tree);
1091
1092 path = g_object_get_data(G_OBJECT(dialog), "identity-sigpath");
1093 g_signal_connect(g_object_get_data(G_OBJECT(path),
1094 LIBBALSA_IDENTITY_CHECK),
1095 "toggled",
1096 G_CALLBACK(md_sig_path_changed_cb), dialog);
1097
1098 gtk_notebook_set_current_page(notebook, 0);
1099
1100 return GTK_WIDGET(notebook);
1101 }
1102
1103 /* Callback for the "changed" signal of the name entry; updates the name
1104 * in the tree. */
1105 static void
md_name_changed(GtkEntry * name,GtkTreeView * tree)1106 md_name_changed(GtkEntry * name, GtkTreeView * tree)
1107 {
1108 set_identity_name_in_tree(tree, get_selected_identity(tree),
1109 gtk_entry_get_text(name));
1110 }
1111
1112 /*
1113 * Create and add a GtkCheckButton to the given dialog with caption
1114 * and add a pointer to it stored under the given key. The check
1115 * button is initialized to the given value.
1116 */
1117 static void
ident_dialog_add_checkbutton(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * check_label,const gchar * check_key,gboolean sensitive)1118 ident_dialog_add_checkbutton(GtkWidget * grid, gint row,
1119 GtkDialog * dialog, const gchar * check_label,
1120 const gchar * check_key, gboolean sensitive)
1121 {
1122 GtkWidget *check;
1123
1124 check = libbalsa_create_grid_check(check_label, grid, row, FALSE);
1125 g_object_set_data(G_OBJECT(dialog), check_key, check);
1126 gtk_widget_set_sensitive(check, sensitive);
1127 }
1128
1129 /*
1130 * Create and add a GtkCheckButton to the given dialog with caption
1131 * and add a pointer to it stored under the given key, which is
1132 * followed by a text entry. The check button is initialized to the
1133 * given value.
1134 */
1135 static void
ident_dialog_add_check_and_entry(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * check_label,const gchar * entry_key)1136 ident_dialog_add_check_and_entry(GtkWidget * grid, gint row,
1137 GtkDialog * dialog, const gchar * check_label,
1138 const gchar * entry_key)
1139 {
1140 GtkWidget *check, *entry;
1141
1142 check = gtk_check_button_new_with_mnemonic(check_label);
1143 entry = gtk_entry_new();
1144
1145
1146 gtk_grid_attach(GTK_GRID(grid), check, 0, row, 1, 1);
1147 gtk_widget_set_hexpand(entry, TRUE);
1148 gtk_grid_attach(GTK_GRID(grid), entry, 1, row, 1, 1);
1149
1150 g_object_set_data(G_OBJECT(dialog), entry_key, entry);
1151 g_object_set_data(G_OBJECT(entry), LIBBALSA_IDENTITY_CHECK, check);
1152 }
1153
1154
1155 /*
1156 * Add a GtkEntry to the given dialog with a label next to it
1157 * explaining the contents. A reference to the entry is stored as
1158 * object data attached to the dialog with the given key. The entry
1159 * is initialized to the init_value given.
1160 */
1161 static void
ident_dialog_add_entry(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * label_name,const gchar * entry_key)1162 ident_dialog_add_entry(GtkWidget * grid, gint row, GtkDialog * dialog,
1163 const gchar * label_name, const gchar * entry_key)
1164 {
1165 GtkWidget *label;
1166 GtkWidget *entry;
1167
1168 label = libbalsa_create_grid_label(label_name, grid, row);
1169
1170 entry = libbalsa_create_grid_entry(grid, NULL, NULL, row, NULL, label);
1171
1172 g_object_set_data(G_OBJECT(dialog), entry_key, entry);
1173 if (row == 0)
1174 gtk_widget_grab_focus(entry);
1175 }
1176
1177 /*
1178 * Add a GtkFileChooserButton to the given dialog with a label next to it
1179 * explaining the contents. A reference to the button is stored as
1180 * object data attached to the dialog with the given key. The entry
1181 * is initialized to the init_value given.
1182 */
1183
1184 /* Callbacks and helpers. */
1185 static void
file_chooser_check_cb(GtkToggleButton * button,GtkWidget * chooser)1186 file_chooser_check_cb(GtkToggleButton * button, GtkWidget * chooser)
1187 {
1188 gtk_widget_set_sensitive(chooser,
1189 gtk_toggle_button_get_active(button));
1190 /* Force validation of current path, if any. */
1191 g_signal_emit_by_name(chooser, "selection-changed");
1192 }
1193
1194 static void
md_face_path_changed(const gchar * filename,gboolean active,LibBalsaIdentityPathType type,gpointer data)1195 md_face_path_changed(const gchar * filename, gboolean active,
1196 LibBalsaIdentityPathType type, gpointer data)
1197 {
1198 gchar *content;
1199 gsize size;
1200 GError *err = NULL;
1201 GtkWidget *image;
1202 GtkWidget *face_box;
1203
1204 face_box = g_object_get_data(G_OBJECT(data), path_info[type].box_key);
1205 if (!active) {
1206 gtk_widget_hide(face_box);
1207 return;
1208 }
1209
1210 content = libbalsa_get_header_from_path(path_info[type].info,
1211 filename, &size, &err);
1212
1213 if (err) {
1214 libbalsa_information(LIBBALSA_INFORMATION_WARNING,
1215 _("Error reading file %s: %s"), filename,
1216 err->message);
1217 g_error_free(err);
1218 gtk_widget_hide(face_box);
1219 return;
1220 }
1221
1222 if (size > 998) {
1223 libbalsa_information(LIBBALSA_INFORMATION_WARNING,
1224 /* Translators: please do not translate Face. */
1225 _("Face header file %s is too long "
1226 "(%lu bytes)."), filename, (unsigned long)size);
1227 g_free(content);
1228 gtk_widget_hide(face_box);
1229 return;
1230 }
1231
1232 if (libbalsa_text_attr_string(content)) {
1233 libbalsa_information(LIBBALSA_INFORMATION_WARNING,
1234 /* Translators: please do not translate Face. */
1235 _("Face header file %s contains "
1236 "binary data."), filename);
1237 g_free(content);
1238 return;
1239 }
1240
1241 if (type == LBI_PATH_TYPE_FACE)
1242 image = libbalsa_get_image_from_face_header(content, &err);
1243 #if HAVE_COMPFACE
1244 else if (type == LBI_PATH_TYPE_XFACE)
1245 image = libbalsa_get_image_from_x_face_header(content, &err);
1246 #endif /* HAVE_COMPFACE */
1247 else {
1248 gtk_widget_hide(face_box);
1249 g_free(content);
1250 return;
1251 }
1252 if (err) {
1253 libbalsa_information(LIBBALSA_INFORMATION_WARNING,
1254 /* Translators: please do not translate Face. */
1255 _("Error loading Face: %s"), err->message);
1256 g_error_free(err);
1257 g_free(content);
1258 return;
1259 }
1260
1261 gtk_container_foreach(GTK_CONTAINER(face_box),
1262 (GtkCallback) gtk_widget_destroy, NULL);
1263 gtk_container_add(GTK_CONTAINER(face_box), image);
1264 gtk_widget_show_all(face_box);
1265
1266 g_free(content);
1267 }
1268
1269 /* Callback for the "selection-changed" signal of the signature path
1270 * file chooser; sets sensitivity of the signature-related buttons. */
1271
1272 static void
md_sig_path_changed(gboolean active,GObject * dialog)1273 md_sig_path_changed(gboolean active, GObject * dialog)
1274 {
1275 guint i;
1276 static gchar *button_key[] = {
1277 "identity-sigexecutable",
1278 "identity-sigappend",
1279 "identity-whenforward",
1280 "identity-whenreply",
1281 "identity-sigseparator",
1282 "identity-sigprepend",
1283 "identity-sigpath",
1284 };
1285
1286 for (i = 0; i < ELEMENTS(button_key); i++) {
1287 GtkWidget *button = g_object_get_data(dialog, button_key[i]);
1288 gtk_widget_set_sensitive(button, active);
1289 }
1290 }
1291
1292 static void
md_sig_path_changed_cb(GtkToggleButton * sig_button,GObject * dialog)1293 md_sig_path_changed_cb(GtkToggleButton *sig_button, GObject *dialog)
1294 {
1295 md_sig_path_changed(gtk_toggle_button_get_active(sig_button), dialog);
1296 }
1297
1298 #define LIBBALSA_IDENTITY_INFO "libbalsa-identity-info"
1299 static void
file_chooser_cb(GtkWidget * chooser,gpointer data)1300 file_chooser_cb(GtkWidget * chooser, gpointer data)
1301 {
1302 gchar *filename;
1303 LibBalsaIdentityPathType type;
1304 GtkToggleButton *check;
1305 gboolean active;
1306
1307 filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1308 if (!filename || !*filename) {
1309 g_free(filename);
1310 return;
1311 }
1312
1313 type = GPOINTER_TO_UINT(g_object_get_data
1314 (G_OBJECT(chooser), LIBBALSA_IDENTITY_INFO));
1315 check = g_object_get_data(G_OBJECT(chooser), LIBBALSA_IDENTITY_CHECK);
1316 active = gtk_toggle_button_get_active(check);
1317
1318 #if 0
1319 if (type == LBI_PATH_TYPE_SIG)
1320 md_sig_path_changed(filename, active, data);
1321 else
1322 #endif
1323 md_face_path_changed(filename, active, type, data);
1324 g_free(filename);
1325 }
1326
1327 static void
ident_dialog_add_file_chooser_button(GtkWidget * grid,gint row,GtkDialog * dialog,LibBalsaIdentityPathType type)1328 ident_dialog_add_file_chooser_button(GtkWidget * grid, gint row,
1329 GtkDialog * dialog,
1330 LibBalsaIdentityPathType type)
1331 {
1332 GtkWidget *check;
1333 gchar *filename;
1334 gchar *title;
1335 GtkWidget *button;
1336
1337 check =
1338 gtk_check_button_new_with_mnemonic(_(path_info[type].mnemonic));
1339 gtk_grid_attach(GTK_GRID(grid), check, 0, row, 1, 1);
1340
1341 filename =
1342 g_build_filename(g_get_home_dir(), path_info[type].basename, NULL);
1343 title = g_strdup_printf("Choose %s file", _(path_info[type].info));
1344 button = gtk_file_chooser_button_new(title,
1345 GTK_FILE_CHOOSER_ACTION_OPEN);
1346 g_free(title);
1347 gtk_file_chooser_set_show_hidden(GTK_FILE_CHOOSER(button), TRUE);
1348 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(button), filename);
1349 g_free(filename);
1350
1351 gtk_widget_set_hexpand(button, TRUE);
1352 gtk_widget_set_vexpand(button, TRUE);
1353 gtk_grid_attach(GTK_GRID(grid), button, 1, row, 1, 1);
1354
1355 g_object_set_data(G_OBJECT(dialog), path_info[type].path_key, button);
1356 g_object_set_data(G_OBJECT(button), LIBBALSA_IDENTITY_CHECK, check);
1357 g_object_set_data(G_OBJECT(button), LIBBALSA_IDENTITY_INFO,
1358 GUINT_TO_POINTER(type));
1359 g_signal_connect(check, "toggled",
1360 G_CALLBACK(file_chooser_check_cb), button);
1361 g_signal_connect(button, "selection-changed",
1362 G_CALLBACK(file_chooser_cb), dialog);
1363 }
1364
1365 static void
ident_dialog_add_boxes(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * key1,const gchar * key2)1366 ident_dialog_add_boxes(GtkWidget * grid, gint row, GtkDialog * dialog,
1367 const gchar * key1, const gchar *key2)
1368 {
1369 GtkWidget *hbox, *vbox;
1370
1371 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 12);
1372 gtk_grid_attach(GTK_GRID(grid), hbox, 1, row, 1, 1);
1373 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1374 g_object_set_data(G_OBJECT(dialog), key1, vbox);
1375 gtk_container_add(GTK_CONTAINER(hbox), vbox);
1376 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
1377 g_object_set_data(G_OBJECT(dialog), key2, vbox);
1378 gtk_container_add(GTK_CONTAINER(hbox), vbox);
1379 }
1380
1381 /* set_identity_name_in_tree:
1382 * update the tree to reflect the (possibly) new name of the identity
1383 */
1384 static void
set_identity_name_in_tree(GtkTreeView * tree,LibBalsaIdentity * identity,const gchar * name)1385 set_identity_name_in_tree(GtkTreeView * tree, LibBalsaIdentity * identity,
1386 const gchar * name)
1387 {
1388 GtkTreeModel *model = gtk_tree_view_get_model(tree);
1389 GtkTreeIter iter;
1390 gboolean valid;
1391
1392 for (valid =
1393 gtk_tree_model_get_iter_first(model, &iter);
1394 valid;
1395 valid = gtk_tree_model_iter_next(model, &iter)) {
1396 LibBalsaIdentity *tmp;
1397
1398 gtk_tree_model_get(model, &iter, IDENT_COLUMN, &tmp, -1);
1399 if (identity == tmp) {
1400 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
1401 NAME_COLUMN, name, -1);
1402 break;
1403 }
1404 }
1405 }
1406
1407 /*
1408 * Update the identity object associated with the edit/new dialog,
1409 * validating along the way. Correct validation results in a true
1410 * return value, otherwise it returns false.
1411 */
1412
1413 static gboolean
ident_dialog_update(GObject * dlg)1414 ident_dialog_update(GObject * dlg)
1415 {
1416 LibBalsaIdentity* id;
1417 LibBalsaIdentity* exist_ident;
1418 InternetAddress *ia;
1419 GtkWidget *tree;
1420 GList **identities, *list;
1421 gchar* text;
1422
1423 id = g_object_get_data(dlg, "identity");
1424 if (!id)
1425 return TRUE;
1426 tree = g_object_get_data(dlg, "tree");
1427 identities = g_object_get_data(G_OBJECT(tree), "identities");
1428
1429 text = ident_dialog_get_text(dlg, "identity-name");
1430 g_return_val_if_fail(text != NULL, FALSE);
1431
1432 if (text[0] == '\0') {
1433 libbalsa_information(LIBBALSA_INFORMATION_ERROR,
1434 _("Error: The identity does not have a name"));
1435 return FALSE;
1436 }
1437
1438 for (list = *identities; list; list = g_list_next(list)) {
1439 exist_ident = list->data;
1440
1441 if (g_ascii_strcasecmp(exist_ident->identity_name, text) == 0
1442 && id != exist_ident) {
1443 libbalsa_information(LIBBALSA_INFORMATION_ERROR,
1444 _("Error: An identity with that"
1445 " name already exists"));
1446 return FALSE;
1447 }
1448 }
1449
1450 g_free(id->identity_name); id->identity_name = text;
1451 set_identity_name_in_tree(GTK_TREE_VIEW(tree), id, text);
1452
1453 text = ident_dialog_get_text(dlg, "identity-address");
1454 g_return_val_if_fail(text != NULL, FALSE);
1455 ia = internet_address_mailbox_new(NULL, text);
1456 g_free(text);
1457
1458 text = ident_dialog_get_text(dlg, "identity-fullname");
1459 internet_address_set_name(ia, text);
1460 libbalsa_identity_set_address(id, ia);
1461 g_free(text);
1462
1463 g_free(id->replyto);
1464 id->replyto = ident_dialog_get_text(dlg, "identity-replyto");
1465 g_free(id->domain);
1466 id->domain = ident_dialog_get_text(dlg, "identity-domain");
1467 g_free(id->bcc);
1468 id->bcc = ident_dialog_get_text(dlg, "identity-bcc");
1469 g_free(id->reply_string);
1470 id->reply_string = ident_dialog_get_text(dlg, "identity-replystring");
1471 g_free(id->forward_string);
1472 id->forward_string = ident_dialog_get_text(dlg, "identity-forwardstring");
1473 id->send_mp_alternative = ident_dialog_get_bool(dlg, "identity-sendmpalternative");
1474 #if ENABLE_ESMTP
1475 if(id->smtp_server) g_object_unref(id->smtp_server);
1476 id->smtp_server = ident_dialog_get_value(dlg, "identity-smtp-server");
1477 g_object_ref(id->smtp_server);
1478 #endif /* ENABLE_ESMTP */
1479
1480 g_free(id->signature_path);
1481 id->signature_path = ident_dialog_get_text(dlg, "identity-sigpath");
1482
1483 id->sig_executable = ident_dialog_get_bool(dlg, "identity-sigexecutable");
1484 id->sig_sending = ident_dialog_get_bool(dlg, "identity-sigappend");
1485 id->sig_whenforward = ident_dialog_get_bool(dlg, "identity-whenforward");
1486 id->sig_whenreply = ident_dialog_get_bool(dlg, "identity-whenreply");
1487 id->sig_separator = ident_dialog_get_bool(dlg, "identity-sigseparator");
1488 id->sig_prepend = ident_dialog_get_bool(dlg, "identity-sigprepend");
1489
1490 g_free(id->face);
1491 id->face = ident_dialog_get_path(dlg, "identity-facepath");
1492 g_free(id->x_face);
1493 id->x_face = ident_dialog_get_path(dlg, "identity-xfacepath");
1494 id->request_mdn = ident_dialog_get_bool(dlg, "identity-requestmdn");
1495
1496 id->gpg_sign = ident_dialog_get_bool(dlg, "identity-gpgsign");
1497 id->gpg_encrypt = ident_dialog_get_bool(dlg, "identity-gpgencrypt");
1498 id->always_trust = ident_dialog_get_bool(dlg, "identity-trust-always");
1499 id->warn_send_plain = ident_dialog_get_bool(dlg, "identity-warn-send-plain");
1500 id->crypt_protocol = GPOINTER_TO_INT(ident_dialog_get_value
1501 (dlg, "identity-crypt-protocol"));
1502 g_free(id->force_key_id);
1503 id->force_key_id = g_strstrip(ident_dialog_get_text(dlg, "identity-keyid"));
1504
1505 return TRUE;
1506 }
1507
1508
1509 /*
1510 * Get the text from an entry in the editing/creation dialog. The
1511 * given key accesses the entry using object data.
1512 */
1513 static gchar*
ident_dialog_get_text(GObject * dialog,const gchar * key)1514 ident_dialog_get_text(GObject * dialog, const gchar * key)
1515 {
1516 GtkEditable *entry;
1517 GtkToggleButton *check;
1518
1519 entry = g_object_get_data(dialog, key);
1520 check = g_object_get_data(G_OBJECT(entry), LIBBALSA_IDENTITY_CHECK);
1521 if (check && !gtk_toggle_button_get_active(check))
1522 return NULL;
1523 return gtk_editable_get_chars(entry, 0, -1);
1524 }
1525
1526
1527 /*
1528 * Get the value of a check button from the editing dialog. The key
1529 * is used to retreive the reference to the check button using object
1530 * data
1531 */
1532 static gboolean
ident_dialog_get_bool(GObject * dialog,const gchar * key)1533 ident_dialog_get_bool(GObject* dialog, const gchar* key)
1534 {
1535 GtkToggleButton *button;
1536
1537 button = g_object_get_data(dialog, key);
1538 return gtk_toggle_button_get_active(button);
1539 }
1540
1541
1542 /*
1543 * Get the path from a file chooser in the editing/creation dialog. The
1544 * given key accesses the file chooser using object data.
1545 */
1546 static gchar *
ident_dialog_get_path(GObject * dialog,const gchar * key)1547 ident_dialog_get_path(GObject * dialog, const gchar * key)
1548 {
1549 GtkWidget *chooser;
1550
1551 chooser = g_object_get_data(dialog, key);
1552 if (!gtk_widget_get_sensitive(chooser))
1553 return NULL;
1554
1555 return gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(chooser));
1556 }
1557
1558
1559 /*
1560 * Set the default identity to the currently selected.
1561 */
1562 static void
set_default_ident_cb(GtkTreeView * tree,GtkTreePath * path,GtkTreeViewColumn * column,gpointer data)1563 set_default_ident_cb(GtkTreeView * tree, GtkTreePath * path,
1564 GtkTreeViewColumn * column, gpointer data)
1565 {
1566 LibBalsaIdentity *ident, **default_id;
1567
1568 default_id = g_object_get_data(G_OBJECT(tree), "default-id");
1569 ident = get_selected_identity(tree);
1570 g_return_if_fail(ident != NULL);
1571 *default_id = ident;
1572
1573 identity_list_update(tree);
1574 }
1575
1576
1577 /*
1578 * Confirm the deletion of an identity, do the actual deletion here,
1579 * and close the dialog.
1580 */
1581 static void
identity_delete_selected(GtkTreeView * tree,GtkWidget * dialog)1582 identity_delete_selected(GtkTreeView * tree, GtkWidget * dialog)
1583 {
1584 GtkTreeSelection *selection = gtk_tree_view_get_selection(tree);
1585 GtkTreeModel *model;
1586 GtkTreeIter iter;
1587 GtkTreePath *path;
1588 LibBalsaIdentity *ident;
1589 GList **identities;
1590 void (*cb)(gpointer) = g_object_get_data(G_OBJECT(tree), "callback");
1591 gpointer data = g_object_get_data(G_OBJECT(tree), "cb-data");
1592
1593 /* Save the path to the current row. */
1594 if (!gtk_tree_selection_get_selected(selection, &model, &iter))
1595 return;
1596 path = gtk_tree_model_get_path(model, &iter);
1597
1598 ident = get_selected_identity(tree);
1599 identities = g_object_get_data(G_OBJECT(tree), "identities");
1600 *identities = g_list_remove(*identities, ident);
1601 g_object_set_data(G_OBJECT(dialog), "identity", NULL);
1602 identity_list_update(tree);
1603 g_object_unref(ident);
1604
1605 /* Select the row at the saved path, or the previous one. */
1606 if (gtk_tree_model_get_iter(model, &iter, path)
1607 || gtk_tree_path_prev(path)) {
1608 gtk_tree_view_set_cursor(tree, path, NULL, FALSE);
1609 gtk_tree_view_scroll_to_cell(tree, path, NULL,
1610 FALSE, 0, 0);
1611 }
1612 gtk_tree_path_free(path);
1613 gtk_widget_grab_focus(GTK_WIDGET(tree));
1614 cb(data);
1615 }
1616
1617 /*
1618 * Delete the currently selected identity after confirming.
1619 */
1620 struct _IdentityDeleteInfo {
1621 GtkTreeView *tree;
1622 GtkWidget *dialog;
1623 };
1624 static void
delete_ident_cb(GtkTreeView * tree,GtkWidget * dialog)1625 delete_ident_cb(GtkTreeView * tree, GtkWidget * dialog)
1626 {
1627 LibBalsaIdentity* ident, **default_id;
1628 GtkWidget* confirm;
1629 IdentityDeleteInfo *di;
1630
1631 ident = get_selected_identity(tree);
1632 default_id = g_object_get_data(G_OBJECT(tree), "default-id");
1633 g_return_if_fail(ident != *default_id);
1634 confirm = gtk_message_dialog_new(GTK_WINDOW(dialog),
1635 GTK_DIALOG_DESTROY_WITH_PARENT,
1636 GTK_MESSAGE_QUESTION,
1637 GTK_BUTTONS_OK_CANCEL,
1638 _("Do you really want to delete"
1639 " the selected identity?"));
1640 #if HAVE_MACOSX_DESKTOP
1641 libbalsa_macosx_menu_for_parent(confirm, GTK_WINDOW(dialog));
1642 #endif
1643 di = g_new(IdentityDeleteInfo, 1);
1644 di->tree = tree;
1645 di->dialog = dialog;
1646 g_signal_connect(confirm, "response",
1647 G_CALLBACK(delete_ident_response), di);
1648 g_object_weak_ref(G_OBJECT(confirm), (GWeakNotify) g_free, di);
1649 gtk_widget_set_sensitive(dialog, FALSE);
1650 gtk_widget_show_all(confirm);
1651 }
1652
1653 static void
delete_ident_response(GtkWidget * confirm,gint response,IdentityDeleteInfo * di)1654 delete_ident_response(GtkWidget * confirm, gint response,
1655 IdentityDeleteInfo * di)
1656 {
1657 if(response == GTK_RESPONSE_OK)
1658 identity_delete_selected(di->tree, di->dialog);
1659 gtk_widget_set_sensitive(di->dialog, TRUE);
1660 gtk_widget_destroy(confirm);
1661 }
1662
1663 /*
1664 * Show the help file.
1665 */
1666 static void
help_ident_cb(GtkWidget * widget)1667 help_ident_cb(GtkWidget * widget)
1668 {
1669 GdkScreen *screen;
1670 GError *err = NULL;
1671
1672 screen = gtk_widget_get_screen(widget);
1673 gtk_show_uri(screen, "ghelp:balsa?identities",
1674 gtk_get_current_event_time(), &err);
1675
1676 if (err) {
1677 g_print(_("Error displaying help for identities: %s\n"),
1678 err->message);
1679 g_error_free(err);
1680 }
1681 }
1682
1683 /* libbalsa_identity_config_dialog displays an identity management
1684 dialog. The dialog has a specified parent, existing list of
1685 identites, the default one. Additionally, a callback is passed that
1686 will be executed when the identity list is modified: new entries
1687 are added or other entries are removed. */
1688 #if ENABLE_ESMTP
1689 static void
lbi_free_smtp_server_list(GSList ** smtp_server_list)1690 lbi_free_smtp_server_list(GSList ** smtp_server_list)
1691 {
1692 g_slist_foreach(*smtp_server_list, (GFunc) g_object_unref, NULL);
1693 g_slist_free(*smtp_server_list);
1694 g_free(smtp_server_list);
1695 }
1696 #endif /* ENABLE_ESMTP */
1697
1698 void
libbalsa_identity_config_dialog(GtkWindow * parent,GList ** identities,LibBalsaIdentity ** default_id,GSList * smtp_servers,void (* changed_cb)(gpointer))1699 libbalsa_identity_config_dialog(GtkWindow *parent, GList **identities,
1700 LibBalsaIdentity **default_id,
1701 #if ENABLE_ESMTP
1702 GSList * smtp_servers,
1703 #endif /* ENABLE_ESMTP */
1704 void (*changed_cb)(gpointer))
1705 {
1706 static GtkWidget *dialog = NULL;
1707 GtkWidget* frame;
1708 GtkWidget* display_frame;
1709 GtkWidget* hbox;
1710 GtkTreeView* tree;
1711 GtkTreeSelection *select;
1712 #if ENABLE_ESMTP
1713 GSList **smtp_server_list;
1714 #endif /* ENABLE_ESMTP */
1715
1716 /* Show only one dialog at a time. */
1717 if (dialog) {
1718 gtk_window_present(GTK_WINDOW(dialog));
1719 return;
1720 }
1721
1722 dialog =
1723 gtk_dialog_new_with_buttons(_("Manage Identities"),
1724 parent, /* must NOT be modal */
1725 GTK_DIALOG_DESTROY_WITH_PARENT,
1726 GTK_STOCK_HELP, IDENTITY_RESPONSE_HELP,
1727 GTK_STOCK_NEW, IDENTITY_RESPONSE_NEW,
1728 GTK_STOCK_REMOVE, IDENTITY_RESPONSE_REMOVE,
1729 GTK_STOCK_CLOSE, IDENTITY_RESPONSE_CLOSE,
1730 NULL);
1731 #if HAVE_MACOSX_DESKTOP
1732 libbalsa_macosx_menu_for_parent(dialog, parent);
1733 #endif
1734
1735 frame = libbalsa_identity_config_frame(identities, default_id, dialog,
1736 changed_cb, parent);
1737 tree = GTK_TREE_VIEW(gtk_bin_get_child(GTK_BIN(frame)));
1738
1739 g_signal_connect(dialog, "response",
1740 G_CALLBACK(md_response_cb), tree);
1741 g_object_set_data(G_OBJECT(dialog), "tree", tree);
1742 g_object_add_weak_pointer(G_OBJECT(dialog), (gpointer) & dialog);
1743 gtk_dialog_set_default_response(GTK_DIALOG(dialog),
1744 IDENTITY_RESPONSE_CLOSE);
1745
1746 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, padding);
1747 gtk_box_pack_start(GTK_BOX
1748 (gtk_dialog_get_content_area(GTK_DIALOG(dialog))),
1749 hbox, TRUE, TRUE, 0);
1750
1751 gtk_box_pack_start(GTK_BOX(hbox), frame, FALSE, FALSE, 0);
1752
1753 #if ENABLE_ESMTP
1754 smtp_server_list = g_new(GSList *, 1);
1755 *smtp_server_list = g_slist_copy(smtp_servers);
1756 g_slist_foreach(smtp_servers, (GFunc) g_object_ref, NULL);
1757 display_frame = setup_ident_frame(GTK_DIALOG(dialog),
1758 FALSE, tree, smtp_servers);
1759 g_object_weak_ref(G_OBJECT(display_frame),
1760 (GWeakNotify) lbi_free_smtp_server_list,
1761 smtp_server_list);
1762 #else /* ENABLE_ESMTP */
1763 display_frame = setup_ident_frame(GTK_DIALOG(dialog), FALSE, tree);
1764 #endif /* ENABLE_ESMTP */
1765
1766 gtk_box_pack_start(GTK_BOX(hbox), display_frame, TRUE, TRUE, 0);
1767
1768 select = gtk_tree_view_get_selection(tree);
1769 g_signal_connect(select, "changed",
1770 G_CALLBACK(config_frame_button_select_cb), dialog);
1771 config_dialog_select(select, GTK_DIALOG(dialog));
1772
1773 gtk_widget_show_all(dialog);
1774 gtk_widget_grab_focus(GTK_WIDGET(tree));
1775 }
1776
1777 /* Callback for the "response" signal of the dialog. */
1778 static void
md_response_cb(GtkWidget * dialog,gint response,GtkTreeView * tree)1779 md_response_cb(GtkWidget * dialog, gint response, GtkTreeView * tree)
1780 {
1781 switch (response) {
1782 case IDENTITY_RESPONSE_CLOSE:
1783 if (close_cb(G_OBJECT(dialog)))
1784 break;
1785 return;
1786 case IDENTITY_RESPONSE_NEW:
1787 new_ident_cb(tree, G_OBJECT(dialog));
1788 return;
1789 case IDENTITY_RESPONSE_REMOVE:
1790 delete_ident_cb(tree, dialog);
1791 return;
1792 case IDENTITY_RESPONSE_HELP:
1793 help_ident_cb(dialog);
1794 return;
1795 default:
1796 break;
1797 }
1798
1799 gtk_widget_destroy(dialog);
1800 }
1801
1802 /* config_dialog_select
1803 *
1804 * called when the tree's selection changes
1805 * manage the button sensitivity, and update the display frame
1806 */
1807 static void
config_dialog_select(GtkTreeSelection * selection,GtkDialog * dialog)1808 config_dialog_select(GtkTreeSelection * selection, GtkDialog * dialog)
1809 {
1810 LibBalsaIdentity *ident, **default_id;
1811 GtkTreeView *tree = gtk_tree_selection_get_tree_view(selection);
1812
1813 ident = get_selected_identity(tree);
1814 default_id = g_object_get_data(G_OBJECT(tree), "default-id");
1815 gtk_dialog_set_response_sensitive(dialog, IDENTITY_RESPONSE_REMOVE,
1816 ident && ident != *default_id);
1817 display_frame_update(G_OBJECT(dialog), ident);
1818 g_object_set_data(G_OBJECT(dialog), "identity", ident);
1819 }
1820
1821 static void
display_frame_update(GObject * dialog,LibBalsaIdentity * ident)1822 display_frame_update(GObject * dialog, LibBalsaIdentity* ident)
1823 {
1824 GtkWidget *face_box;
1825
1826 if (!ident)
1827 return;
1828
1829 ident_dialog_update(dialog);
1830 display_frame_set_field(dialog, "identity-name", ident->identity_name);
1831 display_frame_set_field(dialog, "identity-fullname", ident->ia ? ident->ia->name : NULL);
1832 if (ident->ia && INTERNET_ADDRESS_IS_MAILBOX (ident->ia))
1833 display_frame_set_field(dialog, "identity-address",
1834 INTERNET_ADDRESS_MAILBOX(ident->ia)->addr);
1835 else
1836 display_frame_set_field(dialog, "identity-address", NULL);
1837
1838 display_frame_set_field(dialog, "identity-replyto", ident->replyto);
1839 display_frame_set_field(dialog, "identity-domain", ident->domain);
1840 display_frame_set_field(dialog, "identity-bcc", ident->bcc);
1841 display_frame_set_field(dialog, "identity-replystring",
1842 ident->reply_string);
1843 display_frame_set_field(dialog, "identity-forwardstring",
1844 ident->forward_string);
1845 display_frame_set_boolean(dialog, "identity-sendmpalternative",
1846 ident->send_mp_alternative);
1847 #if ENABLE_ESMTP
1848 display_frame_set_server(dialog, "identity-smtp-server",
1849 ident->smtp_server);
1850 #endif /* ENABLE_ESMTP */
1851
1852 display_frame_set_path(dialog, "identity-sigpath",
1853 ident->signature_path, FALSE);
1854 display_frame_set_boolean(dialog, "identity-sigexecutable", ident->sig_executable);
1855
1856 display_frame_set_boolean(dialog, "identity-sigappend", ident->sig_sending);
1857 display_frame_set_boolean(dialog, "identity-whenforward",
1858 ident->sig_whenforward);
1859 display_frame_set_boolean(dialog, "identity-whenreply",
1860 ident->sig_whenreply);
1861 display_frame_set_boolean(dialog, "identity-sigseparator",
1862 ident->sig_separator);
1863 display_frame_set_boolean(dialog, "identity-sigprepend",
1864 ident->sig_prepend);
1865
1866 face_box = g_object_get_data(G_OBJECT(dialog),
1867 path_info[LBI_PATH_TYPE_FACE].box_key);
1868 gtk_widget_hide(face_box);
1869 display_frame_set_path(dialog, path_info[LBI_PATH_TYPE_FACE].path_key,
1870 ident->face, TRUE);
1871
1872 face_box = g_object_get_data(G_OBJECT(dialog),
1873 path_info[LBI_PATH_TYPE_XFACE].box_key);
1874 gtk_widget_hide(face_box);
1875 display_frame_set_path(dialog, path_info[LBI_PATH_TYPE_XFACE].path_key,
1876 ident->x_face, TRUE);
1877 display_frame_set_boolean(dialog, "identity-requestmdn",
1878 ident->request_mdn);
1879
1880 display_frame_set_boolean(dialog, "identity-gpgsign",
1881 ident->gpg_sign);
1882 display_frame_set_boolean(dialog, "identity-gpgencrypt",
1883 ident->gpg_encrypt);
1884 display_frame_set_boolean(dialog, "identity-trust-always",
1885 ident->always_trust);
1886 display_frame_set_boolean(dialog, "identity-warn-send-plain",
1887 ident->warn_send_plain);
1888 display_frame_set_gpg_mode(dialog, "identity-crypt-protocol",
1889 &ident->crypt_protocol);
1890 display_frame_set_field(dialog, "identity-keyid", ident->force_key_id);
1891 }
1892
1893
1894 static void
display_frame_set_field(GObject * dialog,const gchar * key,const gchar * value)1895 display_frame_set_field(GObject * dialog,
1896 const gchar* key,
1897 const gchar* value)
1898 {
1899 GtkEntry *entry = g_object_get_data(dialog, key);
1900
1901 gtk_entry_set_text(entry, value ? value : "");
1902 }
1903
1904 static void
display_frame_set_boolean(GObject * dialog,const gchar * key,gboolean value)1905 display_frame_set_boolean(GObject * dialog,
1906 const gchar* key,
1907 gboolean value)
1908 {
1909 GtkToggleButton *check = g_object_get_data(dialog, key);
1910
1911 gtk_toggle_button_set_active(check, value);
1912 }
1913
1914 static void
display_frame_set_path(GObject * dialog,const gchar * key,const gchar * value,gboolean use_chooser)1915 display_frame_set_path(GObject * dialog,
1916 const gchar* key,
1917 const gchar* value, gboolean use_chooser)
1918 {
1919 gboolean set = (value && *value);
1920 GtkWidget *chooser = g_object_get_data(dialog, key);
1921 GtkToggleButton *check =
1922 g_object_get_data(G_OBJECT(chooser), LIBBALSA_IDENTITY_CHECK);
1923
1924 if (set) {
1925 if(use_chooser)
1926 gtk_file_chooser_set_filename(GTK_FILE_CHOOSER(chooser), value);
1927 else
1928 gtk_entry_set_text(GTK_ENTRY(chooser), value);
1929 }
1930 gtk_widget_set_sensitive(GTK_WIDGET(chooser), set);
1931 gtk_toggle_button_set_active(check, set);
1932 }
1933
1934
1935 /* libbalsa_identity_new_config:
1936 factory-type method creating new Identity object from given
1937 configuration data.
1938 */
1939 LibBalsaIdentity*
libbalsa_identity_new_config(const gchar * name)1940 libbalsa_identity_new_config(const gchar* name)
1941 {
1942 LibBalsaIdentity* ident;
1943 gchar *fname, *email;
1944 gchar* tmpstr;
1945
1946 fname = libbalsa_conf_get_string("FullName");
1947 email = libbalsa_conf_get_string("Address");
1948
1949 ident = LIBBALSA_IDENTITY(libbalsa_identity_new_with_name(name));
1950 ident->ia = internet_address_mailbox_new (fname, email);
1951 g_free(fname);
1952 g_free(email);
1953
1954 ident->replyto = libbalsa_conf_get_string("ReplyTo");
1955 ident->domain = libbalsa_conf_get_string("Domain");
1956 ident->bcc = libbalsa_conf_get_string("Bcc");
1957
1958 /*
1959 * these two have defaults, so we need to use the appropriate
1960 * functions to manage the memory.
1961 */
1962 if ((tmpstr = libbalsa_conf_get_string("ReplyString"))) {
1963 g_free(ident->reply_string);
1964 ident->reply_string = tmpstr;
1965 }
1966
1967 if ((tmpstr = libbalsa_conf_get_string("ForwardString"))) {
1968 g_free(ident->forward_string);
1969 ident->forward_string = tmpstr;
1970 }
1971 ident->send_mp_alternative =
1972 libbalsa_conf_get_bool("SendMultipartAlternative");
1973
1974 ident->signature_path = libbalsa_conf_get_string("SignaturePath");
1975 ident->sig_executable = libbalsa_conf_get_bool("SigExecutable");
1976 ident->sig_sending = libbalsa_conf_get_bool("SigSending");
1977 ident->sig_whenforward = libbalsa_conf_get_bool("SigForward");
1978 ident->sig_whenreply = libbalsa_conf_get_bool("SigReply");
1979 ident->sig_separator = libbalsa_conf_get_bool("SigSeparator");
1980 ident->sig_prepend = libbalsa_conf_get_bool("SigPrepend");
1981 ident->face = libbalsa_conf_get_string("FacePath");
1982 ident->x_face = libbalsa_conf_get_string("XFacePath");
1983 ident->request_mdn = libbalsa_conf_get_bool("RequestMDN");
1984
1985 ident->gpg_sign = libbalsa_conf_get_bool("GpgSign");
1986 ident->gpg_encrypt = libbalsa_conf_get_bool("GpgEncrypt");
1987 ident->always_trust = libbalsa_conf_get_bool("GpgTrustAlways");
1988 ident->warn_send_plain = libbalsa_conf_get_bool("GpgWarnSendPlain=true");
1989 ident->crypt_protocol = libbalsa_conf_get_int("CryptProtocol=16");
1990 ident->force_key_id = libbalsa_conf_get_string("ForceKeyID");
1991
1992 return ident;
1993 }
1994
1995 void
libbalsa_identity_save(LibBalsaIdentity * ident,const gchar * group)1996 libbalsa_identity_save(LibBalsaIdentity* ident, const gchar* group)
1997 {
1998 g_return_if_fail(ident);
1999
2000 libbalsa_conf_push_group(group);
2001 libbalsa_conf_set_string("FullName", ident->ia ? ident->ia->name : NULL);
2002
2003 if (ident->ia && INTERNET_ADDRESS_IS_MAILBOX (ident->ia))
2004 libbalsa_conf_set_string("Address", INTERNET_ADDRESS_MAILBOX(ident->ia)->addr);
2005
2006 libbalsa_conf_set_string("ReplyTo", ident->replyto);
2007 libbalsa_conf_set_string("Domain", ident->domain);
2008 libbalsa_conf_set_string("Bcc", ident->bcc);
2009 libbalsa_conf_set_string("ReplyString", ident->reply_string);
2010 libbalsa_conf_set_string("ForwardString", ident->forward_string);
2011 libbalsa_conf_set_bool("SendMultipartAlternative", ident->send_mp_alternative);
2012 #if ENABLE_ESMTP
2013 libbalsa_conf_set_string("SmtpServer",
2014 libbalsa_smtp_server_get_name(ident->
2015 smtp_server));
2016 #endif /* ENABLE_ESMTP */
2017
2018 libbalsa_conf_set_string("SignaturePath", ident->signature_path);
2019 libbalsa_conf_set_bool("SigExecutable", ident->sig_executable);
2020 libbalsa_conf_set_bool("SigSending", ident->sig_sending);
2021 libbalsa_conf_set_bool("SigForward", ident->sig_whenforward);
2022 libbalsa_conf_set_bool("SigReply", ident->sig_whenreply);
2023 libbalsa_conf_set_bool("SigSeparator", ident->sig_separator);
2024 libbalsa_conf_set_bool("SigPrepend", ident->sig_prepend);
2025 if (ident->face)
2026 libbalsa_conf_set_string("FacePath", ident->face);
2027 if (ident->x_face)
2028 libbalsa_conf_set_string("XFacePath", ident->x_face);
2029 libbalsa_conf_set_bool("RequestMDN", ident->request_mdn);
2030
2031 libbalsa_conf_set_bool("GpgSign", ident->gpg_sign);
2032 libbalsa_conf_set_bool("GpgEncrypt", ident->gpg_encrypt);
2033 libbalsa_conf_set_bool("GpgTrustAlways", ident->always_trust);
2034 libbalsa_conf_set_bool("GpgWarnSendPlain", ident->warn_send_plain);
2035 libbalsa_conf_set_int("CryptProtocol", ident->crypt_protocol);
2036 libbalsa_conf_set_string("ForceKeyID", ident->force_key_id);
2037
2038 libbalsa_conf_pop_group();
2039 }
2040
2041
2042 /* collected helper stuff for GPGME support */
2043
2044 void
libbalsa_identity_set_gpg_sign(LibBalsaIdentity * ident,gboolean sign)2045 libbalsa_identity_set_gpg_sign(LibBalsaIdentity* ident, gboolean sign)
2046 {
2047 g_return_if_fail(ident != NULL);
2048 ident->gpg_sign = sign;
2049 }
2050
2051
2052 void
libbalsa_identity_set_gpg_encrypt(LibBalsaIdentity * ident,gboolean encrypt)2053 libbalsa_identity_set_gpg_encrypt(LibBalsaIdentity* ident, gboolean encrypt)
2054 {
2055 g_return_if_fail(ident != NULL);
2056 ident->gpg_encrypt = encrypt;
2057 }
2058
2059
2060 void
libbalsa_identity_set_crypt_protocol(LibBalsaIdentity * ident,gint protocol)2061 libbalsa_identity_set_crypt_protocol(LibBalsaIdentity* ident, gint protocol)
2062 {
2063 g_return_if_fail(ident != NULL);
2064 ident->crypt_protocol = protocol;
2065 }
2066
2067
2068
2069 static void
display_frame_set_gpg_mode(GObject * dialog,const gchar * key,gint * value)2070 display_frame_set_gpg_mode(GObject * dialog, const gchar* key, gint * value)
2071 {
2072 GtkComboBox *opt_menu = g_object_get_data(G_OBJECT(dialog), key);
2073
2074 switch (*value)
2075 {
2076 case LIBBALSA_PROTECT_OPENPGP:
2077 gtk_combo_box_set_active(opt_menu, 1);
2078 break;
2079 #ifdef HAVE_SMIME
2080 case LIBBALSA_PROTECT_SMIMEV3:
2081 gtk_combo_box_set_active(opt_menu, 2);
2082 break;
2083 #endif
2084 case LIBBALSA_PROTECT_RFC3156:
2085 default:
2086 gtk_combo_box_set_active(opt_menu, 0);
2087 *value = LIBBALSA_PROTECT_RFC3156;
2088 }
2089 }
2090
2091 /*
2092 * Add an option menu to the given dialog with a label next to it
2093 * explaining the contents. A reference to the entry is stored as
2094 * object data attached to the dialog with the given key.
2095 */
2096
2097 static void
ident_dialog_add_gpg_menu(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * label_name,const gchar * menu_key)2098 ident_dialog_add_gpg_menu(GtkWidget * grid, gint row, GtkDialog * dialog,
2099 const gchar * label_name, const gchar * menu_key)
2100 {
2101 GtkWidget *label;
2102 GtkWidget *opt_menu;
2103 GPtrArray *values;
2104
2105 label = gtk_label_new_with_mnemonic(label_name);
2106 gtk_widget_set_halign(label, GTK_ALIGN_START);
2107 gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
2108
2109 opt_menu = gtk_combo_box_text_new();
2110 values = g_ptr_array_sized_new(3);
2111 g_object_set_data_full(G_OBJECT(opt_menu), "identity-value", values,
2112 (GDestroyNotify) ident_dialog_free_values);
2113 gtk_grid_attach(GTK_GRID(grid), opt_menu, 1, row, 1, 1);
2114 g_object_set_data(G_OBJECT(dialog), menu_key, opt_menu);
2115
2116 add_show_menu(_("GnuPG MIME mode"),
2117 GINT_TO_POINTER(LIBBALSA_PROTECT_RFC3156), opt_menu);
2118 add_show_menu(_("GnuPG OpenPGP mode"),
2119 GINT_TO_POINTER(LIBBALSA_PROTECT_OPENPGP), opt_menu);
2120 #ifdef HAVE_SMIME
2121 add_show_menu(_("GpgSM S/MIME mode"),
2122 GINT_TO_POINTER(LIBBALSA_PROTECT_SMIMEV3), opt_menu);
2123 #endif
2124 }
2125
2126 /* add_show_menu: helper function */
2127 static void
add_show_menu(const char * label,gpointer data,GtkWidget * menu)2128 add_show_menu(const char *label, gpointer data, GtkWidget * menu)
2129 {
2130 GPtrArray *values =
2131 g_object_get_data(G_OBJECT(menu), "identity-value");
2132
2133 gtk_combo_box_text_append_text(GTK_COMBO_BOX_TEXT(menu), label);
2134 g_ptr_array_add(values, data);
2135 }
2136
2137 /* ident_dialog_free_values: helper function */
2138 static void
ident_dialog_free_values(GPtrArray * values)2139 ident_dialog_free_values(GPtrArray * values)
2140 {
2141 g_ptr_array_free(values, TRUE);
2142 }
2143
2144 #if ENABLE_ESMTP
2145 static void
ident_dialog_add_smtp_menu(GtkWidget * grid,gint row,GtkDialog * dialog,const gchar * label_name,const gchar * menu_key,GSList * smtp_servers)2146 ident_dialog_add_smtp_menu(GtkWidget * grid, gint row, GtkDialog * dialog,
2147 const gchar * label_name,
2148 const gchar * menu_key, GSList * smtp_servers)
2149 {
2150 GtkWidget *label;
2151 GtkWidget *combo_box;
2152 GSList *list;
2153 GPtrArray *values;
2154
2155 label = gtk_label_new_with_mnemonic(label_name);
2156 gtk_widget_set_halign(label, GTK_ALIGN_START);
2157 gtk_grid_attach(GTK_GRID(grid), label, 0, row, 1, 1);
2158
2159 combo_box = gtk_combo_box_text_new();
2160 gtk_label_set_mnemonic_widget(GTK_LABEL(label), combo_box);
2161 values = g_ptr_array_sized_new(g_slist_length(smtp_servers));
2162 g_object_set_data_full(G_OBJECT(combo_box), "identity-value", values,
2163 (GDestroyNotify) ident_dialog_free_values);
2164 gtk_grid_attach(GTK_GRID(grid), combo_box, 1, row, 1, 1);
2165 g_object_set_data(G_OBJECT(dialog), menu_key, combo_box);
2166
2167 for (list = smtp_servers; list; list = list->next) {
2168 LibBalsaSmtpServer *smtp_server = LIBBALSA_SMTP_SERVER(list->data);
2169 add_show_menu(libbalsa_smtp_server_get_name(smtp_server),
2170 smtp_server, combo_box);
2171 }
2172 }
2173
2174 static void
display_frame_set_server(GObject * dialog,const gchar * key,LibBalsaSmtpServer * smtp_server)2175 display_frame_set_server(GObject * dialog, const gchar * key,
2176 LibBalsaSmtpServer * smtp_server)
2177 {
2178 GtkComboBox *combo_box = g_object_get_data(G_OBJECT(dialog), key);
2179 GPtrArray *values;
2180 guint i;
2181
2182 values = g_object_get_data(G_OBJECT(combo_box), "identity-value");
2183
2184 for (i = 0; i < values->len; i++) {
2185 if (g_ptr_array_index(values, i) == smtp_server)
2186 gtk_combo_box_set_active(combo_box, i);
2187 }
2188 }
2189
2190 #endif /* ENABLE_ESMTP */
2191
2192 /*
2193 * Get the value of the active option menu item
2194 */
2195 static gpointer
ident_dialog_get_value(GObject * dialog,const gchar * key)2196 ident_dialog_get_value(GObject * dialog, const gchar * key)
2197 {
2198 GtkWidget * menu;
2199 gint value;
2200 GPtrArray *values;
2201
2202 menu = g_object_get_data(dialog, key);
2203 value = gtk_combo_box_get_active(GTK_COMBO_BOX(menu));
2204 values = g_object_get_data(G_OBJECT(menu), "identity-value");
2205
2206 return g_ptr_array_index(values, value);
2207 }
2208