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