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