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