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