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