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