local account without SMTP
[claws.git] / src / account.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999,2000 Hiroyuki Yamamoto
4  *
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 2 of the License, or
8  * (at your option) any later version.
9  *
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.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #include <glib.h>
27 #include <gtk/gtk.h>
28 #include <gdk/gdkkeysyms.h>
29 #include <stdio.h>
30 #include <errno.h>
31
32 #include "intl.h"
33 #include "main.h"
34 #include "mainwindow.h"
35 #include "folderview.h"
36 #include "folder.h"
37 #include "account.h"
38 #include "prefs.h"
39 #include "prefs_account.h"
40 #include "compose.h"
41 #include "manage_window.h"
42 #include "gtkutils.h"
43 #include "utils.h"
44 #include "alertpanel.h"
45
46 typedef enum
47 {
48         COL_DEFAULT     = 0,
49         COL_NAME        = 1,
50         COL_PROTOCOL    = 2,
51         COL_SERVER      = 3
52 } EditAccountColumnPos;
53
54 # define N_EDIT_ACCOUNT_COLS    4
55
56 #define PREFSBUFSIZE            1024
57
58 PrefsAccount *cur_account;
59
60 static GList *account_list = NULL;
61
62 static struct EditAccount {
63         GtkWidget *window;
64         GtkWidget *clist;
65         GtkWidget *close_btn;
66 } edit_account;
67
68 static void account_edit_create         (void);
69
70 static void account_edit_prefs          (void);
71 static void account_delete              (void);
72
73 static void account_up                  (void);
74 static void account_down                (void);
75
76 static void account_set_default         (void);
77
78 static void account_edit_close          (void);
79 static void account_key_pressed         (GtkWidget      *widget,
80                                          GdkEventKey    *event,
81                                          gpointer        data);
82
83 static gint account_clist_set_row       (PrefsAccount   *ac_prefs,
84                                          gint            row);
85 static void account_clist_set           (void);
86
87 static void account_list_set            (void);
88
89 void account_read_config_all(void)
90 {
91         GSList *ac_label_list = NULL, *cur;
92         gchar *rcpath;
93         FILE *fp;
94         gchar buf[PREFSBUFSIZE];
95         PrefsAccount *ac_prefs;
96
97         debug_print(_("Reading all config for each account...\n"));
98
99         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, ACCOUNT_RC, NULL);
100         if ((fp = fopen(rcpath, "r")) == NULL) {
101                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
102                 g_free(rcpath);
103                 return;
104         }
105         g_free(rcpath);
106
107         while (fgets(buf, sizeof(buf), fp) != NULL) {
108                 if (!strncmp(buf, "[Account: ", 10)) {
109                         strretchomp(buf);
110                         memmove(buf, buf + 1, strlen(buf));
111                         buf[strlen(buf) - 1] = '\0';
112                         debug_print(_("Found label: %s\n"), buf);
113                         ac_label_list = g_slist_append(ac_label_list,
114                                                        g_strdup(buf));
115                 }
116         }
117         fclose(fp);
118
119         /* read config data from file */
120         cur_account = NULL;
121         for (cur = ac_label_list; cur != NULL; cur = cur->next) {
122                 ac_prefs = g_new0(PrefsAccount, 1);
123                 prefs_account_read_config(ac_prefs, (gchar *)cur->data);
124                 account_list = g_list_append(account_list, ac_prefs);
125                 if (ac_prefs->is_default)
126                         cur_account = ac_prefs;
127         }
128         /* if default is not set, assume first account as default */
129         if (!cur_account && account_list) {
130                 ac_prefs = (PrefsAccount *)account_list->data;
131                 account_set_as_default(ac_prefs);
132                 cur_account = ac_prefs;
133         }
134
135         account_set_menu();
136         main_window_reflect_prefs_all();
137
138         while (ac_label_list) {
139                 g_free(ac_label_list->data);
140                 ac_label_list = g_slist_remove(ac_label_list,
141                                                ac_label_list->data);
142         }
143 }
144
145 void account_save_config_all(void)
146 {
147         prefs_account_save_config_all(account_list);
148 }
149
150 PrefsAccount *account_find_from_smtp_server(const gchar *address,
151                                             const gchar *smtp_server)
152 {
153         GList *cur;
154         PrefsAccount *ac;
155
156         for (cur = account_list; cur != NULL; cur = cur->next) {
157                 ac = (PrefsAccount *)cur->data;
158                 if (!strcmp2(address, ac->address) &&
159                     !strcmp2(smtp_server, ac->smtp_server))
160                         return ac;
161         }
162
163         return NULL;
164 }
165
166 PrefsAccount *account_find_from_id(gint id)
167 {
168         GList *cur;
169         PrefsAccount *ac;
170
171         for (cur = account_list; cur != NULL; cur = cur->next) {
172                 ac = (PrefsAccount *)cur->data;
173                 if (id == ac->account_id)
174                         return ac;
175         }
176
177         return NULL;
178 }
179
180 void account_set_menu(void)
181 {
182         main_window_set_account_menu(account_list);
183 }
184
185 void account_foreach(AccountFunc func, gpointer user_data)
186 {
187         GList *cur;
188
189         for (cur = account_list; cur != NULL; cur = cur->next)
190                 if (func((PrefsAccount *)cur->data, user_data) != 0)
191                         return;
192 }
193
194 GList *account_get_list(void)
195 {
196         return account_list;
197 }
198
199 void account_edit_open(void)
200 {
201         if (compose_get_compose_list()) {
202                 alertpanel_notice(_("Some composing windows are open.\n"
203                                     "Please close all the composing windows before editing the accounts."));
204                 return;
205         }
206
207         debug_print(_("Opening account edit window...\n"));
208
209         if (!edit_account.window)
210                 account_edit_create();
211
212         account_clist_set();
213
214         manage_window_set_transient(GTK_WINDOW(edit_account.window));
215         gtk_widget_grab_focus(edit_account.close_btn);
216         gtk_widget_show(edit_account.window);
217
218         manage_window_focus_in(edit_account.window, NULL, NULL);
219 }
220
221 void account_add(void)
222 {
223         PrefsAccount *ac_prefs;
224
225         ac_prefs = prefs_account_open(NULL);
226
227         if (!ac_prefs) return;
228
229         account_list = g_list_append(account_list, ac_prefs);
230
231         if (ac_prefs->is_default)
232                 account_set_as_default(ac_prefs);
233
234         account_clist_set();
235
236         if (ac_prefs->protocol == A_IMAP4 || ac_prefs->protocol == A_NNTP) {
237                 Folder *folder;
238
239                 if (ac_prefs->protocol == A_IMAP4) {
240                         folder = folder_new(F_IMAP, ac_prefs->account_name,
241                                             ac_prefs->recv_server);
242                 } else {
243                         folder = folder_new(F_NEWS, ac_prefs->account_name,
244                                             ac_prefs->nntp_server);
245                 }
246                 folder->account = ac_prefs;
247                 ac_prefs->folder = REMOTE_FOLDER(folder);
248                 folder_add(folder);
249                 folderview_update_all();
250         }
251 }
252
253 void account_set_as_default(PrefsAccount *ac_prefs)
254 {
255         PrefsAccount *ap;
256         GList *cur;
257
258         for (cur = account_list; cur != NULL; cur = cur->next) {
259                 ap = (PrefsAccount *)cur->data;
260                 if (ap->is_default)
261                         ap->is_default = FALSE;
262         }
263
264         ac_prefs->is_default = TRUE;
265 }
266
267 PrefsAccount *account_get_default(void)
268 {
269         PrefsAccount *ap;
270         GList *cur;
271
272         for (cur = account_list; cur != NULL; cur = cur->next) {
273                 ap = (PrefsAccount *)cur->data;
274                 if (ap->is_default)
275                         return ap;
276         }
277
278         return NULL;
279 }
280
281 void account_set_missing_folder(void)
282 {
283         PrefsAccount *ap;
284         GList *cur;
285
286         for (cur = account_list; cur != NULL; cur = cur->next) {
287                 ap = (PrefsAccount *)cur->data;
288                 if ((ap->protocol == A_IMAP4 || ap->protocol == A_NNTP) &&
289                     !ap->folder) {
290                         Folder *folder;
291
292                         if (ap->protocol == A_IMAP4) {
293                                 folder = folder_new(F_IMAP, ap->account_name,
294                                                     ap->recv_server);
295                         } else {
296                                 folder = folder_new(F_NEWS, ap->account_name,
297                                                     ap->nntp_server);
298                         }
299                         folder->account = ap;
300                         ap->folder = REMOTE_FOLDER(folder);
301                         folder_add(folder);
302                 }
303         }
304 }
305
306 void account_destroy(PrefsAccount *ac_prefs)
307 {
308         g_return_if_fail(ac_prefs != NULL);
309
310         prefs_account_free(ac_prefs);
311         account_list = g_list_remove(account_list, ac_prefs);
312
313         if (cur_account == ac_prefs) cur_account = NULL;
314         if (!cur_account && account_list) {
315                 cur_account = account_get_default();
316                 if (!cur_account) {
317                         ac_prefs = (PrefsAccount *)account_list->data;
318                         account_set_as_default(ac_prefs);
319                         cur_account = ac_prefs;
320                 }
321         }
322 }
323
324
325 static void account_edit_create(void)
326 {
327         GtkWidget *window;
328         GtkWidget *vbox;
329         GtkWidget *hbox;
330         GtkWidget *scrolledwin;
331         GtkWidget *clist;
332         gchar *titles[N_EDIT_ACCOUNT_COLS];
333         gint i;
334
335         GtkWidget *vbox2;
336         GtkWidget *add_btn;
337         GtkWidget *edit_btn;
338         GtkWidget *del_btn;
339         GtkWidget *up_btn;
340         GtkWidget *down_btn;
341
342         GtkWidget *default_btn;
343
344         GtkWidget *hbbox;
345         GtkWidget *close_btn;
346
347         debug_print(_("Creating account edit window...\n"));
348
349         window = gtk_window_new (GTK_WINDOW_DIALOG);
350         gtk_widget_set_usize (window, 500, 320);
351         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
352         gtk_window_set_title (GTK_WINDOW (window), _("Edit accounts"));
353         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
354         gtk_signal_connect (GTK_OBJECT (window), "delete_event",
355                             GTK_SIGNAL_FUNC (account_edit_close), NULL);
356         gtk_signal_connect (GTK_OBJECT (window), "key_press_event",
357                             GTK_SIGNAL_FUNC (account_key_pressed), NULL);
358         gtk_signal_connect (GTK_OBJECT (window), "focus_in_event",
359                             GTK_SIGNAL_FUNC (manage_window_focus_in), NULL);
360         gtk_signal_connect (GTK_OBJECT (window), "focus_out_event",
361                             GTK_SIGNAL_FUNC (manage_window_focus_out), NULL);
362
363         vbox = gtk_vbox_new (FALSE, 12);
364         gtk_widget_show (vbox);
365         gtk_container_add (GTK_CONTAINER (window), vbox);
366
367         hbox = gtk_hbox_new (FALSE, 8);
368         gtk_widget_show (hbox);
369         gtk_box_pack_start (GTK_BOX (vbox), hbox, TRUE, TRUE, 0);
370         gtk_container_set_border_width (GTK_CONTAINER (hbox), 2);
371
372         scrolledwin = gtk_scrolled_window_new (NULL, NULL);
373         gtk_widget_show (scrolledwin);
374         gtk_box_pack_start (GTK_BOX (hbox), scrolledwin, TRUE, TRUE, 0);
375         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolledwin),
376                                         GTK_POLICY_AUTOMATIC,
377                                         GTK_POLICY_AUTOMATIC);
378
379         titles[COL_DEFAULT]  = "";
380         titles[COL_NAME]     = _("Name");
381         titles[COL_PROTOCOL] = _("Protocol");
382         titles[COL_SERVER]   = _("Server");
383
384         clist = gtk_clist_new_with_titles (N_EDIT_ACCOUNT_COLS, titles);
385         gtk_widget_show (clist);
386         gtk_container_add (GTK_CONTAINER (scrolledwin), clist);
387         gtk_clist_set_column_width (GTK_CLIST(clist), COL_DEFAULT , 16);
388         gtk_clist_set_column_width (GTK_CLIST(clist), COL_NAME    , 100);
389         gtk_clist_set_column_width (GTK_CLIST(clist), COL_PROTOCOL, 70);
390         gtk_clist_set_column_width (GTK_CLIST(clist), COL_SERVER  , 100);
391         gtk_clist_set_selection_mode (GTK_CLIST(clist), GTK_SELECTION_BROWSE);
392
393         for (i = 0; i < N_EDIT_ACCOUNT_COLS; i++)
394                 GTK_WIDGET_UNSET_FLAGS(GTK_CLIST(clist)->column[i].button,
395                                        GTK_CAN_FOCUS);
396
397         vbox2 = gtk_vbox_new (FALSE, 0);
398         gtk_widget_show (vbox2);
399         gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
400
401         add_btn = gtk_button_new_with_label (_("Add"));
402         gtk_widget_show (add_btn);
403         gtk_box_pack_start (GTK_BOX (vbox2), add_btn, FALSE, FALSE, 4);
404         gtk_signal_connect (GTK_OBJECT(add_btn), "clicked",
405                             GTK_SIGNAL_FUNC (account_add), NULL);
406
407         edit_btn = gtk_button_new_with_label (_("Edit"));
408         gtk_widget_show (edit_btn);
409         gtk_box_pack_start (GTK_BOX (vbox2), edit_btn, FALSE, FALSE, 4);
410         gtk_signal_connect (GTK_OBJECT(edit_btn), "clicked",
411                             GTK_SIGNAL_FUNC (account_edit_prefs), NULL);
412
413         del_btn = gtk_button_new_with_label (_(" Delete "));
414         gtk_widget_show (del_btn);
415         gtk_box_pack_start (GTK_BOX (vbox2), del_btn, FALSE, FALSE, 4);
416         gtk_signal_connect (GTK_OBJECT(del_btn), "clicked",
417                             GTK_SIGNAL_FUNC (account_delete), NULL);
418
419         down_btn = gtk_button_new_with_label (_("Down"));
420         gtk_widget_show (down_btn);
421         gtk_box_pack_end (GTK_BOX (vbox2), down_btn, FALSE, FALSE, 4);
422         gtk_signal_connect (GTK_OBJECT(down_btn), "clicked",
423                             GTK_SIGNAL_FUNC (account_down), NULL);
424
425         up_btn = gtk_button_new_with_label (_("Up"));
426         gtk_widget_show (up_btn);
427         gtk_box_pack_end (GTK_BOX (vbox2), up_btn, FALSE, FALSE, 4);
428         gtk_signal_connect (GTK_OBJECT(up_btn), "clicked",
429                             GTK_SIGNAL_FUNC (account_up), NULL);
430
431         hbox = gtk_hbox_new (FALSE, 8);
432         gtk_widget_show (hbox);
433         gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
434
435         vbox2 = gtk_vbox_new(FALSE, 0);
436         gtk_widget_show (vbox2);
437         gtk_box_pack_start (GTK_BOX (hbox), vbox2, FALSE, FALSE, 0);
438
439         default_btn = gtk_button_new_with_label (_(" Set as usually used account "));
440         gtk_widget_show (default_btn);
441         gtk_box_pack_start (GTK_BOX (vbox2), default_btn, TRUE, FALSE, 0);
442         gtk_signal_connect (GTK_OBJECT(default_btn), "clicked",
443                             GTK_SIGNAL_FUNC (account_set_default), NULL);
444
445         gtkut_button_set_create(&hbbox, &close_btn, _("Close"),
446                                 NULL, NULL, NULL, NULL);
447         gtk_widget_show(hbbox);
448         gtk_box_pack_end (GTK_BOX (hbox), hbbox, FALSE, FALSE, 0);
449         gtk_widget_grab_default (close_btn);
450
451         gtk_signal_connect (GTK_OBJECT (close_btn), "clicked",
452                             GTK_SIGNAL_FUNC (account_edit_close),
453                             NULL);
454
455         edit_account.window    = window;
456         edit_account.clist     = clist;
457         edit_account.close_btn = close_btn;
458 }
459
460 static void account_edit_prefs(void)
461 {
462         GtkCList *clist = GTK_CLIST(edit_account.clist);
463         PrefsAccount *ac_prefs;
464         gint row;
465         gboolean prev_default;
466         gchar *ac_name;
467
468         if (!clist->selection) return;
469
470         row = GPOINTER_TO_INT(clist->selection->data);
471         ac_prefs = gtk_clist_get_row_data(clist, row);
472         prev_default = ac_prefs->is_default;
473         Xstrdup_a(ac_name, ac_prefs->account_name, return);
474
475         prefs_account_open(ac_prefs);
476
477         if (!prev_default && ac_prefs->is_default)
478                 account_set_as_default(ac_prefs);
479
480         if ((ac_prefs->protocol == A_IMAP4 || ac_prefs->protocol == A_NNTP) &&
481             ac_prefs->folder && strcmp(ac_name, ac_prefs->account_name) != 0) {
482                 folder_set_name(FOLDER(ac_prefs->folder),
483                                 ac_prefs->account_name);
484                 folderview_update_all();
485         }
486
487         account_clist_set();
488 }
489
490 static void account_delete(void)
491 {
492         GtkCList *clist = GTK_CLIST(edit_account.clist);
493         PrefsAccount *ac_prefs;
494         gint row;
495
496         if (!clist->selection) return;
497
498         if (alertpanel(_("Delete account"),
499                        _("Do you really want to delete this account?"),
500                        _("Yes"), _("+No"), NULL) != G_ALERTDEFAULT)
501                 return;
502
503         row = GPOINTER_TO_INT(clist->selection->data);
504         ac_prefs = gtk_clist_get_row_data(clist, row);
505         if (ac_prefs->folder) {
506                 folder_destroy(FOLDER(ac_prefs->folder));
507                 folderview_update_all();
508         }
509         account_destroy(ac_prefs);
510         account_clist_set();
511 }
512
513 static void account_up(void)
514 {
515         GtkCList *clist = GTK_CLIST(edit_account.clist);
516         gint row;
517
518         if (!clist->selection) return;
519
520         row = GPOINTER_TO_INT(clist->selection->data);
521         if (row > 0) {
522                 gtk_clist_row_move(clist, row, row - 1);
523                 account_list_set();
524         }
525 }
526
527 static void account_down(void)
528 {
529         GtkCList *clist = GTK_CLIST(edit_account.clist);
530         gint row;
531
532         if (!clist->selection) return;
533
534         row = GPOINTER_TO_INT(clist->selection->data);
535         if (row < clist->rows - 1) {
536                 gtk_clist_row_move(clist, row, row + 1);
537                 account_list_set();
538         }
539 }
540
541 static void account_set_default(void)
542 {
543         GtkCList *clist = GTK_CLIST(edit_account.clist);
544         gint row;
545         PrefsAccount *ac_prefs;
546
547         if (!clist->selection) return;
548
549         row = GPOINTER_TO_INT(clist->selection->data);
550         ac_prefs = gtk_clist_get_row_data(clist, row);
551         account_set_as_default(ac_prefs);
552         account_clist_set();
553
554         cur_account = ac_prefs;
555         account_set_menu();
556         main_window_reflect_prefs_all();
557 }
558
559 static void account_edit_close(void)
560 {
561         account_list_set();
562         account_save_config_all();
563
564         if (!cur_account && account_list) {
565                 PrefsAccount *ac_prefs = (PrefsAccount *)account_list->data;
566                 account_set_as_default(ac_prefs);
567                 cur_account = ac_prefs;
568         }
569
570         account_set_menu();
571         main_window_reflect_prefs_all();
572
573         gtk_widget_hide(edit_account.window);
574 }
575
576 static void account_key_pressed(GtkWidget *widget, GdkEventKey *event,
577                                 gpointer data)
578 {
579         if (event && event->keyval == GDK_Escape)
580                 account_edit_close();
581 }
582
583 /* set one CList row or add new row */
584 static gint account_clist_set_row(PrefsAccount *ac_prefs, gint row)
585 {
586         GtkCList *clist = GTK_CLIST(edit_account.clist);
587         gchar *text[N_EDIT_ACCOUNT_COLS];
588
589         text[COL_DEFAULT] = ac_prefs->is_default ? "*" : "";
590         text[COL_NAME] = ac_prefs->account_name;
591         text[COL_PROTOCOL] = ac_prefs->protocol == A_POP3  ? "POP3"  :
592                              ac_prefs->protocol == A_APOP  ? "APOP"  :
593                              ac_prefs->protocol == A_IMAP4 ? "IMAP4" :
594                              ac_prefs->protocol == A_NNTP  ? "NNTP"  :  "";
595         text[COL_SERVER] = ac_prefs->protocol == A_NNTP
596                 ? ac_prefs->nntp_server : ac_prefs->recv_server;
597
598         if (row < 0)
599                 row = gtk_clist_append(clist, text);
600         else {
601                 gtk_clist_set_text(clist, row, COL_DEFAULT, text[COL_DEFAULT]);
602                 gtk_clist_set_text(clist, row, COL_NAME, text[COL_NAME]);
603                 gtk_clist_set_text(clist, row, COL_PROTOCOL, text[COL_PROTOCOL]);
604                 gtk_clist_set_text(clist, row, COL_SERVER, text[COL_SERVER]);
605         }
606
607         gtk_clist_set_row_data(clist, row, ac_prefs);
608
609         return row;
610 }
611
612 /* set CList from account list */
613 static void account_clist_set(void)
614 {
615         GtkCList *clist = GTK_CLIST(edit_account.clist);
616         GList *cur;
617         gint prev_row;
618
619         if (clist->selection)
620                 prev_row = GPOINTER_TO_INT(clist->selection->data);
621         else
622                 prev_row = -1;
623
624         gtk_clist_freeze(clist);
625         gtk_clist_clear(clist);
626
627         for (cur = account_list; cur != NULL; cur = cur->next) {
628                 gint row;
629
630                 row = account_clist_set_row((PrefsAccount *)cur->data, -1);
631                 if ((PrefsAccount *)cur->data == cur_account)
632                         gtk_clist_select_row(clist, row, -1);
633         }
634
635         if (prev_row >= 0)
636                 gtk_clist_select_row(clist, prev_row, -1);
637
638         gtk_clist_thaw(clist);
639 }
640
641 /* set account list from CList */
642 static void account_list_set(void)
643 {
644         GtkCList *clist = GTK_CLIST(edit_account.clist);
645         gint row;
646         PrefsAccount *ac_prefs;
647
648         while (account_list)
649                 account_list = g_list_remove(account_list, account_list->data);
650
651         for (row = 0; (ac_prefs = gtk_clist_get_row_data(clist, row)) != NULL;
652              row++)
653                 account_list = g_list_append(account_list, ac_prefs);
654 }