removed local account without SMTP server - added option to use mail command
[claws.git] / src / prefs_headers.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2001 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 <stdlib.h>
31 #include <string.h>
32 #include <errno.h>
33
34 #include "intl.h"
35 #include "main.h"
36 #include "prefs.h"
37 #include "prefs_headers.h"
38 #include "prefs_common.h"
39 #include "prefs_account.h"
40 #include "mainwindow.h"
41 #include "foldersel.h"
42 #include "manage_window.h"
43 #include "customheader.h"
44 #include "utils.h"
45 #include "gtkutils.h"
46 #include "alertpanel.h"
47 #include "folder.h"
48
49 static struct Headers {
50         GtkWidget *window;
51
52         GtkWidget *close_btn;
53
54         GtkWidget *hdr_combo;
55         GtkWidget *hdr_entry;
56         GtkWidget *key_entry;
57         GtkWidget *headers_clist;
58 } headers;
59
60 /*
61    parameter name, default value, pointer to the prefs variable, data type,
62    pointer to the widget pointer,
63    pointer to the function for data setting,
64    pointer to the function for widget setting
65  */
66
67 #define VSPACING                12
68 #define VSPACING_NARROW         4
69 #define DEFAULT_ENTRY_WIDTH     80
70 #define PREFSBUFSIZE            1024
71
72 /* widget creating functions */
73 static void prefs_headers_create                (void);
74
75 static void prefs_headers_set_dialog    (PrefsAccount * ac);
76 static void prefs_headers_set_list      (PrefsAccount * ac);
77 static gint prefs_headers_clist_set_row (PrefsAccount * ac,
78                                          gint    row);
79
80 /* callback functions */
81 static void prefs_headers_select_dest_cb        (void);
82 static void prefs_headers_register_cb   (void);
83 static void prefs_headers_substitute_cb (void);
84 static void prefs_headers_delete_cb     (void);
85 static void prefs_headers_up            (void);
86 static void prefs_headers_down          (void);
87 static void prefs_headers_select                (GtkCList       *clist,
88                                          gint            row,
89                                          gint            column,
90                                          GdkEvent       *event);
91
92 static void prefs_headers_key_pressed   (GtkWidget      *widget,
93                                          GdkEventKey    *event,
94                                          gpointer        data);
95 static void prefs_headers_close         (GtkButton      *button);
96
97 static PrefsAccount * cur_ac = NULL;
98
99 void prefs_headers_open(PrefsAccount * ac)
100 {
101         if (!headers.window) {
102                 prefs_headers_create();
103         }
104
105         manage_window_set_transient(GTK_WINDOW(headers.window));
106         gtk_widget_grab_focus(headers.close_btn);
107
108         prefs_headers_set_dialog(ac);
109
110         cur_ac = ac;
111
112         gtk_widget_show(headers.window);
113 }
114
115 static void prefs_headers_create(void)
116 {
117         GtkWidget *window;
118         GtkWidget *vbox;
119         GtkWidget *close_btn;
120         GtkWidget *confirm_area;
121
122         GtkWidget *vbox1;
123
124         GtkWidget *table1;
125         GtkWidget *hdr_label;
126         GtkWidget *hdr_combo;
127         GtkWidget *key_label;
128         GtkWidget *key_entry;
129
130         GtkWidget *reg_hbox;
131         GtkWidget *btn_hbox;
132         GtkWidget *arrow;
133         GtkWidget *reg_btn;
134         GtkWidget *subst_btn;
135         GtkWidget *del_btn;
136
137         GtkWidget *ch_hbox;
138         GtkWidget *ch_scrolledwin;
139         GtkWidget *headers_clist;
140
141         GtkWidget *btn_vbox;
142         GtkWidget *up_btn;
143         GtkWidget *down_btn;
144
145         gchar *title[] = {_("Custom headers")};
146
147         debug_print(_("Creating headers setting window...\n"));
148
149         window = gtk_window_new (GTK_WINDOW_DIALOG);
150         gtk_container_set_border_width (GTK_CONTAINER (window), 8);
151         gtk_window_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
152         gtk_window_set_modal (GTK_WINDOW (window), TRUE);
153         gtk_window_set_policy (GTK_WINDOW (window), FALSE, TRUE, FALSE);
154
155         vbox = gtk_vbox_new (FALSE, 6);
156         gtk_widget_show (vbox);
157         gtk_container_add (GTK_CONTAINER (window), vbox);
158
159         gtkut_button_set_create (&confirm_area, &close_btn, _("Close"),
160                                  NULL, NULL, NULL, NULL);
161         gtk_widget_show (confirm_area);
162         gtk_box_pack_end (GTK_BOX(vbox), confirm_area, FALSE, FALSE, 0);
163         gtk_widget_grab_default (close_btn);
164
165         gtk_window_set_title (GTK_WINDOW(window),
166                               _("Headers setting"));
167         gtk_signal_connect (GTK_OBJECT(window), "delete_event",
168                             GTK_SIGNAL_FUNC(gtk_widget_hide_on_delete), NULL);
169         gtk_signal_connect (GTK_OBJECT(window), "key_press_event",
170                             GTK_SIGNAL_FUNC(prefs_headers_key_pressed), NULL);
171         gtk_signal_connect (GTK_OBJECT(window), "focus_in_event",
172                             GTK_SIGNAL_FUNC(manage_window_focus_in), NULL);
173         gtk_signal_connect (GTK_OBJECT(window), "focus_out_event",
174                             GTK_SIGNAL_FUNC(manage_window_focus_out), NULL);
175         gtk_signal_connect (GTK_OBJECT(close_btn), "clicked",
176                             GTK_SIGNAL_FUNC(prefs_headers_close), NULL);
177
178         vbox1 = gtk_vbox_new (FALSE, VSPACING);
179         gtk_widget_show (vbox1);
180         gtk_box_pack_start (GTK_BOX (vbox), vbox1, TRUE, TRUE, 0);
181         gtk_container_set_border_width (GTK_CONTAINER (vbox1), 2);
182
183         table1 = gtk_table_new (2, 2, FALSE);
184         gtk_widget_show (table1);
185         gtk_box_pack_start (GTK_BOX (vbox1), table1,
186                             FALSE, FALSE, 0);
187         gtk_table_set_row_spacings (GTK_TABLE (table1), 8);
188         gtk_table_set_col_spacings (GTK_TABLE (table1), 8);
189
190         hdr_label = gtk_label_new (_("Header"));
191         gtk_widget_show (hdr_label);
192         gtk_table_attach (GTK_TABLE (table1), hdr_label, 0, 1, 0, 1,
193                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
194                           0, 0, 0);
195         gtk_misc_set_alignment (GTK_MISC (hdr_label), 0, 0.5);
196         
197         hdr_combo = gtk_combo_new ();
198         gtk_widget_show (hdr_combo);
199         gtk_table_attach (GTK_TABLE (table1), hdr_combo, 0, 1, 1, 2,
200                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
201                           0, 0, 0);
202         gtk_widget_set_usize (hdr_combo, 150 /* 96 */, -1);
203         gtkut_combo_set_items (GTK_COMBO (hdr_combo),
204                                "User-Agent", "X-Operating-System", NULL);
205
206         key_label = gtk_label_new (_("Value"));
207         gtk_widget_show (key_label);
208         gtk_table_attach (GTK_TABLE (table1), key_label, 1, 2, 0, 1,
209                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
210                           0, 0, 0);
211         gtk_misc_set_alignment (GTK_MISC (key_label), 0, 0.5);
212         
213         key_entry = gtk_entry_new ();
214         gtk_widget_show (key_entry);
215         gtk_table_attach (GTK_TABLE (table1), key_entry, 1, 2, 1, 2,
216                           GTK_EXPAND | GTK_SHRINK | GTK_FILL,
217                           0, 0, 0);
218
219         /* register / substitute / delete */
220
221         reg_hbox = gtk_hbox_new (FALSE, 4);
222         gtk_widget_show (reg_hbox);
223         gtk_box_pack_start (GTK_BOX (vbox1), reg_hbox,
224                             FALSE, FALSE, 0);
225
226         arrow = gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_OUT);
227         gtk_widget_show (arrow);
228         gtk_box_pack_start (GTK_BOX (reg_hbox), arrow, FALSE, FALSE, 0);
229         gtk_widget_set_usize (arrow, -1, 16);
230
231         btn_hbox = gtk_hbox_new (TRUE, 4);
232         gtk_widget_show (btn_hbox);
233         gtk_box_pack_start (GTK_BOX (reg_hbox), btn_hbox, FALSE, FALSE, 0);
234
235         reg_btn = gtk_button_new_with_label (_("Add"));
236         gtk_widget_show (reg_btn);
237         gtk_box_pack_start (GTK_BOX (btn_hbox), reg_btn, FALSE, TRUE, 0);
238         gtk_signal_connect (GTK_OBJECT (reg_btn), "clicked",
239                             GTK_SIGNAL_FUNC (prefs_headers_register_cb), NULL);
240
241         subst_btn = gtk_button_new_with_label (_(" Substitute "));
242         gtk_widget_show (subst_btn);
243         gtk_box_pack_start (GTK_BOX (btn_hbox), subst_btn, FALSE, TRUE, 0);
244         gtk_signal_connect (GTK_OBJECT (subst_btn), "clicked",
245                             GTK_SIGNAL_FUNC (prefs_headers_substitute_cb),
246                             NULL);
247
248         del_btn = gtk_button_new_with_label (_("Delete"));
249         gtk_widget_show (del_btn);
250         gtk_box_pack_start (GTK_BOX (btn_hbox), del_btn, FALSE, TRUE, 0);
251         gtk_signal_connect (GTK_OBJECT (del_btn), "clicked",
252                             GTK_SIGNAL_FUNC (prefs_headers_delete_cb), NULL);
253
254
255         ch_hbox = gtk_hbox_new (FALSE, 8);
256         gtk_widget_show (ch_hbox);
257         gtk_box_pack_start (GTK_BOX (vbox1), ch_hbox,
258                             TRUE, TRUE, 0);
259
260         ch_scrolledwin = gtk_scrolled_window_new (NULL, NULL);
261         gtk_widget_set_usize (ch_scrolledwin, -1, 100);
262         gtk_widget_show (ch_scrolledwin);
263         gtk_box_pack_start (GTK_BOX (ch_hbox), ch_scrolledwin,
264                             TRUE, TRUE, 0);
265         gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (ch_scrolledwin),
266                                         GTK_POLICY_AUTOMATIC,
267                                         GTK_POLICY_AUTOMATIC);
268
269         headers_clist = gtk_clist_new_with_titles(1, title);
270         gtk_widget_show (headers_clist);
271         gtk_container_add (GTK_CONTAINER (ch_scrolledwin), headers_clist);
272         gtk_clist_set_column_width (GTK_CLIST (headers_clist), 0, 80);
273         gtk_clist_set_selection_mode (GTK_CLIST (headers_clist),
274                                       GTK_SELECTION_BROWSE);
275         GTK_WIDGET_UNSET_FLAGS (GTK_CLIST (headers_clist)->column[0].button,
276                                 GTK_CAN_FOCUS);
277         gtk_signal_connect (GTK_OBJECT (headers_clist), "select_row",
278                             GTK_SIGNAL_FUNC (prefs_headers_select),
279                             NULL);
280
281
282         btn_vbox = gtk_vbox_new (FALSE, 8);
283         gtk_widget_show (btn_vbox);
284         gtk_box_pack_start (GTK_BOX (ch_hbox), btn_vbox, FALSE, FALSE, 0);
285
286         up_btn = gtk_button_new_with_label (_("Up"));
287         gtk_widget_show (up_btn);
288         gtk_box_pack_start (GTK_BOX (btn_vbox), up_btn, FALSE, FALSE, 0);
289         gtk_signal_connect (GTK_OBJECT (up_btn), "clicked",
290                             GTK_SIGNAL_FUNC (prefs_headers_up), NULL);
291
292         down_btn = gtk_button_new_with_label (_("Down"));
293         gtk_widget_show (down_btn);
294         gtk_box_pack_start (GTK_BOX (btn_vbox), down_btn, FALSE, FALSE, 0);
295         gtk_signal_connect (GTK_OBJECT (down_btn), "clicked",
296                             GTK_SIGNAL_FUNC (prefs_headers_down), NULL);
297
298
299         gtk_widget_show_all(window);
300
301         headers.window    = window;
302         headers.close_btn = close_btn;
303
304         headers.hdr_combo  = hdr_combo;
305         headers.hdr_entry  = GTK_COMBO (hdr_combo)->entry;
306         headers.key_entry  = key_entry;
307         headers.headers_clist   = headers_clist;
308 }
309
310 void prefs_headers_read_config(PrefsAccount * ac)
311 {
312         gchar *rcpath;
313         FILE *fp;
314         gchar buf[PREFSBUFSIZE];
315         CustomHeader *ch;
316
317         debug_print(_("Reading headers configuration...\n"));
318
319         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, HEADERS_RC, NULL);
320         if ((fp = fopen(rcpath, "r")) == NULL) {
321                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
322                 g_free(rcpath);
323                 ac->customhdr_list = NULL;
324                 return;
325         }
326         g_free(rcpath);
327
328         /* remove all previous headers list */
329         while (ac->customhdr_list != NULL) {
330                 ch = (CustomHeader *)ac->customhdr_list->data;
331                 custom_header_free(ch);
332                 ac->customhdr_list = g_slist_remove(ac->customhdr_list, ch);
333         }
334  
335         while (fgets(buf, sizeof(buf), fp) != NULL) {
336                 g_strchomp(buf);
337                 ch = custom_header_read_str(buf);
338                 if (ch) {
339                         if (ch->account_id == ac->account_id)
340                                 ac->customhdr_list =
341                                         g_slist_append(ac->customhdr_list, ch);
342                         else
343                                 custom_header_free(ch);
344                 }
345         }
346  
347         fclose(fp);
348 }
349
350 void prefs_headers_write_config(PrefsAccount * ac)
351 {
352         gchar *rcpath;
353         PrefFile *pfile;
354         GSList *cur;
355         gchar buf[PREFSBUFSIZE];
356         FILE * fp;
357         CustomHeader *ch;
358
359         GSList *all_hdrs = NULL;
360
361         debug_print(_("Writing headers configuration...\n"));
362
363         rcpath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S, HEADERS_RC, NULL);
364
365         if ((fp = fopen(rcpath, "r")) == NULL) {
366                 if (ENOENT != errno) FILE_OP_ERROR(rcpath, "fopen");
367         }
368         else {
369                 all_hdrs = NULL;
370
371                 while (fgets(buf, sizeof(buf), fp) != NULL) {
372                         g_strchomp(buf);
373                         ch = custom_header_read_str(buf);
374                         if (ch) {
375                                 if (ch->account_id != ac->account_id)
376                                         all_hdrs =
377                                                 g_slist_append(all_hdrs, ch);
378                                 else
379                                         custom_header_free(ch);
380                         }
381                 }
382
383                 fclose(fp);
384         }
385
386         if ((pfile = prefs_write_open(rcpath)) == NULL) {
387                 g_warning(_("failed to write configuration to file\n"));
388                 g_free(rcpath);
389                 return;
390         }
391
392         for (cur = all_hdrs; cur != NULL; cur = cur->next) {
393                 CustomHeader *hdr = (CustomHeader *)cur->data;
394                 gchar *chstr;
395
396                 chstr = custom_header_get_str(hdr);
397                 if (fputs(chstr, pfile->fp) == EOF ||
398                     fputc('\n', pfile->fp) == EOF) {
399                         FILE_OP_ERROR(rcpath, "fputs || fputc");
400                         prefs_write_close_revert(pfile);
401                         g_free(rcpath);
402                         g_free(chstr);
403                         return;
404                 }
405                 g_free(chstr);
406         }
407
408         for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) {
409                 CustomHeader *hdr = (CustomHeader *)cur->data;
410                 gchar *chstr;
411
412                 chstr = custom_header_get_str(hdr);
413                 if (fputs(chstr, pfile->fp) == EOF ||
414                     fputc('\n', pfile->fp) == EOF) {
415                         FILE_OP_ERROR(rcpath, "fputs || fputc");
416                         prefs_write_close_revert(pfile);
417                         g_free(rcpath);
418                         g_free(chstr);
419                         return;
420                 }
421                 g_free(chstr);
422         }
423
424         g_free(rcpath);
425
426         while (all_hdrs != NULL) {
427                 ch = (CustomHeader *)all_hdrs->data;
428                 custom_header_free(ch);
429                 all_hdrs = g_slist_remove(all_hdrs, ch);
430         }
431
432         if (prefs_write_close(pfile) < 0) {
433                 g_warning(_("failed to write configuration to file\n"));
434                 return;
435         }
436 }
437
438 static void prefs_headers_set_dialog(PrefsAccount * ac)
439 {
440         GtkCList *clist = GTK_CLIST(headers.headers_clist);
441         GSList *cur;
442         gchar *ch_str[1];
443         gint row;
444
445         gtk_clist_freeze(clist);
446         gtk_clist_clear(clist);
447
448         ch_str[0] = _("(New)");
449         row = gtk_clist_append(clist, ch_str);
450         gtk_clist_set_row_data(clist, row, NULL);
451
452         for (cur = ac->customhdr_list; cur != NULL; cur = cur->next) {
453                 CustomHeader *ch = (CustomHeader *)cur->data;
454
455                 ch_str[0] = g_strdup_printf("%s: %s", ch->name, ch->value);
456                 row = gtk_clist_append(clist, ch_str);
457                 gtk_clist_set_row_data(clist, row, ch);
458
459                 g_free(ch_str[0]);
460         }
461
462         gtk_clist_thaw(clist);
463 }
464
465 static void prefs_headers_set_list(PrefsAccount * ac)
466 {
467         gint row = 1;
468         CustomHeader *ch;
469
470         g_slist_free(ac->customhdr_list);
471         ac->customhdr_list = NULL;
472
473         while ((ch = gtk_clist_get_row_data(GTK_CLIST(headers.headers_clist),
474                 row)) != NULL) {
475                 ac->customhdr_list = g_slist_append(ac->customhdr_list,
476                                                       ch);
477                 row++;
478         }
479 }
480
481 #define GET_ENTRY(entry) \
482         entry_text = gtk_entry_get_text(GTK_ENTRY(entry))
483
484 static gint prefs_headers_clist_set_row(PrefsAccount * ac, gint row)
485 {
486         GtkCList *clist = GTK_CLIST(headers.headers_clist);
487         CustomHeader *ch;
488         gchar *entry_text;
489         gchar *ch_str[1];
490
491         g_return_val_if_fail(row != 0, -1);
492
493         GET_ENTRY(headers.hdr_entry);
494         if (entry_text[0] == '\0') {
495                 alertpanel_error(_("Header name is not set."));
496                 return -1;
497         }
498
499         ch = g_new0(CustomHeader, 1);
500
501         ch->account_id = ac->account_id;
502
503         ch->name = g_strdup(entry_text);
504
505         GET_ENTRY(headers.key_entry);
506         if (entry_text[0] != '\0')
507                 ch->value = g_strdup(entry_text);
508
509         ch_str[0] = g_strdup_printf("%s: %s", ch->name, ch->value);
510
511         if (row < 0)
512                 row = gtk_clist_append(clist, ch_str);
513         else {
514                 CustomHeader *tmpch;
515
516                 gtk_clist_set_text(clist, row, 0, ch_str[0]);
517                 tmpch = gtk_clist_get_row_data(clist, row);
518                 if (tmpch)
519                         custom_header_free(tmpch);
520         }
521
522         gtk_clist_set_row_data(clist, row, ch);
523
524         g_free(ch_str[0]);
525
526         prefs_headers_set_list(cur_ac);
527
528         return row;
529 }
530
531 static void prefs_headers_register_cb(void)
532 {
533         prefs_headers_clist_set_row(cur_ac, -1);
534 }
535
536 static void prefs_headers_substitute_cb(void)
537 {
538         GtkCList *clist = GTK_CLIST(headers.headers_clist);
539         CustomHeader *ch;
540         gint row;
541
542         if (!clist->selection) return;
543
544         row = GPOINTER_TO_INT(clist->selection->data);
545         if (row == 0) return;
546
547         ch = gtk_clist_get_row_data(clist, row);
548         if (!ch) return;
549
550         prefs_headers_clist_set_row(cur_ac, row);
551 }
552
553 static void prefs_headers_delete_cb(void)
554 {
555         GtkCList *clist = GTK_CLIST(headers.headers_clist);
556         CustomHeader *ch;
557         gint row;
558
559         if (!clist->selection) return;
560         row = GPOINTER_TO_INT(clist->selection->data);
561         if (row == 0) return;
562
563         if (alertpanel(_("Delete header"),
564                        _("Do you really want to delete this header?"),
565                        _("Yes"), _("No"), NULL) == G_ALERTALTERNATE)
566                 return;
567
568         ch = gtk_clist_get_row_data(clist, row);
569         custom_header_free(ch);
570         gtk_clist_remove(clist, row);
571         cur_ac->customhdr_list = g_slist_remove(cur_ac->customhdr_list, ch);
572 }
573
574 static void prefs_headers_up(void)
575 {
576         GtkCList *clist = GTK_CLIST(headers.headers_clist);
577         gint row;
578
579         if (!clist->selection) return;
580
581         row = GPOINTER_TO_INT(clist->selection->data);
582         if (row > 1) {
583                 gtk_clist_row_move(clist, row, row - 1);
584                 prefs_headers_set_list(cur_ac);
585         }
586 }
587
588 static void prefs_headers_down(void)
589 {
590         GtkCList *clist = GTK_CLIST(headers.headers_clist);
591         gint row;
592
593         if (!clist->selection) return;
594
595         row = GPOINTER_TO_INT(clist->selection->data);
596         if (row > 0 && row < clist->rows - 1) {
597                 gtk_clist_row_move(clist, row, row + 1);
598                 prefs_headers_set_list(cur_ac);
599         }
600 }
601
602 #define ENTRY_SET_TEXT(entry, str) \
603         gtk_entry_set_text(GTK_ENTRY(entry), str ? str : "")
604
605 static void prefs_headers_select(GtkCList *clist, gint row, gint column,
606                                 GdkEvent *event)
607 {
608         CustomHeader *ch;
609         CustomHeader default_ch = { 0, "", NULL };
610  
611         ch = gtk_clist_get_row_data(clist, row);
612         if (!ch)
613                 ch = &default_ch;
614  
615         ENTRY_SET_TEXT(headers.hdr_entry, ch->name);
616         ENTRY_SET_TEXT(headers.key_entry, ch->value);
617 }
618
619 static void prefs_headers_key_pressed(GtkWidget *widget, GdkEventKey *event,
620                                      gpointer data)
621 {
622         if (event && event->keyval == GDK_Escape)
623                 gtk_widget_hide(headers.window);
624 }
625
626 static void prefs_headers_close(GtkButton *button)
627 {
628         prefs_headers_write_config(cur_ac);
629         gtk_widget_hide(headers.window);
630 }