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