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