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