6b999b145fea41164026a73c453b7498640e0dbf
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2013 Hiroyuki Yamamoto and the Claws Mail team
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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #include "claws-features.h"
23 #endif
24
25 #include "defs.h"
26
27 #ifndef PANGO_ENABLE_ENGINE
28 #  define PANGO_ENABLE_ENGINE
29 #endif
30
31 #include <glib.h>
32 #include <glib/gi18n.h>
33 #include <gdk/gdkkeysyms.h>
34 #include <gtk/gtk.h>
35
36 #include <pango/pango-break.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <ctype.h>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <unistd.h>
44 #include <time.h>
45 #include <stdlib.h>
46 #if HAVE_SYS_WAIT_H
47 #  include <sys/wait.h>
48 #endif
49 #include <signal.h>
50 #include <errno.h>
51 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
52 #include <libgen.h>
53 #endif
54
55 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
56 #  include <wchar.h>
57 #  include <wctype.h>
58 #endif
59
60 #include "claws.h"
61 #include "main.h"
62 #include "mainwindow.h"
63 #include "compose.h"
64 #ifndef USE_NEW_ADDRBOOK
65         #include "addressbook.h"
66 #else
67         #include "addressbook-dbus.h"
68         #include "addressadd.h"
69 #endif
70 #include "folderview.h"
71 #include "procmsg.h"
72 #include "menu.h"
73 #include "stock_pixmap.h"
74 #include "send_message.h"
75 #include "imap.h"
76 #include "news.h"
77 #include "customheader.h"
78 #include "prefs_common.h"
79 #include "prefs_account.h"
80 #include "action.h"
81 #include "account.h"
82 #include "filesel.h"
83 #include "procheader.h"
84 #include "procmime.h"
85 #include "statusbar.h"
86 #include "about.h"
87 #include "base64.h"
88 #include "quoted-printable.h"
89 #include "codeconv.h"
90 #include "utils.h"
91 #include "gtkutils.h"
92 #include "gtkshruler.h"
93 #include "socket.h"
94 #include "alertpanel.h"
95 #include "manage_window.h"
96 #include "folder.h"
97 #include "folder_item_prefs.h"
98 #include "addr_compl.h"
99 #include "quote_fmt.h"
100 #include "undo.h"
101 #include "foldersel.h"
102 #include "toolbar.h"
103 #include "inc.h"
104 #include "message_search.h"
105 #include "combobox.h"
106 #include "hooks.h"
107 #include "privacy.h"
108 #include "timing.h"
109 #include "autofaces.h"
110 #include "spell_entry.h"
111
112 enum
113 {
114         COL_MIMETYPE = 0,
115         COL_SIZE     = 1,
116         COL_NAME     = 2,
117         COL_CHARSET  = 3,
118         COL_DATA     = 4,
119         COL_AUTODATA = 5,
120         N_COL_COLUMNS
121 };
122
123 #define N_ATTACH_COLS   (N_COL_COLUMNS)
124
125 typedef enum
126 {
127         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
128         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
129         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
130         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
131         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
132         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
133         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
134         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
135         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
136         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
137         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
138         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
139         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
140         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
141 } ComposeCallAdvancedAction;
142
143 typedef enum
144 {
145         PRIORITY_HIGHEST = 1,
146         PRIORITY_HIGH,
147         PRIORITY_NORMAL,
148         PRIORITY_LOW,
149         PRIORITY_LOWEST
150 } PriorityLevel;
151
152 typedef enum
153 {
154         COMPOSE_INSERT_SUCCESS,
155         COMPOSE_INSERT_READ_ERROR,
156         COMPOSE_INSERT_INVALID_CHARACTER,
157         COMPOSE_INSERT_NO_FILE
158 } ComposeInsertResult;
159
160 typedef enum
161 {
162         COMPOSE_WRITE_FOR_SEND,
163         COMPOSE_WRITE_FOR_STORE
164 } ComposeWriteType;
165
166 typedef enum
167 {
168         COMPOSE_QUOTE_FORCED,
169         COMPOSE_QUOTE_CHECK,
170         COMPOSE_QUOTE_SKIP
171 } ComposeQuoteMode;
172
173 typedef enum {
174     TO_FIELD_PRESENT,
175     SUBJECT_FIELD_PRESENT,
176     BODY_FIELD_PRESENT,
177     NO_FIELD_PRESENT
178 } MailField;
179
180 #define B64_LINE_SIZE           57
181 #define B64_BUFFSIZE            77
182
183 #define MAX_REFERENCES_LEN      999
184
185 static GList *compose_list = NULL;
186 static GSList *extra_headers = NULL;
187
188 static Compose *compose_generic_new                     (PrefsAccount   *account,
189                                                  const gchar    *to,
190                                                  FolderItem     *item,
191                                                  GList          *attach_files,
192                                                  GList          *listAddress );
193
194 static Compose *compose_create                  (PrefsAccount   *account,
195                                                  FolderItem              *item,
196                                                  ComposeMode     mode,
197                                                  gboolean batch);
198
199 static void compose_entry_mark_default_to       (Compose          *compose,
200                                          const gchar      *address);
201 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
202                                          ComposeQuoteMode        quote_mode,
203                                          gboolean        to_all,
204                                          gboolean        to_sender,
205                                          const gchar    *body);
206 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
207                                          GSList         *msginfo_list);
208 static Compose *compose_reply                   (MsgInfo        *msginfo,
209                                          ComposeQuoteMode        quote_mode,
210                                          gboolean        to_all,
211                                          gboolean        to_ml,
212                                          gboolean        to_sender,
213                                          const gchar    *body);
214 static Compose *compose_reply_mode              (ComposeMode     mode, 
215                                          GSList         *msginfo_list, 
216                                          gchar          *body);
217 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
218 static void compose_update_privacy_systems_menu(Compose *compose);
219
220 static GtkWidget *compose_account_option_menu_create
221                                                 (Compose        *compose);
222 static void compose_set_out_encoding            (Compose        *compose);
223 static void compose_set_template_menu           (Compose        *compose);
224 static void compose_destroy                     (Compose        *compose);
225
226 static MailField compose_entries_set            (Compose        *compose,
227                                                  const gchar    *mailto,
228                                                  ComposeEntryType to_type);
229 static gint compose_parse_header                (Compose        *compose,
230                                                  MsgInfo        *msginfo);
231 static gint compose_parse_manual_headers        (Compose        *compose,
232                                                  MsgInfo        *msginfo,
233                                                  HeaderEntry    *entries);
234 static gchar *compose_parse_references          (const gchar    *ref,
235                                                  const gchar    *msgid);
236
237 static gchar *compose_quote_fmt                 (Compose        *compose,
238                                                  MsgInfo        *msginfo,
239                                                  const gchar    *fmt,
240                                                  const gchar    *qmark,
241                                                  const gchar    *body,
242                                                  gboolean        rewrap,
243                                                  gboolean        need_unescape,
244                                                  const gchar *err_msg);
245
246 static void compose_reply_set_entry             (Compose        *compose,
247                                                  MsgInfo        *msginfo,
248                                                  gboolean        to_all,
249                                                  gboolean        to_ml,
250                                                  gboolean        to_sender,
251                                                  gboolean
252                                                  followup_and_reply_to);
253 static void compose_reedit_set_entry            (Compose        *compose,
254                                                  MsgInfo        *msginfo);
255
256 static void compose_insert_sig                  (Compose        *compose,
257                                                  gboolean        replace);
258 static ComposeInsertResult compose_insert_file  (Compose        *compose,
259                                                  const gchar    *file);
260
261 static gboolean compose_attach_append           (Compose        *compose,
262                                                  const gchar    *file,
263                                                  const gchar    *type,
264                                                  const gchar    *content_type,
265                                                  const gchar    *charset);
266 static void compose_attach_parts                (Compose        *compose,
267                                                  MsgInfo        *msginfo);
268
269 static gboolean compose_beautify_paragraph      (Compose        *compose,
270                                                  GtkTextIter    *par_iter,
271                                                  gboolean        force);
272 static void compose_wrap_all                    (Compose        *compose);
273 static void compose_wrap_all_full               (Compose        *compose,
274                                                  gboolean        autowrap);
275
276 static void compose_set_title                   (Compose        *compose);
277 static void compose_select_account              (Compose        *compose,
278                                                  PrefsAccount   *account,
279                                                  gboolean        init);
280
281 static PrefsAccount *compose_current_mail_account(void);
282 /* static gint compose_send                     (Compose        *compose); */
283 static gboolean compose_check_for_valid_recipient
284                                                 (Compose        *compose);
285 static gboolean compose_check_entries           (Compose        *compose,
286                                                  gboolean       check_everything);
287 static gint compose_write_to_file               (Compose        *compose,
288                                                  FILE           *fp,
289                                                  gint            action,
290                                                  gboolean        attach_parts);
291 static gint compose_write_body_to_file          (Compose        *compose,
292                                                  const gchar    *file);
293 static gint compose_remove_reedit_target        (Compose        *compose,
294                                                  gboolean        force);
295 static void compose_remove_draft                        (Compose        *compose);
296 static gint compose_queue_sub                   (Compose        *compose,
297                                                  gint           *msgnum,
298                                                  FolderItem     **item,
299                                                  gchar          **msgpath,
300                                                  gboolean       check_subject,
301                                                  gboolean       remove_reedit_target);
302 static int compose_add_attachments              (Compose        *compose,
303                                                  MimeInfo       *parent);
304 static gchar *compose_get_header                (Compose        *compose);
305 static gchar *compose_get_manual_headers_info   (Compose        *compose);
306
307 static void compose_convert_header              (Compose        *compose,
308                                                  gchar          *dest,
309                                                  gint            len,
310                                                  gchar          *src,
311                                                  gint            header_len,
312                                                  gboolean        addr_field);
313
314 static void compose_attach_info_free            (AttachInfo     *ainfo);
315 static void compose_attach_remove_selected      (GtkAction      *action,
316                                                  gpointer        data);
317
318 static void compose_template_apply              (Compose        *compose,
319                                                  Template       *tmpl,
320                                                  gboolean        replace);
321 static void compose_attach_property             (GtkAction      *action,
322                                                  gpointer        data);
323 static void compose_attach_property_create      (gboolean       *cancelled);
324 static void attach_property_ok                  (GtkWidget      *widget,
325                                                  gboolean       *cancelled);
326 static void attach_property_cancel              (GtkWidget      *widget,
327                                                  gboolean       *cancelled);
328 static gint attach_property_delete_event        (GtkWidget      *widget,
329                                                  GdkEventAny    *event,
330                                                  gboolean       *cancelled);
331 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
332                                                  GdkEventKey    *event,
333                                                  gboolean       *cancelled);
334
335 static void compose_exec_ext_editor             (Compose        *compose);
336 #ifdef G_OS_UNIX
337 static gint compose_exec_ext_editor_real        (const gchar    *file);
338 static gboolean compose_ext_editor_kill         (Compose        *compose);
339 static gboolean compose_input_cb                (GIOChannel     *source,
340                                                  GIOCondition    condition,
341                                                  gpointer        data);
342 static void compose_set_ext_editor_sensitive    (Compose        *compose,
343                                                  gboolean        sensitive);
344 #endif /* G_OS_UNIX */
345
346 static void compose_undo_state_changed          (UndoMain       *undostruct,
347                                                  gint            undo_state,
348                                                  gint            redo_state,
349                                                  gpointer        data);
350
351 static void compose_create_header_entry (Compose *compose);
352 static void compose_add_header_entry    (Compose *compose, const gchar *header,
353                                          gchar *text, ComposePrefType pref_type);
354 static void compose_remove_header_entries(Compose *compose);
355
356 static void compose_update_priority_menu_item(Compose * compose);
357 #if USE_ENCHANT
358 static void compose_spell_menu_changed  (void *data);
359 static void compose_dict_changed        (void *data);
360 #endif
361 static void compose_add_field_list      ( Compose *compose,
362                                           GList *listAddress );
363
364 /* callback functions */
365
366 static void compose_notebook_size_alloc (GtkNotebook *notebook,
367                                          GtkAllocation *allocation,
368                                          Compose *compose);
369 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
370                                          GtkAllocation  *allocation,
371                                          GtkSHRuler     *shruler);
372 static void account_activated           (GtkComboBox *optmenu,
373                                          gpointer        data);
374 static void attach_selected             (GtkTreeView    *tree_view, 
375                                          GtkTreePath    *tree_path,
376                                          GtkTreeViewColumn *column, 
377                                          Compose *compose);
378 static gboolean attach_button_pressed   (GtkWidget      *widget,
379                                          GdkEventButton *event,
380                                          gpointer        data);
381 static gboolean attach_key_pressed      (GtkWidget      *widget,
382                                          GdkEventKey    *event,
383                                          gpointer        data);
384 static void compose_send_cb             (GtkAction      *action, gpointer data);
385 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
386
387 static void compose_save_cb             (GtkAction      *action,
388                                          gpointer        data);
389
390 static void compose_attach_cb           (GtkAction      *action,
391                                          gpointer        data);
392 static void compose_insert_file_cb      (GtkAction      *action,
393                                          gpointer        data);
394 static void compose_insert_sig_cb       (GtkAction      *action,
395                                          gpointer        data);
396
397 static void compose_close_cb            (GtkAction      *action,
398                                          gpointer        data);
399 static void compose_print_cb            (GtkAction      *action,
400                                          gpointer        data);
401
402 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
403
404 static void compose_address_cb          (GtkAction      *action,
405                                          gpointer        data);
406 static void about_show_cb               (GtkAction      *action,
407                                          gpointer        data);
408 static void compose_template_activate_cb(GtkWidget      *widget,
409                                          gpointer        data);
410
411 static void compose_ext_editor_cb       (GtkAction      *action,
412                                          gpointer        data);
413
414 static gint compose_delete_cb           (GtkWidget      *widget,
415                                          GdkEventAny    *event,
416                                          gpointer        data);
417
418 static void compose_undo_cb             (GtkAction      *action,
419                                          gpointer        data);
420 static void compose_redo_cb             (GtkAction      *action,
421                                          gpointer        data);
422 static void compose_cut_cb              (GtkAction      *action,
423                                          gpointer        data);
424 static void compose_copy_cb             (GtkAction      *action,
425                                          gpointer        data);
426 static void compose_paste_cb            (GtkAction      *action,
427                                          gpointer        data);
428 static void compose_paste_as_quote_cb   (GtkAction      *action,
429                                          gpointer        data);
430 static void compose_paste_no_wrap_cb    (GtkAction      *action,
431                                          gpointer        data);
432 static void compose_paste_wrap_cb       (GtkAction      *action,
433                                          gpointer        data);
434 static void compose_allsel_cb           (GtkAction      *action,
435                                          gpointer        data);
436
437 static void compose_advanced_action_cb  (GtkAction      *action,
438                                          gpointer        data);
439
440 static void compose_grab_focus_cb       (GtkWidget      *widget,
441                                          Compose        *compose);
442
443 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
444                                          Compose        *compose);
445
446 static void compose_wrap_cb             (GtkAction      *action,
447                                          gpointer        data);
448 static void compose_wrap_all_cb         (GtkAction      *action,
449                                          gpointer        data);
450 static void compose_find_cb             (GtkAction      *action,
451                                          gpointer        data);
452 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
453                                          gpointer        data);
454 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
455                                          gpointer        data);
456
457 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
458                                          gpointer        data);
459 static void compose_toggle_sign_cb      (GtkToggleAction *action,
460                                          gpointer        data);
461 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
462                                          gpointer        data);
463 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
464 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
465 static void activate_privacy_system     (Compose *compose, 
466                                          PrefsAccount *account,
467                                          gboolean warn);
468 static void compose_use_signing(Compose *compose, gboolean use_signing);
469 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
470 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
471                                          gpointer        data);
472 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
473                                          gpointer        data);
474 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
475 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
476 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
477
478 static void compose_attach_drag_received_cb (GtkWidget          *widget,
479                                              GdkDragContext     *drag_context,
480                                              gint                x,
481                                              gint                y,
482                                              GtkSelectionData   *data,
483                                              guint               info,
484                                              guint               time,
485                                              gpointer            user_data);
486 static void compose_insert_drag_received_cb (GtkWidget          *widget,
487                                              GdkDragContext     *drag_context,
488                                              gint                x,
489                                              gint                y,
490                                              GtkSelectionData   *data,
491                                              guint               info,
492                                              guint               time,
493                                              gpointer            user_data);
494 static void compose_header_drag_received_cb (GtkWidget          *widget,
495                                              GdkDragContext     *drag_context,
496                                              gint                x,
497                                              gint                y,
498                                              GtkSelectionData   *data,
499                                              guint               info,
500                                              guint               time,
501                                              gpointer            user_data);
502
503 static gboolean compose_drag_drop           (GtkWidget *widget,
504                                              GdkDragContext *drag_context,
505                                              gint x, gint y,
506                                              guint time, gpointer user_data);
507 static gboolean completion_set_focus_to_subject
508                                         (GtkWidget    *widget,
509                                          GdkEventKey  *event,
510                                          Compose      *user_data);
511
512 static void text_inserted               (GtkTextBuffer  *buffer,
513                                          GtkTextIter    *iter,
514                                          const gchar    *text,
515                                          gint            len,
516                                          Compose        *compose);
517 static Compose *compose_generic_reply(MsgInfo *msginfo,
518                                   ComposeQuoteMode quote_mode,
519                                   gboolean to_all,
520                                   gboolean to_ml,
521                                   gboolean to_sender,
522                                   gboolean followup_and_reply_to,
523                                   const gchar *body);
524
525 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
526                                             ComposeHeaderEntry *headerentry);
527 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
528                                             GdkEventKey        *event,
529                                             ComposeHeaderEntry *headerentry);
530 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
531                                         ComposeHeaderEntry *headerentry);
532
533 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
534
535 static void compose_allow_user_actions (Compose *compose, gboolean allow);
536
537 static void compose_nothing_cb             (GtkAction *action, gpointer data)
538 {
539
540 }
541
542 #if USE_ENCHANT
543 static void compose_check_all              (GtkAction *action, gpointer data);
544 static void compose_highlight_all          (GtkAction *action, gpointer data);
545 static void compose_check_backwards        (GtkAction *action, gpointer data);
546 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
547 #endif
548
549 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
550
551 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
552
553 #ifdef USE_ENCHANT
554 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
555                                                 FolderItem *folder_item);
556 #endif
557 static void compose_attach_update_label(Compose *compose);
558 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
559                                      gboolean respect_default_to);
560 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
561
562 static GtkActionEntry compose_popup_entries[] =
563 {
564         {"Compose",                     NULL, "Compose" },
565         {"Compose/Add",                 NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
566         {"Compose/Remove",                      NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
567         {"Compose/---",                 NULL, "---", NULL, NULL, NULL },
568         {"Compose/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
569 };
570
571 static GtkActionEntry compose_entries[] =
572 {
573         {"Menu",                                NULL, "Menu" },
574 /* menus */
575         {"Message",                     NULL, N_("_Message") },
576         {"Edit",                        NULL, N_("_Edit") },
577 #if USE_ENCHANT
578         {"Spelling",                    NULL, N_("_Spelling") },
579 #endif
580         {"Options",                     NULL, N_("_Options") },
581         {"Tools",                       NULL, N_("_Tools") },
582         {"Help",                        NULL, N_("_Help") },
583 /* Message menu */
584         {"Message/Send",                NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
585         {"Message/SendLater",           NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
586         {"Message/---",                 NULL, "---" },
587
588         {"Message/AttachFile",          NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
589         {"Message/InsertFile",          NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
590         {"Message/InsertSig",           NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
591         /* {"Message/---",              NULL, "---" }, */
592         {"Message/Save",                NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
593         /* {"Message/---",              NULL, "---" }, */
594         {"Message/Print",               NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
595         /* {"Message/---",              NULL, "---" }, */
596         {"Message/Close",               NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
597
598 /* Edit menu */
599         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
600         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
601         {"Edit/---",                    NULL, "---" },
602
603         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
604         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
605         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
606
607         {"Edit/SpecialPaste",           NULL, N_("_Special paste") },
608         {"Edit/SpecialPaste/AsQuotation",       NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
609         {"Edit/SpecialPaste/Wrapped",   NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
610         {"Edit/SpecialPaste/Unwrapped", NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
611
612         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
613
614         {"Edit/Advanced",               NULL, N_("A_dvanced") },
615         {"Edit/Advanced/BackChar",      NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
616         {"Edit/Advanced/ForwChar",      NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
617         {"Edit/Advanced/BackWord",      NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
618         {"Edit/Advanced/ForwWord",      NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
619         {"Edit/Advanced/BegLine",       NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
620         {"Edit/Advanced/EndLine",       NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
621         {"Edit/Advanced/PrevLine",      NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
622         {"Edit/Advanced/NextLine",      NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
623         {"Edit/Advanced/DelBackChar",   NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
624         {"Edit/Advanced/DelForwChar",   NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
625         {"Edit/Advanced/DelBackWord",   NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
626         {"Edit/Advanced/DelForwWord",   NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
627         {"Edit/Advanced/DelLine",       NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
628         {"Edit/Advanced/DelEndLine",    NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
629
630         /* {"Edit/---",                 NULL, "---" }, */
631         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
632
633         /* {"Edit/---",                 NULL, "---" }, */
634         {"Edit/WrapPara",               NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
635         {"Edit/WrapAllLines",           NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
636         /* {"Edit/---",                 NULL, "---" }, */
637         {"Edit/ExtEditor",              NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
638 #if USE_ENCHANT
639 /* Spelling menu */
640         {"Spelling/CheckAllSel",        NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
641         {"Spelling/HighlightAll",       NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
642         {"Spelling/CheckBackwards",     NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
643         {"Spelling/ForwardNext",        NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
644
645         {"Spelling/---",                NULL, "---" },
646         {"Spelling/Options",            NULL, N_("_Options") },
647 #endif
648
649 /* Options menu */
650
651         {"Options/ReplyMode",           NULL, N_("Reply _mode") },
652         {"Options/---",                 NULL, "---" },
653         {"Options/PrivacySystem",       NULL, N_("Privacy _System") },
654         {"Options/PrivacySystem/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655
656         /* {"Options/---",              NULL, "---" }, */
657
658         {"Options/Priority",            NULL, N_("_Priority") },
659
660         {"Options/Encoding",            NULL, N_("Character _encoding") },
661         {"Options/Encoding/---",        NULL, "---" },
662 #define ENC_ACTION(cs_char,c_char,string) \
663         { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
664
665         {"Options/Encoding/Western",    NULL, N_("Western European") },
666         {"Options/Encoding/Baltic",     NULL, N_("Baltic") },
667         {"Options/Encoding/Hebrew",     NULL, N_("Hebrew") },
668         {"Options/Encoding/Arabic",     NULL, N_("Arabic") },
669         {"Options/Encoding/Cyrillic",   NULL, N_("Cyrillic") },
670         {"Options/Encoding/Japanese",   NULL, N_("Japanese") },
671         {"Options/Encoding/Chinese",    NULL, N_("Chinese") },
672         {"Options/Encoding/Korean",     NULL, N_("Korean") },
673         {"Options/Encoding/Thai",       NULL, N_("Thai") },
674
675 /* Tools menu */
676         {"Tools/AddressBook",           NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
677
678         {"Tools/Template",      NULL, N_("_Template") },
679         {"Tools/Template/PlaceHolder",  NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
680         {"Tools/Actions",       NULL, N_("Actio_ns") },
681         {"Tools/Actions/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
682
683 /* Help menu */
684         {"Help/About",          NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
685 };
686
687 static GtkToggleActionEntry compose_toggle_entries[] =
688 {
689         {"Edit/AutoWrap",               NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
690         {"Edit/AutoIndent",             NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
691         {"Options/Sign",                NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
692         {"Options/Encrypt",             NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
693         {"Options/RequestRetRcpt",      NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
694         {"Options/RemoveReferences",    NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
695         {"Tools/ShowRuler",             NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
696 };
697
698 static GtkRadioActionEntry compose_radio_rm_entries[] =
699 {
700         {"Options/ReplyMode/Normal",    NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
701         {"Options/ReplyMode/All",       NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
702         {"Options/ReplyMode/Sender",    NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
703         {"Options/ReplyMode/List",      NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
704 };
705
706 static GtkRadioActionEntry compose_radio_prio_entries[] =
707 {
708         {"Options/Priority/Highest",    NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
709         {"Options/Priority/High",       NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
710         {"Options/Priority/Normal",     NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
711         {"Options/Priority/Low",        NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
712         {"Options/Priority/Lowest",     NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
713 };
714
715 static GtkRadioActionEntry compose_radio_enc_entries[] =
716 {
717         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
718         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
719         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
720         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
721         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
722         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
723         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
724         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
725         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
726         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
727         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
728         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
729         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
730         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
731         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
732         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
733         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
734         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
735         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
736         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
737         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
738         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
739         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
740         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
741         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
742         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
743         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
744         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
745         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
746         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
747         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
748         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
749 };
750
751 static GtkTargetEntry compose_mime_types[] =
752 {
753         {"text/uri-list", 0, 0},
754         {"UTF8_STRING", 0, 0},
755         {"text/plain", 0, 0}
756 };
757
758 static gboolean compose_put_existing_to_front(MsgInfo *info)
759 {
760         GList *compose_list = compose_get_compose_list();
761         GList *elem = NULL;
762         
763         if (compose_list) {
764                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
765                      elem = elem->next) {
766                         Compose *c = (Compose*)elem->data;
767
768                         if (!c->targetinfo || !c->targetinfo->msgid ||
769                             !info->msgid)
770                                 continue;
771
772                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
773                                 gtkut_window_popup(c->window);
774                                 return TRUE;
775                         }
776                 }
777         }
778         return FALSE;
779 }
780
781 static GdkColor quote_color1 = 
782         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
783 static GdkColor quote_color2 = 
784         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
785 static GdkColor quote_color3 = 
786         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
787
788 static GdkColor quote_bgcolor1 = 
789         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
790 static GdkColor quote_bgcolor2 = 
791         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
792 static GdkColor quote_bgcolor3 = 
793         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
794
795 static GdkColor signature_color = {
796         (gulong)0,
797         (gushort)0x7fff,
798         (gushort)0x7fff,
799         (gushort)0x7fff
800 };
801
802 static GdkColor uri_color = {
803         (gulong)0,
804         (gushort)0,
805         (gushort)0,
806         (gushort)0
807 };
808
809 static void compose_create_tags(GtkTextView *text, Compose *compose)
810 {
811         GtkTextBuffer *buffer;
812         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
813 #if !GTK_CHECK_VERSION(2, 24, 0)
814         GdkColormap *cmap;
815         gboolean success[8];
816         int i;
817         GdkColor color[8];
818 #endif
819
820         buffer = gtk_text_view_get_buffer(text);
821
822         if (prefs_common.enable_color) {
823                 /* grab the quote colors, converting from an int to a GdkColor */
824                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
825                                                &quote_color1);
826                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
827                                                &quote_color2);
828                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
829                                                &quote_color3);
830                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
831                                                &quote_bgcolor1);
832                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
833                                                &quote_bgcolor2);
834                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
835                                                &quote_bgcolor3);
836                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
837                                                &signature_color);
838                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
839                                                &uri_color);
840         } else {
841                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
842                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
843         }
844
845         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
846                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
847                                            "foreground-gdk", &quote_color1,
848                                            "paragraph-background-gdk", &quote_bgcolor1,
849                                            NULL);
850                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
851                                            "foreground-gdk", &quote_color2,
852                                            "paragraph-background-gdk", &quote_bgcolor2,
853                                            NULL);
854                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
855                                            "foreground-gdk", &quote_color3,
856                                            "paragraph-background-gdk", &quote_bgcolor3,
857                                            NULL);
858         } else {
859                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
860                                            "foreground-gdk", &quote_color1,
861                                            NULL);
862                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
863                                            "foreground-gdk", &quote_color2,
864                                            NULL);
865                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
866                                            "foreground-gdk", &quote_color3,
867                                            NULL);
868         }
869         
870         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
871                                    "foreground-gdk", &signature_color,
872                                    NULL);
873         
874         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
875                                         "foreground-gdk", &uri_color,
876                                          NULL);
877         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
878         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
879
880 #if !GTK_CHECK_VERSION(2, 24, 0)
881         color[0] = quote_color1;
882         color[1] = quote_color2;
883         color[2] = quote_color3;
884         color[3] = quote_bgcolor1;
885         color[4] = quote_bgcolor2;
886         color[5] = quote_bgcolor3;
887         color[6] = signature_color;
888         color[7] = uri_color;
889
890         cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
891         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
892
893         for (i = 0; i < 8; i++) {
894                 if (success[i] == FALSE) {
895                         g_warning("Compose: color allocation failed.\n");
896                         quote_color1 = quote_color2 = quote_color3 = 
897                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
898                                 signature_color = uri_color = black;
899                 }
900         }
901 #endif
902 }
903
904 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
905                      GList *attach_files)
906 {
907         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
908 }
909
910 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
911 {
912         return compose_generic_new(account, mailto, item, NULL, NULL);
913 }
914
915 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
916 {
917         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
918 }
919
920 #define SCROLL_TO_CURSOR(compose) {                             \
921         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
922                 gtk_text_view_get_buffer(                       \
923                         GTK_TEXT_VIEW(compose->text)));         \
924         gtk_text_view_scroll_mark_onscreen(                     \
925                 GTK_TEXT_VIEW(compose->text),                   \
926                 cmark);                                         \
927 }
928
929 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
930 {
931         GtkEditable *entry;
932         if (folderidentifier) {
933 #if !GTK_CHECK_VERSION(2, 24, 0)
934                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
935 #else
936                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
937 #endif
938                 prefs_common.compose_save_to_history = add_history(
939                                 prefs_common.compose_save_to_history, folderidentifier);
940 #if !GTK_CHECK_VERSION(2, 24, 0)
941                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
942                                 prefs_common.compose_save_to_history);
943 #else
944                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
945                                 prefs_common.compose_save_to_history);
946 #endif
947         }
948
949         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950         if (folderidentifier)
951                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
952         else
953                 gtk_entry_set_text(GTK_ENTRY(entry), "");
954 }
955
956 static gchar *compose_get_save_to(Compose *compose)
957 {
958         GtkEditable *entry;
959         gchar *result = NULL;
960         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
961         result = gtk_editable_get_chars(entry, 0, -1);
962         
963         if (result) {
964 #if !GTK_CHECK_VERSION(2, 24, 0)
965                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
966 #else
967                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
968 #endif
969                 prefs_common.compose_save_to_history = add_history(
970                                 prefs_common.compose_save_to_history, result);
971 #if !GTK_CHECK_VERSION(2, 24, 0)
972                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
973                                 prefs_common.compose_save_to_history);
974 #else
975                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
976                                 prefs_common.compose_save_to_history);
977 #endif
978         }
979         return result;
980 }
981
982 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
983                              GList *attach_files, GList *listAddress )
984 {
985         Compose *compose;
986         GtkTextView *textview;
987         GtkTextBuffer *textbuf;
988         GtkTextIter iter;
989         const gchar *subject_format = NULL;
990         const gchar *body_format = NULL;
991         gchar *mailto_from = NULL;
992         PrefsAccount *mailto_account = NULL;
993         MsgInfo* dummyinfo = NULL;
994         gint cursor_pos = -1;
995         MailField mfield = NO_FIELD_PRESENT;
996         gchar* buf;
997         GtkTextMark *mark;
998
999         /* check if mailto defines a from */
1000         if (mailto && *mailto != '\0') {
1001                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1002                 /* mailto defines a from, check if we can get account prefs from it,
1003                    if not, the account prefs will be guessed using other ways, but we'll keep
1004                    the from anyway */
1005                 if (mailto_from) {
1006                         mailto_account = account_find_from_address(mailto_from, TRUE);
1007                         if (mailto_account == NULL) {
1008                                 gchar *tmp_from;
1009                                 Xstrdup_a(tmp_from, mailto_from, return NULL);
1010                                 extract_address(tmp_from);
1011                                 mailto_account = account_find_from_address(tmp_from, TRUE);
1012                         }
1013                 }
1014                 if (mailto_account)
1015                         account = mailto_account;
1016         }
1017
1018         /* if no account prefs set from mailto, set if from folder prefs (if any) */
1019         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
1020                 account = account_find_from_id(item->prefs->default_account);
1021
1022         /* if no account prefs set, fallback to the current one */
1023         if (!account) account = cur_account;
1024         cm_return_val_if_fail(account != NULL, NULL);
1025
1026         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1027
1028         /* override from name if mailto asked for it */
1029         if (mailto_from) {
1030                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1031                 g_free(mailto_from);
1032         } else
1033                 /* override from name according to folder properties */
1034                 if (item && item->prefs &&
1035                         item->prefs->compose_with_format &&
1036                         item->prefs->compose_override_from_format &&
1037                         *item->prefs->compose_override_from_format != '\0') {
1038
1039                         gchar *tmp = NULL;
1040                         gchar *buf = NULL;
1041
1042                         dummyinfo = compose_msginfo_new_from_compose(compose);
1043
1044                         /* decode \-escape sequences in the internal representation of the quote format */
1045                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1046                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1047
1048 #ifdef USE_ENCHANT
1049                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1050                                         compose->gtkaspell);
1051 #else
1052                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1053 #endif
1054                         quote_fmt_scan_string(tmp);
1055                         quote_fmt_parse();
1056
1057                         buf = quote_fmt_get_buffer();
1058                         if (buf == NULL)
1059                                 alertpanel_error(_("New message From format error."));
1060                         else
1061                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1062                         quote_fmt_reset_vartable();
1063
1064                         g_free(tmp);
1065                 }
1066
1067         compose->replyinfo = NULL;
1068         compose->fwdinfo   = NULL;
1069
1070         textview = GTK_TEXT_VIEW(compose->text);
1071         textbuf = gtk_text_view_get_buffer(textview);
1072         compose_create_tags(textview, compose);
1073
1074         undo_block(compose->undostruct);
1075 #ifdef USE_ENCHANT
1076         compose_set_dictionaries_from_folder_prefs(compose, item);
1077 #endif
1078
1079         if (account->auto_sig)
1080                 compose_insert_sig(compose, FALSE);
1081         gtk_text_buffer_get_start_iter(textbuf, &iter);
1082         gtk_text_buffer_place_cursor(textbuf, &iter);
1083
1084         if (account->protocol != A_NNTP) {
1085                 if (mailto && *mailto != '\0') {
1086                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1087
1088                 } else {
1089                         compose_set_folder_prefs(compose, item, TRUE);
1090                 }
1091                 if (item && item->ret_rcpt) {
1092                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1093                 }
1094         } else {
1095                 if (mailto && *mailto != '\0') {
1096                         if (!strchr(mailto, '@'))
1097                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1098                         else
1099                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1100                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1101                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1102                         mfield = TO_FIELD_PRESENT;
1103                 }
1104                 /*
1105                  * CLAWS: just don't allow return receipt request, even if the user
1106                  * may want to send an email. simple but foolproof.
1107                  */
1108                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1109         }
1110         compose_add_field_list( compose, listAddress );
1111
1112         if (item && item->prefs && item->prefs->compose_with_format) {
1113                 subject_format = item->prefs->compose_subject_format;
1114                 body_format = item->prefs->compose_body_format;
1115         } else if (account->compose_with_format) {
1116                 subject_format = account->compose_subject_format;
1117                 body_format = account->compose_body_format;
1118         } else if (prefs_common.compose_with_format) {
1119                 subject_format = prefs_common.compose_subject_format;
1120                 body_format = prefs_common.compose_body_format;
1121         }
1122
1123         if (subject_format || body_format) {
1124
1125                 if ( subject_format
1126                          && *subject_format != '\0' )
1127                 {
1128                         gchar *subject = NULL;
1129                         gchar *tmp = NULL;
1130                         gchar *buf = NULL;
1131
1132                         if (!dummyinfo)
1133                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1134
1135                         /* decode \-escape sequences in the internal representation of the quote format */
1136                         tmp = g_malloc(strlen(subject_format)+1);
1137                         pref_get_unescaped_pref(tmp, subject_format);
1138
1139                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1140 #ifdef USE_ENCHANT
1141                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1142                                         compose->gtkaspell);
1143 #else
1144                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1145 #endif
1146                         quote_fmt_scan_string(tmp);
1147                         quote_fmt_parse();
1148
1149                         buf = quote_fmt_get_buffer();
1150                         if (buf == NULL)
1151                                 alertpanel_error(_("New message subject format error."));
1152                         else
1153                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1154                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1155                         quote_fmt_reset_vartable();
1156
1157                         g_free(subject);
1158                         g_free(tmp);
1159                         mfield = SUBJECT_FIELD_PRESENT;
1160                 }
1161
1162                 if ( body_format
1163                          && *body_format != '\0' )
1164                 {
1165                         GtkTextView *text;
1166                         GtkTextBuffer *buffer;
1167                         GtkTextIter start, end;
1168                         gchar *tmp = NULL;
1169
1170                         if (!dummyinfo)
1171                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1172
1173                         text = GTK_TEXT_VIEW(compose->text);
1174                         buffer = gtk_text_view_get_buffer(text);
1175                         gtk_text_buffer_get_start_iter(buffer, &start);
1176                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1177                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1178
1179                         compose_quote_fmt(compose, dummyinfo,
1180                                           body_format,
1181                                           NULL, tmp, FALSE, TRUE,
1182                                                   _("The body of the \"New message\" template has an error at line %d."));
1183                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1184                         quote_fmt_reset_vartable();
1185
1186                         g_free(tmp);
1187 #ifdef USE_ENCHANT
1188                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1189                                 gtkaspell_highlight_all(compose->gtkaspell);
1190 #endif
1191                         mfield = BODY_FIELD_PRESENT;
1192                 }
1193
1194         }
1195         procmsg_msginfo_free( dummyinfo );
1196
1197         if (attach_files) {
1198                 GList *curr;
1199                 AttachInfo *ainfo;
1200
1201                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1202                         ainfo = (AttachInfo *) curr->data;
1203                         compose_attach_append(compose, ainfo->file, ainfo->name,
1204                                         ainfo->content_type, ainfo->charset);
1205                 }
1206         }
1207
1208         compose_show_first_last_header(compose, TRUE);
1209
1210         /* Set save folder */
1211         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1212                 gchar *folderidentifier;
1213
1214                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1215                 folderidentifier = folder_item_get_identifier(item);
1216                 compose_set_save_to(compose, folderidentifier);
1217                 g_free(folderidentifier);
1218         }
1219
1220         /* Place cursor according to provided input (mfield) */
1221         switch (mfield) { 
1222                 case NO_FIELD_PRESENT:
1223                         if (compose->header_last)
1224                                 gtk_widget_grab_focus(compose->header_last->entry);
1225                         break;
1226                 case TO_FIELD_PRESENT:
1227                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1228                         if (buf) {
1229                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1230                                 g_free(buf);
1231                         }
1232                         gtk_widget_grab_focus(compose->subject_entry);
1233                         break;
1234                 case SUBJECT_FIELD_PRESENT:
1235                         textview = GTK_TEXT_VIEW(compose->text);
1236                         if (!textview)
1237                                 break;
1238                         textbuf = gtk_text_view_get_buffer(textview);
1239                         if (!textbuf)
1240                                 break;
1241                         mark = gtk_text_buffer_get_insert(textbuf);
1242                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1243                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1244                     /* 
1245                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1246                      * only defers where it comes to the variable body
1247                      * is not null. If no body is present compose->text
1248                      * will be null in which case you cannot place the
1249                      * cursor inside the component so. An empty component
1250                      * is therefore created before placing the cursor
1251                      */
1252                 case BODY_FIELD_PRESENT:
1253                         cursor_pos = quote_fmt_get_cursor_pos();
1254                         if (cursor_pos == -1)
1255                                 gtk_widget_grab_focus(compose->header_last->entry);
1256                         else
1257                                 gtk_widget_grab_focus(compose->text);
1258                         break;
1259         }
1260
1261         undo_unblock(compose->undostruct);
1262
1263         if (prefs_common.auto_exteditor)
1264                 compose_exec_ext_editor(compose);
1265
1266         compose->draft_timeout_tag = -1;
1267         SCROLL_TO_CURSOR(compose);
1268
1269         compose->modified = FALSE;
1270         compose_set_title(compose);
1271
1272         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1273
1274         return compose;
1275 }
1276
1277 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1278                 gboolean override_pref, const gchar *system)
1279 {
1280         const gchar *privacy = NULL;
1281
1282         cm_return_if_fail(compose != NULL);
1283         cm_return_if_fail(account != NULL);
1284
1285         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1286                 return;
1287
1288         if (system)
1289                 privacy = system;
1290         else if (account->default_privacy_system
1291         &&  strlen(account->default_privacy_system)) {
1292                 privacy = account->default_privacy_system;
1293         } else {
1294                 GSList *privacy_avail = privacy_get_system_ids();
1295                 if (privacy_avail && g_slist_length(privacy_avail)) {
1296                         privacy = (gchar *)(privacy_avail->data);
1297                 }
1298         }
1299         if (privacy != NULL) {
1300                 if (system) {
1301                         g_free(compose->privacy_system);
1302                         compose->privacy_system = NULL;
1303                 }
1304                 if (compose->privacy_system == NULL)
1305                         compose->privacy_system = g_strdup(privacy);
1306                 else if (*(compose->privacy_system) == '\0') {
1307                         g_free(compose->privacy_system);
1308                         compose->privacy_system = g_strdup(privacy);
1309                 }
1310                 compose_update_privacy_system_menu_item(compose, FALSE);
1311                 compose_use_encryption(compose, TRUE);
1312         }
1313 }       
1314
1315 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1316 {
1317         const gchar *privacy = NULL;
1318
1319         if (system)
1320                 privacy = system;
1321         else if (account->default_privacy_system
1322         &&  strlen(account->default_privacy_system)) {
1323                 privacy = account->default_privacy_system;
1324         } else {
1325                 GSList *privacy_avail = privacy_get_system_ids();
1326                 if (privacy_avail && g_slist_length(privacy_avail)) {
1327                         privacy = (gchar *)(privacy_avail->data);
1328                 }
1329         }
1330
1331         if (privacy != NULL) {
1332                 if (system) {
1333                         g_free(compose->privacy_system);
1334                         compose->privacy_system = NULL;
1335                 }
1336                 if (compose->privacy_system == NULL)
1337                         compose->privacy_system = g_strdup(privacy);
1338                 compose_update_privacy_system_menu_item(compose, FALSE);
1339                 compose_use_signing(compose, TRUE);
1340         }
1341 }       
1342
1343 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1344 {
1345         MsgInfo *msginfo;
1346         guint list_len;
1347         Compose *compose = NULL;
1348         
1349         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1350
1351         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1352         cm_return_val_if_fail(msginfo != NULL, NULL);
1353
1354         list_len = g_slist_length(msginfo_list);
1355
1356         switch (mode) {
1357         case COMPOSE_REPLY:
1358         case COMPOSE_REPLY_TO_ADDRESS:
1359                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1360                               FALSE, prefs_common.default_reply_list, FALSE, body);
1361                 break;
1362         case COMPOSE_REPLY_WITH_QUOTE:
1363                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1364                         FALSE, prefs_common.default_reply_list, FALSE, body);
1365                 break;
1366         case COMPOSE_REPLY_WITHOUT_QUOTE:
1367                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1368                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1369                 break;
1370         case COMPOSE_REPLY_TO_SENDER:
1371                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1372                               FALSE, FALSE, TRUE, body);
1373                 break;
1374         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1375                 compose = compose_followup_and_reply_to(msginfo,
1376                                               COMPOSE_QUOTE_CHECK,
1377                                               FALSE, FALSE, body);
1378                 break;
1379         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1380                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1381                         FALSE, FALSE, TRUE, body);
1382                 break;
1383         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1384                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1385                         FALSE, FALSE, TRUE, NULL);
1386                 break;
1387         case COMPOSE_REPLY_TO_ALL:
1388                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1389                         TRUE, FALSE, FALSE, body);
1390                 break;
1391         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1392                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1393                         TRUE, FALSE, FALSE, body);
1394                 break;
1395         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1396                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1397                         TRUE, FALSE, FALSE, NULL);
1398                 break;
1399         case COMPOSE_REPLY_TO_LIST:
1400                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1401                         FALSE, TRUE, FALSE, body);
1402                 break;
1403         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1404                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1405                         FALSE, TRUE, FALSE, body);
1406                 break;
1407         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1408                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1409                         FALSE, TRUE, FALSE, NULL);
1410                 break;
1411         case COMPOSE_FORWARD:
1412                 if (prefs_common.forward_as_attachment) {
1413                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1414                         return compose;
1415                 } else {
1416                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1417                         return compose;
1418                 }
1419                 break;
1420         case COMPOSE_FORWARD_INLINE:
1421                 /* check if we reply to more than one Message */
1422                 if (list_len == 1) {
1423                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1424                         break;
1425                 } 
1426                 /* more messages FALL THROUGH */
1427         case COMPOSE_FORWARD_AS_ATTACH:
1428                 compose = compose_forward_multiple(NULL, msginfo_list);
1429                 break;
1430         case COMPOSE_REDIRECT:
1431                 compose = compose_redirect(NULL, msginfo, FALSE);
1432                 break;
1433         default:
1434                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1435         }
1436         
1437         if (compose == NULL) {
1438                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1439                 return NULL;
1440         }
1441
1442         compose->rmode = mode;
1443         switch (compose->rmode) {
1444         case COMPOSE_REPLY:
1445         case COMPOSE_REPLY_WITH_QUOTE:
1446         case COMPOSE_REPLY_WITHOUT_QUOTE:
1447         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1448                 debug_print("reply mode Normal\n");
1449                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1450                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1451                 break;
1452         case COMPOSE_REPLY_TO_SENDER:
1453         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1454         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1455                 debug_print("reply mode Sender\n");
1456                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1457                 break;
1458         case COMPOSE_REPLY_TO_ALL:
1459         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1460         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1461                 debug_print("reply mode All\n");
1462                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1463                 break;
1464         case COMPOSE_REPLY_TO_LIST:
1465         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1466         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1467                 debug_print("reply mode List\n");
1468                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1469                 break;
1470         case COMPOSE_REPLY_TO_ADDRESS:
1471                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1472                 break;
1473         default:
1474                 break;
1475         }
1476         return compose;
1477 }
1478
1479 static Compose *compose_reply(MsgInfo *msginfo,
1480                                    ComposeQuoteMode quote_mode,
1481                                    gboolean to_all,
1482                                    gboolean to_ml,
1483                                    gboolean to_sender, 
1484                                    const gchar *body)
1485 {
1486         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1487                               to_sender, FALSE, body);
1488 }
1489
1490 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1491                                    ComposeQuoteMode quote_mode,
1492                                    gboolean to_all,
1493                                    gboolean to_sender,
1494                                    const gchar *body)
1495 {
1496         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1497                               to_sender, TRUE, body);
1498 }
1499
1500 static void compose_extract_original_charset(Compose *compose)
1501 {
1502         MsgInfo *info = NULL;
1503         if (compose->replyinfo) {
1504                 info = compose->replyinfo;
1505         } else if (compose->fwdinfo) {
1506                 info = compose->fwdinfo;
1507         } else if (compose->targetinfo) {
1508                 info = compose->targetinfo;
1509         }
1510         if (info) {
1511                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1512                 MimeInfo *partinfo = mimeinfo;
1513                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1514                         partinfo = procmime_mimeinfo_next(partinfo);
1515                 if (partinfo) {
1516                         compose->orig_charset = 
1517                                 g_strdup(procmime_mimeinfo_get_parameter(
1518                                                 partinfo, "charset"));
1519                 }
1520                 procmime_mimeinfo_free_all(mimeinfo);
1521         }
1522 }
1523
1524 #define SIGNAL_BLOCK(buffer) {                                  \
1525         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1526                                 G_CALLBACK(compose_changed_cb), \
1527                                 compose);                       \
1528         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1529                                 G_CALLBACK(text_inserted),      \
1530                                 compose);                       \
1531 }
1532
1533 #define SIGNAL_UNBLOCK(buffer) {                                \
1534         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1535                                 G_CALLBACK(compose_changed_cb), \
1536                                 compose);                       \
1537         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1538                                 G_CALLBACK(text_inserted),      \
1539                                 compose);                       \
1540 }
1541
1542 static Compose *compose_generic_reply(MsgInfo *msginfo,
1543                                   ComposeQuoteMode quote_mode,
1544                                   gboolean to_all, gboolean to_ml,
1545                                   gboolean to_sender,
1546                                   gboolean followup_and_reply_to,
1547                                   const gchar *body)
1548 {
1549         Compose *compose;
1550         PrefsAccount *account = NULL;
1551         GtkTextView *textview;
1552         GtkTextBuffer *textbuf;
1553         gboolean quote = FALSE;
1554         const gchar *qmark = NULL;
1555         const gchar *body_fmt = NULL;
1556         gchar *s_system = NULL;
1557         START_TIMING("");
1558         cm_return_val_if_fail(msginfo != NULL, NULL);
1559         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1560
1561         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1562
1563         cm_return_val_if_fail(account != NULL, NULL);
1564
1565         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1566
1567         compose->updating = TRUE;
1568
1569         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1570         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1571
1572         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1573         if (!compose->replyinfo)
1574                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1575
1576         compose_extract_original_charset(compose);
1577         
1578         if (msginfo->folder && msginfo->folder->ret_rcpt)
1579                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1580
1581         /* Set save folder */
1582         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1583                 gchar *folderidentifier;
1584
1585                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1586                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1587                 compose_set_save_to(compose, folderidentifier);
1588                 g_free(folderidentifier);
1589         }
1590
1591         if (compose_parse_header(compose, msginfo) < 0) {
1592                 compose->updating = FALSE;
1593                 compose_destroy(compose);
1594                 return NULL;
1595         }
1596
1597         /* override from name according to folder properties */
1598         if (msginfo->folder && msginfo->folder->prefs &&
1599                 msginfo->folder->prefs->reply_with_format &&
1600                 msginfo->folder->prefs->reply_override_from_format &&
1601                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1602
1603                 gchar *tmp = NULL;
1604                 gchar *buf = NULL;
1605
1606                 /* decode \-escape sequences in the internal representation of the quote format */
1607                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1608                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1609
1610 #ifdef USE_ENCHANT
1611                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1612                                 compose->gtkaspell);
1613 #else
1614                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1615 #endif
1616                 quote_fmt_scan_string(tmp);
1617                 quote_fmt_parse();
1618
1619                 buf = quote_fmt_get_buffer();
1620                 if (buf == NULL)
1621                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1622                 else
1623                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1624                 quote_fmt_reset_vartable();
1625
1626                 g_free(tmp);
1627         }
1628
1629         textview = (GTK_TEXT_VIEW(compose->text));
1630         textbuf = gtk_text_view_get_buffer(textview);
1631         compose_create_tags(textview, compose);
1632
1633         undo_block(compose->undostruct);
1634 #ifdef USE_ENCHANT
1635         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1636         gtkaspell_block_check(compose->gtkaspell);
1637 #endif
1638
1639         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1640                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1641                 /* use the reply format of folder (if enabled), or the account's one
1642                    (if enabled) or fallback to the global reply format, which is always
1643                    enabled (even if empty), and use the relevant quotemark */
1644                 quote = TRUE;
1645                 if (msginfo->folder && msginfo->folder->prefs &&
1646                                 msginfo->folder->prefs->reply_with_format) {
1647                         qmark = msginfo->folder->prefs->reply_quotemark;
1648                         body_fmt = msginfo->folder->prefs->reply_body_format;
1649
1650                 } else if (account->reply_with_format) {
1651                         qmark = account->reply_quotemark;
1652                         body_fmt = account->reply_body_format;
1653
1654                 } else {
1655                         qmark = prefs_common.quotemark;
1656                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1657                                 body_fmt = gettext(prefs_common.quotefmt);
1658                         else
1659                                 body_fmt = "";
1660                 }
1661         }
1662
1663         if (quote) {
1664                 /* empty quotemark is not allowed */
1665                 if (qmark == NULL || *qmark == '\0')
1666                         qmark = "> ";
1667                 compose_quote_fmt(compose, compose->replyinfo,
1668                                   body_fmt, qmark, body, FALSE, TRUE,
1669                                           _("The body of the \"Reply\" template has an error at line %d."));
1670                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1671                 quote_fmt_reset_vartable();
1672         }
1673
1674         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1675                 compose_force_encryption(compose, account, FALSE, s_system);
1676         }
1677
1678         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1679         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1680                 compose_force_signing(compose, account, s_system);
1681         }
1682         g_free(s_system);
1683
1684         SIGNAL_BLOCK(textbuf);
1685         
1686         if (account->auto_sig)
1687                 compose_insert_sig(compose, FALSE);
1688
1689         compose_wrap_all(compose);
1690
1691 #ifdef USE_ENCHANT
1692         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1693                 gtkaspell_highlight_all(compose->gtkaspell);
1694         gtkaspell_unblock_check(compose->gtkaspell);
1695 #endif
1696         SIGNAL_UNBLOCK(textbuf);
1697         
1698         gtk_widget_grab_focus(compose->text);
1699
1700         undo_unblock(compose->undostruct);
1701
1702         if (prefs_common.auto_exteditor)
1703                 compose_exec_ext_editor(compose);
1704                 
1705         compose->modified = FALSE;
1706         compose_set_title(compose);
1707
1708         compose->updating = FALSE;
1709         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1710         SCROLL_TO_CURSOR(compose);
1711         
1712         if (compose->deferred_destroy) {
1713                 compose_destroy(compose);
1714                 return NULL;
1715         }
1716         END_TIMING();
1717
1718         return compose;
1719 }
1720
1721 #define INSERT_FW_HEADER(var, hdr) \
1722 if (msginfo->var && *msginfo->var) { \
1723         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1724         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1725         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1726 }
1727
1728 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1729                          gboolean as_attach, const gchar *body,
1730                          gboolean no_extedit,
1731                          gboolean batch)
1732 {
1733         Compose *compose;
1734         GtkTextView *textview;
1735         GtkTextBuffer *textbuf;
1736         gint cursor_pos = -1;
1737         ComposeMode mode;
1738
1739         cm_return_val_if_fail(msginfo != NULL, NULL);
1740         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1741
1742         if (!account && 
1743             !(account = compose_guess_forward_account_from_msginfo
1744                                 (msginfo)))
1745                 account = cur_account;
1746
1747         if (!prefs_common.forward_as_attachment)
1748                 mode = COMPOSE_FORWARD_INLINE;
1749         else
1750                 mode = COMPOSE_FORWARD;
1751         compose = compose_create(account, msginfo->folder, mode, batch);
1752
1753         compose->updating = TRUE;
1754         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1755         if (!compose->fwdinfo)
1756                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1757
1758         compose_extract_original_charset(compose);
1759
1760         if (msginfo->subject && *msginfo->subject) {
1761                 gchar *buf, *buf2, *p;
1762
1763                 buf = p = g_strdup(msginfo->subject);
1764                 p += subject_get_prefix_length(p);
1765                 memmove(buf, p, strlen(p) + 1);
1766
1767                 buf2 = g_strdup_printf("Fw: %s", buf);
1768                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1769                 
1770                 g_free(buf);
1771                 g_free(buf2);
1772         }
1773
1774         /* override from name according to folder properties */
1775         if (msginfo->folder && msginfo->folder->prefs &&
1776                 msginfo->folder->prefs->forward_with_format &&
1777                 msginfo->folder->prefs->forward_override_from_format &&
1778                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1779
1780                 gchar *tmp = NULL;
1781                 gchar *buf = NULL;
1782                 MsgInfo *full_msginfo = NULL;
1783
1784                 if (!as_attach)
1785                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1786                 if (!full_msginfo)
1787                         full_msginfo = procmsg_msginfo_copy(msginfo);
1788
1789                 /* decode \-escape sequences in the internal representation of the quote format */
1790                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1791                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1792
1793 #ifdef USE_ENCHANT
1794                 gtkaspell_block_check(compose->gtkaspell);
1795                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1796                                 compose->gtkaspell);
1797 #else
1798                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1799 #endif
1800                 quote_fmt_scan_string(tmp);
1801                 quote_fmt_parse();
1802
1803                 buf = quote_fmt_get_buffer();
1804                 if (buf == NULL)
1805                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1806                 else
1807                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1808                 quote_fmt_reset_vartable();
1809
1810                 g_free(tmp);
1811                 procmsg_msginfo_free(full_msginfo);
1812         }
1813
1814         textview = GTK_TEXT_VIEW(compose->text);
1815         textbuf = gtk_text_view_get_buffer(textview);
1816         compose_create_tags(textview, compose);
1817         
1818         undo_block(compose->undostruct);
1819         if (as_attach) {
1820                 gchar *msgfile;
1821
1822                 msgfile = procmsg_get_message_file(msginfo);
1823                 if (!is_file_exist(msgfile))
1824                         g_warning("%s: file not exist\n", msgfile);
1825                 else
1826                         compose_attach_append(compose, msgfile, msgfile,
1827                                               "message/rfc822", NULL);
1828
1829                 g_free(msgfile);
1830         } else {
1831                 const gchar *qmark = NULL;
1832                 const gchar *body_fmt = NULL;
1833                 MsgInfo *full_msginfo;
1834
1835                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1836                 if (!full_msginfo)
1837                         full_msginfo = procmsg_msginfo_copy(msginfo);
1838
1839                 /* use the forward format of folder (if enabled), or the account's one
1840                    (if enabled) or fallback to the global forward format, which is always
1841                    enabled (even if empty), and use the relevant quotemark */
1842                 if (msginfo->folder && msginfo->folder->prefs &&
1843                                 msginfo->folder->prefs->forward_with_format) {
1844                         qmark = msginfo->folder->prefs->forward_quotemark;
1845                         body_fmt = msginfo->folder->prefs->forward_body_format;
1846
1847                 } else if (account->forward_with_format) {
1848                         qmark = account->forward_quotemark;
1849                         body_fmt = account->forward_body_format;
1850
1851                 } else {
1852                         qmark = prefs_common.fw_quotemark;
1853                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1854                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1855                         else
1856                                 body_fmt = "";
1857                 }
1858
1859                 /* empty quotemark is not allowed */
1860                 if (qmark == NULL || *qmark == '\0')
1861                         qmark = "> ";
1862
1863                 compose_quote_fmt(compose, full_msginfo,
1864                                   body_fmt, qmark, body, FALSE, TRUE,
1865                                           _("The body of the \"Forward\" template has an error at line %d."));
1866                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1867                 quote_fmt_reset_vartable();
1868                 compose_attach_parts(compose, msginfo);
1869
1870                 procmsg_msginfo_free(full_msginfo);
1871         }
1872
1873         SIGNAL_BLOCK(textbuf);
1874
1875         if (account->auto_sig)
1876                 compose_insert_sig(compose, FALSE);
1877
1878         compose_wrap_all(compose);
1879
1880 #ifdef USE_ENCHANT
1881         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1882                 gtkaspell_highlight_all(compose->gtkaspell);
1883         gtkaspell_unblock_check(compose->gtkaspell);
1884 #endif
1885         SIGNAL_UNBLOCK(textbuf);
1886         
1887         cursor_pos = quote_fmt_get_cursor_pos();
1888         if (cursor_pos == -1)
1889                 gtk_widget_grab_focus(compose->header_last->entry);
1890         else
1891                 gtk_widget_grab_focus(compose->text);
1892
1893         if (!no_extedit && prefs_common.auto_exteditor)
1894                 compose_exec_ext_editor(compose);
1895         
1896         /*save folder*/
1897         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1898                 gchar *folderidentifier;
1899
1900                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1901                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1902                 compose_set_save_to(compose, folderidentifier);
1903                 g_free(folderidentifier);
1904         }
1905
1906         undo_unblock(compose->undostruct);
1907         
1908         compose->modified = FALSE;
1909         compose_set_title(compose);
1910
1911         compose->updating = FALSE;
1912         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1913         SCROLL_TO_CURSOR(compose);
1914
1915         if (compose->deferred_destroy) {
1916                 compose_destroy(compose);
1917                 return NULL;
1918         }
1919
1920         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1921
1922         return compose;
1923 }
1924
1925 #undef INSERT_FW_HEADER
1926
1927 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1928 {
1929         Compose *compose;
1930         GtkTextView *textview;
1931         GtkTextBuffer *textbuf;
1932         GtkTextIter iter;
1933         GSList *msginfo;
1934         gchar *msgfile;
1935         gboolean single_mail = TRUE;
1936         
1937         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1938
1939         if (g_slist_length(msginfo_list) > 1)
1940                 single_mail = FALSE;
1941
1942         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1943                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1944                         return NULL;
1945
1946         /* guess account from first selected message */
1947         if (!account && 
1948             !(account = compose_guess_forward_account_from_msginfo
1949                                 (msginfo_list->data)))
1950                 account = cur_account;
1951
1952         cm_return_val_if_fail(account != NULL, NULL);
1953
1954         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1955                 if (msginfo->data) {
1956                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1957                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1958                 }
1959         }
1960
1961         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1962                 g_warning("no msginfo_list");
1963                 return NULL;
1964         }
1965
1966         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1967
1968         compose->updating = TRUE;
1969
1970         /* override from name according to folder properties */
1971         if (msginfo_list->data) {
1972                 MsgInfo *msginfo = msginfo_list->data;
1973
1974                 if (msginfo->folder && msginfo->folder->prefs &&
1975                         msginfo->folder->prefs->forward_with_format &&
1976                         msginfo->folder->prefs->forward_override_from_format &&
1977                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1978
1979                         gchar *tmp = NULL;
1980                         gchar *buf = NULL;
1981
1982                         /* decode \-escape sequences in the internal representation of the quote format */
1983                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1984                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1985
1986 #ifdef USE_ENCHANT
1987                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1988                                         compose->gtkaspell);
1989 #else
1990                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1991 #endif
1992                         quote_fmt_scan_string(tmp);
1993                         quote_fmt_parse();
1994
1995                         buf = quote_fmt_get_buffer();
1996                         if (buf == NULL)
1997                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1998                         else
1999                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2000                         quote_fmt_reset_vartable();
2001
2002                         g_free(tmp);
2003                 }
2004         }
2005
2006         textview = GTK_TEXT_VIEW(compose->text);
2007         textbuf = gtk_text_view_get_buffer(textview);
2008         compose_create_tags(textview, compose);
2009         
2010         undo_block(compose->undostruct);
2011         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2012                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2013
2014                 if (!is_file_exist(msgfile))
2015                         g_warning("%s: file not exist\n", msgfile);
2016                 else
2017                         compose_attach_append(compose, msgfile, msgfile,
2018                                 "message/rfc822", NULL);
2019                 g_free(msgfile);
2020         }
2021         
2022         if (single_mail) {
2023                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2024                 if (info->subject && *info->subject) {
2025                         gchar *buf, *buf2, *p;
2026
2027                         buf = p = g_strdup(info->subject);
2028                         p += subject_get_prefix_length(p);
2029                         memmove(buf, p, strlen(p) + 1);
2030
2031                         buf2 = g_strdup_printf("Fw: %s", buf);
2032                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2033
2034                         g_free(buf);
2035                         g_free(buf2);
2036                 }
2037         } else {
2038                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2039                         _("Fw: multiple emails"));
2040         }
2041
2042         SIGNAL_BLOCK(textbuf);
2043         
2044         if (account->auto_sig)
2045                 compose_insert_sig(compose, FALSE);
2046
2047         compose_wrap_all(compose);
2048
2049         SIGNAL_UNBLOCK(textbuf);
2050         
2051         gtk_text_buffer_get_start_iter(textbuf, &iter);
2052         gtk_text_buffer_place_cursor(textbuf, &iter);
2053
2054         gtk_widget_grab_focus(compose->header_last->entry);
2055         undo_unblock(compose->undostruct);
2056         compose->modified = FALSE;
2057         compose_set_title(compose);
2058
2059         compose->updating = FALSE;
2060         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2061         SCROLL_TO_CURSOR(compose);
2062
2063         if (compose->deferred_destroy) {
2064                 compose_destroy(compose);
2065                 return NULL;
2066         }
2067
2068         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2069
2070         return compose;
2071 }
2072
2073 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2074 {
2075         GtkTextIter start = *iter;
2076         GtkTextIter end_iter;
2077         int start_pos = gtk_text_iter_get_offset(&start);
2078         gchar *str = NULL;
2079         if (!compose->account->sig_sep)
2080                 return FALSE;
2081         
2082         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2083                 start_pos+strlen(compose->account->sig_sep));
2084
2085         /* check sig separator */
2086         str = gtk_text_iter_get_text(&start, &end_iter);
2087         if (!strcmp(str, compose->account->sig_sep)) {
2088                 gchar *tmp = NULL;
2089                 /* check end of line (\n) */
2090                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2091                         start_pos+strlen(compose->account->sig_sep));
2092                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2093                         start_pos+strlen(compose->account->sig_sep)+1);
2094                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2095                 if (!strcmp(tmp,"\n")) {
2096                         g_free(str);
2097                         g_free(tmp);
2098                         return TRUE;
2099                 }
2100                 g_free(tmp);    
2101         }
2102         g_free(str);
2103
2104         return FALSE;
2105 }
2106
2107 static void compose_colorize_signature(Compose *compose)
2108 {
2109         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2110         GtkTextIter iter;
2111         GtkTextIter end_iter;
2112         gtk_text_buffer_get_start_iter(buffer, &iter);
2113         while (gtk_text_iter_forward_line(&iter))
2114                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2115                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2116                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2117                 }
2118 }
2119
2120 #define BLOCK_WRAP() {                                                  \
2121         prev_autowrap = compose->autowrap;                              \
2122         buffer = gtk_text_view_get_buffer(                              \
2123                                         GTK_TEXT_VIEW(compose->text));  \
2124         compose->autowrap = FALSE;                                      \
2125                                                                         \
2126         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2127                                 G_CALLBACK(compose_changed_cb),         \
2128                                 compose);                               \
2129         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2130                                 G_CALLBACK(text_inserted),              \
2131                                 compose);                               \
2132 }
2133 #define UNBLOCK_WRAP() {                                                \
2134         compose->autowrap = prev_autowrap;                              \
2135         if (compose->autowrap) {                                        \
2136                 gint old = compose->draft_timeout_tag;                  \
2137                 compose->draft_timeout_tag = -2;                        \
2138                 compose_wrap_all(compose);                              \
2139                 compose->draft_timeout_tag = old;                       \
2140         }                                                               \
2141                                                                         \
2142         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2143                                 G_CALLBACK(compose_changed_cb),         \
2144                                 compose);                               \
2145         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2146                                 G_CALLBACK(text_inserted),              \
2147                                 compose);                               \
2148 }
2149
2150 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2151 {
2152         Compose *compose = NULL;
2153         PrefsAccount *account = NULL;
2154         GtkTextView *textview;
2155         GtkTextBuffer *textbuf;
2156         GtkTextMark *mark;
2157         GtkTextIter iter;
2158         FILE *fp;
2159         gchar buf[BUFFSIZE];
2160         gboolean use_signing = FALSE;
2161         gboolean use_encryption = FALSE;
2162         gchar *privacy_system = NULL;
2163         int priority = PRIORITY_NORMAL;
2164         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2165         gboolean autowrap = prefs_common.autowrap;
2166         gboolean autoindent = prefs_common.auto_indent;
2167         HeaderEntry *manual_headers = NULL;
2168
2169         cm_return_val_if_fail(msginfo != NULL, NULL);
2170         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2171
2172         if (compose_put_existing_to_front(msginfo)) {
2173                 return NULL;
2174         }
2175
2176         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2177             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2178             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2179                 gchar queueheader_buf[BUFFSIZE];
2180                 gint id, param;
2181
2182                 /* Select Account from queue headers */
2183                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2184                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2185                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2186                         account = account_find_from_id(id);
2187                 }
2188                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2189                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2190                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2191                         account = account_find_from_id(id);
2192                 }
2193                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2194                                              sizeof(queueheader_buf), "NAID:")) {
2195                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2196                         account = account_find_from_id(id);
2197                 }
2198                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2199                                                     sizeof(queueheader_buf), "MAID:")) {
2200                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2201                         account = account_find_from_id(id);
2202                 }
2203                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2204                                                                 sizeof(queueheader_buf), "S:")) {
2205                         account = account_find_from_address(queueheader_buf, FALSE);
2206                 }
2207                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2208                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2209                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2210                         use_signing = param;
2211                         
2212                 }
2213                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2214                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2215                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2216                         use_signing = param;
2217                         
2218                 }
2219                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2220                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2221                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2222                         use_encryption = param;
2223                 }
2224                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2225                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2226                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2227                         use_encryption = param;
2228                 }
2229                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2230                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2231                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2232                         autowrap = param;
2233                 }
2234                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2235                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2236                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2237                         autoindent = param;
2238                 }
2239                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2240                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2241                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2242                 }
2243                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2244                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2245                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2246                 }
2247                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2248                                              sizeof(queueheader_buf), "X-Priority: ")) {
2249                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2250                         priority = param;
2251                 }
2252                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2253                                              sizeof(queueheader_buf), "RMID:")) {
2254                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2255                         if (tokens[0] && tokens[1] && tokens[2]) {
2256                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2257                                 if (orig_item != NULL) {
2258                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2259                                 }
2260                         }
2261                         g_strfreev(tokens);
2262                 }
2263                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2264                                              sizeof(queueheader_buf), "FMID:")) {
2265                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2266                         if (tokens[0] && tokens[1] && tokens[2]) {
2267                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2268                                 if (orig_item != NULL) {
2269                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2270                                 }
2271                         }
2272                         g_strfreev(tokens);
2273                 }
2274                 /* Get manual headers */
2275                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2276                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2277                         if (*listmh != '\0') {
2278                                 debug_print("Got manual headers: %s\n", listmh);
2279                                 manual_headers = procheader_entries_from_str(listmh);
2280                         }
2281                         g_free(listmh);
2282                 }
2283         } else {
2284                 account = msginfo->folder->folder->account;
2285         }
2286
2287         if (!account && prefs_common.reedit_account_autosel) {
2288                 gchar from[BUFFSIZE];
2289                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2290                         extract_address(from);
2291                         account = account_find_from_address(from, FALSE);
2292                 }
2293         }
2294         if (!account) {
2295                 account = cur_account;
2296         }
2297         cm_return_val_if_fail(account != NULL, NULL);
2298
2299         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2300
2301         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2302         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2303         compose->autowrap = autowrap;
2304         compose->replyinfo = replyinfo;
2305         compose->fwdinfo = fwdinfo;
2306
2307         compose->updating = TRUE;
2308         compose->priority = priority;
2309
2310         if (privacy_system != NULL) {
2311                 compose->privacy_system = privacy_system;
2312                 compose_use_signing(compose, use_signing);
2313                 compose_use_encryption(compose, use_encryption);
2314                 compose_update_privacy_system_menu_item(compose, FALSE);
2315         } else {
2316                 activate_privacy_system(compose, account, FALSE);
2317         }
2318
2319         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2320
2321         compose_extract_original_charset(compose);
2322
2323         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2324             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2325             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2326                 gchar queueheader_buf[BUFFSIZE];
2327
2328                 /* Set message save folder */
2329                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2330                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2331                         compose_set_save_to(compose, &queueheader_buf[4]);
2332                 }
2333                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2334                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2335                         if (active) {
2336                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2337                         }
2338                 }
2339         }
2340         
2341         if (compose_parse_header(compose, msginfo) < 0) {
2342                 compose->updating = FALSE;
2343                 compose_destroy(compose);
2344                 return NULL;
2345         }
2346         compose_reedit_set_entry(compose, msginfo);
2347
2348         textview = GTK_TEXT_VIEW(compose->text);
2349         textbuf = gtk_text_view_get_buffer(textview);
2350         compose_create_tags(textview, compose);
2351
2352         mark = gtk_text_buffer_get_insert(textbuf);
2353         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2354
2355         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2356                                         G_CALLBACK(compose_changed_cb),
2357                                         compose);
2358         
2359         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2360                 fp = procmime_get_first_encrypted_text_content(msginfo);
2361                 if (fp) {
2362                         compose_force_encryption(compose, account, TRUE, NULL);
2363                 }
2364         } else {
2365                 fp = procmime_get_first_text_content(msginfo);
2366         }
2367         if (fp == NULL) {
2368                 g_warning("Can't get text part\n");
2369         }
2370
2371         if (fp != NULL) {
2372                 gboolean prev_autowrap;
2373                 GtkTextBuffer *buffer;
2374                 BLOCK_WRAP();
2375                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2376                         strcrchomp(buf);
2377                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2378                 }
2379                 UNBLOCK_WRAP();
2380                 fclose(fp);
2381         }
2382         
2383         compose_attach_parts(compose, msginfo);
2384
2385         compose_colorize_signature(compose);
2386
2387         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2388                                         G_CALLBACK(compose_changed_cb),
2389                                         compose);
2390
2391         if (manual_headers != NULL) {
2392                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2393                         procheader_entries_free(manual_headers);
2394                         compose->updating = FALSE;
2395                         compose_destroy(compose);
2396                         return NULL;
2397                 }
2398                 procheader_entries_free(manual_headers);
2399         }
2400
2401         gtk_widget_grab_focus(compose->text);
2402
2403         if (prefs_common.auto_exteditor) {
2404                 compose_exec_ext_editor(compose);
2405         }
2406         compose->modified = FALSE;
2407         compose_set_title(compose);
2408
2409         compose->updating = FALSE;
2410         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2411         SCROLL_TO_CURSOR(compose);
2412
2413         if (compose->deferred_destroy) {
2414                 compose_destroy(compose);
2415                 return NULL;
2416         }
2417         
2418         compose->sig_str = account_get_signature_str(compose->account);
2419         
2420         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2421
2422         return compose;
2423 }
2424
2425 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2426                                                  gboolean batch)
2427 {
2428         Compose *compose;
2429         gchar *filename;
2430         FolderItem *item;
2431
2432         cm_return_val_if_fail(msginfo != NULL, NULL);
2433
2434         if (!account)
2435                 account = account_get_reply_account(msginfo,
2436                                         prefs_common.reply_account_autosel);
2437         cm_return_val_if_fail(account != NULL, NULL);
2438
2439         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2440
2441         compose->updating = TRUE;
2442
2443         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2444         compose->replyinfo = NULL;
2445         compose->fwdinfo = NULL;
2446
2447         compose_show_first_last_header(compose, TRUE);
2448
2449         gtk_widget_grab_focus(compose->header_last->entry);
2450
2451         filename = procmsg_get_message_file(msginfo);
2452
2453         if (filename == NULL) {
2454                 compose->updating = FALSE;
2455                 compose_destroy(compose);
2456
2457                 return NULL;
2458         }
2459
2460         compose->redirect_filename = filename;
2461         
2462         /* Set save folder */
2463         item = msginfo->folder;
2464         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2465                 gchar *folderidentifier;
2466
2467                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2468                 folderidentifier = folder_item_get_identifier(item);
2469                 compose_set_save_to(compose, folderidentifier);
2470                 g_free(folderidentifier);
2471         }
2472
2473         compose_attach_parts(compose, msginfo);
2474
2475         if (msginfo->subject)
2476                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2477                                    msginfo->subject);
2478         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2479
2480         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2481                                           _("The body of the \"Redirect\" template has an error at line %d."));
2482         quote_fmt_reset_vartable();
2483         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2484
2485         compose_colorize_signature(compose);
2486
2487         
2488         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2489         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2490         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2491
2492         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2493         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2494         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2495         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2496         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2497         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2498         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2499         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2500         
2501         if (compose->toolbar->draft_btn)
2502                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2503         if (compose->toolbar->insert_btn)
2504                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2505         if (compose->toolbar->attach_btn)
2506                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2507         if (compose->toolbar->sig_btn)
2508                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2509         if (compose->toolbar->exteditor_btn)
2510                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2511         if (compose->toolbar->linewrap_current_btn)
2512                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2513         if (compose->toolbar->linewrap_all_btn)
2514                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2515
2516         compose->modified = FALSE;
2517         compose_set_title(compose);
2518         compose->updating = FALSE;
2519         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2520         SCROLL_TO_CURSOR(compose);
2521
2522         if (compose->deferred_destroy) {
2523                 compose_destroy(compose);
2524                 return NULL;
2525         }
2526         
2527         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2528
2529         return compose;
2530 }
2531
2532 GList *compose_get_compose_list(void)
2533 {
2534         return compose_list;
2535 }
2536
2537 void compose_entry_append(Compose *compose, const gchar *address,
2538                           ComposeEntryType type, ComposePrefType pref_type)
2539 {
2540         const gchar *header;
2541         gchar *cur, *begin;
2542         gboolean in_quote = FALSE;
2543         if (!address || *address == '\0') return;
2544
2545         switch (type) {
2546         case COMPOSE_CC:
2547                 header = N_("Cc:");
2548                 break;
2549         case COMPOSE_BCC:
2550                 header = N_("Bcc:");
2551                 break;
2552         case COMPOSE_REPLYTO:
2553                 header = N_("Reply-To:");
2554                 break;
2555         case COMPOSE_NEWSGROUPS:
2556                 header = N_("Newsgroups:");
2557                 break;
2558         case COMPOSE_FOLLOWUPTO:
2559                 header = N_( "Followup-To:");
2560                 break;
2561         case COMPOSE_INREPLYTO:
2562                 header = N_( "In-Reply-To:");
2563                 break;
2564         case COMPOSE_TO:
2565         default:
2566                 header = N_("To:");
2567                 break;
2568         }
2569         header = prefs_common_translated_header_name(header);
2570         
2571         cur = begin = (gchar *)address;
2572         
2573         /* we separate the line by commas, but not if we're inside a quoted
2574          * string */
2575         while (*cur != '\0') {
2576                 if (*cur == '"') 
2577                         in_quote = !in_quote;
2578                 if (*cur == ',' && !in_quote) {
2579                         gchar *tmp = g_strdup(begin);
2580                         gchar *o_tmp = tmp;
2581                         tmp[cur-begin]='\0';
2582                         cur++;
2583                         begin = cur;
2584                         while (*tmp == ' ' || *tmp == '\t')
2585                                 tmp++;
2586                         compose_add_header_entry(compose, header, tmp, pref_type);
2587                         g_free(o_tmp);
2588                         continue;
2589                 }
2590                 cur++;
2591         }
2592         if (begin < cur) {
2593                 gchar *tmp = g_strdup(begin);
2594                 gchar *o_tmp = tmp;
2595                 tmp[cur-begin]='\0';
2596                 while (*tmp == ' ' || *tmp == '\t')
2597                         tmp++;
2598                 compose_add_header_entry(compose, header, tmp, pref_type);
2599                 g_free(o_tmp);          
2600         }
2601 }
2602
2603 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2604 {
2605 #if !GTK_CHECK_VERSION(3, 0, 0)
2606         static GdkColor yellow;
2607         static GdkColor black;
2608         static gboolean yellow_initialised = FALSE;
2609 #else
2610         static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2611         static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2612 #endif
2613         GSList *h_list;
2614         GtkEntry *entry;
2615                 
2616 #if !GTK_CHECK_VERSION(3, 0, 0)
2617         if (!yellow_initialised) {
2618                 gdk_color_parse("#f5f6be", &yellow);
2619                 gdk_color_parse("#000000", &black);
2620                 yellow_initialised = gdk_colormap_alloc_color(
2621                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2622                 yellow_initialised &= gdk_colormap_alloc_color(
2623                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2624         }
2625 #endif
2626
2627         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2628                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2629                 if (gtk_entry_get_text(entry) && 
2630                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2631 #if !GTK_CHECK_VERSION(3, 0, 0)
2632                         if (yellow_initialised) {
2633 #endif
2634                                 gtk_widget_modify_base(
2635                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2636                                         GTK_STATE_NORMAL, &yellow);
2637                                 gtk_widget_modify_text(
2638                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2639                                         GTK_STATE_NORMAL, &black);
2640 #if !GTK_CHECK_VERSION(3, 0, 0)
2641                         }
2642 #endif
2643                 }
2644         }
2645 }
2646
2647 void compose_toolbar_cb(gint action, gpointer data)
2648 {
2649         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2650         Compose *compose = (Compose*)toolbar_item->parent;
2651         
2652         cm_return_if_fail(compose != NULL);
2653
2654         switch(action) {
2655         case A_SEND:
2656                 compose_send_cb(NULL, compose);
2657                 break;
2658         case A_SENDL:
2659                 compose_send_later_cb(NULL, compose);
2660                 break;
2661         case A_DRAFT:
2662                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2663                 break;
2664         case A_INSERT:
2665                 compose_insert_file_cb(NULL, compose);
2666                 break;
2667         case A_ATTACH:
2668                 compose_attach_cb(NULL, compose);
2669                 break;
2670         case A_SIG:
2671                 compose_insert_sig(compose, FALSE);
2672                 break;
2673         case A_EXTEDITOR:
2674                 compose_ext_editor_cb(NULL, compose);
2675                 break;
2676         case A_LINEWRAP_CURRENT:
2677                 compose_beautify_paragraph(compose, NULL, TRUE);
2678                 break;
2679         case A_LINEWRAP_ALL:
2680                 compose_wrap_all_full(compose, TRUE);
2681                 break;
2682         case A_ADDRBOOK:
2683                 compose_address_cb(NULL, compose);
2684                 break;
2685 #ifdef USE_ENCHANT
2686         case A_CHECK_SPELLING:
2687                 compose_check_all(NULL, compose);
2688                 break;
2689 #endif
2690         default:
2691                 break;
2692         }
2693 }
2694
2695 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2696 {
2697         gchar *to = NULL;
2698         gchar *cc = NULL;
2699         gchar *bcc = NULL;
2700         gchar *subject = NULL;
2701         gchar *body = NULL;
2702         gchar *temp = NULL;
2703         gsize  len = 0;
2704         gchar **attach = NULL;
2705         gchar *inreplyto = NULL;
2706         MailField mfield = NO_FIELD_PRESENT;
2707
2708         /* get mailto parts but skip from */
2709         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2710
2711         if (to) {
2712                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2713                 mfield = TO_FIELD_PRESENT;
2714         }
2715         if (cc)
2716                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2717         if (bcc)
2718                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2719         if (subject) {
2720                 if (!g_utf8_validate (subject, -1, NULL)) {
2721                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2722                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2723                         g_free(temp);
2724                 } else {
2725                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2726                 }
2727                 mfield = SUBJECT_FIELD_PRESENT;
2728         }
2729         if (body) {
2730                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2731                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2732                 GtkTextMark *mark;
2733                 GtkTextIter iter;
2734                 gboolean prev_autowrap = compose->autowrap;
2735
2736                 compose->autowrap = FALSE;
2737
2738                 mark = gtk_text_buffer_get_insert(buffer);
2739                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2740
2741                 if (!g_utf8_validate (body, -1, NULL)) {
2742                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2743                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2744                         g_free(temp);
2745                 } else {
2746                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2747                 }
2748                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2749
2750                 compose->autowrap = prev_autowrap;
2751                 if (compose->autowrap)
2752                         compose_wrap_all(compose);
2753                 mfield = BODY_FIELD_PRESENT;
2754         }
2755
2756         if (attach) {
2757                 gint i = 0, att = 0;
2758                 gchar *warn_files = NULL;
2759                 while (attach[i] != NULL) {
2760                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2761                         if (utf8_filename) {
2762                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2763                                         gchar *tmp = g_strdup_printf("%s%s\n",
2764                                                         warn_files?warn_files:"",
2765                                                         utf8_filename);
2766                                         g_free(warn_files);
2767                                         warn_files = tmp;
2768                                         att++;
2769                                 }
2770                                 g_free(utf8_filename);
2771                         } else {
2772                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2773                         }
2774                         i++;
2775                 }
2776                 if (warn_files) {
2777                         alertpanel_notice(ngettext(
2778                         "The following file has been attached: \n%s",
2779                         "The following files have been attached: \n%s", att), warn_files);
2780                         g_free(warn_files);
2781                 }
2782         }
2783         if (inreplyto)
2784                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2785
2786         g_free(to);
2787         g_free(cc);
2788         g_free(bcc);
2789         g_free(subject);
2790         g_free(body);
2791         g_strfreev(attach);
2792         g_free(inreplyto);
2793         
2794         return mfield;
2795 }
2796
2797 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2798 {
2799         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2800                                        {"Cc:",          NULL, TRUE},
2801                                        {"References:",  NULL, FALSE},
2802                                        {"Bcc:",         NULL, TRUE},
2803                                        {"Newsgroups:",  NULL, TRUE},
2804                                        {"Followup-To:", NULL, TRUE},
2805                                        {"List-Post:",   NULL, FALSE},
2806                                        {"X-Priority:",  NULL, FALSE},
2807                                        {NULL,           NULL, FALSE}};
2808
2809         enum
2810         {
2811                 H_REPLY_TO      = 0,
2812                 H_CC            = 1,
2813                 H_REFERENCES    = 2,
2814                 H_BCC           = 3,
2815                 H_NEWSGROUPS    = 4,
2816                 H_FOLLOWUP_TO   = 5,
2817                 H_LIST_POST     = 6,
2818                 H_X_PRIORITY    = 7
2819         };
2820
2821         FILE *fp;
2822
2823         cm_return_val_if_fail(msginfo != NULL, -1);
2824
2825         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2826         procheader_get_header_fields(fp, hentry);
2827         fclose(fp);
2828
2829         if (hentry[H_REPLY_TO].body != NULL) {
2830                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2831                         compose->replyto =
2832                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2833                                                    NULL, TRUE);
2834                 }
2835                 g_free(hentry[H_REPLY_TO].body);
2836                 hentry[H_REPLY_TO].body = NULL;
2837         }
2838         if (hentry[H_CC].body != NULL) {
2839                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2840                 g_free(hentry[H_CC].body);
2841                 hentry[H_CC].body = NULL;
2842         }
2843         if (hentry[H_REFERENCES].body != NULL) {
2844                 if (compose->mode == COMPOSE_REEDIT)
2845                         compose->references = hentry[H_REFERENCES].body;
2846                 else {
2847                         compose->references = compose_parse_references
2848                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2849                         g_free(hentry[H_REFERENCES].body);
2850                 }
2851                 hentry[H_REFERENCES].body = NULL;
2852         }
2853         if (hentry[H_BCC].body != NULL) {
2854                 if (compose->mode == COMPOSE_REEDIT)
2855                         compose->bcc =
2856                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2857                 g_free(hentry[H_BCC].body);
2858                 hentry[H_BCC].body = NULL;
2859         }
2860         if (hentry[H_NEWSGROUPS].body != NULL) {
2861                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2862                 hentry[H_NEWSGROUPS].body = NULL;
2863         }
2864         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2865                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2866                         compose->followup_to =
2867                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2868                                                    NULL, TRUE);
2869                 }
2870                 g_free(hentry[H_FOLLOWUP_TO].body);
2871                 hentry[H_FOLLOWUP_TO].body = NULL;
2872         }
2873         if (hentry[H_LIST_POST].body != NULL) {
2874                 gchar *to = NULL, *start = NULL;
2875
2876                 extract_address(hentry[H_LIST_POST].body);
2877                 if (hentry[H_LIST_POST].body[0] != '\0') {
2878                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2879                         
2880                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2881                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2882
2883                         if (to) {
2884                                 g_free(compose->ml_post);
2885                                 compose->ml_post = to;
2886                         }
2887                 }
2888                 g_free(hentry[H_LIST_POST].body);
2889                 hentry[H_LIST_POST].body = NULL;
2890         }
2891
2892         /* CLAWS - X-Priority */
2893         if (compose->mode == COMPOSE_REEDIT)
2894                 if (hentry[H_X_PRIORITY].body != NULL) {
2895                         gint priority;
2896                         
2897                         priority = atoi(hentry[H_X_PRIORITY].body);
2898                         g_free(hentry[H_X_PRIORITY].body);
2899                         
2900                         hentry[H_X_PRIORITY].body = NULL;
2901                         
2902                         if (priority < PRIORITY_HIGHEST || 
2903                             priority > PRIORITY_LOWEST)
2904                                 priority = PRIORITY_NORMAL;
2905                         
2906                         compose->priority =  priority;
2907                 }
2908  
2909         if (compose->mode == COMPOSE_REEDIT) {
2910                 if (msginfo->inreplyto && *msginfo->inreplyto)
2911                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2912                 return 0;
2913         }
2914
2915         if (msginfo->msgid && *msginfo->msgid)
2916                 compose->inreplyto = g_strdup(msginfo->msgid);
2917
2918         if (!compose->references) {
2919                 if (msginfo->msgid && *msginfo->msgid) {
2920                         if (msginfo->inreplyto && *msginfo->inreplyto)
2921                                 compose->references =
2922                                         g_strdup_printf("<%s>\n\t<%s>",
2923                                                         msginfo->inreplyto,
2924                                                         msginfo->msgid);
2925                         else
2926                                 compose->references =
2927                                         g_strconcat("<", msginfo->msgid, ">",
2928                                                     NULL);
2929                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2930                         compose->references =
2931                                 g_strconcat("<", msginfo->inreplyto, ">",
2932                                             NULL);
2933                 }
2934         }
2935
2936         return 0;
2937 }
2938
2939 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2940 {
2941         FILE *fp;
2942         HeaderEntry *he;
2943
2944         cm_return_val_if_fail(msginfo != NULL, -1);
2945
2946         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2947         procheader_get_header_fields(fp, entries);
2948         fclose(fp);
2949
2950         he = entries;
2951         while (he != NULL && he->name != NULL) {
2952                 GtkTreeIter iter;
2953                 GtkListStore *model = NULL;
2954
2955                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2956                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2957                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2958                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2959                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2960                 ++he;
2961         }
2962
2963         return 0;
2964 }
2965
2966 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2967 {
2968         GSList *ref_id_list, *cur;
2969         GString *new_ref;
2970         gchar *new_ref_str;
2971
2972         ref_id_list = references_list_append(NULL, ref);
2973         if (!ref_id_list) return NULL;
2974         if (msgid && *msgid)
2975                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2976
2977         for (;;) {
2978                 gint len = 0;
2979
2980                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2981                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2982                         len += strlen((gchar *)cur->data) + 5;
2983
2984                 if (len > MAX_REFERENCES_LEN) {
2985                         /* remove second message-ID */
2986                         if (ref_id_list && ref_id_list->next &&
2987                             ref_id_list->next->next) {
2988                                 g_free(ref_id_list->next->data);
2989                                 ref_id_list = g_slist_remove
2990                                         (ref_id_list, ref_id_list->next->data);
2991                         } else {
2992                                 slist_free_strings_full(ref_id_list);
2993                                 return NULL;
2994                         }
2995                 } else
2996                         break;
2997         }
2998
2999         new_ref = g_string_new("");
3000         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3001                 if (new_ref->len > 0)
3002                         g_string_append(new_ref, "\n\t");
3003                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3004         }
3005
3006         slist_free_strings_full(ref_id_list);
3007
3008         new_ref_str = new_ref->str;
3009         g_string_free(new_ref, FALSE);
3010
3011         return new_ref_str;
3012 }
3013
3014 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3015                                 const gchar *fmt, const gchar *qmark,
3016                                 const gchar *body, gboolean rewrap,
3017                                 gboolean need_unescape,
3018                                 const gchar *err_msg)
3019 {
3020         MsgInfo* dummyinfo = NULL;
3021         gchar *quote_str = NULL;
3022         gchar *buf;
3023         gboolean prev_autowrap;
3024         const gchar *trimmed_body = body;
3025         gint cursor_pos = -1;
3026         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3027         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3028         GtkTextIter iter;
3029         GtkTextMark *mark;
3030         
3031
3032         SIGNAL_BLOCK(buffer);
3033
3034         if (!msginfo) {
3035                 dummyinfo = compose_msginfo_new_from_compose(compose);
3036                 msginfo = dummyinfo;
3037         }
3038
3039         if (qmark != NULL) {
3040 #ifdef USE_ENCHANT
3041                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3042                                 compose->gtkaspell);
3043 #else
3044                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3045 #endif
3046                 quote_fmt_scan_string(qmark);
3047                 quote_fmt_parse();
3048
3049                 buf = quote_fmt_get_buffer();
3050                 if (buf == NULL)
3051                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3052                 else
3053                         Xstrdup_a(quote_str, buf, goto error)
3054         }
3055
3056         if (fmt && *fmt != '\0') {
3057
3058                 if (trimmed_body)
3059                         while (*trimmed_body == '\n')
3060                                 trimmed_body++;
3061
3062 #ifdef USE_ENCHANT
3063                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3064                                 compose->gtkaspell);
3065 #else
3066                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3067 #endif
3068                 if (need_unescape) {
3069                         gchar *tmp = NULL;
3070
3071                         /* decode \-escape sequences in the internal representation of the quote format */
3072                         tmp = g_malloc(strlen(fmt)+1);
3073                         pref_get_unescaped_pref(tmp, fmt);
3074                         quote_fmt_scan_string(tmp);
3075                         quote_fmt_parse();
3076                         g_free(tmp);
3077                 } else {
3078                         quote_fmt_scan_string(fmt);
3079                         quote_fmt_parse();
3080                 }
3081
3082                 buf = quote_fmt_get_buffer();
3083                 if (buf == NULL) {
3084                         gint line = quote_fmt_get_line();
3085                         alertpanel_error(err_msg, line);
3086                         goto error;
3087                 }
3088         } else
3089                 buf = "";
3090
3091         prev_autowrap = compose->autowrap;
3092         compose->autowrap = FALSE;
3093
3094         mark = gtk_text_buffer_get_insert(buffer);
3095         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3096         if (g_utf8_validate(buf, -1, NULL)) { 
3097                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3098         } else {
3099                 gchar *tmpout = NULL;
3100                 tmpout = conv_codeset_strdup
3101                         (buf, conv_get_locale_charset_str_no_utf8(),
3102                          CS_INTERNAL);
3103                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3104                         g_free(tmpout);
3105                         tmpout = g_malloc(strlen(buf)*2+1);
3106                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3107                 }
3108                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3109                 g_free(tmpout);
3110         }
3111
3112         cursor_pos = quote_fmt_get_cursor_pos();
3113         if (cursor_pos == -1)
3114                 cursor_pos = gtk_text_iter_get_offset(&iter);
3115         compose->set_cursor_pos = cursor_pos;
3116
3117         gtk_text_buffer_get_start_iter(buffer, &iter);
3118         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3119         gtk_text_buffer_place_cursor(buffer, &iter);
3120
3121         compose->autowrap = prev_autowrap;
3122         if (compose->autowrap && rewrap)
3123                 compose_wrap_all(compose);
3124
3125         goto ok;
3126
3127 error:
3128         buf = NULL;
3129 ok:
3130         SIGNAL_UNBLOCK(buffer);
3131
3132         procmsg_msginfo_free( dummyinfo );
3133
3134         return buf;
3135 }
3136
3137 /* if ml_post is of type addr@host and from is of type
3138  * addr-anything@host, return TRUE
3139  */
3140 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3141 {
3142         gchar *left_ml = NULL;
3143         gchar *right_ml = NULL;
3144         gchar *left_from = NULL;
3145         gchar *right_from = NULL;
3146         gboolean result = FALSE;
3147         
3148         if (!ml_post || !from)
3149                 return FALSE;
3150         
3151         left_ml = g_strdup(ml_post);
3152         if (strstr(left_ml, "@")) {
3153                 right_ml = strstr(left_ml, "@")+1;
3154                 *(strstr(left_ml, "@")) = '\0';
3155         }
3156         
3157         left_from = g_strdup(from);
3158         if (strstr(left_from, "@")) {
3159                 right_from = strstr(left_from, "@")+1;
3160                 *(strstr(left_from, "@")) = '\0';
3161         }
3162         
3163         if (left_ml && left_from && right_ml && right_from
3164         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3165         &&  !strcmp(right_from, right_ml)) {
3166                 result = TRUE;
3167         }
3168         g_free(left_ml);
3169         g_free(left_from);
3170         
3171         return result;
3172 }
3173
3174 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3175                                      gboolean respect_default_to)
3176 {
3177         if (!compose)
3178                 return;
3179         if (!folder || !folder->prefs)
3180                 return;
3181
3182         if (respect_default_to && folder->prefs->enable_default_to) {
3183                 compose_entry_append(compose, folder->prefs->default_to,
3184                                         COMPOSE_TO, PREF_FOLDER);
3185                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3186         }
3187         if (folder->prefs->enable_default_cc)
3188                 compose_entry_append(compose, folder->prefs->default_cc,
3189                                         COMPOSE_CC, PREF_FOLDER);
3190         if (folder->prefs->enable_default_bcc)
3191                 compose_entry_append(compose, folder->prefs->default_bcc,
3192                                         COMPOSE_BCC, PREF_FOLDER);
3193         if (folder->prefs->enable_default_replyto)
3194                 compose_entry_append(compose, folder->prefs->default_replyto,
3195                                         COMPOSE_REPLYTO, PREF_FOLDER);
3196 }
3197
3198 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3199 {
3200         gchar *buf, *buf2;
3201         gchar *p;
3202         
3203         if (!compose || !msginfo)
3204                 return;
3205
3206         if (msginfo->subject && *msginfo->subject) {
3207                 buf = p = g_strdup(msginfo->subject);
3208                 p += subject_get_prefix_length(p);
3209                 memmove(buf, p, strlen(p) + 1);
3210
3211                 buf2 = g_strdup_printf("Re: %s", buf);
3212                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3213
3214                 g_free(buf2);
3215                 g_free(buf);
3216         } else
3217                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3218 }
3219
3220 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3221                                     gboolean to_all, gboolean to_ml,
3222                                     gboolean to_sender,
3223                                     gboolean followup_and_reply_to)
3224 {
3225         GSList *cc_list = NULL;
3226         GSList *cur;
3227         gchar *from = NULL;
3228         gchar *replyto = NULL;
3229         gchar *ac_email = NULL;
3230
3231         gboolean reply_to_ml = FALSE;
3232         gboolean default_reply_to = FALSE;
3233
3234         cm_return_if_fail(compose->account != NULL);
3235         cm_return_if_fail(msginfo != NULL);
3236
3237         reply_to_ml = to_ml && compose->ml_post;
3238
3239         default_reply_to = msginfo->folder && 
3240                 msginfo->folder->prefs->enable_default_reply_to;
3241
3242         if (compose->account->protocol != A_NNTP) {
3243                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3244
3245                 if (reply_to_ml && !default_reply_to) {
3246                         
3247                         gboolean is_subscr = is_subscription(compose->ml_post,
3248                                                              msginfo->from);
3249                         if (!is_subscr) {
3250                                 /* normal answer to ml post with a reply-to */
3251                                 compose_entry_append(compose,
3252                                            compose->ml_post,
3253                                            COMPOSE_TO, PREF_ML);
3254                                 if (compose->replyto)
3255                                         compose_entry_append(compose,
3256                                                 compose->replyto,
3257                                                 COMPOSE_CC, PREF_ML);
3258                         } else {
3259                                 /* answer to subscription confirmation */
3260                                 if (compose->replyto)
3261                                         compose_entry_append(compose,
3262                                                 compose->replyto,
3263                                                 COMPOSE_TO, PREF_ML);
3264                                 else if (msginfo->from)
3265                                         compose_entry_append(compose,
3266                                                 msginfo->from,
3267                                                 COMPOSE_TO, PREF_ML);
3268                         }
3269                 }
3270                 else if (!(to_all || to_sender) && default_reply_to) {
3271                         compose_entry_append(compose,
3272                             msginfo->folder->prefs->default_reply_to,
3273                             COMPOSE_TO, PREF_FOLDER);
3274                         compose_entry_mark_default_to(compose,
3275                                 msginfo->folder->prefs->default_reply_to);
3276                 } else {
3277                         gchar *tmp1 = NULL;
3278                         if (!msginfo->from)
3279                                 return;
3280                         Xstrdup_a(tmp1, msginfo->from, return);
3281                         extract_address(tmp1);
3282                         if (to_all || to_sender ||
3283                             !account_find_from_address(tmp1, FALSE))
3284                                 compose_entry_append(compose,
3285                                  (compose->replyto && !to_sender)
3286                                           ? compose->replyto :
3287                                           msginfo->from ? msginfo->from : "",
3288                                           COMPOSE_TO, PREF_NONE);
3289                         else if (!to_all && !to_sender) {
3290                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3291                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3292                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3293                                         if (compose->replyto) {
3294                                                 compose_entry_append(compose,
3295                                                         compose->replyto,
3296                                                         COMPOSE_TO, PREF_NONE);
3297                                         } else {
3298                                                 compose_entry_append(compose,
3299                                                           msginfo->from ? msginfo->from : "",
3300                                                           COMPOSE_TO, PREF_NONE);
3301                                         }
3302                                 } else {
3303                                         /* replying to own mail, use original recp */
3304                                         compose_entry_append(compose,
3305                                                   msginfo->to ? msginfo->to : "",
3306                                                   COMPOSE_TO, PREF_NONE);
3307                                         compose_entry_append(compose,
3308                                                   msginfo->cc ? msginfo->cc : "",
3309                                                   COMPOSE_CC, PREF_NONE);
3310                                 }
3311                         }
3312                 }
3313         } else {
3314                 if (to_sender || (compose->followup_to && 
3315                         !strncmp(compose->followup_to, "poster", 6)))
3316                         compose_entry_append
3317                                 (compose, 
3318                                  (compose->replyto ? compose->replyto :
3319                                         msginfo->from ? msginfo->from : ""),
3320                                  COMPOSE_TO, PREF_NONE);
3321                                  
3322                 else if (followup_and_reply_to || to_all) {
3323                         compose_entry_append
3324                                 (compose,
3325                                  (compose->replyto ? compose->replyto :
3326                                  msginfo->from ? msginfo->from : ""),
3327                                  COMPOSE_TO, PREF_NONE);                                
3328                 
3329                         compose_entry_append
3330                                 (compose,
3331                                  compose->followup_to ? compose->followup_to :
3332                                  compose->newsgroups ? compose->newsgroups : "",
3333                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3334                 } 
3335                 else 
3336                         compose_entry_append
3337                                 (compose,
3338                                  compose->followup_to ? compose->followup_to :
3339                                  compose->newsgroups ? compose->newsgroups : "",
3340                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3341         }
3342         compose_reply_set_subject(compose, msginfo);
3343
3344         if (to_ml && compose->ml_post) return;
3345         if (!to_all || compose->account->protocol == A_NNTP) return;
3346
3347         if (compose->replyto) {
3348                 Xstrdup_a(replyto, compose->replyto, return);
3349                 extract_address(replyto);
3350         }
3351         if (msginfo->from) {
3352                 Xstrdup_a(from, msginfo->from, return);
3353                 extract_address(from);
3354         }
3355
3356         if (replyto && from)
3357                 cc_list = address_list_append_with_comments(cc_list, from);
3358         if (to_all && msginfo->folder && 
3359             msginfo->folder->prefs->enable_default_reply_to)
3360                 cc_list = address_list_append_with_comments(cc_list,
3361                                 msginfo->folder->prefs->default_reply_to);
3362         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3363         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3364
3365         ac_email = g_utf8_strdown(compose->account->address, -1);
3366
3367         if (cc_list) {
3368                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3369                         gchar *addr = g_utf8_strdown(cur->data, -1);
3370                         extract_address(addr);
3371                 
3372                         if (strcmp(ac_email, addr))
3373                                 compose_entry_append(compose, (gchar *)cur->data,
3374                                                      COMPOSE_CC, PREF_NONE);
3375                         else
3376                                 debug_print("Cc address same as compose account's, ignoring\n");
3377
3378                         g_free(addr);
3379                 }
3380                 
3381                 slist_free_strings_full(cc_list);
3382         }
3383         
3384         g_free(ac_email);
3385 }
3386
3387 #define SET_ENTRY(entry, str) \
3388 { \
3389         if (str && *str) \
3390                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3391 }
3392
3393 #define SET_ADDRESS(type, str) \
3394 { \
3395         if (str && *str) \
3396                 compose_entry_append(compose, str, type, PREF_NONE); \
3397 }
3398
3399 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3400 {
3401         cm_return_if_fail(msginfo != NULL);
3402
3403         SET_ENTRY(subject_entry, msginfo->subject);
3404         SET_ENTRY(from_name, msginfo->from);
3405         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3406         SET_ADDRESS(COMPOSE_CC, compose->cc);
3407         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3408         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3409         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3410         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3411
3412         compose_update_priority_menu_item(compose);
3413         compose_update_privacy_system_menu_item(compose, FALSE);
3414         compose_show_first_last_header(compose, TRUE);
3415 }
3416
3417 #undef SET_ENTRY
3418 #undef SET_ADDRESS
3419
3420 static void compose_insert_sig(Compose *compose, gboolean replace)
3421 {
3422         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3423         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3424         GtkTextMark *mark;
3425         GtkTextIter iter, iter_end;
3426         gint cur_pos, ins_pos;
3427         gboolean prev_autowrap;
3428         gboolean found = FALSE;
3429         gboolean exists = FALSE;
3430         
3431         cm_return_if_fail(compose->account != NULL);
3432
3433         BLOCK_WRAP();
3434
3435         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3436                                         G_CALLBACK(compose_changed_cb),
3437                                         compose);
3438         
3439         mark = gtk_text_buffer_get_insert(buffer);
3440         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3441         cur_pos = gtk_text_iter_get_offset (&iter);
3442         ins_pos = cur_pos;
3443
3444         gtk_text_buffer_get_end_iter(buffer, &iter);
3445
3446         exists = (compose->sig_str != NULL);
3447
3448         if (replace) {
3449                 GtkTextIter first_iter, start_iter, end_iter;
3450
3451                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3452
3453                 if (!exists || compose->sig_str[0] == '\0')
3454                         found = FALSE;
3455                 else
3456                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3457                                         compose->signature_tag);
3458
3459                 if (found) {
3460                         /* include previous \n\n */
3461                         gtk_text_iter_backward_chars(&first_iter, 1);
3462                         start_iter = first_iter;
3463                         end_iter = first_iter;
3464                         /* skip re-start */
3465                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3466                                         compose->signature_tag);
3467                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3468                                         compose->signature_tag);
3469                         if (found) {
3470                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3471                                 iter = start_iter;
3472                         }
3473                 } 
3474         } 
3475
3476         g_free(compose->sig_str);
3477         compose->sig_str = account_get_signature_str(compose->account);
3478
3479         cur_pos = gtk_text_iter_get_offset(&iter);
3480
3481         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3482                 g_free(compose->sig_str);
3483                 compose->sig_str = NULL;
3484         } else {
3485                 if (compose->sig_inserted == FALSE)
3486                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3487                 compose->sig_inserted = TRUE;
3488
3489                 cur_pos = gtk_text_iter_get_offset(&iter);
3490                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3491                 /* remove \n\n */
3492                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3493                 gtk_text_iter_forward_chars(&iter, 1);
3494                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3495                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3496
3497                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3498                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3499         }
3500
3501         /* put the cursor where it should be 
3502          * either where the quote_fmt says, either where it was */
3503         if (compose->set_cursor_pos < 0)
3504                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3505         else
3506                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3507                         compose->set_cursor_pos);
3508         
3509         compose->set_cursor_pos = -1;
3510         gtk_text_buffer_place_cursor(buffer, &iter);
3511         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3512                                         G_CALLBACK(compose_changed_cb),
3513                                         compose);
3514                 
3515         UNBLOCK_WRAP();
3516 }
3517
3518 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3519 {
3520         GtkTextView *text;
3521         GtkTextBuffer *buffer;
3522         GtkTextMark *mark;
3523         GtkTextIter iter;
3524         const gchar *cur_encoding;
3525         gchar buf[BUFFSIZE];
3526         gint len;
3527         FILE *fp;
3528         gboolean prev_autowrap;
3529         gboolean badtxt = FALSE;
3530         struct stat file_stat;
3531         int ret;
3532         GString *file_contents = NULL;
3533
3534         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3535
3536         /* get the size of the file we are about to insert */
3537         ret = g_stat(file, &file_stat);
3538         if (ret != 0) {
3539                 gchar *shortfile = g_path_get_basename(file);
3540                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3541                 g_free(shortfile);
3542                 return COMPOSE_INSERT_NO_FILE;
3543         } else if (prefs_common.warn_large_insert == TRUE) {
3544
3545                 /* ask user for confirmation if the file is large */
3546                 if (prefs_common.warn_large_insert_size < 0 ||
3547                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3548                         AlertValue aval;
3549                         gchar *msg;
3550
3551                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3552                                                 "in the message body. Are you sure you want to do that?"),
3553                                                 to_human_readable(file_stat.st_size));
3554                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3555                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3556                         g_free(msg);
3557
3558                         /* do we ask for confirmation next time? */
3559                         if (aval & G_ALERTDISABLE) {
3560                                 /* no confirmation next time, disable feature in preferences */
3561                                 aval &= ~G_ALERTDISABLE;
3562                                 prefs_common.warn_large_insert = FALSE;
3563                         }
3564
3565                         /* abort file insertion if user canceled action */
3566                         if (aval != G_ALERTALTERNATE) {
3567                                 return COMPOSE_INSERT_NO_FILE;
3568                         }
3569                 }
3570         }
3571
3572
3573         if ((fp = g_fopen(file, "rb")) == NULL) {
3574                 FILE_OP_ERROR(file, "fopen");
3575                 return COMPOSE_INSERT_READ_ERROR;
3576         }
3577
3578         prev_autowrap = compose->autowrap;
3579         compose->autowrap = FALSE;
3580
3581         text = GTK_TEXT_VIEW(compose->text);
3582         buffer = gtk_text_view_get_buffer(text);
3583         mark = gtk_text_buffer_get_insert(buffer);
3584         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3585
3586         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3587                                         G_CALLBACK(text_inserted),
3588                                         compose);
3589
3590         cur_encoding = conv_get_locale_charset_str_no_utf8();
3591
3592         file_contents = g_string_new("");
3593         while (fgets(buf, sizeof(buf), fp) != NULL) {
3594                 gchar *str;
3595
3596                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3597                         str = g_strdup(buf);
3598                 else
3599                         str = conv_codeset_strdup
3600                                 (buf, cur_encoding, CS_INTERNAL);
3601                 if (!str) continue;
3602
3603                 /* strip <CR> if DOS/Windows file,
3604                    replace <CR> with <LF> if Macintosh file. */
3605                 strcrchomp(str);
3606                 len = strlen(str);
3607                 if (len > 0 && str[len - 1] != '\n') {
3608                         while (--len >= 0)
3609                                 if (str[len] == '\r') str[len] = '\n';
3610                 }
3611
3612                 file_contents = g_string_append(file_contents, str);
3613                 g_free(str);
3614         }
3615
3616         gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3617         g_string_free(file_contents, TRUE);
3618
3619         compose_changed_cb(NULL, compose);
3620         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3621                                           G_CALLBACK(text_inserted),
3622                                           compose);
3623         compose->autowrap = prev_autowrap;
3624         if (compose->autowrap)
3625                 compose_wrap_all(compose);
3626
3627         fclose(fp);
3628
3629         if (badtxt)
3630                 return COMPOSE_INSERT_INVALID_CHARACTER;
3631         else 
3632                 return COMPOSE_INSERT_SUCCESS;
3633 }
3634
3635 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3636                                   const gchar *filename,
3637                                   const gchar *content_type,
3638                                   const gchar *charset)
3639 {
3640         AttachInfo *ainfo;
3641         GtkTreeIter iter;
3642         FILE *fp;
3643         off_t size;
3644         GAuto *auto_ainfo;
3645         gchar *size_text;
3646         GtkListStore *store;
3647         gchar *name;
3648         gboolean has_binary = FALSE;
3649
3650         if (!is_file_exist(file)) {
3651                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3652                 gboolean result = FALSE;
3653                 if (file_from_uri && is_file_exist(file_from_uri)) {
3654                         result = compose_attach_append(
3655                                                 compose, file_from_uri,
3656                                                 filename, content_type,
3657                                                 charset);
3658                 }
3659                 g_free(file_from_uri);
3660                 if (result)
3661                         return TRUE;
3662                 alertpanel_error("File %s doesn't exist\n", filename);
3663                 return FALSE;
3664         }
3665         if ((size = get_file_size(file)) < 0) {
3666                 alertpanel_error("Can't get file size of %s\n", filename);
3667                 return FALSE;
3668         }
3669         if (size == 0) {
3670                 alertpanel_error(_("File %s is empty."), filename);
3671                 return FALSE;
3672         }
3673         if ((fp = g_fopen(file, "rb")) == NULL) {
3674          &n