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