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