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