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