automatically use external editor on fwd-as-attachment if option is set
[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         if (prefs_common.auto_exteditor)
2073                 compose_exec_ext_editor(compose);
2074
2075         gtk_widget_grab_focus(compose->header_last->entry);
2076         undo_unblock(compose->undostruct);
2077         compose->modified = FALSE;
2078         compose_set_title(compose);
2079
2080         compose->updating = FALSE;
2081         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2082         SCROLL_TO_CURSOR(compose);
2083
2084         if (compose->deferred_destroy) {
2085                 compose_destroy(compose);
2086                 return NULL;
2087         }
2088
2089         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2090
2091         return compose;
2092 }
2093
2094 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2095 {
2096         GtkTextIter start = *iter;
2097         GtkTextIter end_iter;
2098         int start_pos = gtk_text_iter_get_offset(&start);
2099         gchar *str = NULL;
2100         if (!compose->account->sig_sep)
2101                 return FALSE;
2102         
2103         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2104                 start_pos+strlen(compose->account->sig_sep));
2105
2106         /* check sig separator */
2107         str = gtk_text_iter_get_text(&start, &end_iter);
2108         if (!strcmp(str, compose->account->sig_sep)) {
2109                 gchar *tmp = NULL;
2110                 /* check end of line (\n) */
2111                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2112                         start_pos+strlen(compose->account->sig_sep));
2113                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2114                         start_pos+strlen(compose->account->sig_sep)+1);
2115                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2116                 if (!strcmp(tmp,"\n")) {
2117                         g_free(str);
2118                         g_free(tmp);
2119                         return TRUE;
2120                 }
2121                 g_free(tmp);    
2122         }
2123         g_free(str);
2124
2125         return FALSE;
2126 }
2127
2128 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2129 {
2130         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2131         Compose *compose = (Compose *)data;
2132         FolderItem *old_item = NULL;
2133         FolderItem *new_item = NULL;
2134         gchar *old_id, *new_id;
2135
2136         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2137          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2138                 return FALSE;
2139
2140         old_item = hookdata->item;
2141         new_item = hookdata->item2;
2142
2143         old_id = folder_item_get_identifier(old_item);
2144         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2145
2146         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2147                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2148                 compose->targetinfo->folder = new_item;
2149         }
2150
2151         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2152                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2153                 compose->replyinfo->folder = new_item;
2154         }
2155
2156         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2157                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2158                 compose->fwdinfo->folder = new_item;
2159         }
2160
2161         g_free(old_id);
2162         g_free(new_id);
2163         return FALSE;
2164 }
2165
2166 static void compose_colorize_signature(Compose *compose)
2167 {
2168         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2169         GtkTextIter iter;
2170         GtkTextIter end_iter;
2171         gtk_text_buffer_get_start_iter(buffer, &iter);
2172         while (gtk_text_iter_forward_line(&iter))
2173                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2174                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2175                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2176                 }
2177 }
2178
2179 #define BLOCK_WRAP() {                                                  \
2180         prev_autowrap = compose->autowrap;                              \
2181         buffer = gtk_text_view_get_buffer(                              \
2182                                         GTK_TEXT_VIEW(compose->text));  \
2183         compose->autowrap = FALSE;                                      \
2184                                                                         \
2185         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2186                                 G_CALLBACK(compose_changed_cb),         \
2187                                 compose);                               \
2188         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2189                                 G_CALLBACK(text_inserted),              \
2190                                 compose);                               \
2191 }
2192 #define UNBLOCK_WRAP() {                                                        \
2193         compose->autowrap = prev_autowrap;                                      \
2194         if (compose->autowrap) {                                                \
2195                 gint old = compose->draft_timeout_tag;                          \
2196                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2197                 compose_wrap_all(compose);                                      \
2198                 compose->draft_timeout_tag = old;                               \
2199         }                                                                       \
2200                                                                                 \
2201         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2202                                 G_CALLBACK(compose_changed_cb),                 \
2203                                 compose);                                       \
2204         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2205                                 G_CALLBACK(text_inserted),                      \
2206                                 compose);                                       \
2207 }
2208
2209 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2210 {
2211         Compose *compose = NULL;
2212         PrefsAccount *account = NULL;
2213         GtkTextView *textview;
2214         GtkTextBuffer *textbuf;
2215         GtkTextMark *mark;
2216         GtkTextIter iter;
2217         FILE *fp;
2218         gchar buf[BUFFSIZE];
2219         gboolean use_signing = FALSE;
2220         gboolean use_encryption = FALSE;
2221         gchar *privacy_system = NULL;
2222         int priority = PRIORITY_NORMAL;
2223         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2224         gboolean autowrap = prefs_common.autowrap;
2225         gboolean autoindent = prefs_common.auto_indent;
2226         HeaderEntry *manual_headers = NULL;
2227
2228         cm_return_val_if_fail(msginfo != NULL, NULL);
2229         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2230
2231         if (compose_put_existing_to_front(msginfo)) {
2232                 return NULL;
2233         }
2234
2235         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2236             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2237             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2238                 gchar queueheader_buf[BUFFSIZE];
2239                 gint id, param;
2240
2241                 /* Select Account from queue headers */
2242                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2243                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2244                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2245                         account = account_find_from_id(id);
2246                 }
2247                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2248                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2249                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2250                         account = account_find_from_id(id);
2251                 }
2252                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2253                                              sizeof(queueheader_buf), "NAID:")) {
2254                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2255                         account = account_find_from_id(id);
2256                 }
2257                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2258                                                     sizeof(queueheader_buf), "MAID:")) {
2259                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2260                         account = account_find_from_id(id);
2261                 }
2262                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2263                                                                 sizeof(queueheader_buf), "S:")) {
2264                         account = account_find_from_address(queueheader_buf, FALSE);
2265                 }
2266                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2267                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2268                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2269                         use_signing = param;
2270                         
2271                 }
2272                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2273                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2274                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2275                         use_signing = param;
2276                         
2277                 }
2278                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2279                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2280                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2281                         use_encryption = param;
2282                 }
2283                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2284                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2285                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2286                         use_encryption = param;
2287                 }
2288                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2289                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2290                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2291                         autowrap = param;
2292                 }
2293                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2294                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2295                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2296                         autoindent = param;
2297                 }
2298                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2299                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2300                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2301                 }
2302                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2303                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2304                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2305                 }
2306                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2307                                              sizeof(queueheader_buf), "X-Priority: ")) {
2308                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2309                         priority = param;
2310                 }
2311                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2312                                              sizeof(queueheader_buf), "RMID:")) {
2313                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2314                         if (tokens[0] && tokens[1] && tokens[2]) {
2315                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2316                                 if (orig_item != NULL) {
2317                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2318                                 }
2319                         }
2320                         g_strfreev(tokens);
2321                 }
2322                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2323                                              sizeof(queueheader_buf), "FMID:")) {
2324                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2325                         if (tokens[0] && tokens[1] && tokens[2]) {
2326                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2327                                 if (orig_item != NULL) {
2328                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2329                                 }
2330                         }
2331                         g_strfreev(tokens);
2332                 }
2333                 /* Get manual headers */
2334                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2335                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2336                         if (*listmh != '\0') {
2337                                 debug_print("Got manual headers: %s\n", listmh);
2338                                 manual_headers = procheader_entries_from_str(listmh);
2339                         }
2340                         g_free(listmh);
2341                 }
2342         } else {
2343                 account = msginfo->folder->folder->account;
2344         }
2345
2346         if (!account && prefs_common.reedit_account_autosel) {
2347                 gchar from[BUFFSIZE];
2348                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2349                         extract_address(from);
2350                         account = account_find_from_address(from, FALSE);
2351                 }
2352         }
2353         if (!account) {
2354                 account = cur_account;
2355         }
2356         cm_return_val_if_fail(account != NULL, NULL);
2357
2358         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2359
2360         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2361         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2362         compose->autowrap = autowrap;
2363         compose->replyinfo = replyinfo;
2364         compose->fwdinfo = fwdinfo;
2365
2366         compose->updating = TRUE;
2367         compose->priority = priority;
2368
2369         if (privacy_system != NULL) {
2370                 compose->privacy_system = privacy_system;
2371                 compose_use_signing(compose, use_signing);
2372                 compose_use_encryption(compose, use_encryption);
2373                 compose_update_privacy_system_menu_item(compose, FALSE);
2374         } else {
2375                 activate_privacy_system(compose, account, FALSE);
2376         }
2377
2378         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2379
2380         compose_extract_original_charset(compose);
2381
2382         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2383             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2384             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2385                 gchar queueheader_buf[BUFFSIZE];
2386
2387                 /* Set message save folder */
2388                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2389                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2390                         compose_set_save_to(compose, &queueheader_buf[4]);
2391                 }
2392                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2393                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2394                         if (active) {
2395                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2396                         }
2397                 }
2398         }
2399         
2400         if (compose_parse_header(compose, msginfo) < 0) {
2401                 compose->updating = FALSE;
2402                 compose_destroy(compose);
2403                 return NULL;
2404         }
2405         compose_reedit_set_entry(compose, msginfo);
2406
2407         textview = GTK_TEXT_VIEW(compose->text);
2408         textbuf = gtk_text_view_get_buffer(textview);
2409         compose_create_tags(textview, compose);
2410
2411         mark = gtk_text_buffer_get_insert(textbuf);
2412         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2413
2414         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2415                                         G_CALLBACK(compose_changed_cb),
2416                                         compose);
2417         
2418         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2419                 fp = procmime_get_first_encrypted_text_content(msginfo);
2420                 if (fp) {
2421                         compose_force_encryption(compose, account, TRUE, NULL);
2422                 }
2423         } else {
2424                 fp = procmime_get_first_text_content(msginfo);
2425         }
2426         if (fp == NULL) {
2427                 g_warning("Can't get text part");
2428         }
2429
2430         if (fp != NULL) {
2431                 gboolean prev_autowrap;
2432                 GtkTextBuffer *buffer;
2433                 BLOCK_WRAP();
2434                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2435                         strcrchomp(buf);
2436                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2437                 }
2438                 UNBLOCK_WRAP();
2439                 fclose(fp);
2440         }
2441         
2442         compose_attach_parts(compose, msginfo);
2443
2444         compose_colorize_signature(compose);
2445
2446         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2447                                         G_CALLBACK(compose_changed_cb),
2448                                         compose);
2449
2450         if (manual_headers != NULL) {
2451                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2452                         procheader_entries_free(manual_headers);
2453                         compose->updating = FALSE;
2454                         compose_destroy(compose);
2455                         return NULL;
2456                 }
2457                 procheader_entries_free(manual_headers);
2458         }
2459
2460         gtk_widget_grab_focus(compose->text);
2461
2462         if (prefs_common.auto_exteditor) {
2463                 compose_exec_ext_editor(compose);
2464         }
2465         compose->modified = FALSE;
2466         compose_set_title(compose);
2467
2468         compose->updating = FALSE;
2469         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2470         SCROLL_TO_CURSOR(compose);
2471
2472         if (compose->deferred_destroy) {
2473                 compose_destroy(compose);
2474                 return NULL;
2475         }
2476         
2477         compose->sig_str = account_get_signature_str(compose->account);
2478         
2479         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2480
2481         return compose;
2482 }
2483
2484 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2485                                                  gboolean batch)
2486 {
2487         Compose *compose;
2488         gchar *filename;
2489         FolderItem *item;
2490
2491         cm_return_val_if_fail(msginfo != NULL, NULL);
2492
2493         if (!account)
2494                 account = account_get_reply_account(msginfo,
2495                                         prefs_common.reply_account_autosel);
2496         cm_return_val_if_fail(account != NULL, NULL);
2497
2498         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2499
2500         compose->updating = TRUE;
2501
2502         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2503         compose->replyinfo = NULL;
2504         compose->fwdinfo = NULL;
2505
2506         compose_show_first_last_header(compose, TRUE);
2507
2508         gtk_widget_grab_focus(compose->header_last->entry);
2509
2510         filename = procmsg_get_message_file(msginfo);
2511
2512         if (filename == NULL) {
2513                 compose->updating = FALSE;
2514                 compose_destroy(compose);
2515
2516                 return NULL;
2517         }
2518
2519         compose->redirect_filename = filename;
2520         
2521         /* Set save folder */
2522         item = msginfo->folder;
2523         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2524                 gchar *folderidentifier;
2525
2526                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2527                 folderidentifier = folder_item_get_identifier(item);
2528                 compose_set_save_to(compose, folderidentifier);
2529                 g_free(folderidentifier);
2530         }
2531
2532         compose_attach_parts(compose, msginfo);
2533
2534         if (msginfo->subject)
2535                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2536                                    msginfo->subject);
2537         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2538
2539         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2540                                           _("The body of the \"Redirect\" template has an error at line %d."));
2541         quote_fmt_reset_vartable();
2542         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2543
2544         compose_colorize_signature(compose);
2545
2546         
2547         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2548         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2549         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2550
2551         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2552         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2553         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2554         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2555         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2556         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2557         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2558         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2559         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2560         
2561         if (compose->toolbar->draft_btn)
2562                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2563         if (compose->toolbar->insert_btn)
2564                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2565         if (compose->toolbar->attach_btn)
2566                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2567         if (compose->toolbar->sig_btn)
2568                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2569         if (compose->toolbar->exteditor_btn)
2570                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2571         if (compose->toolbar->linewrap_current_btn)
2572                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2573         if (compose->toolbar->linewrap_all_btn)
2574                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2575
2576         compose->modified = FALSE;
2577         compose_set_title(compose);
2578         compose->updating = FALSE;
2579         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2580         SCROLL_TO_CURSOR(compose);
2581
2582         if (compose->deferred_destroy) {
2583                 compose_destroy(compose);
2584                 return NULL;
2585         }
2586         
2587         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2588
2589         return compose;
2590 }
2591
2592 const GList *compose_get_compose_list(void)
2593 {
2594         return compose_list;
2595 }
2596
2597 void compose_entry_append(Compose *compose, const gchar *address,
2598                           ComposeEntryType type, ComposePrefType pref_type)
2599 {
2600         const gchar *header;
2601         gchar *cur, *begin;
2602         gboolean in_quote = FALSE;
2603         if (!address || *address == '\0') return;
2604
2605         switch (type) {
2606         case COMPOSE_CC:
2607                 header = N_("Cc:");
2608                 break;
2609         case COMPOSE_BCC:
2610                 header = N_("Bcc:");
2611                 break;
2612         case COMPOSE_REPLYTO:
2613                 header = N_("Reply-To:");
2614                 break;
2615         case COMPOSE_NEWSGROUPS:
2616                 header = N_("Newsgroups:");
2617                 break;
2618         case COMPOSE_FOLLOWUPTO:
2619                 header = N_( "Followup-To:");
2620                 break;
2621         case COMPOSE_INREPLYTO:
2622                 header = N_( "In-Reply-To:");
2623                 break;
2624         case COMPOSE_TO:
2625         default:
2626                 header = N_("To:");
2627                 break;
2628         }
2629         header = prefs_common_translated_header_name(header);
2630         
2631         cur = begin = (gchar *)address;
2632         
2633         /* we separate the line by commas, but not if we're inside a quoted
2634          * string */
2635         while (*cur != '\0') {
2636                 if (*cur == '"') 
2637                         in_quote = !in_quote;
2638                 if (*cur == ',' && !in_quote) {
2639                         gchar *tmp = g_strdup(begin);
2640                         gchar *o_tmp = tmp;
2641                         tmp[cur-begin]='\0';
2642                         cur++;
2643                         begin = cur;
2644                         while (*tmp == ' ' || *tmp == '\t')
2645                                 tmp++;
2646                         compose_add_header_entry(compose, header, tmp, pref_type);
2647                         g_free(o_tmp);
2648                         continue;
2649                 }
2650                 cur++;
2651         }
2652         if (begin < cur) {
2653                 gchar *tmp = g_strdup(begin);
2654                 gchar *o_tmp = tmp;
2655                 tmp[cur-begin]='\0';
2656                 while (*tmp == ' ' || *tmp == '\t')
2657                         tmp++;
2658                 compose_add_header_entry(compose, header, tmp, pref_type);
2659                 g_free(o_tmp);          
2660         }
2661 }
2662
2663 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2664 {
2665 #if !GTK_CHECK_VERSION(3, 0, 0)
2666         static GdkColor yellow;
2667         static GdkColor black;
2668         static gboolean yellow_initialised = FALSE;
2669 #else
2670         static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2671         static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2672 #endif
2673         GSList *h_list;
2674         GtkEntry *entry;
2675                 
2676 #if !GTK_CHECK_VERSION(3, 0, 0)
2677         if (!yellow_initialised) {
2678                 gdk_color_parse("#f5f6be", &yellow);
2679                 gdk_color_parse("#000000", &black);
2680                 yellow_initialised = gdk_colormap_alloc_color(
2681                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2682                 yellow_initialised &= gdk_colormap_alloc_color(
2683                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2684         }
2685 #endif
2686
2687         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2688                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2689                 if (gtk_entry_get_text(entry) && 
2690                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2691 #if !GTK_CHECK_VERSION(3, 0, 0)
2692                         if (yellow_initialised) {
2693 #endif
2694                                 gtk_widget_modify_base(
2695                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2696                                         GTK_STATE_NORMAL, &yellow);
2697                                 gtk_widget_modify_text(
2698                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2699                                         GTK_STATE_NORMAL, &black);
2700 #if !GTK_CHECK_VERSION(3, 0, 0)
2701                         }
2702 #endif
2703                 }
2704         }
2705 }
2706
2707 void compose_toolbar_cb(gint action, gpointer data)
2708 {
2709         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2710         Compose *compose = (Compose*)toolbar_item->parent;
2711         
2712         cm_return_if_fail(compose != NULL);
2713
2714         switch(action) {
2715         case A_SEND:
2716                 compose_send_cb(NULL, compose);
2717                 break;
2718         case A_SENDL:
2719                 compose_send_later_cb(NULL, compose);
2720                 break;
2721         case A_DRAFT:
2722                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2723                 break;
2724         case A_INSERT:
2725                 compose_insert_file_cb(NULL, compose);
2726                 break;
2727         case A_ATTACH:
2728                 compose_attach_cb(NULL, compose);
2729                 break;
2730         case A_SIG:
2731                 compose_insert_sig(compose, FALSE);
2732                 break;
2733         case A_REP_SIG:
2734                 compose_insert_sig(compose, TRUE);
2735                 break;
2736         case A_EXTEDITOR:
2737                 compose_ext_editor_cb(NULL, compose);
2738                 break;
2739         case A_LINEWRAP_CURRENT:
2740                 compose_beautify_paragraph(compose, NULL, TRUE);
2741                 break;
2742         case A_LINEWRAP_ALL:
2743                 compose_wrap_all_full(compose, TRUE);
2744                 break;
2745         case A_ADDRBOOK:
2746                 compose_address_cb(NULL, compose);
2747                 break;
2748 #ifdef USE_ENCHANT
2749         case A_CHECK_SPELLING:
2750                 compose_check_all(NULL, compose);
2751                 break;
2752 #endif
2753         default:
2754                 break;
2755         }
2756 }
2757
2758 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2759 {
2760         gchar *to = NULL;
2761         gchar *cc = NULL;
2762         gchar *bcc = NULL;
2763         gchar *subject = NULL;
2764         gchar *body = NULL;
2765         gchar *temp = NULL;
2766         gsize  len = 0;
2767         gchar **attach = NULL;
2768         gchar *inreplyto = NULL;
2769         MailField mfield = NO_FIELD_PRESENT;
2770
2771         /* get mailto parts but skip from */
2772         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2773
2774         if (to) {
2775                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2776                 mfield = TO_FIELD_PRESENT;
2777         }
2778         if (cc)
2779                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2780         if (bcc)
2781                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2782         if (subject) {
2783                 if (!g_utf8_validate (subject, -1, NULL)) {
2784                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2785                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2786                         g_free(temp);
2787                 } else {
2788                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2789                 }
2790                 mfield = SUBJECT_FIELD_PRESENT;
2791         }
2792         if (body) {
2793                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2794                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2795                 GtkTextMark *mark;
2796                 GtkTextIter iter;
2797                 gboolean prev_autowrap = compose->autowrap;
2798
2799                 compose->autowrap = FALSE;
2800
2801                 mark = gtk_text_buffer_get_insert(buffer);
2802                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2803
2804                 if (!g_utf8_validate (body, -1, NULL)) {
2805                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2806                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2807                         g_free(temp);
2808                 } else {
2809                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2810                 }
2811                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2812
2813                 compose->autowrap = prev_autowrap;
2814                 if (compose->autowrap)
2815                         compose_wrap_all(compose);
2816                 mfield = BODY_FIELD_PRESENT;
2817         }
2818
2819         if (attach) {
2820                 gint i = 0, att = 0;
2821                 gchar *warn_files = NULL;
2822                 while (attach[i] != NULL) {
2823                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2824                         if (utf8_filename) {
2825                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2826                                         gchar *tmp = g_strdup_printf("%s%s\n",
2827                                                         warn_files?warn_files:"",
2828                                                         utf8_filename);
2829                                         g_free(warn_files);
2830                                         warn_files = tmp;
2831                                         att++;
2832                                 }
2833                                 g_free(utf8_filename);
2834                         } else {
2835                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2836                         }
2837                         i++;
2838                 }
2839                 if (warn_files) {
2840                         alertpanel_notice(ngettext(
2841                         "The following file has been attached: \n%s",
2842                         "The following files have been attached: \n%s", att), warn_files);
2843                         g_free(warn_files);
2844                 }
2845         }
2846         if (inreplyto)
2847                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2848
2849         g_free(to);
2850         g_free(cc);
2851         g_free(bcc);
2852         g_free(subject);
2853         g_free(body);
2854         g_strfreev(attach);
2855         g_free(inreplyto);
2856         
2857         return mfield;
2858 }
2859
2860 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2861 {
2862         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2863                                        {"Cc:",          NULL, TRUE},
2864                                        {"References:",  NULL, FALSE},
2865                                        {"Bcc:",         NULL, TRUE},
2866                                        {"Newsgroups:",  NULL, TRUE},
2867                                        {"Followup-To:", NULL, TRUE},
2868                                        {"List-Post:",   NULL, FALSE},
2869                                        {"X-Priority:",  NULL, FALSE},
2870                                        {NULL,           NULL, FALSE}};
2871
2872         enum
2873         {
2874                 H_REPLY_TO      = 0,
2875                 H_CC            = 1,
2876                 H_REFERENCES    = 2,
2877                 H_BCC           = 3,
2878                 H_NEWSGROUPS    = 4,
2879                 H_FOLLOWUP_TO   = 5,
2880                 H_LIST_POST     = 6,
2881                 H_X_PRIORITY    = 7
2882         };
2883
2884         FILE *fp;
2885
2886         cm_return_val_if_fail(msginfo != NULL, -1);
2887
2888         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2889         procheader_get_header_fields(fp, hentry);
2890         fclose(fp);
2891
2892         if (hentry[H_REPLY_TO].body != NULL) {
2893                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2894                         compose->replyto =
2895                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2896                                                    NULL, TRUE);
2897                 }
2898                 g_free(hentry[H_REPLY_TO].body);
2899                 hentry[H_REPLY_TO].body = NULL;
2900         }
2901         if (hentry[H_CC].body != NULL) {
2902                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2903                 g_free(hentry[H_CC].body);
2904                 hentry[H_CC].body = NULL;
2905         }
2906         if (hentry[H_REFERENCES].body != NULL) {
2907                 if (compose->mode == COMPOSE_REEDIT)
2908                         compose->references = hentry[H_REFERENCES].body;
2909                 else {
2910                         compose->references = compose_parse_references
2911                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2912                         g_free(hentry[H_REFERENCES].body);
2913                 }
2914                 hentry[H_REFERENCES].body = NULL;
2915         }
2916         if (hentry[H_BCC].body != NULL) {
2917                 if (compose->mode == COMPOSE_REEDIT)
2918                         compose->bcc =
2919                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2920                 g_free(hentry[H_BCC].body);
2921                 hentry[H_BCC].body = NULL;
2922         }
2923         if (hentry[H_NEWSGROUPS].body != NULL) {
2924                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2925                 hentry[H_NEWSGROUPS].body = NULL;
2926         }
2927         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2928                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2929                         compose->followup_to =
2930                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2931                                                    NULL, TRUE);
2932                 }
2933                 g_free(hentry[H_FOLLOWUP_TO].body);
2934                 hentry[H_FOLLOWUP_TO].body = NULL;
2935         }
2936         if (hentry[H_LIST_POST].body != NULL) {
2937                 gchar *to = NULL, *start = NULL;
2938
2939                 extract_address(hentry[H_LIST_POST].body);
2940                 if (hentry[H_LIST_POST].body[0] != '\0') {
2941                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2942                         
2943                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2944                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2945
2946                         if (to) {
2947                                 g_free(compose->ml_post);
2948                                 compose->ml_post = to;
2949                         }
2950                 }
2951                 g_free(hentry[H_LIST_POST].body);
2952                 hentry[H_LIST_POST].body = NULL;
2953         }
2954
2955         /* CLAWS - X-Priority */
2956         if (compose->mode == COMPOSE_REEDIT)
2957                 if (hentry[H_X_PRIORITY].body != NULL) {
2958                         gint priority;
2959                         
2960                         priority = atoi(hentry[H_X_PRIORITY].body);
2961                         g_free(hentry[H_X_PRIORITY].body);
2962                         
2963                         hentry[H_X_PRIORITY].body = NULL;
2964                         
2965                         if (priority < PRIORITY_HIGHEST || 
2966                             priority > PRIORITY_LOWEST)
2967                                 priority = PRIORITY_NORMAL;
2968                         
2969                         compose->priority =  priority;
2970                 }
2971  
2972         if (compose->mode == COMPOSE_REEDIT) {
2973                 if (msginfo->inreplyto && *msginfo->inreplyto)
2974                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2975                 return 0;
2976         }
2977
2978         if (msginfo->msgid && *msginfo->msgid)
2979                 compose->inreplyto = g_strdup(msginfo->msgid);
2980
2981         if (!compose->references) {
2982                 if (msginfo->msgid && *msginfo->msgid) {
2983                         if (msginfo->inreplyto && *msginfo->inreplyto)
2984                                 compose->references =
2985                                         g_strdup_printf("<%s>\n\t<%s>",
2986                                                         msginfo->inreplyto,
2987                                                         msginfo->msgid);
2988                         else
2989                                 compose->references =
2990                                         g_strconcat("<", msginfo->msgid, ">",
2991                                                     NULL);
2992                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2993                         compose->references =
2994                                 g_strconcat("<", msginfo->inreplyto, ">",
2995                                             NULL);
2996                 }
2997         }
2998
2999         return 0;
3000 }
3001
3002 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3003 {
3004         FILE *fp;
3005         HeaderEntry *he;
3006
3007         cm_return_val_if_fail(msginfo != NULL, -1);
3008
3009         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3010         procheader_get_header_fields(fp, entries);
3011         fclose(fp);
3012
3013         he = entries;
3014         while (he != NULL && he->name != NULL) {
3015                 GtkTreeIter iter;
3016                 GtkListStore *model = NULL;
3017
3018                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3019                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3020                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3021                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3022                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3023                 ++he;
3024         }
3025
3026         return 0;
3027 }
3028
3029 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3030 {
3031         GSList *ref_id_list, *cur;
3032         GString *new_ref;
3033         gchar *new_ref_str;
3034
3035         ref_id_list = references_list_append(NULL, ref);
3036         if (!ref_id_list) return NULL;
3037         if (msgid && *msgid)
3038                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3039
3040         for (;;) {
3041                 gint len = 0;
3042
3043                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3044                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3045                         len += strlen((gchar *)cur->data) + 5;
3046
3047                 if (len > MAX_REFERENCES_LEN) {
3048                         /* remove second message-ID */
3049                         if (ref_id_list && ref_id_list->next &&
3050                             ref_id_list->next->next) {
3051                                 g_free(ref_id_list->next->data);
3052                                 ref_id_list = g_slist_remove
3053                                         (ref_id_list, ref_id_list->next->data);
3054                         } else {
3055                                 slist_free_strings_full(ref_id_list);
3056                                 return NULL;
3057                         }
3058                 } else
3059                         break;
3060         }
3061
3062         new_ref = g_string_new("");
3063         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3064                 if (new_ref->len > 0)
3065                         g_string_append(new_ref, "\n\t");
3066                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3067         }
3068
3069         slist_free_strings_full(ref_id_list);
3070
3071         new_ref_str = new_ref->str;
3072         g_string_free(new_ref, FALSE);
3073
3074         return new_ref_str;
3075 }
3076
3077 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3078                                 const gchar *fmt, const gchar *qmark,
3079                                 const gchar *body, gboolean rewrap,
3080                                 gboolean need_unescape,
3081                                 const gchar *err_msg)
3082 {
3083         MsgInfo* dummyinfo = NULL;
3084         gchar *quote_str = NULL;
3085         gchar *buf;
3086         gboolean prev_autowrap;
3087         const gchar *trimmed_body = body;
3088         gint cursor_pos = -1;
3089         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3090         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3091         GtkTextIter iter;
3092         GtkTextMark *mark;
3093         
3094
3095         SIGNAL_BLOCK(buffer);
3096
3097         if (!msginfo) {
3098                 dummyinfo = compose_msginfo_new_from_compose(compose);
3099                 msginfo = dummyinfo;
3100         }
3101
3102         if (qmark != NULL) {
3103 #ifdef USE_ENCHANT
3104                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3105                                 compose->gtkaspell);
3106 #else
3107                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3108 #endif
3109                 quote_fmt_scan_string(qmark);
3110                 quote_fmt_parse();
3111
3112                 buf = quote_fmt_get_buffer();
3113                 if (buf == NULL)
3114                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3115                 else
3116                         Xstrdup_a(quote_str, buf, goto error)
3117         }
3118
3119         if (fmt && *fmt != '\0') {
3120
3121                 if (trimmed_body)
3122                         while (*trimmed_body == '\n')
3123                                 trimmed_body++;
3124
3125 #ifdef USE_ENCHANT
3126                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3127                                 compose->gtkaspell);
3128 #else
3129                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3130 #endif
3131                 if (need_unescape) {
3132                         gchar *tmp = NULL;
3133
3134                         /* decode \-escape sequences in the internal representation of the quote format */
3135                         tmp = g_malloc(strlen(fmt)+1);
3136                         pref_get_unescaped_pref(tmp, fmt);
3137                         quote_fmt_scan_string(tmp);
3138                         quote_fmt_parse();
3139                         g_free(tmp);
3140                 } else {
3141                         quote_fmt_scan_string(fmt);
3142                         quote_fmt_parse();
3143                 }
3144
3145                 buf = quote_fmt_get_buffer();
3146                 if (buf == NULL) {
3147                         gint line = quote_fmt_get_line();
3148                         alertpanel_error(err_msg, line);
3149                         goto error;
3150                 }
3151         } else
3152                 buf = "";
3153
3154         prev_autowrap = compose->autowrap;
3155         compose->autowrap = FALSE;
3156
3157         mark = gtk_text_buffer_get_insert(buffer);
3158         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3159         if (g_utf8_validate(buf, -1, NULL)) { 
3160                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3161         } else {
3162                 gchar *tmpout = NULL;
3163                 tmpout = conv_codeset_strdup
3164                         (buf, conv_get_locale_charset_str_no_utf8(),
3165                          CS_INTERNAL);
3166                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3167                         g_free(tmpout);
3168                         tmpout = g_malloc(strlen(buf)*2+1);
3169                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3170                 }
3171                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3172                 g_free(tmpout);
3173         }
3174
3175         cursor_pos = quote_fmt_get_cursor_pos();
3176         if (cursor_pos == -1)