make the creation and updating of .mh_sequences optional and disabled by default
[claws.git] / src / account.c
blob13cffa9ad6df7bfa66180441b5bb66a4976bf48f
1 /*
2 * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2024 the Claws Mail team and Hiroyuki Yamamoto
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #ifdef HAVE_CONFIG_H
20 # include "config.h"
21 #include "claws-features.h"
22 #endif
24 #include "defs.h"
26 #include <glib.h>
27 #include <glib/gi18n.h>
28 #include <gtk/gtk.h>
29 #include <gdk/gdkkeysyms.h>
30 #include <stdio.h>
31 #include <errno.h>
33 #include "main.h"
34 #include "mainwindow.h"
35 #include "folderview.h"
36 #include "folder.h"
37 #include "account.h"
38 #include "prefs_gtk.h"
39 #include "prefs_account.h"
40 #include "prefs_common.h"
41 #include "folder_item_prefs.h"
42 #include "compose.h"
43 #include "manage_window.h"
44 #include "stock_pixmap.h"
45 #include "inc.h"
46 #include "gtkutils.h"
47 #include "utils.h"
48 #include "alertpanel.h"
49 #include "procheader.h"
50 #include "customheader.h"
51 #include "remotefolder.h"
52 #include "manual.h"
53 #include "filtering.h"
54 #include "prefs_actions.h"
55 #include "hooks.h"
56 #include "passwordstore.h"
57 #include "file-utils.h"
59 enum {
60 ACCOUNT_IS_DEFAULT,
61 ACCOUNT_ENABLE_GET_ALL,
62 ACCOUNT_NAME,
63 ACCOUNT_PROTOCOL,
64 ACCOUNT_SERVER,
65 ACCOUNT_DATA,
66 N_ACCOUNT_COLUMNS
70 typedef enum
72 COL_DEFAULT = 0,
73 COL_GETALL,
74 COL_NAME,
75 COL_PROTOCOL,
76 COL_SERVER,
77 N_EDIT_ACCOUNT_COLS
78 } EditAccountColumnPos;
80 PrefsAccount *cur_account;
82 static GList *account_list = NULL;
83 static gboolean account_list_dirty = FALSE;
85 static struct EditAccount {
86 GtkWidget *window;
87 GtkWidget *list_view;
88 GtkWidget *close_btn;
89 } edit_account;
91 static void account_edit_create (void);
92 static void account_destroy (PrefsAccount *ac_prefs);
93 static void account_set_as_default (PrefsAccount *ac_prefs);
94 static void account_set_menu (void);
96 static void account_edit_prefs (GtkWidget *widget, gpointer data);
97 static void account_delete (GtkWidget *widget, gpointer data);
98 static void account_clone (GtkWidget *widget, gpointer data);
100 static void account_up (GtkWidget *widget, gpointer data);
101 static void account_down (GtkWidget *widget, gpointer data);
103 static void account_set_default (GtkWidget *widget, gpointer data);
105 static void account_edit_close (GtkWidget *widget, gpointer data);
107 static gint account_delete_event (GtkWidget *widget,
108 GdkEventAny *event,
109 gpointer data);
110 static void account_size_allocate_cb(GtkWidget *widget,
111 GtkAllocation *allocation);
112 static gboolean account_key_pressed (GtkWidget *widget,
113 GdkEventKey *event,
114 gpointer data);
115 static gboolean account_search_func_cb (GtkTreeModel *model, gint column,
116 const gchar *key, GtkTreeIter *iter,
117 gpointer search_data);
118 static void account_list_view_set (void);
120 static void account_list_set (void);
122 typedef struct FindAccountInStore {
123 gint account_id;
124 GtkTreePath *path;
125 GtkTreeIter iter;
126 } FindAccountInStore;
128 static GtkListStore* account_create_data_store (void);
130 static void account_list_store_insert_account_item (GtkListStore *list_store,
131 PrefsAccount *account_data);
133 static GtkWidget *account_list_view_create (void);
134 static void account_create_list_view_columns (GtkWidget *list_view);
136 static gint account_list_view_get_selected_account_id (GtkWidget *list_view);
137 static GtkTreePath *account_list_view_get_selected_account_path (GtkWidget *list_view);
138 static PrefsAccount *account_list_view_get_selected_account (GtkWidget *list_view);
139 static gboolean account_list_view_select_account (GtkWidget *list_view,
140 gint account_id);
142 static void account_list_view_set_default_by_id(GtkWidget *list_view,
143 gint account_id);
145 static gboolean set_new_default_account (GtkTreeModel *model,
146 GtkTreePath *path,
147 GtkTreeIter *iter,
148 gint *account_id);
150 static gboolean find_account_in_store (GtkTreeModel *model,
151 GtkTreePath *path,
152 GtkTreeIter *iter,
153 FindAccountInStore *data);
155 static void account_get_all_toggled (GtkCellRendererToggle *widget,
156 gchar *path,
157 GtkWidget *list_view);
159 static void account_double_clicked (GtkTreeView *list_view,
160 GtkTreePath *path,
161 GtkTreeViewColumn *column,
162 gpointer data);
164 static void drag_begin (GtkTreeView *list_view,
165 GdkDragContext *context,
166 gpointer data);
168 static void drag_end (GtkTreeView *list_view,
169 GdkDragContext *context,
170 gpointer data);
172 static void account_row_changed_while_drag_drop (GtkTreeModel *model,
173 GtkTreePath *path,
174 GtkTreeIter *iter,
175 gpointer arg3,
176 GtkTreeView *list_view);
178 static void account_flush_state(void)
180 account_set_menu();
181 main_window_reflect_prefs_all();
183 account_list_dirty = FALSE;
186 void account_read_config_all(void)
188 GSList *ac_label_list = NULL, *cur;
189 gchar *rcpath;
190 FILE *fp;
191 gchar buf[PREFSBUFSIZE];
192 PrefsAccount *ac_prefs;
194 debug_print("Reading all config for each account...\n");
196 rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL);
197 if ((fp = claws_fopen(rcpath, "rb")) == NULL) {
198 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "claws_fopen");
199 g_free(rcpath);
200 return;
202 g_free(rcpath);
204 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
205 if (!strncmp(buf, "[Account: ", 10)) {
206 strretchomp(buf);
207 memmove(buf, buf + 1, sizeof(buf) - 1);
208 buf[strlen(buf) - 1] = '\0';
209 debug_print("Found label: %s\n", buf);
210 ac_label_list = g_slist_append(ac_label_list,
211 g_strdup(buf));
214 claws_fclose(fp);
216 /* read config data from file */
217 cur_account = NULL;
218 for (cur = ac_label_list; cur != NULL; cur = cur->next) {
219 ac_prefs = prefs_account_new_from_config((gchar *)cur->data);
220 account_list = g_list_append(account_list, ac_prefs);
221 if (ac_prefs->is_default)
222 cur_account = ac_prefs;
224 /* if default is not set, assume first account as default */
225 if (!cur_account && account_list) {
226 ac_prefs = (PrefsAccount *)account_list->data;
227 account_set_as_default(ac_prefs);
228 cur_account = ac_prefs;
231 account_set_menu();
232 main_window_reflect_prefs_all_now();
234 while (ac_label_list) {
235 g_free(ac_label_list->data);
236 ac_label_list = g_slist_remove(ac_label_list,
237 ac_label_list->data);
241 void account_write_config_all(void)
243 prefs_account_write_config_all(account_list);
247 * account_find_all_from_address:
248 * @ac_list: initial list of accounts. NULL to create a new one.
249 * Accounts found in the @address will be appended to this list.
250 * @address: Email address string.
252 * Find all the mail (not news) accounts within the specified address.
254 * Return value: the original accounts list with the found accounts appended.
256 GList *account_find_all_from_address(GList *ac_list, const gchar *address)
258 GList *cur;
259 PrefsAccount *ac;
261 if (address == NULL)
262 return ac_list;
264 for (cur = account_list; cur != NULL; cur = cur->next) {
265 ac = (PrefsAccount *)cur->data;
266 if (ac->protocol != A_NNTP && ac->address &&
267 strcasestr(address, ac->address) != NULL)
268 ac_list = g_list_append(ac_list, ac);
270 return ac_list;
273 GList *account_find_all(void)
275 GList *cur;
276 PrefsAccount *ac;
277 GList *ac_list = NULL;
279 for (cur = account_list; cur != NULL; cur = cur->next) {
280 ac = (PrefsAccount *)cur->data;
281 ac_list = g_list_append(ac_list, ac);
283 return ac_list;
286 PrefsAccount *account_find_from_smtp_server(const gchar *address,
287 const gchar *smtp_server)
289 GList *cur;
290 PrefsAccount *ac;
292 cm_return_val_if_fail(address != NULL, NULL);
293 cm_return_val_if_fail(smtp_server != NULL, NULL);
295 for (cur = account_list; cur != NULL; cur = cur->next) {
296 ac = (PrefsAccount *)cur->data;
297 if (!g_strcmp0(address, ac->address) &&
298 !g_strcmp0(smtp_server, ac->smtp_server))
299 return ac;
302 return NULL;
306 * account_find_from_address:
307 * @address: Email address string.
309 * Find a mail (not news if newsgroups_ok is FALSE) account with the specified email address.
311 * Return value: The found account, or NULL if not found.
313 PrefsAccount *account_find_from_address(const gchar *address, gboolean newsgroups_ok)
315 GList *cur;
316 PrefsAccount *ac;
318 cm_return_val_if_fail(address != NULL, NULL);
320 for (cur = account_list; cur != NULL; cur = cur->next) {
321 ac = (PrefsAccount *)cur->data;
322 if ((ac->protocol != A_NNTP || newsgroups_ok) && ac->address &&
323 g_ascii_strcasecmp(address, ac->address) == 0)
324 return ac;
327 return NULL;
330 PrefsAccount *account_find_from_id(gint id)
332 GList *cur;
333 PrefsAccount *ac;
335 for (cur = account_list; cur != NULL; cur = cur->next) {
336 ac = (PrefsAccount *)cur->data;
337 if (id == ac->account_id)
338 return ac;
341 return NULL;
344 PrefsAccount *account_find_from_item(FolderItem *item)
346 PrefsAccount *ac;
348 cm_return_val_if_fail(item != NULL, NULL);
350 ac = item->account;
351 if (!ac) {
352 FolderItem *cur_item = folder_item_parent(item);
353 while (cur_item != NULL) {
354 if (cur_item->account && cur_item->apply_sub) {
355 ac = cur_item->account;
356 break;
358 cur_item = folder_item_parent(cur_item);
361 if (!ac)
362 ac = item->folder->account;
364 return ac;
367 static void account_set_menu(void)
369 main_window_set_account_menu(account_list);
372 void account_set_menu_only_toolbar(void)
374 main_window_set_account_menu_only_toolbar(account_list);
377 GList *account_get_list(void)
379 return account_list;
382 void account_edit_focus(void)
384 if (edit_account.window == NULL) {
385 return;
387 manage_window_set_transient(GTK_WINDOW(edit_account.window));
388 gtk_widget_grab_focus(edit_account.close_btn);
389 gtk_widget_show(edit_account.window);
390 gtk_window_set_modal(GTK_WINDOW(edit_account.window), TRUE);
391 manage_window_focus_in(edit_account.window, NULL, NULL);
394 void account_edit_open(gpointer a, gpointer b)
396 inc_lock();
398 account_list_dirty = FALSE;
400 if (compose_get_compose_list()) {
401 alertpanel_error(_("Some composing windows are open.\n"
402 "Please close all the composing "
403 "windows before editing accounts."));
404 inc_unlock();
405 return;
408 debug_print("Opening account edit window...\n");
410 if (!edit_account.window)
411 account_edit_create();
413 account_list_view_set();
415 account_edit_focus();
418 void account_add(void)
420 PrefsAccount *ac_prefs;
422 ac_prefs = prefs_account_open(NULL, &account_list_dirty);
424 if (!ac_prefs) return;
426 account_edit_focus();
428 account_list = g_list_append(account_list, ac_prefs);
430 if (ac_prefs->is_default)
431 account_set_as_default(ac_prefs);
433 account_list_view_set();
435 if (ac_prefs->protocol == A_IMAP4 || ac_prefs->protocol == A_NNTP) {
436 Folder *folder;
438 if (ac_prefs->protocol == A_IMAP4) {
439 folder = folder_new(folder_get_class_from_string("imap"), ac_prefs->account_name,
440 ac_prefs->recv_server);
441 } else {
442 folder = folder_new(folder_get_class_from_string("news"), ac_prefs->account_name,
443 ac_prefs->nntp_server);
445 if (folder == NULL) {
446 alertpanel_error(_("Can't create folder."));
447 return;
449 folder->account = ac_prefs;
450 ac_prefs->folder = folder;
451 folder_add(folder);
452 if (ac_prefs->protocol == A_IMAP4)
453 folder->klass->create_tree(folder);
454 folderview_set_all();
455 folder_write_list();
459 void account_open(PrefsAccount *ac_prefs, gboolean called_from_acc_list)
461 gboolean prev_default;
462 gchar *ac_name, *old_prefix, *new_prefix;
463 gboolean account_dirty = FALSE;
465 cm_return_if_fail(ac_prefs != NULL);
467 if (compose_get_compose_list()) {
468 alertpanel_error(_("Some composing windows are open.\n"
469 "Please close all the composing "
470 "windows before editing accounts."));
471 return;
474 prev_default = ac_prefs->is_default;
475 Xstrdup_a(ac_name, ac_prefs->account_name ? ac_prefs->account_name : "",
476 return);
478 prefs_account_open(ac_prefs, &account_dirty);
480 if (called_from_acc_list)
481 account_edit_focus();
483 if (account_dirty) {
484 if (!prev_default && ac_prefs->is_default)
485 account_set_as_default(ac_prefs);
487 if (ac_prefs->folder && g_strcmp0(ac_name, ac_prefs->account_name) != 0) {
488 old_prefix = folder_get_identifier(FOLDER(ac_prefs->folder));
489 folder_set_name(FOLDER(ac_prefs->folder),
490 ac_prefs->account_name);
491 folderview_set_all();
492 folder_prefs_save_config_recursive(FOLDER(ac_prefs->folder));
493 new_prefix = folder_get_identifier(FOLDER(ac_prefs->folder));
495 account_rename_path(old_prefix, new_prefix);
496 prefs_filtering_rename_path(old_prefix, new_prefix);
497 prefs_actions_rename_path(old_prefix, new_prefix);
499 g_free(old_prefix);
500 g_free(new_prefix);
503 account_write_config_all();
505 account_flush_state();
509 static void account_set_as_default(PrefsAccount *ac_prefs)
511 PrefsAccount *ap;
512 GList *cur;
514 for (cur = account_list; cur != NULL; cur = cur->next) {
515 ap = (PrefsAccount *)cur->data;
516 if (ap->is_default)
517 ap->is_default = FALSE;
520 ac_prefs->is_default = TRUE;
523 PrefsAccount *account_get_default(void)
525 PrefsAccount *ap;
526 GList *cur;
528 for (cur = account_list; cur != NULL; cur = cur->next) {
529 ap = (PrefsAccount *)cur->data;
530 if (ap->is_default)
531 return ap;
534 return NULL;
537 void account_set_missing_folder(void)
539 PrefsAccount *ap;
540 GList *cur;
542 for (cur = account_list; cur != NULL; cur = cur->next) {
543 ap = (PrefsAccount *)cur->data;
544 if ((ap->protocol == A_IMAP4 || ap->protocol == A_NNTP) &&
545 !ap->folder) {
546 Folder *folder;
548 if (ap->protocol == A_IMAP4) {
549 folder = folder_new(folder_get_class_from_string("imap"), ap->account_name,
550 ap->recv_server);
551 } else {
552 folder = folder_new(folder_get_class_from_string("news"), ap->account_name,
553 ap->nntp_server);
555 if (folder == NULL)
556 return;
557 folder->account = ap;
558 ap->folder = folder;
559 folder_add(folder);
560 if (ap->protocol == A_IMAP4)
561 folder->klass->create_tree(folder);
562 folder_write_list();
568 #define CHECK_CHANGE_FOLDER(folder) { \
569 if (folder && !strncmp(folder, old_id, strlen(old_id))) { \
570 if (strlen(folder) == strlen(old_id)) { \
571 g_free(folder); \
572 folder = g_strdup(new_id); \
573 } else if (strlen(folder) > strlen(old_id) \
574 && folder[strlen(old_id)] == G_DIR_SEPARATOR) { \
575 gchar *new_path = g_strdup_printf("%s%s", \
576 new_id, (folder + strlen(old_id))); \
577 g_free(folder); \
578 folder = new_path; \
583 void account_rename_path(const gchar *old_id, const gchar *new_id)
585 GList *cur = account_list;
586 for (; cur != NULL; cur = g_list_next(cur)) {
587 PrefsAccount *ap = (PrefsAccount *)cur->data;
588 CHECK_CHANGE_FOLDER(ap->inbox);
589 CHECK_CHANGE_FOLDER(ap->local_inbox);
590 CHECK_CHANGE_FOLDER(ap->queue_folder);
591 CHECK_CHANGE_FOLDER(ap->sent_folder);
592 CHECK_CHANGE_FOLDER(ap->draft_folder);
593 CHECK_CHANGE_FOLDER(ap->trash_folder);
597 FolderItem *account_get_special_folder(PrefsAccount *ac_prefs,
598 SpecialFolderItemType type)
600 FolderItem *item = NULL;
602 cm_return_val_if_fail(ac_prefs != NULL, NULL);
604 switch (type) {
605 case F_INBOX:
606 if (ac_prefs->folder)
607 item = FOLDER(ac_prefs->folder)->inbox;
608 if (!item)
609 item = folder_get_default_inbox();
610 break;
611 case F_OUTBOX:
612 if (ac_prefs->set_sent_folder && ac_prefs->sent_folder) {
613 item = folder_find_item_from_identifier
614 (ac_prefs->sent_folder);
616 if (!item) {
617 if (ac_prefs->folder)
618 item = FOLDER(ac_prefs->folder)->outbox;
619 if (!item)
620 item = folder_get_default_outbox_for_class(F_MH);
621 if (!item)
622 item = folder_get_default_outbox();
624 break;
625 case F_DRAFT:
626 if (ac_prefs->set_draft_folder && ac_prefs->draft_folder) {
627 item = folder_find_item_from_identifier
628 (ac_prefs->draft_folder);
630 if (!item) {
631 if (ac_prefs->folder)
632 item = FOLDER(ac_prefs->folder)->draft;
633 if (!item)
634 item = folder_get_default_draft_for_class(F_MH);
635 if (!item)
636 item = folder_get_default_draft();
638 break;
639 case F_QUEUE:
640 if (ac_prefs->set_queue_folder && ac_prefs->queue_folder) {
641 item = folder_find_item_from_identifier
642 (ac_prefs->queue_folder);
644 if (!item) {
645 if (ac_prefs->folder)
646 item = FOLDER(ac_prefs->folder)->queue;
647 if (!item)
648 item = folder_get_default_queue_for_class(F_MH);
649 if (!item)
650 item = folder_get_default_queue();
652 break;
653 case F_TRASH:
654 if (ac_prefs->set_trash_folder && ac_prefs->trash_folder) {
655 item = folder_find_item_from_identifier
656 (ac_prefs->trash_folder);
658 if (!item) {
659 if (ac_prefs->folder)
660 item = FOLDER(ac_prefs->folder)->trash;
661 if (!item)
662 item = folder_get_default_trash_for_class(F_MH);
663 if (!item)
664 item = folder_get_default_trash();
666 break;
667 default:
668 break;
671 return item;
674 void account_destroy(PrefsAccount *ac_prefs)
676 cm_return_if_fail(ac_prefs != NULL);
678 folder_unref_account_all(ac_prefs);
680 account_list = g_list_remove(account_list, ac_prefs);
682 if (cur_account == ac_prefs) cur_account = NULL;
683 if (!cur_account && account_list) {
684 cur_account = account_get_default();
685 if (!cur_account) {
686 ac_prefs = (PrefsAccount *)account_list->data;
687 account_set_as_default(ac_prefs);
688 cur_account = ac_prefs;
694 *\brief Save Gtk object size to prefs dataset
696 static void account_size_allocate_cb(GtkWidget *widget,
697 GtkAllocation *allocation)
699 cm_return_if_fail(allocation != NULL);
701 gtk_window_get_size(GTK_WINDOW(widget),
702 &prefs_common.accountswin_width, &prefs_common.accountswin_height);
705 static void account_edit_create(void)
707 GtkWidget *window;
708 GtkWidget *vbox;
709 GtkWidget *label;
710 GtkWidget *hbox;
711 GtkWidget *scrolledwin;
712 GtkWidget *list_view;
714 GtkWidget *vbox2;
715 GtkWidget *add_btn;
716 GtkWidget *edit_btn;
717 GtkWidget *del_btn;
718 GtkWidget *clone_btn;
719 GtkWidget *up_btn;
720 GtkWidget *down_btn;
722 GtkWidget *default_btn;
724 GtkWidget *confirm_area;
725 GtkWidget *help_btn;
726 GtkWidget *close_btn;
728 static GdkGeometry geometry;
730 debug_print("Creating account edit window...\n");
732 window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "account");
733 gtk_container_set_border_width (GTK_CONTAINER (window), 8);
734 gtk_window_set_title (GTK_WINDOW (window), _("Edit accounts"));
735 g_signal_connect (G_OBJECT (window), "delete_event",
736 G_CALLBACK (account_delete_event), NULL);
737 g_signal_connect (G_OBJECT (window), "key_press_event",
738 G_CALLBACK (account_key_pressed), NULL);
739 MANAGE_WINDOW_SIGNALS_CONNECT (window);
740 gtk_widget_realize(window);
742 vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
743 gtk_widget_show (vbox);
744 gtk_container_add (GTK_CONTAINER (window), vbox);
746 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
747 gtk_widget_show (hbox);
748 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
750 label = gtk_label_new
751 (_("Using 'Get Mail' will retrieve messages from your Accounts "
752 "in the order given, the checkbox indicates which accounts "
753 "will be included. Bold text indicates the default account."));
754 gtk_widget_show (label);
756 gtk_box_pack_start (GTK_BOX (hbox), label, FALSE, FALSE, 4);
757 gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_LEFT);
758 gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
760 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
761 gtk_widget_show (hbox);
762 gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
763 gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
765 scrolledwin = gtk_scrolled_window_new (NULL, NULL);
766 gtk_widget_show (scrolledwin);
767 gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
768 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
769 GTK_POLICY_AUTOMATIC,
770 GTK_POLICY_AUTOMATIC);
772 list_view = account_list_view_create();
773 gtk_widget_show(list_view);
774 gtk_container_add(GTK_CONTAINER(scrolledwin), list_view);
776 vbox2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
777 gtk_widget_show (vbox2);
778 gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
780 add_btn = gtk_button_new_with_mnemonic(_("_New"));
781 gtk_widget_show (add_btn);
782 gtk_box_pack_start (GTK_BOX (vbox2), add_btn, FALSE, FALSE, 4);
783 g_signal_connect (G_OBJECT(add_btn), "clicked",
784 G_CALLBACK (account_add), NULL);
785 CLAWS_SET_TIP(add_btn,
786 _("Create a new account"));
788 edit_btn = gtk_button_new_with_mnemonic(_("_Edit"));
789 gtk_widget_show (edit_btn);
790 gtk_box_pack_start (GTK_BOX (vbox2), edit_btn, FALSE, FALSE, 4);
791 g_signal_connect (G_OBJECT(edit_btn), "clicked",
792 G_CALLBACK (account_edit_prefs), NULL);
793 CLAWS_SET_TIP(edit_btn,
794 _("Edit preferences for the selected account"));
796 del_btn = gtkut_stock_button("edit-delete", _("D_elete"));
797 gtk_widget_show (del_btn);
798 gtk_box_pack_start (GTK_BOX (vbox2), del_btn, FALSE, FALSE, 4);
799 g_signal_connect (G_OBJECT(del_btn), "clicked",
800 G_CALLBACK (account_delete), NULL);
801 CLAWS_SET_TIP(del_btn,
802 _("Delete the selected account from the list"));
804 clone_btn = gtkut_stock_button("edit-copy", _("_Copy"));
805 gtk_widget_show (clone_btn);
806 gtk_box_pack_start (GTK_BOX (vbox2), clone_btn, FALSE, FALSE, 4);
807 g_signal_connect(G_OBJECT(clone_btn), "clicked",
808 G_CALLBACK(account_clone), NULL);
809 CLAWS_SET_TIP(clone_btn,
810 _("Create a new copy of the selected account"));
812 down_btn = gtkut_stock_button("go-down", _("_Down"));
813 gtk_widget_show (down_btn);
814 gtk_box_pack_end (GTK_BOX (vbox2), down_btn, FALSE, FALSE, 4);
815 g_signal_connect (G_OBJECT(down_btn), "clicked",
816 G_CALLBACK (account_down), NULL);
817 CLAWS_SET_TIP(down_btn,
818 _("Move the selected account down"));
820 up_btn = gtkut_stock_button("go-up", _("_Up"));
821 gtk_widget_show (up_btn);
822 gtk_box_pack_end (GTK_BOX (vbox2), up_btn, FALSE, FALSE, 4);
823 g_signal_connect (G_OBJECT(up_btn), "clicked",
824 G_CALLBACK (account_up), NULL);
825 CLAWS_SET_TIP(up_btn,
826 _("Move the selected account up"));
828 hbox = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 8);
829 gtk_widget_show (hbox);
830 gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
832 default_btn = gtk_button_new_with_mnemonic
833 (_(" _Set as default account "));
834 gtk_widget_show (default_btn);
835 gtk_box_pack_start (GTK_BOX (hbox), default_btn, FALSE, FALSE, 0);
836 g_signal_connect (G_OBJECT(default_btn), "clicked",
837 G_CALLBACK (account_set_default), NULL);
839 gtkut_stock_button_set_create_with_help(&confirm_area, &help_btn,
840 &close_btn, "window-close", _("_Close"),
841 NULL, NULL, NULL, NULL, NULL, NULL);
842 gtk_widget_show(confirm_area);
844 gtk_box_pack_end (GTK_BOX (hbox), confirm_area, FALSE, FALSE, 0);
845 gtk_widget_grab_default (close_btn);
847 g_signal_connect (G_OBJECT (close_btn), "clicked",
848 G_CALLBACK (account_edit_close),
849 NULL);
850 g_signal_connect(G_OBJECT(help_btn), "clicked",
851 G_CALLBACK(manual_open_with_anchor_cb),
852 MANUAL_ANCHOR_ACCOUNTPREFS);
855 g_signal_connect(G_OBJECT(window), "size_allocate",
856 G_CALLBACK(account_size_allocate_cb), NULL);
858 if (!geometry.min_height) {
859 geometry.min_width = 500;
860 geometry.min_height = 380;
863 gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL, &geometry,
864 GDK_HINT_MIN_SIZE);
865 gtk_window_set_default_size(GTK_WINDOW(window), prefs_common.accountswin_width,
866 prefs_common.accountswin_height);
867 #ifdef G_OS_WIN32
868 gtk_window_move(GTK_WINDOW(window), 48, 48);
869 #endif
871 edit_account.window = window;
872 edit_account.list_view = list_view;
873 edit_account.close_btn = close_btn;
876 static void account_edit_prefs(GtkWidget *widget, gpointer data)
878 PrefsAccount *ac_prefs;
880 ac_prefs = account_list_view_get_selected_account(edit_account.list_view);
882 if (ac_prefs) {
883 account_open(ac_prefs, TRUE);
884 account_list_view_set();
888 static gboolean account_delete_references_func(GNode *node, gpointer data)
890 FolderItem *item;
891 gint account;
893 cm_return_val_if_fail(node->data != NULL, FALSE);
895 item = FOLDER_ITEM(node->data);
896 account = GPOINTER_TO_INT(data);
898 if(!item->prefs) /* && item->prefs->stype == F_NORMAL */
899 return FALSE;
900 if(item->prefs->default_account != account)
901 return FALSE;
903 item->prefs->enable_default_account = FALSE;
904 item->prefs->default_account = 0;
905 folder_item_prefs_save_config(item);
907 return FALSE;
911 #define ACP_FDUP(fld) ac_clon->fld = ((ac_prefs->fld) != NULL)?\
912 g_strdup(ac_prefs->fld): NULL
913 #define ACP_FASSIGN(fld) ac_clon->fld = ac_prefs->fld
914 static void account_clone(GtkWidget *widget, gpointer data)
916 PrefsAccount *ac_prefs, *ac_clon;
917 GSList *hdrs = NULL;
918 CustomHeader *cch = NULL, *ch = NULL;
920 ac_prefs = account_list_view_get_selected_account(edit_account.list_view);
921 if (ac_prefs == NULL)
922 return;
924 if (ac_prefs->protocol == A_IMAP4 || ac_prefs->protocol == A_NNTP) {
925 alertpanel_error(_("Accounts with remote folders cannot be copied."));
926 return;
928 account_list_dirty = TRUE;
930 ac_clon = prefs_account_new();
931 /* copy fields */
932 ac_clon->account_name = g_strdup_printf(_("Copy of %s"),
933 ac_prefs->account_name);
934 /* personal */
935 ACP_FDUP(name);
936 ACP_FDUP(address);
937 ACP_FDUP(organization);
939 /* server */
940 ACP_FASSIGN(protocol);
941 ACP_FDUP(recv_server);
942 ACP_FDUP(smtp_server);
943 ACP_FDUP(nntp_server);
944 ACP_FASSIGN(use_nntp_auth);
945 ACP_FASSIGN(use_nntp_auth_onconnect);
946 ACP_FDUP(userid);
947 ACP_FDUP(passwd);
949 ACP_FDUP(local_mbox);
950 ACP_FASSIGN(use_mail_command);
951 ACP_FDUP(mail_command);
953 ACP_FASSIGN(ssl_pop);
954 ACP_FASSIGN(ssl_imap);
955 ACP_FASSIGN(ssl_nntp);
956 ACP_FASSIGN(ssl_smtp);
957 ACP_FASSIGN(ssl_certs_auto_accept);
958 ACP_FASSIGN(use_nonblocking_ssl);
959 ACP_FASSIGN(use_tls_sni);
960 ACP_FASSIGN(in_ssl_client_cert_file);
961 ACP_FASSIGN(in_ssl_client_cert_pass);
962 ACP_FASSIGN(out_ssl_client_cert_file);
963 ACP_FASSIGN(out_ssl_client_cert_pass);
965 /* receive */
966 ACP_FASSIGN(use_pop_auth);
967 ACP_FASSIGN(pop_auth_type);
968 ACP_FASSIGN(rmmail);
969 ACP_FASSIGN(msg_leave_time);
970 ACP_FASSIGN(msg_leave_hour);
971 ACP_FASSIGN(recv_at_getall);
972 ACP_FASSIGN(sd_rmmail_on_download);
973 ACP_FASSIGN(enable_size_limit);
974 ACP_FASSIGN(size_limit);
975 ACP_FASSIGN(filter_on_recv);
976 ACP_FASSIGN(filterhook_on_recv);
977 ACP_FDUP(inbox);
978 ACP_FDUP(local_inbox);
979 ACP_FASSIGN(max_articles);
980 ACP_FASSIGN(autochk_use_default);
981 ACP_FASSIGN(autochk_use_custom);
982 ACP_FASSIGN(autochk_itv);
983 ac_clon->autocheck_timer = 0;
985 ACP_FASSIGN(imap_auth_type);
986 ACP_FASSIGN(imap_batch_size);
988 /* send */
989 ACP_FASSIGN(gen_msgid);
990 ACP_FASSIGN(gen_xmailer);
991 ACP_FASSIGN(add_customhdr);
992 ACP_FASSIGN(use_smtp_auth);
993 ACP_FASSIGN(smtp_auth_type);
994 ACP_FDUP(smtp_userid);
995 ACP_FDUP(smtp_passwd);
997 ACP_FASSIGN(pop_before_smtp);
998 ACP_FASSIGN(pop_before_smtp_timeout);
999 ACP_FASSIGN(last_pop_login_time);
1001 ac_clon->customhdr_list = NULL;
1002 hdrs = ac_prefs->customhdr_list;
1003 while (hdrs != NULL) {
1004 ch = (CustomHeader *)hdrs->data;
1006 cch = g_new0(CustomHeader, 1);
1007 cch->account_id = ac_clon->account_id;
1008 cch->name = (ch->name != NULL) ? g_strdup(ch->name) : NULL;
1009 cch->value = (ch->value != NULL) ? g_strdup(ch->value) : NULL;
1011 ac_clon->customhdr_list = g_slist_append(ac_clon->customhdr_list, cch);
1013 hdrs = g_slist_next(hdrs);
1016 /* compose */
1017 ACP_FASSIGN(sig_type);
1018 ACP_FDUP(sig_path);
1019 ACP_FASSIGN(auto_sig);
1020 ACP_FDUP(sig_sep);
1021 ACP_FASSIGN(set_autocc);
1022 ACP_FDUP(auto_cc);
1023 ACP_FASSIGN(set_autobcc);
1024 ACP_FDUP(auto_bcc);
1025 ACP_FASSIGN(set_autoreplyto);
1026 ACP_FDUP(auto_replyto);
1027 ACP_FASSIGN(enable_default_dictionary);
1028 ACP_FDUP(default_dictionary);
1029 ACP_FASSIGN(enable_default_alt_dictionary);
1030 ACP_FDUP(default_alt_dictionary);
1031 ACP_FASSIGN(compose_with_format);
1032 ACP_FDUP(compose_subject_format);
1033 ACP_FDUP(compose_body_format);
1034 ACP_FASSIGN(reply_with_format);
1035 ACP_FDUP(reply_quotemark);
1036 ACP_FDUP(reply_body_format);
1037 ACP_FASSIGN(forward_with_format);
1038 ACP_FDUP(forward_quotemark);
1039 ACP_FDUP(forward_body_format);
1041 /* privacy */
1042 ACP_FDUP(default_privacy_system);
1043 ACP_FASSIGN(default_encrypt);
1044 ACP_FASSIGN(default_encrypt_reply);
1045 ACP_FASSIGN(default_sign);
1046 ACP_FASSIGN(default_sign_reply);
1047 ACP_FASSIGN(save_encrypted_as_clear_text);
1048 ACP_FASSIGN(encrypt_to_self);
1050 /* advanced */
1051 ACP_FASSIGN(set_smtpport);
1052 ACP_FASSIGN(smtpport);
1053 ACP_FASSIGN(set_popport);
1054 ACP_FASSIGN(popport);
1055 ACP_FASSIGN(set_imapport);
1056 ACP_FASSIGN(imapport);
1057 ACP_FASSIGN(set_nntpport);
1058 ACP_FASSIGN(nntpport);
1059 ACP_FASSIGN(set_domain);
1060 ACP_FDUP(domain);
1061 ACP_FASSIGN(mark_crosspost_read);
1062 ACP_FASSIGN(crosspost_col);
1064 #ifndef G_OS_WIN32
1065 ACP_FASSIGN(set_tunnelcmd);
1066 ACP_FDUP(tunnelcmd);
1067 #endif
1069 ACP_FDUP(imap_dir);
1070 ACP_FASSIGN(imap_subsonly);
1071 ACP_FASSIGN(low_bandwidth);
1073 ACP_FASSIGN(set_sent_folder);
1074 ACP_FDUP(sent_folder);
1075 ACP_FASSIGN(set_queue_folder);
1076 ACP_FDUP(queue_folder);
1077 ACP_FASSIGN(set_draft_folder);
1078 ACP_FDUP(draft_folder);
1079 ACP_FASSIGN(set_trash_folder);
1080 ACP_FDUP(trash_folder);
1081 /* don't want two default accounts */
1082 ac_clon->is_default = FALSE;
1083 ACP_FASSIGN(folder);
1085 ACP_FASSIGN(config_version);
1087 ACP_FASSIGN(use_proxy);
1088 ACP_FASSIGN(use_default_proxy);
1089 ACP_FASSIGN(use_proxy_for_send);
1090 ACP_FASSIGN(proxy_info.proxy_type);
1091 ACP_FDUP(proxy_info.proxy_host);
1092 ACP_FASSIGN(proxy_info.proxy_port);
1093 ACP_FASSIGN(proxy_info.use_proxy_auth);
1094 ACP_FDUP(proxy_info.proxy_name);
1095 ACP_FDUP(proxy_info.proxy_pass);
1097 account_list = g_list_append(account_list, ac_clon);
1098 account_list_view_set();
1100 #undef ACP_FDUP
1101 #undef ACP_FASSIGN
1103 static void account_empty_cache(PrefsAccount *ac_prefs)
1105 gchar *cache_dir;
1107 cache_dir = prefs_account_cache_dir(ac_prefs, FALSE);
1108 if (cache_dir == NULL)
1109 return; /* no cache dir, nothing to do */
1111 if (is_dir_exist(cache_dir) && remove_dir_recursive(cache_dir) < 0) {
1112 g_warning("can't remove directory '%s'", cache_dir);
1113 } else {
1114 gchar *server_dir = prefs_account_cache_dir(ac_prefs, TRUE);
1115 if (g_rmdir(server_dir) == 0)
1116 debug_print("Removed empty cache server directory\n");
1117 else
1118 debug_print("Cache server directory not empty: not removed\n");
1119 g_free(server_dir);
1121 g_free(cache_dir);
1124 static void account_delete(GtkWidget *widget, gpointer data)
1126 PrefsAccount *ac_prefs;
1127 gchar buf[BUFFSIZE];
1128 GList *list;
1129 Folder *folder;
1130 GSList *cur;
1132 ac_prefs = account_list_view_get_selected_account(edit_account.list_view);
1133 if (ac_prefs == NULL)
1134 return;
1136 g_snprintf(buf, sizeof(buf),
1137 _("Do you really want to delete the account '%s'?"),
1138 ac_prefs->account_name ? ac_prefs->account_name :
1139 _("(Untitled)"));
1140 if (alertpanel_full(_("Delete account"), buf,
1141 NULL, _("_Cancel"), "edit-delete", _("_Delete"),
1142 NULL, NULL, ALERTFOCUS_FIRST, FALSE,
1143 NULL, ALERT_WARNING) != G_ALERTALTERNATE)
1144 return;
1145 account_list_dirty = TRUE;
1147 if (ac_prefs->folder) {
1148 FolderItem *item;
1150 item = mainwindow_get_mainwindow()->summaryview->folder_item;
1151 if (item && item->folder == FOLDER(ac_prefs->folder))
1152 summary_clear_all(mainwindow_get_mainwindow()->summaryview);
1153 folder_destroy(FOLDER(ac_prefs->folder));
1154 folderview_set_all();
1157 inc_account_autocheck_timer_remove(ac_prefs);
1159 account_destroy(ac_prefs);
1160 account_list_view_set();
1162 debug_print("Removing deleted account references for all the folders...\n");
1163 list = folder_get_list();
1164 for (; list != NULL; list = list->next) {
1165 folder = FOLDER(list->data);
1166 if (folder->node) /* && folder->type == F_? */
1167 g_node_traverse(folder->node, G_PRE_ORDER,
1168 G_TRAVERSE_ALL, -1,
1169 account_delete_references_func,
1170 GINT_TO_POINTER(ac_prefs->account_id));
1173 gchar *uid = g_strdup_printf("%d", ac_prefs->account_id);
1174 passwd_store_delete_block(PWS_ACCOUNT, uid);
1175 g_free(uid);
1177 debug_print("Removing filter rules relative to this account...\n");
1178 for(cur = filtering_rules ; cur != NULL ;) {
1179 FilteringProp * prop = (FilteringProp *) cur->data;
1181 if (prop && (prop->account_id == ac_prefs->account_id)) {
1182 /* get next item before we kill the current one */
1183 cur = g_slist_next(cur);
1185 /* unallocate filteringprop and unchain it from the list */
1186 filteringprop_free(prop);
1187 filtering_rules = g_slist_remove(filtering_rules, prop);
1188 } else {
1189 cur = g_slist_next(cur);
1193 debug_print("Removing cache directory of this account...\n");
1194 account_empty_cache(ac_prefs);
1196 folder_write_list();
1199 static void account_up(GtkWidget *widget, gpointer data)
1201 GtkTreePath *sel = account_list_view_get_selected_account_path
1202 (edit_account.list_view),
1203 *up;
1204 GtkTreeIter isel, iup;
1205 GtkTreeModel *model = gtk_tree_view_get_model
1206 (GTK_TREE_VIEW(edit_account.list_view));
1208 if (!sel)
1209 return;
1210 account_list_dirty = TRUE;
1212 up = gtk_tree_path_copy(sel);
1213 if (!up) {
1214 gtk_tree_path_free(sel);
1215 return;
1218 if (!gtk_tree_path_prev(up)) {
1219 gtk_tree_path_free(up);
1220 gtk_tree_path_free(sel);
1221 return;
1224 if (!gtk_tree_model_get_iter(model, &isel, sel)
1225 || !gtk_tree_model_get_iter(model, &iup, up)) {
1226 gtk_tree_path_free(up);
1227 gtk_tree_path_free(sel);
1228 return;
1231 gtk_list_store_swap(GTK_LIST_STORE(model), &isel, &iup);
1233 account_list_set();
1235 gtk_tree_path_free(up);
1236 gtk_tree_path_free(sel);
1239 static void account_down(GtkWidget *widget, gpointer data)
1241 GtkTreePath *sel = account_list_view_get_selected_account_path
1242 (edit_account.list_view),
1243 *dn;
1244 GtkTreeIter isel, idn;
1245 GtkTreeModel *model = gtk_tree_view_get_model
1246 (GTK_TREE_VIEW(edit_account.list_view));
1248 if (!sel)
1249 return;
1250 account_list_dirty = TRUE;
1252 dn = gtk_tree_path_copy(sel);
1253 if (!dn) {
1254 gtk_tree_path_free(sel);
1255 return;
1258 /* XXX no check possible??? however, if down but at bottom, then
1259 * nothing seems to happen much anyway, so the following seems to
1260 * be okay */
1261 gtk_tree_path_next(dn);
1263 if (!gtk_tree_model_get_iter(model, &isel, sel)
1264 || !gtk_tree_model_get_iter(model, &idn, dn)) {
1265 gtk_tree_path_free(dn);
1266 gtk_tree_path_free(sel);
1267 return;
1270 gtk_list_store_swap(GTK_LIST_STORE(model), &isel, &idn);
1272 account_list_set();
1274 gtk_tree_path_free(dn);
1275 gtk_tree_path_free(sel);
1278 static void account_set_default(GtkWidget *widget, gpointer data)
1280 PrefsAccount *ac_prefs;
1282 if (NULL == (ac_prefs = account_list_view_get_selected_account
1283 (edit_account.list_view)))
1284 return;
1286 /* we need to change the store variables by resetting everything
1287 * and setting the new default one */
1288 account_list_view_set_default_by_id(edit_account.list_view,
1289 ac_prefs->account_id);
1291 account_set_as_default(ac_prefs);
1292 account_list_view_set();
1294 cur_account = ac_prefs;
1295 account_flush_state();
1298 static void account_edit_close(GtkWidget *widget, gpointer data)
1300 account_list_set();
1301 account_write_config_all();
1303 if (!cur_account && account_list) {
1304 PrefsAccount *ac_prefs = (PrefsAccount *)account_list->data;
1305 account_set_as_default(ac_prefs);
1306 cur_account = ac_prefs;
1309 if (account_list_dirty)
1310 account_flush_state();
1312 gtk_widget_hide(edit_account.window);
1313 gtk_window_set_modal(GTK_WINDOW(edit_account.window), FALSE);
1314 inc_unlock();
1317 static gint account_delete_event(GtkWidget *widget, GdkEventAny *event,
1318 gpointer data)
1320 account_edit_close(NULL, NULL);
1321 return TRUE;
1324 static gboolean account_key_pressed(GtkWidget *widget, GdkEventKey *event,
1325 gpointer data)
1327 if (event && event->keyval == GDK_KEY_Escape)
1328 account_edit_close(NULL, NULL);
1329 return FALSE;
1332 static gboolean account_search_func_cb (GtkTreeModel *model, gint column, const gchar *key,
1333 GtkTreeIter *iter, gpointer search_data)
1335 gboolean retval;
1336 PrefsAccount *ac;
1338 gtk_tree_model_get (model, iter, ACCOUNT_DATA, &ac, -1);
1340 if (!ac->name || !key) return FALSE;
1342 retval = (strncmp (key, ac->account_name, strlen(key)) != 0);
1344 debug_print("selecting row\n");
1345 account_list_view_select_account(edit_account.list_view, ac->account_id);
1347 return retval;
1350 static void account_list_view_set(void)
1352 GList *cur;
1353 gint prev_sel_account;
1354 GtkListStore *store;
1356 store = GTK_LIST_STORE(gtk_tree_view_get_model
1357 (GTK_TREE_VIEW(edit_account.list_view)));
1359 prev_sel_account = account_list_view_get_selected_account_id
1360 (edit_account.list_view);
1362 gtk_list_store_clear(store);
1364 for (cur = account_list; cur != NULL; cur = cur->next) {
1365 account_list_store_insert_account_item(store, (PrefsAccount *)cur->data);
1366 if ((PrefsAccount *)cur->data == cur_account)
1367 account_list_view_select_account
1368 (edit_account.list_view,
1369 cur_account->account_id);
1372 if (prev_sel_account >= 0)
1373 account_list_view_select_account(edit_account.list_view,
1374 prev_sel_account);
1377 /* set account list from CList */
1378 static void account_list_set(void)
1380 /* want to make sure we iterate *IN ORDER*, so therefore using
1381 * gtk_tree_model_XXXX_nth_child() */
1382 gint row, n_rows;
1383 PrefsAccount *ac_prefs;
1384 GtkTreeModel *model = gtk_tree_view_get_model
1385 (GTK_TREE_VIEW(edit_account.list_view));
1387 while (account_list)
1388 account_list = g_list_remove(account_list, account_list->data);
1390 n_rows = gtk_tree_model_iter_n_children(model, NULL);
1392 for (row = 0; row < n_rows; row++) {
1393 GtkTreeIter iter;
1395 if (!gtk_tree_model_iter_nth_child(model, &iter, NULL, row)) {
1396 g_warning("%s(%d) - no iter found???", __FILE__, __LINE__);
1397 continue;
1400 ac_prefs = NULL;
1401 gtk_tree_model_get(model, &iter,
1402 ACCOUNT_DATA, &ac_prefs,
1403 -1);
1404 if (ac_prefs)
1405 account_list = g_list_append(account_list, ac_prefs);
1410 *\brief finds the PrefsAccounts which should be used to answer a mail
1412 *\param msginfo The message to be answered
1413 *\param reply_autosel Indicates whether reply account autoselection is on
1415 *\return PrefsAccount * the correct account, NULL if not found
1417 PrefsAccount *account_get_reply_account(MsgInfo *msginfo, gboolean reply_autosel)
1419 PrefsAccount *account = NULL;
1420 /* select the account set in folderitem's property (if enabled) */
1421 if (msginfo->folder->prefs && msginfo->folder->prefs->enable_default_account)
1422 account = account_find_from_id(msginfo->folder->prefs->default_account);
1423 else if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1424 folder_has_parent_of_type(msginfo->folder, F_OUTBOX) ||
1425 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1426 gchar *from = NULL;
1427 if (!procheader_get_header_from_msginfo
1428 (msginfo, &from, "From:")) {
1429 gchar *buf = from + strlen("From:");
1430 extract_address(buf);
1431 account = account_find_from_address(buf, FALSE);
1432 g_free(from);
1435 /* select account by to: and cc: header if enabled */
1436 if (reply_autosel) {
1437 gchar * field = NULL;
1438 int fieldno = 0;
1439 for (field = msginfo->to; fieldno++ < 2; field = msginfo->cc) {
1440 if (!account && field) {
1441 gchar *f = g_strdup(field);
1442 if (f) {
1443 gchar *cur = f;
1444 gchar *next = NULL;
1445 gchar *to = NULL;
1446 do {
1447 next = strchr_with_skip_quote(cur, '"', ',');
1448 if (next)
1449 (*next) = 0;
1450 Xstrdup_a(to, cur, return NULL);
1451 extract_address(to);
1452 account = account_find_from_address(to, FALSE);
1453 if (next)
1454 cur = next + 1;
1455 else
1456 break;
1457 } while (!account);
1458 g_free(f);
1462 if (!account) {
1463 gchar *deliveredto = NULL;
1464 if (!procheader_get_header_from_msginfo
1465 (msginfo, &deliveredto, "Delivered-To:")) {
1466 gchar *buf = deliveredto + strlen("Delivered-To:");
1467 extract_address(buf);
1468 account = account_find_from_address(buf, FALSE);
1469 g_free(deliveredto);
1474 /* select the account for the whole folder (IMAP / NNTP) */
1475 if (!account)
1476 /* FIXME: this is not right, because folder may be nested. we should
1477 * ascend the tree until we find a parent with proper account
1478 * information */
1479 account = msginfo->folder->folder->account;
1481 /* select current account */
1482 if (!account) account = cur_account;
1484 return account;
1488 *\brief Create data store
1490 static GtkListStore* account_create_data_store(void)
1492 return gtk_list_store_new(N_ACCOUNT_COLUMNS,
1493 G_TYPE_INT, /* ACCOUNT_IS_DEFAULT */
1494 G_TYPE_BOOLEAN, /* ACCOUNT_ENABLE_GET_ALL */
1495 G_TYPE_STRING, /* ACCOUNT_NAME */
1496 G_TYPE_STRING, /* ACCOUNT_PROTOCOL */
1497 G_TYPE_STRING, /* ACCOUNT_SERVER */
1498 G_TYPE_POINTER, /* ACCOUNT_DATA */
1499 -1);
1503 *\brief Insert an account item in the list.
1505 *\return GtkTreeRowReference * A tree row reference, which is guaranteed to
1506 * stable whatever operations are performed on the list.
1508 static void account_list_store_insert_account_item(GtkListStore *list_store,
1509 PrefsAccount *ac_prefs)
1511 GtkTreeIter iter;
1512 gboolean is_get_all = (ac_prefs->protocol == A_POP3 ||
1513 ac_prefs->protocol == A_IMAP4 ||
1514 ac_prefs->protocol == A_NNTP ||
1515 ac_prefs->protocol == A_LOCAL) &&
1516 ac_prefs->recv_at_getall;
1517 gchar *protocol, *server;
1519 #ifdef USE_GNUTLS
1520 protocol = ac_prefs->protocol == A_POP3 ?
1521 (ac_prefs->ssl_pop == SSL_TUNNEL ?
1522 "POP (TLS)" :
1523 ac_prefs->ssl_pop == SSL_STARTTLS ?
1524 "POP (STARTTLS)" : "POP") :
1525 ac_prefs->protocol == A_IMAP4 ?
1526 (ac_prefs->ssl_imap == SSL_TUNNEL ?
1527 "IMAP (TLS)" :
1528 ac_prefs->ssl_imap == SSL_STARTTLS ?
1529 "IMAP (STARTTLS)" : "IMAP") :
1530 ac_prefs->protocol == A_NNTP ?
1531 (ac_prefs->ssl_nntp == SSL_TUNNEL ?
1532 "NNTP (TLS)" : "NNTP") :
1533 ac_prefs->protocol == A_LOCAL ? "Local" :
1534 ac_prefs->protocol == A_NONE ? "SMTP" : "-";
1535 #else
1536 protocol = ac_prefs->protocol == A_POP3 ? "POP" :
1537 ac_prefs->protocol == A_IMAP4 ? "IMAP" :
1538 ac_prefs->protocol == A_LOCAL ? "Local" :
1539 ac_prefs->protocol == A_NNTP ? "NNTP" :
1540 ac_prefs->protocol == A_NONE ? "SMTP" : "-";
1541 #endif
1543 server= ac_prefs->protocol == A_NNTP ? ac_prefs->nntp_server :
1544 ac_prefs->protocol == A_LOCAL ? "-" :
1545 ac_prefs->protocol == A_NONE ? ac_prefs->smtp_server :
1546 ac_prefs->recv_server;
1548 gtk_list_store_append(list_store, &iter);
1549 gtk_list_store_set(list_store, &iter,
1550 ACCOUNT_IS_DEFAULT, ac_prefs->is_default ? PANGO_WEIGHT_BOLD : PANGO_WEIGHT_NORMAL,
1551 ACCOUNT_ENABLE_GET_ALL, is_get_all,
1552 ACCOUNT_NAME, ac_prefs->account_name,
1553 ACCOUNT_PROTOCOL, protocol,
1554 ACCOUNT_SERVER, server,
1555 ACCOUNT_DATA, ac_prefs,
1556 -1);
1560 *\brief Create and set up account list view, including tasks like
1561 * creating the data store (\ref account_create_data_store()),
1562 * and setting up the account list's individual columns (\ref
1563 * account_create_list_view_columns()).
1565 *\return GtkWidget * The created list view widget.
1567 static GtkWidget *account_list_view_create(void)
1569 GtkTreeView *list_view;
1570 GtkTreeSelection *selector;
1571 GtkListStore *store = account_create_data_store();
1573 list_view = GTK_TREE_VIEW(gtk_tree_view_new_with_model(GTK_TREE_MODEL(store)));
1574 g_object_unref(G_OBJECT(store));
1575 #ifdef GENERIC_UMPC
1576 g_object_set(list_view, "allow-checkbox-mode", FALSE, NULL);
1577 #endif
1578 gtk_tree_view_set_reorderable(list_view, TRUE);
1580 selector = gtk_tree_view_get_selection(list_view);
1581 gtk_tree_selection_set_mode(selector, GTK_SELECTION_BROWSE);
1583 /* create the columns */
1584 account_create_list_view_columns(GTK_WIDGET(list_view));
1586 /* set a double click listener */
1587 g_signal_connect(G_OBJECT(list_view), "row_activated",
1588 G_CALLBACK(account_double_clicked),
1589 list_view);
1591 g_signal_connect(G_OBJECT(list_view), "drag_begin",
1592 G_CALLBACK(drag_begin),
1593 list_view);
1595 g_signal_connect(G_OBJECT(list_view), "drag_end",
1596 G_CALLBACK(drag_end),
1597 list_view);
1599 gtk_tree_view_set_reorderable(list_view, TRUE);
1600 return GTK_WIDGET(list_view);
1603 static void account_create_list_view_columns(GtkWidget *list_view)
1605 GtkTreeViewColumn *column;
1606 GtkCellRenderer *renderer;
1608 renderer = gtk_cell_renderer_toggle_new();
1609 g_object_set(renderer,
1610 "radio", FALSE,
1611 "activatable", TRUE,
1612 NULL);
1613 column = gtk_tree_view_column_new_with_attributes
1614 (C_("Accounts List Get Column Name", "G"), renderer,
1615 "active", ACCOUNT_ENABLE_GET_ALL,
1616 NULL);
1617 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1618 gtk_tree_view_column_set_alignment (column, 0.5);
1619 CLAWS_SET_TIP(gtk_tree_view_column_get_widget(column),
1620 _("'Get Mail' retrieves mail from the checked accounts"));
1621 g_signal_connect(G_OBJECT(renderer), "toggled",
1622 G_CALLBACK(account_get_all_toggled),
1623 list_view);
1625 renderer = gtk_cell_renderer_text_new();
1626 column = gtk_tree_view_column_new_with_attributes
1627 (_("Name"), renderer,
1628 "text", ACCOUNT_NAME,
1629 "weight", ACCOUNT_IS_DEFAULT,
1630 NULL);
1631 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1633 renderer = gtk_cell_renderer_text_new();
1634 column = gtk_tree_view_column_new_with_attributes
1635 (_("Protocol"), renderer,
1636 "text", ACCOUNT_PROTOCOL,
1637 "weight", ACCOUNT_IS_DEFAULT,
1638 NULL);
1639 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1641 renderer = gtk_cell_renderer_text_new();
1642 column = gtk_tree_view_column_new_with_attributes
1643 (_("Server"), renderer,
1644 "text", ACCOUNT_SERVER,
1645 "weight", ACCOUNT_IS_DEFAULT,
1646 NULL);
1647 gtk_tree_view_set_search_column(GTK_TREE_VIEW(list_view), ACCOUNT_NAME);
1648 gtk_tree_view_set_search_equal_func(GTK_TREE_VIEW(list_view), account_search_func_cb , NULL, NULL);
1649 gtk_tree_view_append_column(GTK_TREE_VIEW(list_view), column);
1653 *\brief Get currently selected account (by its unique ID)
1655 static gint account_list_view_get_selected_account_id(GtkWidget *list_view)
1657 PrefsAccount *res = NULL;
1659 res = (PrefsAccount *)gtkut_tree_view_get_selected_pointer(
1660 GTK_TREE_VIEW(list_view), ACCOUNT_DATA,
1661 NULL, NULL, NULL);
1663 return (res != NULL ? res->account_id : -1);
1667 *\brief Get the tree path of the currently selected account
1669 static GtkTreePath *account_list_view_get_selected_account_path(GtkWidget *list_view)
1671 GtkTreeSelection *selector;
1672 GtkTreeModel *model;
1673 GtkTreeIter iter;
1675 selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
1677 if (!gtk_tree_selection_get_selected(selector, &model, &iter))
1678 return NULL;
1680 return gtk_tree_model_get_path(gtk_tree_view_get_model
1681 (GTK_TREE_VIEW(list_view)), &iter);
1685 *\brief Get the account data of the currently selected account
1687 static PrefsAccount *account_list_view_get_selected_account(GtkWidget *list_view)
1689 PrefsAccount *res =
1690 (PrefsAccount *)gtkut_tree_view_get_selected_pointer(
1691 GTK_TREE_VIEW(list_view), ACCOUNT_DATA,
1692 NULL, NULL, NULL);
1694 return res;
1698 *\brief Select a row by the account it represents
1700 *\return gboolean TRUE if found and selected, FALSE if not.
1702 static gboolean account_list_view_select_account(GtkWidget *list_view, gint account_id)
1704 FindAccountInStore fis;
1705 GtkTreeModel *model;
1707 fis.account_id = account_id;
1708 fis.path = NULL;
1710 model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view));
1712 gtk_tree_model_foreach(model, (GtkTreeModelForeachFunc) find_account_in_store,
1713 &fis);
1715 if (fis.path) {
1716 GtkTreeSelection *selection;
1717 GtkTreePath* path;
1719 selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(list_view));
1720 gtk_tree_selection_select_iter(selection, &fis.iter);
1721 path = gtk_tree_model_get_path(model, &fis.iter);
1722 /* XXX returned path may not be valid??? create new one to be sure */
1723 gtk_tree_view_set_cursor(GTK_TREE_VIEW(list_view), path, NULL, FALSE);
1724 gtk_tree_path_free(path);
1727 return fis.path != NULL;
1731 *\brief Set a new default account by its ID. (There is only one
1732 * default account.)
1734 static void account_list_view_set_default_by_id(GtkWidget *list_view,
1735 gint account_id)
1737 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view));
1739 gtk_tree_model_foreach
1740 (model, (GtkTreeModelForeachFunc) set_new_default_account,
1741 &account_id);
1744 static gboolean set_new_default_account(GtkTreeModel *model,
1745 GtkTreePath *path,
1746 GtkTreeIter *iter,
1747 gint *account_id)
1749 PrefsAccount *account = NULL;
1750 PangoWeight weight;
1752 gtk_tree_model_get(model, iter,
1753 ACCOUNT_DATA, &account,
1754 -1);
1756 if (*account_id == account->account_id)
1757 weight = PANGO_WEIGHT_NORMAL;
1758 else
1759 weight = PANGO_WEIGHT_BOLD;
1761 gtk_list_store_set(GTK_LIST_STORE(model), iter,
1762 ACCOUNT_IS_DEFAULT, weight, -1);
1764 return FALSE;
1767 static gboolean find_account_in_store(GtkTreeModel *model,
1768 GtkTreePath *path,
1769 GtkTreeIter *iter,
1770 FindAccountInStore *data)
1772 PrefsAccount *account = NULL;
1773 gtk_tree_model_get(model, iter, ACCOUNT_DATA, &account, -1);
1775 if (data->account_id == account->account_id) {
1776 data->path = path; /* signal we found it */
1777 data->iter = *iter;
1778 return TRUE;
1781 return FALSE;
1785 *\brief Triggered when "get all" column is activated or de-activated
1787 static void account_get_all_toggled(GtkCellRendererToggle *widget,
1788 gchar *path,
1789 GtkWidget *list_view)
1791 GtkTreeIter iter;
1792 GtkTreeModel *model = gtk_tree_view_get_model(GTK_TREE_VIEW(list_view));
1793 PrefsAccount *ac = NULL;
1794 gboolean get_all;
1796 if (!gtk_tree_model_get_iter_from_string(model, &iter, path))
1797 return;
1799 gtk_tree_model_get(model, &iter,
1800 ACCOUNT_DATA, &ac,
1801 ACCOUNT_ENABLE_GET_ALL, &get_all,
1802 -1);
1804 /* check if the account has a selectable get all checkbox anyway... */
1805 if (!(ac->protocol == A_POP3 ||
1806 ac->protocol == A_IMAP4 ||
1807 ac->protocol == A_NNTP ||
1808 ac->protocol == A_LOCAL))
1809 return;
1811 /* set value in store */
1812 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
1813 ACCOUNT_ENABLE_GET_ALL, !get_all,
1814 -1);
1816 /* set value in account */
1817 ac->recv_at_getall ^= TRUE;
1820 static void account_double_clicked(GtkTreeView *list_view,
1821 GtkTreePath *path,
1822 GtkTreeViewColumn *column,
1823 gpointer data)
1825 account_edit_prefs(NULL, NULL);
1828 static void drag_begin(GtkTreeView *list_view,
1829 GdkDragContext *context,
1830 gpointer data)
1832 /* XXX unfortunately a completed drag & drop does not emit
1833 * a "rows_reordered" signal, but a "row_changed" signal.
1834 * So during drag and drop, listen to "row_changed", and
1835 * update the account list accordingly */
1837 GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1838 g_signal_connect(G_OBJECT(model), "row_changed",
1839 G_CALLBACK(account_row_changed_while_drag_drop),
1840 list_view);
1843 static void drag_end(GtkTreeView *list_view,
1844 GdkDragContext *context,
1845 gpointer data)
1847 GtkTreeModel *model = gtk_tree_view_get_model(list_view);
1848 g_signal_handlers_disconnect_by_func(G_OBJECT(model),
1849 G_CALLBACK(account_row_changed_while_drag_drop),
1850 list_view);
1853 static void account_row_changed_while_drag_drop(GtkTreeModel *model,
1854 GtkTreePath *path,
1855 GtkTreeIter *iter,
1856 gpointer arg3,
1857 GtkTreeView *list_view)
1859 account_list_set();
1862 gchar *account_get_signature_str(PrefsAccount *account)
1864 gchar *sig_body = NULL;
1865 gchar *sig_str = NULL;
1866 gchar *utf8_sig_str = NULL;
1868 cm_return_val_if_fail(account != NULL, NULL);
1870 if (!account->sig_path)
1871 return NULL;
1873 if (account->sig_type == SIG_FILE) {
1874 gchar *sig_full_path;
1875 if (!g_path_is_absolute(account->sig_path)) {
1876 sig_full_path = g_build_filename(get_home_dir(), account->sig_path, NULL);
1877 } else {
1878 sig_full_path = g_strdup(account->sig_path);
1881 if (!is_file_or_fifo_exist(sig_full_path)) {
1882 g_warning("can't open signature file: '%s'", sig_full_path);
1883 g_free(sig_full_path);
1884 return NULL;
1887 debug_print("Reading signature from file '%s'\n", sig_full_path);
1888 gchar *tmp = file_read_to_str(sig_full_path);
1889 g_free(sig_full_path);
1891 if (!tmp)
1892 return NULL;
1894 sig_body = normalize_newlines(tmp);
1895 g_free(tmp);
1896 } else {
1897 sig_body = get_command_output(account->sig_path);
1900 if (account->sig_sep) {
1901 sig_str = g_strconcat("\n", account->sig_sep, "\n", sig_body,
1902 NULL);
1903 g_free(sig_body);
1904 } else
1905 sig_str = g_strconcat("\n", sig_body, NULL);
1907 if (sig_str) {
1908 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
1909 utf8_sig_str = sig_str;
1910 else {
1911 utf8_sig_str = conv_codeset_strdup
1912 (sig_str, conv_get_locale_charset_str_no_utf8(),
1913 CS_INTERNAL);
1914 g_free(sig_str);
1918 return utf8_sig_str;
1921 PrefsAccount *account_get_cur_account (void)
1923 return cur_account;
1926 gboolean password_get(const gchar *user,
1927 const gchar *server,
1928 const gchar *protocol,
1929 guint16 port,
1930 gchar **password)
1932 PasswordRequest req;
1934 /* all have to be set */
1935 cm_return_val_if_fail(user != NULL, FALSE);
1936 cm_return_val_if_fail(server != NULL, FALSE);
1937 cm_return_val_if_fail(protocol != NULL, FALSE);
1938 cm_return_val_if_fail(port != 0, FALSE);
1940 req.user = user;
1941 req.server = server;
1942 req.protocol = protocol;
1943 req.port = port;
1945 if (hooks_invoke(PASSWORD_GET_HOOKLIST, &req)) {
1946 *password = req.password;
1947 return TRUE;
1949 return FALSE;
1952 static GSList *account_sigsep_list = NULL;
1954 /* create a list of unique signatures from accounts list */
1955 void account_sigsep_matchlist_create(void)
1957 GList *cur_ac = NULL;
1958 PrefsAccount *ac_prefs = NULL;
1960 if (account_sigsep_list)
1961 return;
1963 account_sigsep_list = g_slist_prepend(account_sigsep_list, g_strdup("-- "));
1964 for (cur_ac = account_get_list();
1965 cur_ac != NULL;
1966 cur_ac = g_list_next(cur_ac)) {
1967 ac_prefs = (PrefsAccount *)cur_ac->data;
1969 if (ac_prefs->sig_sep && *ac_prefs->sig_sep != '\0') {
1970 if (!g_slist_find_custom(account_sigsep_list, ac_prefs->sig_sep,
1971 (GCompareFunc)g_strcmp0)) {
1972 account_sigsep_list = g_slist_prepend(account_sigsep_list,
1973 g_strdup(ac_prefs->sig_sep));
1979 /* delete the list of signatures created by account_sigsep_matchlist_create() */
1980 void account_sigsep_matchlist_delete(void)
1982 if (account_sigsep_list) {
1983 slist_free_strings_full(account_sigsep_list);
1984 account_sigsep_list = NULL;
1988 /* match a string against all signatures in list, using the specified format */
1989 gboolean account_sigsep_matchlist_str_found(const gchar *str, const gchar *format)
1991 gchar *tmp = NULL;
1992 gboolean found = FALSE;
1993 GSList *item;
1995 for (item = account_sigsep_list;
1996 item != NULL && !found;
1997 item = g_slist_next(item)) {
1998 tmp = g_strdup_printf(format, (gchar *)item->data);
1999 if (tmp) {
2000 found = (strcmp(tmp, str) == 0);
2001 g_free(tmp);
2002 } else {
2003 g_warning("account_sigsep_matchlist_str_found: g_strdup_printf failed, check format '%s'",
2004 format);
2005 return FALSE;
2008 return found;
2011 /* match M first char of a string against all signatures in list, using the specified format */
2012 gboolean account_sigsep_matchlist_nchar_found(const gchar *str, const gchar *format)
2014 gchar *tmp = NULL;
2015 gboolean found = FALSE;
2016 GSList *item;
2017 gint len;
2019 for (item = account_sigsep_list;
2020 item != NULL && !found;
2021 item = g_slist_next(item)) {
2022 tmp = g_strdup_printf(format, (gchar *)item->data);
2023 if (tmp) {
2024 len = strlen(tmp);
2025 found = (strncmp(tmp, str, len) == 0);
2026 g_free(tmp);
2027 } else {
2028 g_warning("account_sigsep_matchlist_nchar_found: g_strdup_printf failed, check format '%s'",
2029 format);
2030 return FALSE;
2033 return found;