a2b189103cff5832d79c625c11925d374cddbccb
[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                         quote_fmtlex_destroy();
1088
1089                         g_free(tmp);
1090                 }
1091
1092         compose->replyinfo = NULL;
1093         compose->fwdinfo   = NULL;
1094
1095         textview = GTK_TEXT_VIEW(compose->text);
1096         textbuf = gtk_text_view_get_buffer(textview);
1097         compose_create_tags(textview, compose);
1098
1099         undo_block(compose->undostruct);
1100 #ifdef USE_ENCHANT
1101         compose_set_dictionaries_from_folder_prefs(compose, item);
1102 #endif
1103
1104         if (account->auto_sig)
1105                 compose_insert_sig(compose, FALSE);
1106         gtk_text_buffer_get_start_iter(textbuf, &iter);
1107         gtk_text_buffer_place_cursor(textbuf, &iter);
1108
1109         if (account->protocol != A_NNTP) {
1110                 if (mailto && *mailto != '\0') {
1111                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1112
1113                 } else {
1114                         compose_set_folder_prefs(compose, item, TRUE);
1115                 }
1116                 if (item && item->ret_rcpt) {
1117                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1118                 }
1119         } else {
1120                 if (mailto && *mailto != '\0') {
1121                         if (!strchr(mailto, '@'))
1122                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1123                         else
1124                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1125                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1126                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1127                         mfield = TO_FIELD_PRESENT;
1128                 }
1129                 /*
1130                  * CLAWS: just don't allow return receipt request, even if the user
1131                  * may want to send an email. simple but foolproof.
1132                  */
1133                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1134         }
1135         compose_add_field_list( compose, listAddress );
1136
1137         if (item && item->prefs && item->prefs->compose_with_format) {
1138                 subject_format = item->prefs->compose_subject_format;
1139                 body_format = item->prefs->compose_body_format;
1140         } else if (account->compose_with_format) {
1141                 subject_format = account->compose_subject_format;
1142                 body_format = account->compose_body_format;
1143         } else if (prefs_common.compose_with_format) {
1144                 subject_format = prefs_common.compose_subject_format;
1145                 body_format = prefs_common.compose_body_format;
1146         }
1147
1148         if (subject_format || body_format) {
1149
1150                 if ( subject_format
1151                          && *subject_format != '\0' )
1152                 {
1153                         gchar *subject = NULL;
1154                         gchar *tmp = NULL;
1155                         gchar *buf = NULL;
1156
1157                         if (!dummyinfo)
1158                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1159
1160                         /* decode \-escape sequences in the internal representation of the quote format */
1161                         tmp = g_malloc(strlen(subject_format)+1);
1162                         pref_get_unescaped_pref(tmp, subject_format);
1163
1164                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1165 #ifdef USE_ENCHANT
1166                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1167                                         compose->gtkaspell);
1168 #else
1169                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1170 #endif
1171                         quote_fmt_scan_string(tmp);
1172                         quote_fmt_parse();
1173
1174                         buf = quote_fmt_get_buffer();
1175                         if (buf == NULL)
1176                                 alertpanel_error(_("New message subject format error."));
1177                         else
1178                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1179                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1180                         quote_fmt_reset_vartable();
1181                         quote_fmtlex_destroy();
1182
1183                         g_free(subject);
1184                         g_free(tmp);
1185                         mfield = SUBJECT_FIELD_PRESENT;
1186                 }
1187
1188                 if ( body_format
1189                          && *body_format != '\0' )
1190                 {
1191                         GtkTextView *text;
1192                         GtkTextBuffer *buffer;
1193                         GtkTextIter start, end;
1194                         gchar *tmp = NULL;
1195
1196                         if (!dummyinfo)
1197                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1198
1199                         text = GTK_TEXT_VIEW(compose->text);
1200                         buffer = gtk_text_view_get_buffer(text);
1201                         gtk_text_buffer_get_start_iter(buffer, &start);
1202                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1203                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1204
1205                         compose_quote_fmt(compose, dummyinfo,
1206                                           body_format,
1207                                           NULL, tmp, FALSE, TRUE,
1208                                                   _("The body of the \"New message\" template has an error at line %d."));
1209                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1210                         quote_fmt_reset_vartable();
1211
1212                         g_free(tmp);
1213 #ifdef USE_ENCHANT
1214                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1215                                 gtkaspell_highlight_all(compose->gtkaspell);
1216 #endif
1217                         mfield = BODY_FIELD_PRESENT;
1218                 }
1219
1220         }
1221         procmsg_msginfo_free( &dummyinfo );
1222
1223         if (attach_files) {
1224                 GList *curr;
1225                 AttachInfo *ainfo;
1226
1227                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1228                         ainfo = (AttachInfo *) curr->data;
1229                         compose_attach_append(compose, ainfo->file, ainfo->file,
1230                                         ainfo->content_type, ainfo->charset);
1231                 }
1232         }
1233
1234         compose_show_first_last_header(compose, TRUE);
1235
1236         /* Set save folder */
1237         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1238                 gchar *folderidentifier;
1239
1240                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1241                 folderidentifier = folder_item_get_identifier(item);
1242                 compose_set_save_to(compose, folderidentifier);
1243                 g_free(folderidentifier);
1244         }
1245
1246         /* Place cursor according to provided input (mfield) */
1247         switch (mfield) { 
1248                 case NO_FIELD_PRESENT:
1249                         if (compose->header_last)
1250                                 gtk_widget_grab_focus(compose->header_last->entry);
1251                         break;
1252                 case TO_FIELD_PRESENT:
1253                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1254                         if (buf) {
1255                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1256                                 g_free(buf);
1257                         }
1258                         gtk_widget_grab_focus(compose->subject_entry);
1259                         break;
1260                 case SUBJECT_FIELD_PRESENT:
1261                         textview = GTK_TEXT_VIEW(compose->text);
1262                         if (!textview)
1263                                 break;
1264                         textbuf = gtk_text_view_get_buffer(textview);
1265                         if (!textbuf)
1266                                 break;
1267                         mark = gtk_text_buffer_get_insert(textbuf);
1268                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1269                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1270                     /* 
1271                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1272                      * only defers where it comes to the variable body
1273                      * is not null. If no body is present compose->text
1274                      * will be null in which case you cannot place the
1275                      * cursor inside the component so. An empty component
1276                      * is therefore created before placing the cursor
1277                      */
1278                 case BODY_FIELD_PRESENT:
1279                         cursor_pos = quote_fmt_get_cursor_pos();
1280                         if (cursor_pos == -1)
1281                                 gtk_widget_grab_focus(compose->header_last->entry);
1282                         else
1283                                 gtk_widget_grab_focus(compose->text);
1284                         break;
1285         }
1286
1287         undo_unblock(compose->undostruct);
1288
1289         if (prefs_common.auto_exteditor)
1290                 compose_exec_ext_editor(compose);
1291
1292         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1293
1294         SCROLL_TO_CURSOR(compose);
1295
1296         compose->modified = FALSE;
1297         compose_set_title(compose);
1298
1299         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1300
1301         return compose;
1302 }
1303
1304 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1305                 gboolean override_pref, const gchar *system)
1306 {
1307         const gchar *privacy = NULL;
1308
1309         cm_return_if_fail(compose != NULL);
1310         cm_return_if_fail(account != NULL);
1311
1312         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1313                 return;
1314
1315         if (account->default_privacy_system && strlen(account->default_privacy_system))
1316                 privacy = account->default_privacy_system;
1317         else if (system)
1318                 privacy = system;
1319         else {
1320                 GSList *privacy_avail = privacy_get_system_ids();
1321                 if (privacy_avail && g_slist_length(privacy_avail)) {
1322                         privacy = (gchar *)(privacy_avail->data);
1323                 }
1324                 g_slist_free_full(privacy_avail, g_free);
1325         }
1326         if (privacy != NULL) {
1327                 if (system) {
1328                         g_free(compose->privacy_system);
1329                         compose->privacy_system = NULL;
1330                         g_free(compose->encdata);
1331                         compose->encdata = NULL;
1332                 }
1333                 if (compose->privacy_system == NULL)
1334                         compose->privacy_system = g_strdup(privacy);
1335                 else if (*(compose->privacy_system) == '\0') {
1336                         g_free(compose->privacy_system);
1337                         g_free(compose->encdata);
1338                         compose->encdata = NULL;
1339                         compose->privacy_system = g_strdup(privacy);
1340                 }
1341                 compose_update_privacy_system_menu_item(compose, FALSE);
1342                 compose_use_encryption(compose, TRUE);
1343         }
1344 }       
1345
1346 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1347 {
1348         const gchar *privacy = NULL;
1349
1350         if (account->default_privacy_system && strlen(account->default_privacy_system))
1351                 privacy = account->default_privacy_system;
1352         else if (system)
1353                 privacy = system;
1354         else {
1355                 GSList *privacy_avail = privacy_get_system_ids();
1356                 if (privacy_avail && g_slist_length(privacy_avail)) {
1357                         privacy = (gchar *)(privacy_avail->data);
1358                 }
1359         }
1360
1361         if (privacy != NULL) {
1362                 if (system) {
1363                         g_free(compose->privacy_system);
1364                         compose->privacy_system = NULL;
1365                         g_free(compose->encdata);
1366                         compose->encdata = NULL;
1367                 }
1368                 if (compose->privacy_system == NULL)
1369                         compose->privacy_system = g_strdup(privacy);
1370                 compose_update_privacy_system_menu_item(compose, FALSE);
1371                 compose_use_signing(compose, TRUE);
1372         }
1373 }       
1374
1375 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1376 {
1377         MsgInfo *msginfo;
1378         guint list_len;
1379         Compose *compose = NULL;
1380         
1381         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1382
1383         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1384         cm_return_val_if_fail(msginfo != NULL, NULL);
1385
1386         list_len = g_slist_length(msginfo_list);
1387
1388         switch (mode) {
1389         case COMPOSE_REPLY:
1390         case COMPOSE_REPLY_TO_ADDRESS:
1391                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1392                               FALSE, prefs_common.default_reply_list, FALSE, body);
1393                 break;
1394         case COMPOSE_REPLY_WITH_QUOTE:
1395                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1396                         FALSE, prefs_common.default_reply_list, FALSE, body);
1397                 break;
1398         case COMPOSE_REPLY_WITHOUT_QUOTE:
1399                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1400                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1401                 break;
1402         case COMPOSE_REPLY_TO_SENDER:
1403                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1404                               FALSE, FALSE, TRUE, body);
1405                 break;
1406         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1407                 compose = compose_followup_and_reply_to(msginfo,
1408                                               COMPOSE_QUOTE_CHECK,
1409                                               FALSE, FALSE, body);
1410                 break;
1411         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1412                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1413                         FALSE, FALSE, TRUE, body);
1414                 break;
1415         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1416                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1417                         FALSE, FALSE, TRUE, NULL);
1418                 break;
1419         case COMPOSE_REPLY_TO_ALL:
1420                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1421                         TRUE, FALSE, FALSE, body);
1422                 break;
1423         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1424                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1425                         TRUE, FALSE, FALSE, body);
1426                 break;
1427         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1428                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1429                         TRUE, FALSE, FALSE, NULL);
1430                 break;
1431         case COMPOSE_REPLY_TO_LIST:
1432                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1433                         FALSE, TRUE, FALSE, body);
1434                 break;
1435         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1436                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1437                         FALSE, TRUE, FALSE, body);
1438                 break;
1439         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1440                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1441                         FALSE, TRUE, FALSE, NULL);
1442                 break;
1443         case COMPOSE_FORWARD:
1444                 if (prefs_common.forward_as_attachment) {
1445                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1446                         return compose;
1447                 } else {
1448                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1449                         return compose;
1450                 }
1451                 break;
1452         case COMPOSE_FORWARD_INLINE:
1453                 /* check if we reply to more than one Message */
1454                 if (list_len == 1) {
1455                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1456                         break;
1457                 } 
1458                 /* more messages FALL THROUGH */
1459         case COMPOSE_FORWARD_AS_ATTACH:
1460                 compose = compose_forward_multiple(NULL, msginfo_list);
1461                 break;
1462         case COMPOSE_REDIRECT:
1463                 compose = compose_redirect(NULL, msginfo, FALSE);
1464                 break;
1465         default:
1466                 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1467         }
1468         
1469         if (compose == NULL) {
1470                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1471                 return NULL;
1472         }
1473
1474         compose->rmode = mode;
1475         switch (compose->rmode) {
1476         case COMPOSE_REPLY:
1477         case COMPOSE_REPLY_WITH_QUOTE:
1478         case COMPOSE_REPLY_WITHOUT_QUOTE:
1479         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1480                 debug_print("reply mode Normal\n");
1481                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1482                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1483                 break;
1484         case COMPOSE_REPLY_TO_SENDER:
1485         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1486         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1487                 debug_print("reply mode Sender\n");
1488                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1489                 break;
1490         case COMPOSE_REPLY_TO_ALL:
1491         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1492         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1493                 debug_print("reply mode All\n");
1494                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1495                 break;
1496         case COMPOSE_REPLY_TO_LIST:
1497         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1498         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1499                 debug_print("reply mode List\n");
1500                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1501                 break;
1502         case COMPOSE_REPLY_TO_ADDRESS:
1503                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1504                 break;
1505         default:
1506                 break;
1507         }
1508         return compose;
1509 }
1510
1511 static Compose *compose_reply(MsgInfo *msginfo,
1512                                    ComposeQuoteMode quote_mode,
1513                                    gboolean to_all,
1514                                    gboolean to_ml,
1515                                    gboolean to_sender, 
1516                                    const gchar *body)
1517 {
1518         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1519                               to_sender, FALSE, body);
1520 }
1521
1522 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1523                                    ComposeQuoteMode quote_mode,
1524                                    gboolean to_all,
1525                                    gboolean to_sender,
1526                                    const gchar *body)
1527 {
1528         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1529                               to_sender, TRUE, body);
1530 }
1531
1532 static void compose_extract_original_charset(Compose *compose)
1533 {
1534         MsgInfo *info = NULL;
1535         if (compose->replyinfo) {
1536                 info = compose->replyinfo;
1537         } else if (compose->fwdinfo) {
1538                 info = compose->fwdinfo;
1539         } else if (compose->targetinfo) {
1540                 info = compose->targetinfo;
1541         }
1542         if (info) {
1543                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1544                 MimeInfo *partinfo = mimeinfo;
1545                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1546                         partinfo = procmime_mimeinfo_next(partinfo);
1547                 if (partinfo) {
1548                         compose->orig_charset = 
1549                                 g_strdup(procmime_mimeinfo_get_parameter(
1550                                                 partinfo, "charset"));
1551                 }
1552                 procmime_mimeinfo_free_all(&mimeinfo);
1553         }
1554 }
1555
1556 #define SIGNAL_BLOCK(buffer) {                                  \
1557         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1558                                 G_CALLBACK(compose_changed_cb), \
1559                                 compose);                       \
1560         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1561                                 G_CALLBACK(text_inserted),      \
1562                                 compose);                       \
1563 }
1564
1565 #define SIGNAL_UNBLOCK(buffer) {                                \
1566         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1567                                 G_CALLBACK(compose_changed_cb), \
1568                                 compose);                       \
1569         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1570                                 G_CALLBACK(text_inserted),      \
1571                                 compose);                       \
1572 }
1573
1574 static Compose *compose_generic_reply(MsgInfo *msginfo,
1575                                   ComposeQuoteMode quote_mode,
1576                                   gboolean to_all, gboolean to_ml,
1577                                   gboolean to_sender,
1578                                   gboolean followup_and_reply_to,
1579                                   const gchar *body)
1580 {
1581         Compose *compose;
1582         PrefsAccount *account = NULL;
1583         GtkTextView *textview;
1584         GtkTextBuffer *textbuf;
1585         gboolean quote = FALSE;
1586         const gchar *qmark = NULL;
1587         const gchar *body_fmt = NULL;
1588         gchar *s_system = NULL;
1589         START_TIMING("");
1590         cm_return_val_if_fail(msginfo != NULL, NULL);
1591         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1592
1593         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1594
1595         cm_return_val_if_fail(account != NULL, NULL);
1596
1597         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1598
1599         compose->updating = TRUE;
1600
1601         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1602         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1603
1604         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1605         if (!compose->replyinfo)
1606                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1607
1608         compose_extract_original_charset(compose);
1609         
1610         if (msginfo->folder && msginfo->folder->ret_rcpt)
1611                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1612
1613         /* Set save folder */
1614         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1615                 gchar *folderidentifier;
1616
1617                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1618                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1619                 compose_set_save_to(compose, folderidentifier);
1620                 g_free(folderidentifier);
1621         }
1622
1623         if (compose_parse_header(compose, msginfo) < 0) {
1624                 compose->updating = FALSE;
1625                 compose_destroy(compose);
1626                 return NULL;
1627         }
1628
1629         /* override from name according to folder properties */
1630         if (msginfo->folder && msginfo->folder->prefs &&
1631                 msginfo->folder->prefs->reply_with_format &&
1632                 msginfo->folder->prefs->reply_override_from_format &&
1633                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1634
1635                 gchar *tmp = NULL;
1636                 gchar *buf = NULL;
1637
1638                 /* decode \-escape sequences in the internal representation of the quote format */
1639                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1640                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1641
1642 #ifdef USE_ENCHANT
1643                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1644                                 compose->gtkaspell);
1645 #else
1646                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1647 #endif
1648                 quote_fmt_scan_string(tmp);
1649                 quote_fmt_parse();
1650
1651                 buf = quote_fmt_get_buffer();
1652                 if (buf == NULL)
1653                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1654                 else
1655                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1656                 quote_fmt_reset_vartable();
1657                 quote_fmtlex_destroy();
1658
1659                 g_free(tmp);
1660         }
1661
1662         textview = (GTK_TEXT_VIEW(compose->text));
1663         textbuf = gtk_text_view_get_buffer(textview);
1664         compose_create_tags(textview, compose);
1665
1666         undo_block(compose->undostruct);
1667 #ifdef USE_ENCHANT
1668         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1669         gtkaspell_block_check(compose->gtkaspell);
1670 #endif
1671
1672         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1673                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1674                 /* use the reply format of folder (if enabled), or the account's one
1675                    (if enabled) or fallback to the global reply format, which is always
1676                    enabled (even if empty), and use the relevant quotemark */
1677                 quote = TRUE;
1678                 if (msginfo->folder && msginfo->folder->prefs &&
1679                                 msginfo->folder->prefs->reply_with_format) {
1680                         qmark = msginfo->folder->prefs->reply_quotemark;
1681                         body_fmt = msginfo->folder->prefs->reply_body_format;
1682
1683                 } else if (account->reply_with_format) {
1684                         qmark = account->reply_quotemark;
1685                         body_fmt = account->reply_body_format;
1686
1687                 } else {
1688                         qmark = prefs_common.quotemark;
1689                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1690                                 body_fmt = gettext(prefs_common.quotefmt);
1691                         else
1692                                 body_fmt = "";
1693                 }
1694         }
1695
1696         if (quote) {
1697                 /* empty quotemark is not allowed */
1698                 if (qmark == NULL || *qmark == '\0')
1699                         qmark = "> ";
1700                 compose_quote_fmt(compose, compose->replyinfo,
1701                                   body_fmt, qmark, body, FALSE, TRUE,
1702                                           _("The body of the \"Reply\" template has an error at line %d."));
1703                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1704                 quote_fmt_reset_vartable();
1705         }
1706
1707         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1708                 compose_force_encryption(compose, account, FALSE, s_system);
1709         }
1710
1711         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1712         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1713                 compose_force_signing(compose, account, s_system);
1714         }
1715         g_free(s_system);
1716
1717         SIGNAL_BLOCK(textbuf);
1718         
1719         if (account->auto_sig)
1720                 compose_insert_sig(compose, FALSE);
1721
1722         compose_wrap_all(compose);
1723
1724 #ifdef USE_ENCHANT
1725         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1726                 gtkaspell_highlight_all(compose->gtkaspell);
1727         gtkaspell_unblock_check(compose->gtkaspell);
1728 #endif
1729         SIGNAL_UNBLOCK(textbuf);
1730         
1731         gtk_widget_grab_focus(compose->text);
1732
1733         undo_unblock(compose->undostruct);
1734
1735         if (prefs_common.auto_exteditor)
1736                 compose_exec_ext_editor(compose);
1737                 
1738         compose->modified = FALSE;
1739         compose_set_title(compose);
1740
1741         compose->updating = FALSE;
1742         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1743         SCROLL_TO_CURSOR(compose);
1744         
1745         if (compose->deferred_destroy) {
1746                 compose_destroy(compose);
1747                 return NULL;
1748         }
1749         END_TIMING();
1750
1751         return compose;
1752 }
1753
1754 #define INSERT_FW_HEADER(var, hdr) \
1755 if (msginfo->var && *msginfo->var) { \
1756         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1757         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1758         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1759 }
1760
1761 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1762                          gboolean as_attach, const gchar *body,
1763                          gboolean no_extedit,
1764                          gboolean batch)
1765 {
1766         Compose *compose;
1767         GtkTextView *textview;
1768         GtkTextBuffer *textbuf;
1769         gint cursor_pos = -1;
1770         ComposeMode mode;
1771
1772         cm_return_val_if_fail(msginfo != NULL, NULL);
1773         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1774
1775         if (!account && !(account = compose_find_account(msginfo)))
1776                 account = cur_account;
1777
1778         if (!prefs_common.forward_as_attachment)
1779                 mode = COMPOSE_FORWARD_INLINE;
1780         else
1781                 mode = COMPOSE_FORWARD;
1782         compose = compose_create(account, msginfo->folder, mode, batch);
1783
1784         compose->updating = TRUE;
1785         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1786         if (!compose->fwdinfo)
1787                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1788
1789         compose_extract_original_charset(compose);
1790
1791         if (msginfo->subject && *msginfo->subject) {
1792                 gchar *buf, *buf2, *p;
1793
1794                 buf = p = g_strdup(msginfo->subject);
1795                 p += subject_get_prefix_length(p);
1796                 memmove(buf, p, strlen(p) + 1);
1797
1798                 buf2 = g_strdup_printf("Fw: %s", buf);
1799                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1800                 
1801                 g_free(buf);
1802                 g_free(buf2);
1803         }
1804
1805         /* override from name according to folder properties */
1806         if (msginfo->folder && msginfo->folder->prefs &&
1807                 msginfo->folder->prefs->forward_with_format &&
1808                 msginfo->folder->prefs->forward_override_from_format &&
1809                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1810
1811                 gchar *tmp = NULL;
1812                 gchar *buf = NULL;
1813                 MsgInfo *full_msginfo = NULL;
1814
1815                 if (!as_attach)
1816                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1817                 if (!full_msginfo)
1818                         full_msginfo = procmsg_msginfo_copy(msginfo);
1819
1820                 /* decode \-escape sequences in the internal representation of the quote format */
1821                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1822                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1823
1824 #ifdef USE_ENCHANT
1825                 gtkaspell_block_check(compose->gtkaspell);
1826                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1827                                 compose->gtkaspell);
1828 #else
1829                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1830 #endif
1831                 quote_fmt_scan_string(tmp);
1832                 quote_fmt_parse();
1833
1834                 buf = quote_fmt_get_buffer();
1835                 if (buf == NULL)
1836                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1837                 else
1838                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1839                 quote_fmt_reset_vartable();
1840                 quote_fmtlex_destroy();
1841
1842                 g_free(tmp);
1843                 procmsg_msginfo_free(&full_msginfo);
1844         }
1845
1846         textview = GTK_TEXT_VIEW(compose->text);
1847         textbuf = gtk_text_view_get_buffer(textview);
1848         compose_create_tags(textview, compose);
1849         
1850         undo_block(compose->undostruct);
1851         if (as_attach) {
1852                 gchar *msgfile;
1853
1854                 msgfile = procmsg_get_message_file(msginfo);
1855                 if (!is_file_exist(msgfile))
1856                         g_warning("%s: file does not exist", msgfile);
1857                 else
1858                         compose_attach_append(compose, msgfile, msgfile,
1859                                               "message/rfc822", NULL);
1860
1861                 g_free(msgfile);
1862         } else {
1863                 const gchar *qmark = NULL;
1864                 const gchar *body_fmt = NULL;
1865                 MsgInfo *full_msginfo;
1866
1867                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1868                 if (!full_msginfo)
1869                         full_msginfo = procmsg_msginfo_copy(msginfo);
1870
1871                 /* use the forward format of folder (if enabled), or the account's one
1872                    (if enabled) or fallback to the global forward format, which is always
1873                    enabled (even if empty), and use the relevant quotemark */
1874                 if (msginfo->folder && msginfo->folder->prefs &&
1875                                 msginfo->folder->prefs->forward_with_format) {
1876                         qmark = msginfo->folder->prefs->forward_quotemark;
1877                         body_fmt = msginfo->folder->prefs->forward_body_format;
1878
1879                 } else if (account->forward_with_format) {
1880                         qmark = account->forward_quotemark;
1881                         body_fmt = account->forward_body_format;
1882
1883                 } else {
1884                         qmark = prefs_common.fw_quotemark;
1885                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1886                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1887                         else
1888                                 body_fmt = "";
1889                 }
1890
1891                 /* empty quotemark is not allowed */
1892                 if (qmark == NULL || *qmark == '\0')
1893                         qmark = "> ";
1894
1895                 compose_quote_fmt(compose, full_msginfo,
1896                                   body_fmt, qmark, body, FALSE, TRUE,
1897                                           _("The body of the \"Forward\" template has an error at line %d."));
1898                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1899                 quote_fmt_reset_vartable();
1900                 compose_attach_parts(compose, msginfo);
1901
1902                 procmsg_msginfo_free(&full_msginfo);
1903         }
1904
1905         SIGNAL_BLOCK(textbuf);
1906
1907         if (account->auto_sig)
1908                 compose_insert_sig(compose, FALSE);
1909
1910         compose_wrap_all(compose);
1911
1912 #ifdef USE_ENCHANT
1913         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1914                 gtkaspell_highlight_all(compose->gtkaspell);
1915         gtkaspell_unblock_check(compose->gtkaspell);
1916 #endif
1917         SIGNAL_UNBLOCK(textbuf);
1918         
1919         cursor_pos = quote_fmt_get_cursor_pos();
1920         if (cursor_pos == -1)
1921                 gtk_widget_grab_focus(compose->header_last->entry);
1922         else
1923                 gtk_widget_grab_focus(compose->text);
1924
1925         if (!no_extedit && prefs_common.auto_exteditor)
1926                 compose_exec_ext_editor(compose);
1927         
1928         /*save folder*/
1929         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1930                 gchar *folderidentifier;
1931
1932                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1933                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1934                 compose_set_save_to(compose, folderidentifier);
1935                 g_free(folderidentifier);
1936         }
1937
1938         undo_unblock(compose->undostruct);
1939         
1940         compose->modified = FALSE;
1941         compose_set_title(compose);
1942
1943         compose->updating = FALSE;
1944         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1945         SCROLL_TO_CURSOR(compose);
1946
1947         if (compose->deferred_destroy) {
1948                 compose_destroy(compose);
1949                 return NULL;
1950         }
1951
1952         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1953
1954         return compose;
1955 }
1956
1957 #undef INSERT_FW_HEADER
1958
1959 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1960 {
1961         Compose *compose;
1962         GtkTextView *textview;
1963         GtkTextBuffer *textbuf;
1964         GtkTextIter iter;
1965         GSList *msginfo;
1966         gchar *msgfile;
1967         gboolean single_mail = TRUE;
1968         
1969         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1970
1971         if (g_slist_length(msginfo_list) > 1)
1972                 single_mail = FALSE;
1973
1974         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1975                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1976                         return NULL;
1977
1978         /* guess account from first selected message */
1979         if (!account && 
1980             !(account = compose_find_account(msginfo_list->data)))
1981                 account = cur_account;
1982
1983         cm_return_val_if_fail(account != NULL, NULL);
1984
1985         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1986                 if (msginfo->data) {
1987                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1988                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1989                 }
1990         }
1991
1992         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1993                 g_warning("no msginfo_list");
1994                 return NULL;
1995         }
1996
1997         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1998
1999         compose->updating = TRUE;
2000
2001         /* override from name according to folder properties */
2002         if (msginfo_list->data) {
2003                 MsgInfo *msginfo = msginfo_list->data;
2004
2005                 if (msginfo->folder && msginfo->folder->prefs &&
2006                         msginfo->folder->prefs->forward_with_format &&
2007                         msginfo->folder->prefs->forward_override_from_format &&
2008                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
2009
2010                         gchar *tmp = NULL;
2011                         gchar *buf = NULL;
2012
2013                         /* decode \-escape sequences in the internal representation of the quote format */
2014                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
2015                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
2016
2017 #ifdef USE_ENCHANT
2018                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2019                                         compose->gtkaspell);
2020 #else
2021                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2022 #endif
2023                         quote_fmt_scan_string(tmp);
2024                         quote_fmt_parse();
2025
2026                         buf = quote_fmt_get_buffer();
2027                         if (buf == NULL)
2028                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2029                         else
2030                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2031                         quote_fmt_reset_vartable();
2032                         quote_fmtlex_destroy();
2033
2034                         g_free(tmp);
2035                 }
2036         }
2037
2038         textview = GTK_TEXT_VIEW(compose->text);
2039         textbuf = gtk_text_view_get_buffer(textview);
2040         compose_create_tags(textview, compose);
2041         
2042         undo_block(compose->undostruct);
2043         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2044                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2045
2046                 if (!is_file_exist(msgfile))
2047                         g_warning("%s: file does not exist", msgfile);
2048                 else
2049                         compose_attach_append(compose, msgfile, msgfile,
2050                                 "message/rfc822", NULL);
2051                 g_free(msgfile);
2052         }
2053         
2054         if (single_mail) {
2055                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2056                 if (info->subject && *info->subject) {
2057                         gchar *buf, *buf2, *p;
2058
2059                         buf = p = g_strdup(info->subject);
2060                         p += subject_get_prefix_length(p);
2061                         memmove(buf, p, strlen(p) + 1);
2062
2063                         buf2 = g_strdup_printf("Fw: %s", buf);
2064                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2065
2066                         g_free(buf);
2067                         g_free(buf2);
2068                 }
2069         } else {
2070                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2071                         _("Fw: multiple emails"));
2072         }
2073
2074         SIGNAL_BLOCK(textbuf);
2075         
2076         if (account->auto_sig)
2077                 compose_insert_sig(compose, FALSE);
2078
2079         compose_wrap_all(compose);
2080
2081         SIGNAL_UNBLOCK(textbuf);
2082         
2083         gtk_text_buffer_get_start_iter(textbuf, &iter);
2084         gtk_text_buffer_place_cursor(textbuf, &iter);
2085
2086         if (prefs_common.auto_exteditor)
2087                 compose_exec_ext_editor(compose);
2088
2089         gtk_widget_grab_focus(compose->header_last->entry);
2090         undo_unblock(compose->undostruct);
2091         compose->modified = FALSE;
2092         compose_set_title(compose);
2093
2094         compose->updating = FALSE;
2095         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2096         SCROLL_TO_CURSOR(compose);
2097
2098         if (compose->deferred_destroy) {
2099                 compose_destroy(compose);
2100                 return NULL;
2101         }
2102
2103         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2104
2105         return compose;
2106 }
2107
2108 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2109 {
2110         GtkTextIter start = *iter;
2111         GtkTextIter end_iter;
2112         int start_pos = gtk_text_iter_get_offset(&start);
2113         gchar *str = NULL;
2114         if (!compose->account->sig_sep)
2115                 return FALSE;
2116         
2117         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2118                 start_pos+strlen(compose->account->sig_sep));
2119
2120         /* check sig separator */
2121         str = gtk_text_iter_get_text(&start, &end_iter);
2122         if (!strcmp(str, compose->account->sig_sep)) {
2123                 gchar *tmp = NULL;
2124                 /* check end of line (\n) */
2125                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2126                         start_pos+strlen(compose->account->sig_sep));
2127                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2128                         start_pos+strlen(compose->account->sig_sep)+1);
2129                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2130                 if (!strcmp(tmp,"\n")) {
2131                         g_free(str);
2132                         g_free(tmp);
2133                         return TRUE;
2134                 }
2135                 g_free(tmp);    
2136         }
2137         g_free(str);
2138
2139         return FALSE;
2140 }
2141
2142 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2143 {
2144         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2145         Compose *compose = (Compose *)data;
2146         FolderItem *old_item = NULL;
2147         FolderItem *new_item = NULL;
2148         gchar *old_id, *new_id;
2149
2150         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2151          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2152                 return FALSE;
2153
2154         old_item = hookdata->item;
2155         new_item = hookdata->item2;
2156
2157         old_id = folder_item_get_identifier(old_item);
2158         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2159
2160         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2161                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2162                 compose->targetinfo->folder = new_item;
2163         }
2164
2165         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2166                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2167                 compose->replyinfo->folder = new_item;
2168         }
2169
2170         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2171                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2172                 compose->fwdinfo->folder = new_item;
2173         }
2174
2175         g_free(old_id);
2176         g_free(new_id);
2177         return FALSE;
2178 }
2179
2180 static void compose_colorize_signature(Compose *compose)
2181 {
2182         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2183         GtkTextIter iter;
2184         GtkTextIter end_iter;
2185         gtk_text_buffer_get_start_iter(buffer, &iter);
2186         while (gtk_text_iter_forward_line(&iter))
2187                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2188                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2189                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2190                 }
2191 }
2192
2193 #define BLOCK_WRAP() {                                                  \
2194         prev_autowrap = compose->autowrap;                              \
2195         buffer = gtk_text_view_get_buffer(                              \
2196                                         GTK_TEXT_VIEW(compose->text));  \
2197         compose->autowrap = FALSE;                                      \
2198                                                                         \
2199         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2200                                 G_CALLBACK(compose_changed_cb),         \
2201                                 compose);                               \
2202         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2203                                 G_CALLBACK(text_inserted),              \
2204                                 compose);                               \
2205 }
2206 #define UNBLOCK_WRAP() {                                                        \
2207         compose->autowrap = prev_autowrap;                                      \
2208         if (compose->autowrap) {                                                \
2209                 gint old = compose->draft_timeout_tag;                          \
2210                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2211                 compose_wrap_all(compose);                                      \
2212                 compose->draft_timeout_tag = old;                               \
2213         }                                                                       \
2214                                                                                 \
2215         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2216                                 G_CALLBACK(compose_changed_cb),                 \
2217                                 compose);                                       \
2218         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2219                                 G_CALLBACK(text_inserted),                      \
2220                                 compose);                                       \
2221 }
2222
2223 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2224 {
2225         Compose *compose = NULL;
2226         PrefsAccount *account = NULL;
2227         GtkTextView *textview;
2228         GtkTextBuffer *textbuf;
2229         GtkTextMark *mark;
2230         GtkTextIter iter;
2231         FILE *fp;
2232         gboolean use_signing = FALSE;
2233         gboolean use_encryption = FALSE;
2234         gchar *privacy_system = NULL;
2235         int priority = PRIORITY_NORMAL;
2236         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2237         gboolean autowrap = prefs_common.autowrap;
2238         gboolean autoindent = prefs_common.auto_indent;
2239         HeaderEntry *manual_headers = NULL;
2240
2241         cm_return_val_if_fail(msginfo != NULL, NULL);
2242         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2243
2244         if (compose_put_existing_to_front(msginfo)) {
2245                 return NULL;
2246         }
2247
2248         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2249             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2250             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2251                 gchar *queueheader_buf = NULL;
2252                 gint id, param;
2253
2254                 /* Select Account from queue headers */
2255                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2256                                                                                         "X-Claws-Account-Id:")) {
2257                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2258                         account = account_find_from_id(id);
2259                         g_free(queueheader_buf);
2260                 }
2261                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2262                                                                                         "X-Sylpheed-Account-Id:")) {
2263                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2264                         account = account_find_from_id(id);
2265                         g_free(queueheader_buf);
2266                 }
2267                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2268                                                                                         "NAID:")) {
2269                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2270                         account = account_find_from_id(id);
2271                         g_free(queueheader_buf);
2272                 }
2273                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2274                                                                                         "MAID:")) {
2275                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2276                         account = account_find_from_id(id);
2277                         g_free(queueheader_buf);
2278                 }
2279                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2280                                                                                         "S:")) {
2281                         account = account_find_from_address(queueheader_buf, FALSE);
2282                         g_free(queueheader_buf);
2283                 }
2284                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2285                                                                                 "X-Claws-Sign:")) {
2286                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2287                         use_signing = param;
2288                         g_free(queueheader_buf);
2289                 }
2290                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2291                                                                                 "X-Sylpheed-Sign:")) {
2292                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2293                         use_signing = param;
2294                         g_free(queueheader_buf);
2295                 }
2296                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2297                                                                                 "X-Claws-Encrypt:")) {
2298                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2299                         use_encryption = param;
2300                         g_free(queueheader_buf);
2301                 }
2302                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2303                                                                                 "X-Sylpheed-Encrypt:")) {
2304                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2305                         use_encryption = param;
2306                         g_free(queueheader_buf);
2307                 }
2308                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2309                                                                                 "X-Claws-Auto-Wrapping:")) {
2310                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2311                         autowrap = param;
2312                         g_free(queueheader_buf);
2313                 }
2314                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2315                                                                                 "X-Claws-Auto-Indent:")) {
2316                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2317                         autoindent = param;
2318                         g_free(queueheader_buf);
2319                 }
2320                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2321                                         "X-Claws-Privacy-System:")) {
2322                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2323                         g_free(queueheader_buf);
2324                 }
2325                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2326                                         "X-Sylpheed-Privacy-System:")) {
2327                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2328                         g_free(queueheader_buf);
2329                 }
2330                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2331                                                                                 "X-Priority: ")) {
2332                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2333                         priority = param;
2334                         g_free(queueheader_buf);
2335                 }
2336                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2337                                                                                         "RMID:")) {
2338                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2339                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2340                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2341                                 if (orig_item != NULL) {
2342                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2343                                 }
2344                                 g_strfreev(tokens);
2345                         }
2346                         g_free(queueheader_buf);
2347                 }
2348                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2349                                                                                 "FMID:")) {
2350                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2351                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2352                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2353                                 if (orig_item != NULL) {
2354                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2355                                 }
2356                                 g_strfreev(tokens);
2357                         }
2358                         g_free(queueheader_buf);
2359                 }
2360                 /* Get manual headers */
2361                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2362                                                                                         "X-Claws-Manual-Headers:")) {
2363                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2364                         if (listmh && *listmh != '\0') {
2365                                 debug_print("Got manual headers: %s\n", listmh);
2366                                 manual_headers = procheader_entries_from_str(listmh);
2367                                 g_free(listmh);
2368                         }
2369                         g_free(queueheader_buf);
2370                 }
2371         } else {
2372                 account = msginfo->folder->folder->account;
2373         }
2374
2375         if (!account && prefs_common.reedit_account_autosel) {
2376                 gchar *from = NULL;
2377                 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2378                         extract_address(from);
2379                         account = account_find_from_address(from, FALSE);
2380                         g_free(from);
2381                 }
2382         }
2383         if (!account) {
2384                 account = cur_account;
2385         }
2386         cm_return_val_if_fail(account != NULL, NULL);
2387
2388         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2389
2390         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2391         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2392         compose->autowrap = autowrap;
2393         compose->replyinfo = replyinfo;
2394         compose->fwdinfo = fwdinfo;
2395
2396         compose->updating = TRUE;
2397         compose->priority = priority;
2398
2399         if (privacy_system != NULL) {
2400                 compose->privacy_system = privacy_system;
2401                 compose_use_signing(compose, use_signing);
2402                 compose_use_encryption(compose, use_encryption);
2403                 compose_update_privacy_system_menu_item(compose, FALSE);
2404         } else {
2405                 activate_privacy_system(compose, account, FALSE);
2406         }
2407
2408         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2409
2410         compose_extract_original_charset(compose);
2411
2412         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2413             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2414             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2415                 gchar *queueheader_buf = NULL;
2416
2417                 /* Set message save folder */
2418                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2419                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2420                         compose_set_save_to(compose, &queueheader_buf[4]);
2421                         g_free(queueheader_buf);
2422                 }
2423                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2424                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2425                         if (active) {
2426                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2427                         }
2428                         g_free(queueheader_buf);
2429                 }
2430         }
2431         
2432         if (compose_parse_header(compose, msginfo) < 0) {
2433                 compose->updating = FALSE;
2434                 compose_destroy(compose);
2435                 return NULL;
2436         }
2437         compose_reedit_set_entry(compose, msginfo);
2438
2439         textview = GTK_TEXT_VIEW(compose->text);
2440         textbuf = gtk_text_view_get_buffer(textview);
2441         compose_create_tags(textview, compose);
2442
2443         mark = gtk_text_buffer_get_insert(textbuf);
2444         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2445
2446         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2447                                         G_CALLBACK(compose_changed_cb),
2448                                         compose);
2449         
2450         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2451                 fp = procmime_get_first_encrypted_text_content(msginfo);
2452                 if (fp) {
2453                         compose_force_encryption(compose, account, TRUE, NULL);
2454                 }
2455         } else {
2456                 fp = procmime_get_first_text_content(msginfo);
2457         }
2458         if (fp == NULL) {
2459                 g_warning("Can't get text part");
2460         }
2461
2462         if (fp != NULL) {
2463                 gchar buf[BUFFSIZE];
2464                 gboolean prev_autowrap;
2465                 GtkTextBuffer *buffer;
2466                 BLOCK_WRAP();
2467                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2468                         strcrchomp(buf);
2469                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2470                 }
2471                 UNBLOCK_WRAP();
2472                 fclose(fp);
2473         }
2474         
2475         compose_attach_parts(compose, msginfo);
2476
2477         compose_colorize_signature(compose);
2478
2479         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2480                                         G_CALLBACK(compose_changed_cb),
2481                                         compose);
2482
2483         if (manual_headers != NULL) {
2484                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2485                         procheader_entries_free(manual_headers);
2486                         compose->updating = FALSE;
2487                         compose_destroy(compose);
2488                         return NULL;
2489                 }
2490                 procheader_entries_free(manual_headers);
2491         }
2492
2493         gtk_widget_grab_focus(compose->text);
2494
2495         if (prefs_common.auto_exteditor) {
2496                 compose_exec_ext_editor(compose);
2497         }
2498         compose->modified = FALSE;
2499         compose_set_title(compose);
2500
2501         compose->updating = FALSE;
2502         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2503         SCROLL_TO_CURSOR(compose);
2504
2505         if (compose->deferred_destroy) {
2506                 compose_destroy(compose);
2507                 return NULL;
2508         }
2509         
2510         compose->sig_str = account_get_signature_str(compose->account);
2511         
2512         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2513
2514         return compose;
2515 }
2516
2517 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2518                                                  gboolean batch)
2519 {
2520         Compose *compose;
2521         gchar *filename;
2522         FolderItem *item;
2523
2524         cm_return_val_if_fail(msginfo != NULL, NULL);
2525
2526         if (!account)
2527                 account = account_get_reply_account(msginfo,
2528                                         prefs_common.reply_account_autosel);
2529         cm_return_val_if_fail(account != NULL, NULL);
2530
2531         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2532
2533         compose->updating = TRUE;
2534
2535         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2536         compose->replyinfo = NULL;
2537         compose->fwdinfo = NULL;
2538
2539         compose_show_first_last_header(compose, TRUE);
2540
2541         gtk_widget_grab_focus(compose->header_last->entry);
2542
2543         filename = procmsg_get_message_file(msginfo);
2544
2545         if (filename == NULL) {
2546                 compose->updating = FALSE;
2547                 compose_destroy(compose);
2548
2549                 return NULL;
2550         }
2551
2552         compose->redirect_filename = filename;
2553         
2554         /* Set save folder */
2555         item = msginfo->folder;
2556         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2557                 gchar *folderidentifier;
2558
2559                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2560                 folderidentifier = folder_item_get_identifier(item);
2561                 compose_set_save_to(compose, folderidentifier);
2562                 g_free(folderidentifier);
2563         }
2564
2565         compose_attach_parts(compose, msginfo);
2566
2567         if (msginfo->subject)
2568                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2569                                    msginfo->subject);
2570         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2571
2572         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2573                                           _("The body of the \"Redirect\" template has an error at line %d."));
2574         quote_fmt_reset_vartable();
2575         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2576
2577         compose_colorize_signature(compose);
2578
2579         
2580         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2581         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2582         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2583
2584         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2585         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2586         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2587         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2588         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2589         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2590         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2591         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2592         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2593         
2594         if (compose->toolbar->draft_btn)
2595                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2596         if (compose->toolbar->insert_btn)
2597                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2598         if (compose->toolbar->attach_btn)
2599                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2600         if (compose->toolbar->sig_btn)
2601                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2602         if (compose->toolbar->exteditor_btn)
2603                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2604         if (compose->toolbar->linewrap_current_btn)
2605                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2606         if (compose->toolbar->linewrap_all_btn)
2607                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2608
2609         compose->modified = FALSE;
2610         compose_set_title(compose);
2611         compose->updating = FALSE;
2612         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2613         SCROLL_TO_CURSOR(compose);
2614
2615         if (compose->deferred_destroy) {
2616                 compose_destroy(compose);
2617                 return NULL;
2618         }
2619         
2620         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2621
2622         return compose;
2623 }
2624
2625 const GList *compose_get_compose_list(void)
2626 {
2627         return compose_list;
2628 }
2629
2630 void compose_entry_append(Compose *compose, const gchar *address,
2631                           ComposeEntryType type, ComposePrefType pref_type)
2632 {
2633         const gchar *header;
2634         gchar *cur, *begin;
2635         gboolean in_quote = FALSE;
2636         if (!address || *address == '\0') return;
2637
2638         switch (type) {
2639         case COMPOSE_CC:
2640                 header = N_("Cc:");
2641                 break;
2642         case COMPOSE_BCC:
2643                 header = N_("Bcc:");
2644                 break;
2645         case COMPOSE_REPLYTO:
2646                 header = N_("Reply-To:");
2647                 break;
2648         case COMPOSE_NEWSGROUPS:
2649                 header = N_("Newsgroups:");
2650                 break;
2651         case COMPOSE_FOLLOWUPTO:
2652                 header = N_( "Followup-To:");
2653                 break;
2654         case COMPOSE_INREPLYTO:
2655                 header = N_( "In-Reply-To:");
2656                 break;
2657         case COMPOSE_TO:
2658         default:
2659                 header = N_("To:");
2660                 break;
2661         }
2662         header = prefs_common_translated_header_name(header);
2663         
2664         cur = begin = (gchar *)address;
2665         
2666         /* we separate the line by commas, but not if we're inside a quoted
2667          * string */
2668         while (*cur != '\0') {
2669                 if (*cur == '"') 
2670                         in_quote = !in_quote;
2671                 if (*cur == ',' && !in_quote) {
2672                         gchar *tmp = g_strdup(begin);
2673                         gchar *o_tmp = tmp;
2674                         tmp[cur-begin]='\0';
2675                         cur++;
2676                         begin = cur;
2677                         while (*tmp == ' ' || *tmp == '\t')
2678                                 tmp++;
2679                         compose_add_header_entry(compose, header, tmp, pref_type);
2680                         compose_entry_indicate(compose, tmp);
2681                         g_free(o_tmp);
2682                         continue;
2683                 }
2684                 cur++;
2685         }
2686         if (begin < cur) {
2687                 gchar *tmp = g_strdup(begin);
2688                 gchar *o_tmp = tmp;
2689                 tmp[cur-begin]='\0';
2690                 while (*tmp == ' ' || *tmp == '\t')
2691                         tmp++;
2692                 compose_add_header_entry(compose, header, tmp, pref_type);
2693                 compose_entry_indicate(compose, tmp);
2694                 g_free(o_tmp);          
2695         }
2696 }
2697
2698 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2699 {
2700         GSList *h_list;
2701         GtkEntry *entry;
2702                 
2703         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2704                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2705                 if (gtk_entry_get_text(entry) && 
2706                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2707                                 gtk_widget_modify_base(
2708                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2709                                         GTK_STATE_NORMAL, &default_header_bgcolor);
2710                                 gtk_widget_modify_text(
2711                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2712                                         GTK_STATE_NORMAL, &default_header_color);
2713                 }
2714         }
2715 }
2716
2717 void compose_toolbar_cb(gint action, gpointer data)
2718 {
2719         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2720         Compose *compose = (Compose*)toolbar_item->parent;
2721         
2722         cm_return_if_fail(compose != NULL);
2723
2724         switch(action) {
2725         case A_SEND:
2726                 compose_send_cb(NULL, compose);
2727                 break;
2728         case A_SEND_LATER:
2729                 compose_send_later_cb(NULL, compose);
2730                 break;
2731         case A_DRAFT:
2732                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2733                 break;
2734         case A_INSERT:
2735                 compose_insert_file_cb(NULL, compose);
2736                 break;
2737         case A_ATTACH:
2738                 compose_attach_cb(NULL, compose);
2739                 break;
2740         case A_SIG:
2741                 compose_insert_sig(compose, FALSE);
2742                 break;
2743         case A_REP_SIG:
2744                 compose_insert_sig(compose, TRUE);
2745                 break;
2746         case A_EXTEDITOR:
2747                 compose_ext_editor_cb(NULL, compose);
2748                 break;
2749         case A_LINEWRAP_CURRENT:
2750                 compose_beautify_paragraph(compose, NULL, TRUE);
2751                 break;
2752         case A_LINEWRAP_ALL:
2753                 compose_wrap_all_full(compose, TRUE);
2754                 break;
2755         case A_ADDRBOOK:
2756                 compose_address_cb(NULL, compose);
2757                 break;
2758 #ifdef USE_ENCHANT
2759         case A_CHECK_SPELLING:
2760                 compose_check_all(NULL, compose);
2761                 break;
2762 #endif
2763         case A_PRIVACY_SIGN:
2764                 break;
2765         case A_PRIVACY_ENCRYPT:
2766                 break;
2767         default:
2768                 break;
2769         }
2770 }
2771
2772 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2773 {
2774         gchar *to = NULL;
2775         gchar *cc = NULL;
2776         gchar *bcc = NULL;
2777         gchar *subject = NULL;
2778         gchar *body = NULL;
2779         gchar *temp = NULL;
2780         gsize  len = 0;
2781         gchar **attach = NULL;
2782         gchar *inreplyto = NULL;
2783         MailField mfield = NO_FIELD_PRESENT;
2784
2785         /* get mailto parts but skip from */
2786         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2787
2788         if (to) {
2789                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2790                 mfield = TO_FIELD_PRESENT;
2791         }
2792         if (cc)
2793                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2794         if (bcc)
2795                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2796         if (subject) {
2797                 if (!g_utf8_validate (subject, -1, NULL)) {
2798                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2799                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2800                         g_free(temp);
2801                 } else {
2802                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2803                 }
2804                 mfield = SUBJECT_FIELD_PRESENT;
2805         }
2806         if (body) {
2807                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2808                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2809                 GtkTextMark *mark;
2810                 GtkTextIter iter;
2811                 gboolean prev_autowrap = compose->autowrap;
2812
2813                 compose->autowrap = FALSE;
2814
2815                 mark = gtk_text_buffer_get_insert(buffer);
2816                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2817
2818                 if (!g_utf8_validate (body, -1, NULL)) {
2819                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2820                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2821                         g_free(temp);
2822                 } else {
2823                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2824                 }
2825                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2826
2827                 compose->autowrap = prev_autowrap;
2828                 if (compose->autowrap)
2829                         compose_wrap_all(compose);
2830                 mfield = BODY_FIELD_PRESENT;
2831         }
2832
2833         if (attach) {
2834                 gint i = 0, att = 0;
2835                 gchar *warn_files = NULL;
2836                 while (attach[i] != NULL) {
2837                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2838                         if (utf8_filename) {
2839                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2840                                         gchar *tmp = g_strdup_printf("%s%s\n",
2841                                                         warn_files?warn_files:"",
2842                                                         utf8_filename);
2843                                         g_free(warn_files);
2844                                         warn_files = tmp;
2845                                         att++;
2846                                 }
2847                                 g_free(utf8_filename);
2848                         } else {
2849                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2850                         }
2851                         i++;
2852                 }
2853                 if (warn_files) {
2854                         alertpanel_notice(ngettext(
2855                         "The following file has been attached: \n%s",
2856                         "The following files have been attached: \n%s", att), warn_files);
2857                         g_free(warn_files);
2858                 }
2859         }
2860         if (inreplyto)
2861                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2862
2863         g_free(to);
2864         g_free(cc);
2865         g_free(bcc);
2866         g_free(subject);
2867         g_free(body);
2868         g_strfreev(attach);
2869         g_free(inreplyto);
2870         
2871         return mfield;
2872 }
2873
2874 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2875 {
2876         static HeaderEntry hentry[] = {
2877                                        {"Reply-To:",    NULL, TRUE },
2878                                        {"Cc:",          NULL, TRUE },
2879                                        {"References:",  NULL, FALSE },
2880                                        {"Bcc:",         NULL, TRUE },
2881                                        {"Newsgroups:",  NULL, TRUE },
2882                                        {"Followup-To:", NULL, TRUE },
2883                                        {"List-Post:",   NULL, FALSE },
2884                                        {"X-Priority:",  NULL, FALSE },
2885                                        {NULL,           NULL, FALSE }
2886         };
2887
2888         enum
2889         {
2890                 H_REPLY_TO    = 0,
2891                 H_CC          = 1,
2892                 H_REFERENCES  = 2,
2893                 H_BCC         = 3,
2894                 H_NEWSGROUPS  = 4,
2895                 H_FOLLOWUP_TO = 5,
2896                 H_LIST_POST   = 6,
2897                 H_X_PRIORITY  = 7
2898         };
2899
2900         FILE *fp;
2901
2902         cm_return_val_if_fail(msginfo != NULL, -1);
2903
2904         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2905         procheader_get_header_fields(fp, hentry);
2906         fclose(fp);
2907
2908         if (hentry[H_REPLY_TO].body != NULL) {
2909                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2910                         compose->replyto =
2911                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2912                                                    NULL, TRUE);
2913                 }
2914                 g_free(hentry[H_REPLY_TO].body);
2915                 hentry[H_REPLY_TO].body = NULL;
2916         }
2917         if (hentry[H_CC].body != NULL) {
2918                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2919                 g_free(hentry[H_CC].body);
2920                 hentry[H_CC].body = NULL;
2921         }
2922         if (hentry[H_REFERENCES].body != NULL) {
2923                 if (compose->mode == COMPOSE_REEDIT)
2924                         compose->references = hentry[H_REFERENCES].body;
2925                 else {
2926                         compose->references = compose_parse_references
2927                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2928                         g_free(hentry[H_REFERENCES].body);
2929                 }
2930                 hentry[H_REFERENCES].body = NULL;
2931         }
2932         if (hentry[H_BCC].body != NULL) {
2933                 if (compose->mode == COMPOSE_REEDIT)
2934                         compose->bcc =
2935                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2936                 g_free(hentry[H_BCC].body);
2937                 hentry[H_BCC].body = NULL;
2938         }
2939         if (hentry[H_NEWSGROUPS].body != NULL) {
2940                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2941                 hentry[H_NEWSGROUPS].body = NULL;
2942         }
2943         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2944                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2945                         compose->followup_to =
2946                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2947                                                    NULL, TRUE);
2948                 }
2949                 g_free(hentry[H_FOLLOWUP_TO].body);
2950                 hentry[H_FOLLOWUP_TO].body = NULL;
2951         }
2952         if (hentry[H_LIST_POST].body != NULL) {
2953                 gchar *to = NULL, *start = NULL;
2954
2955                 extract_address(hentry[H_LIST_POST].body);
2956                 if (hentry[H_LIST_POST].body[0] != '\0') {
2957                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2958                         
2959                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2960                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2961
2962                         if (to) {
2963                                 g_free(compose->ml_post);
2964                                 compose->ml_post = to;
2965                         }
2966                 }
2967                 g_free(hentry[H_LIST_POST].body);
2968                 hentry[H_LIST_POST].body = NULL;
2969         }
2970
2971         /* CLAWS - X-Priority */
2972         if (compose->mode == COMPOSE_REEDIT)
2973                 if (hentry[H_X_PRIORITY].body != NULL) {
2974                         gint priority;
2975                         
2976                         priority = atoi(hentry[H_X_PRIORITY].body);
2977                         g_free(hentry[H_X_PRIORITY].body);
2978                         
2979                         hentry[H_X_PRIORITY].body = NULL;
2980                         
2981                         if (priority < PRIORITY_HIGHEST || 
2982                             priority > PRIORITY_LOWEST)
2983                                 priority = PRIORITY_NORMAL;
2984                         
2985                         compose->priority =  priority;
2986                 }
2987  
2988         if (compose->mode == COMPOSE_REEDIT) {
2989                 if (msginfo->inreplyto && *msginfo->inreplyto)
2990                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2991
2992                 if (msginfo->msgid && *msginfo->msgid &&
2993                                 compose->folder != NULL &&
2994                                 compose->folder->stype ==  F_DRAFT)
2995                         compose->msgid = g_strdup(msginfo->msgid);
2996         } else {
2997                 if (msginfo->msgid && *msginfo->msgid)
2998                         compose->inreplyto = g_strdup(msginfo->msgid);
2999
3000                 if (!compose->references) {
3001                         if (msginfo->msgid && *msginfo->msgid) {
3002                                 if (msginfo->inreplyto && *msginfo->inreplyto)
3003                                         compose->references =
3004                                                 g_strdup_printf("<%s>\n\t<%s>",
3005                                                                 msginfo->inreplyto,
3006                                                                 msginfo->msgid);
3007                                 else
3008                                         compose->references =
3009                                                 g_strconcat("<", msginfo->msgid, ">",
3010                                                             NULL);
3011                         } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3012                                 compose->references =
3013                                         g_strconcat("<", msginfo->inreplyto, ">",
3014                                                     NULL);
3015                         }
3016                 }
3017         }
3018
3019         return 0;
3020 }
3021
3022 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3023 {
3024         FILE *fp;
3025         HeaderEntry *he;
3026
3027         cm_return_val_if_fail(msginfo != NULL, -1);
3028
3029         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
3030         procheader_get_header_fields(fp, entries);
3031         fclose(fp);
3032
3033         he = entries;
3034         while (he != NULL && he->name != NULL) {
3035                 GtkTreeIter iter;
3036                 GtkListStore *model = NULL;
3037
3038                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3039                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3040                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3041                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3042                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3043                 ++he;
3044         }
3045
3046         return 0;
3047 }
3048
3049 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3050 {
3051         GSList *ref_id_list, *cur;
3052         GString *new_ref;
3053         gchar *new_ref_str;
3054
3055         ref_id_list = references_list_append(NULL, ref);
3056         if (!ref_id_list) return NULL;
3057         if (msgid && *msgid)
3058                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3059
3060         for (;;) {
3061                 gint len = 0;
3062
3063                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3064                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3065                         len += strlen((gchar *)cur->data) + 5;
3066
3067                 if (len > MAX_REFERENCES_LEN) {
3068                         /* remove second message-ID */
3069                         if (ref_id_list && ref_id_list->next &&
3070                             ref_id_list->next->next) {
3071                                 g_free(ref_id_list->next->data);
3072                                 ref_id_list = g_slist_remove
3073                                         (ref_id_list, ref_id_list->next->data);
3074                         } else {
3075                                 slist_free_strings_full(ref_id_list);
3076                                 return NULL;
3077                         }
3078                 } else
3079                         break;
3080         }
3081
3082         new_ref = g_string_new("");
3083         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3084                 if (new_ref->len > 0)
3085                         g_string_append(new_ref, "\n\t");
3086                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3087         }
3088
3089         slist_free_strings_full(ref_id_list);
3090
3091         new_ref_str = new_ref->str;
3092         g_string_free(new_ref, FALSE);
3093
3094         return new_ref_str;
3095 }
3096
3097 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3098                                 const gchar *fmt, const gchar *qmark,
3099                                 const gchar *body, gboolean rewrap,
3100                                 gboolean need_unescape,
3101                                 const gchar *err_msg)
3102 {
3103         MsgInfo* dummyinfo = NULL;
3104         gchar *quote_str = NULL;
3105         gchar *buf;
3106         gboolean prev_autowrap;
3107         const gchar *trimmed_body = body;
3108         gint cursor_pos = -1;
3109         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3110         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3111         GtkTextIter iter;
3112         GtkTextMark *mark;
3113         
3114
3115         SIGNAL_BLOCK(buffer);
3116
3117         if (!msginfo) {
3118                 dummyinfo = compose_msginfo_new_from_compose(compose);
3119                 msginfo = dummyinfo;
3120         }
3121
3122         if (qmark != NULL) {
3123 #ifdef USE_ENCHANT
3124                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3125                                 compose->gtkaspell);
3126 #else
3127                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3128 #endif
3129                 quote_fmt_scan_string(qmark);
3130                 quote_fmt_parse();
3131
3132                 buf = quote_fmt_get_buffer();
3133
3134                 quote_fmt_reset_vartable();
3135                 quote_fmtlex_destroy();
3136
3137                 if (buf == NULL)
3138                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3139                 else
3140                         Xstrdup_a(quote_str, buf, goto error)
3141         }
3142
3143         if (fmt && *fmt != '\0') {
3144
3145                 if (trimmed_body)
3146                         while (*trimmed_body == '\n')
3147                                 trimmed_body++;
3148
3149 #ifdef USE_ENCHANT
3150                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3151                                 compose->gtkaspell);
3152 #else
3153                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3154 #endif
3155                 if (need_unescape) {
3156                         gchar *tmp = NULL;
3157
3158                         /* decode \-escape sequences in the internal representation of the quote format */
3159                         tmp = g_malloc(strlen(fmt)+1);
3160                         pref_get_unescaped_pref(tmp, fmt);
3161                         quote_fmt_scan_string(tmp);
3162                         quote_fmt_parse();
3163                         g_free(tmp);
3164                 } else {
3165                         quote_fmt_scan_string(fmt);
3166                         quote_fmt_parse();
3167                 }
3168
3169                 buf = quote_fmt_get_buffer();
3170
3171                 if (buf == NULL) {
3172                         gint line = quote_fmt_get_line();
3173                         alertpanel_error(err_msg, line);
3174                         quote_fmt_reset_vartable();
3175                         quote_fmtlex_destroy();
3176
3177                         goto error;
3178                 }
3179                 quote_fmt_reset_vartable();
3180                 quote_fmtlex_destroy();
3181
3182         } else
3183                 buf = "";
3184
3185         prev_autowrap = compose->autowrap;
3186         compose->autowrap = FALSE;
3187
3188         mark = gtk_text_buffer_get_insert(buffer);
3189         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3190         if (g_utf8_validate(buf, -1, NULL)) { 
3191                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3192         } else {
3193                 gchar *tmpout = NULL;
3194                 tmpout = conv_codeset_strdup
3195                         (buf, conv_get_locale_charset_str_no_utf8(),
3196                          CS_INTERNAL);
3197                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3198                         g_free(tmpout);
3199                         tmpout = g_malloc(strlen(buf)*2+1);
3200                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3201                 }
3202                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3203                 g_free(tmpout);
3204         }
3205
3206         cursor_pos = quote_fmt_get_cursor_pos();
3207         if (cursor_pos == -1)
3208                 cursor_pos = gtk_text_iter_get_offset(&iter);
3209         compose->set_cursor_pos = cursor_pos;
3210
3211         gtk_text_buffer_get_start_iter(buffer, &iter);
3212         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3213         gtk_text_buffer_place_cursor(buffer, &iter);
3214
3215         compose->autowrap = prev_autowrap;
3216         if (compose->autowrap && rewrap)
3217                 compose_wrap_all(compose);
3218
3219         goto ok;
3220
3221 error:
3222         buf = NULL;
3223 ok:
3224         SIGNAL_UNBLOCK(buffer);
3225
3226         procmsg_msginfo_free( &dummyinfo );
3227
3228         return buf;
3229 }
3230
3231 /* if ml_post is of type addr@host and from is of type
3232  * addr-anything@host, return TRUE
3233  */
3234 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3235 {
3236         gchar *left_ml = NULL;
3237         gchar *right_ml = NULL;
3238         gchar *left_from = NULL;
3239         gchar *right_from = NULL;
3240         gboolean result = FALSE;
3241         
3242         if (!ml_post || !from)
3243                 return FALSE;
3244         
3245         left_ml = g_strdup(ml_post);
3246         if (strstr(left_ml, "@")) {
3247                 right_ml = strstr(left_ml, "@")+1;
3248                 *(strstr(left_ml, "@")) = '\0';
3249         }
3250         
3251         left_from = g_strdup(from);
3252         if (strstr(left_from, "@")) {
3253                 right_from = strstr(left_from, "@")+1;
3254                 *(strstr(left_from, "@")) = '\0';
3255         }
3256         
3257         if (right_ml && right_from
3258         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3259         &&  !strcmp(right_from, right_ml)) {
3260                 result = TRUE;
3261         }
3262         g_free(left_ml);
3263         g_free(left_from);
3264         
3265         return result;
3266 }
3267
3268 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3269                                      gboolean respect_default_to)
3270 {
3271         if (!compose)
3272                 return;
3273         if (!folder || !folder->prefs)
3274                 return;
3275
3276         if (respect_default_to && folder->prefs->enable_default_to) {
3277                 compose_entry_append(compose, folder->prefs->default_to,
3278                                         COMPOSE_TO, PREF_FOLDER);
3279                 compose_entry_indicate(compose, folder->prefs->default_to);
3280         }
3281         if (folder->prefs->enable_default_cc) {
3282                 compose_entry_append(compose, folder->prefs->default_cc,
3283                                         COMPOSE_CC, PREF_FOLDER);
3284                 compose_entry_indicate(compose, folder->prefs->default_cc);
3285         }
3286         if (folder->prefs->enable_default_bcc) {
3287                 compose_entry_append(compose, folder->prefs->default_bcc,
3288                                         COMPOSE_BCC, PREF_FOLDER);
3289                 compose_entry_indicate(compose, folder->prefs->default_bcc);
3290         }
3291         if (folder->prefs->enable_default_replyto) {
3292                 compose_entry_append(compose, folder->prefs->default_replyto,
3293                                         COMPOSE_REPLYTO, PREF_FOLDER);
3294                 compose_entry_indicate(compose, folder->prefs->default_replyto);
3295         }
3296 }
3297
3298 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3299 {
3300         gchar *buf, *buf2;
3301         gchar *p;
3302         
3303         if (!compose || !msginfo)
3304                 return;
3305
3306         if (msginfo->subject && *msginfo->subject) {
3307                 buf = p = g_strdup(msginfo->subject);
3308                 p += subject_get_prefix_length(p);
3309                 memmove(buf, p, strlen(p) + 1);
3310
3311                 buf2 = g_strdup_printf("Re: %s", buf);
3312                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3313
3314                 g_free(buf2);
3315                 g_free(buf);
3316         } else
3317                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3318 }
3319
3320 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3321                                     gboolean to_all, gboolean to_ml,
3322                                     gboolean to_sender,
3323                                     gboolean followup_and_reply_to)
3324 {
3325         GSList *cc_list = NULL;
3326         GSList *cur;
3327         gchar *from = NULL;
3328         gchar *replyto = NULL;
3329         gchar *ac_email = NULL;
3330
3331         gboolean reply_to_ml = FALSE;
3332         gboolean default_reply_to = FALSE;
3333
3334         cm_return_if_fail(compose->account != NULL);
3335         cm_return_if_fail(msginfo != NULL);
3336
3337         reply_to_ml = to_ml && compose->ml_post;
3338
3339         default_reply_to = msginfo->folder && 
3340                 msginfo->folder->prefs->enable_default_reply_to;
3341
3342         if (compose->account->protocol != A_NNTP) {
3343                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3344
3345                 if (reply_to_ml && !default_reply_to) {
3346                         
3347                         gboolean is_subscr = is_subscription(compose->ml_post,
3348                                                              msginfo->from);
3349                         if (!is_subscr) {
3350                                 /* normal answer to ml post with a reply-to */
3351                                 compose_entry_append(compose,
3352                                            compose->ml_post,
3353                                            COMPOSE_TO, PREF_ML);
3354                                 if (compose->replyto)
3355                                         compose_entry_append(compose,
3356                                                 compose->replyto,
3357                                                 COMPOSE_CC, PREF_ML);
3358                         } else {
3359                                 /* answer to subscription confirmation */
3360                                 if (compose->replyto)
3361                                         compose_entry_append(compose,
3362                                                 compose->replyto,
3363                                                 COMPOSE_TO, PREF_ML);
3364                                 else if (msginfo->from)
3365                                         compose_entry_append(compose,
3366                                                 msginfo->from,
3367                                                 COMPOSE_TO, PREF_ML);
3368                         }
3369                 }
3370                 else if (!(to_all || to_sender) && default_reply_to) {
3371                         compose_entry_append(compose,
3372                             msginfo->folder->prefs->default_reply_to,
3373                             COMPOSE_TO, PREF_FOLDER);
3374                         compose_entry_indicate(compose,
3375                                 msginfo->folder->prefs->default_reply_to);
3376                 } else {
3377                         gchar *tmp1 = NULL;
3378                         if (!msginfo->from)
3379                                 return;
3380                         if (to_sender)
3381                                 compose_entry_append(compose, msginfo->from,
3382                                                      COMPOSE_TO, PREF_NONE);
3383                         else if (to_all) {
3384                                 Xstrdup_a(tmp1, msginfo->from, return);
3385                                 extract_address(tmp1);
3386                                 compose_entry_append(compose,
3387                                  (!account_find_from_address(tmp1, FALSE))
3388                                           ? msginfo->from :
3389                                           msginfo->to,
3390                                           COMPOSE_TO, PREF_NONE);
3391                                 if (compose->replyto)
3392                                                 compose_entry_append(compose,
3393                                                         compose->replyto,
3394                                                         COMPOSE_CC, PREF_NONE);
3395                         } else {
3396                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3397                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3398                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3399                                         if (compose->replyto) {
3400                                                 compose_entry_append(compose,
3401                                                         compose->replyto,
3402                                                         COMPOSE_TO, PREF_NONE);
3403                                         } else {
3404                                                 compose_entry_append(compose,
3405                                                           msginfo->from ? msginfo->from : "",
3406                                                           COMPOSE_TO, PREF_NONE);
3407                                         }
3408                                 } else {
3409                                         /* replying to own mail, use original recp */
3410                                         compose_entry_append(compose,
3411                                                   msginfo->to ? msginfo->to : "",
3412                                                   COMPOSE_TO, PREF_NONE);
3413                                         compose_entry_append(compose,
3414                                                   msginfo->cc ? msginfo->cc : "",
3415                                                   COMPOSE_CC, PREF_NONE);
3416                                 }
3417                         }
3418                 }
3419         } else {
3420                 if (to_sender || (compose->followup_to && 
3421                         !strncmp(compose->followup_to, "poster", 6)))
3422                         compose_entry_append
3423                                 (compose, 
3424                                  (compose->replyto ? compose->replyto :
3425                                         msginfo->from ? msginfo->from : ""),
3426                                  COMPOSE_TO, PREF_NONE);
3427                                  
3428                 else if (followup_and_reply_to || to_all) {
3429                         compose_entry_append
3430                                 (compose,
3431                                  (compose->replyto ? compose->replyto :
3432                                  msginfo->from ? msginfo->from : ""),
3433                                  COMPOSE_TO, PREF_NONE);                                
3434                 
3435                         compose_entry_append
3436                                 (compose,
3437                                  compose->followup_to ? compose->followup_to :
3438                                  compose->newsgroups ? compose->newsgroups : "",
3439                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3440
3441                         compose_entry_append
3442                                 (compose,
3443                                  msginfo->cc ? msginfo->cc : "",
3444                                  COMPOSE_CC, PREF_NONE);
3445                 } 
3446                 else 
3447                         compose_entry_append
3448                                 (compose,
3449                                  compose->followup_to ? compose->followup_to :
3450                                  compose->newsgroups ? compose->newsgroups : "",
3451                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3452         }
3453         compose_reply_set_subject(compose, msginfo);
3454
3455         if (to_ml && compose->ml_post) return;
3456         if (!to_all || compose->account->protocol == A_NNTP) return;
3457
3458         if (compose->replyto) {
3459                 Xstrdup_a(replyto, compose->replyto, return);
3460                 extract_address(replyto);
3461         }
3462         if (msginfo->from) {
3463                 Xstrdup_a(from, msginfo->from, return);
3464                 extract_address(from);
3465         }
3466
3467         if (replyto && from)
3468                 cc_list = address_list_append_with_comments(cc_list, from);
3469         if (to_all && msginfo->folder && 
3470             msginfo->folder->prefs->enable_default_reply_to)
3471                 cc_list = address_list_append_with_comments(cc_list,
3472                                 msginfo->folder->prefs->default_reply_to);
3473         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3474         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3475
3476         ac_email = g_utf8_strdown(compose->account->address, -1);
3477
3478         if (cc_list) {
3479                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3480                         gchar *addr = g_utf8_strdown(cur->data, -1);
3481                         extract_address(addr);
3482                 
3483                         if (strcmp(ac_email, addr))
3484                                 compose_entry_append(compose, (gchar *)cur->data,
3485                                                      COMPOSE_CC, PREF_NONE);
3486                         else
3487                                 debug_print("Cc address same as compose account's, ignoring\n");
3488
3489                         g_free(addr);
3490                 }
3491                 
3492                 slist_free_strings_full(cc_list);
3493         }
3494         
3495         g_free(ac_email);
3496 }
3497
3498 #define SET_ENTRY(entry, str) \
3499 { \
3500         if (str && *str) \
3501                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3502 }
3503
3504 #define SET_ADDRESS(type, str) \
3505 { \
3506         if (str && *str) \
3507                 compose_entry_append(compose, str, type, PREF_NONE); \
3508 }
3509
3510 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3511 {
3512         cm_return_if_fail(msginfo != NULL);
3513
3514         SET_ENTRY(subject_entry, msginfo->subject);
3515         SET_ENTRY(from_name, msginfo->from);
3516         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3517         SET_ADDRESS(COMPOSE_CC, compose->cc);
3518         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3519         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3520         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3521         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3522
3523         compose_update_priority_menu_item(compose);
3524         compose_update_privacy_system_menu_item(compose, FALSE);
3525         compose_show_first_last_header(compose, TRUE);
3526 }
3527
3528 #undef SET_ENTRY
3529 #undef SET_ADDRESS
3530
3531 static void compose_insert_sig(Compose *compose, gboolean replace)
3532 {
3533         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3534         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3535         GtkTextMark *mark;
3536         GtkTextIter iter, iter_end;
3537         gint cur_pos, ins_pos;
3538         gboolean prev_autowrap;
3539         gboolean found = FALSE;
3540         gboolean exists = FALSE;
3541         
3542         cm_return_if_fail(compose->account != NULL);
3543
3544         BLOCK_WRAP();
3545
3546         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3547                                         G_CALLBACK(compose_changed_cb),
3548                                         compose);
3549         
3550         mark = gtk_text_buffer_get_insert(buffer);
3551         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3552         cur_pos = gtk_text_iter_get_offset (&iter);
3553         ins_pos = cur_pos;
3554
3555         gtk_text_buffer_get_end_iter(buffer, &iter);
3556
3557         exists = (compose->sig_str != NULL);
3558
3559         if (replace) {
3560                 GtkTextIter first_iter, start_iter, end_iter;
3561
3562                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3563
3564                 if (!exists || compose->sig_str[0] == '\0')
3565                         found = FALSE;
3566                 else
3567                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3568                                         compose->signature_tag);
3569
3570                 if (found) {
3571                         /* include previous \n\n */
3572                         gtk_text_iter_backward_chars(&first_iter, 1);
3573                         start_iter = first_iter;
3574                         end_iter = first_iter;
3575                         /* skip re-start */
3576                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3577                                         compose->signature_tag);
3578                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3579                                         compose->signature_tag);
3580                         if (found) {
3581                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3582                                 iter = start_iter;
3583                         }
3584                 } 
3585         } 
3586
3587         g_free(compose->sig_str);
3588         compose->sig_str = account_get_signature_str(compose->account);
3589
3590         cur_pos = gtk_text_iter_get_offset(&iter);
3591
3592         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3593                 g_free(compose->sig_str);
3594                 compose->sig_str = NULL;
3595         } else {
3596                 if (compose->sig_inserted == FALSE)
3597                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3598                 compose->sig_inserted = TRUE;
3599
3600                 cur_pos = gtk_text_iter_get_offset(&iter);
3601                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3602                 /* remove \n\n */
3603                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3604                 gtk_text_iter_forward_chars(&iter, 1);
3605                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3606                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3607
3608                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3609                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3610         }
3611
3612         /* put the cursor where it should be 
3613          * either where the quote_fmt says, either where it was */
3614         if (compose->set_cursor_pos < 0)
3615                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3616         else
3617                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3618                         compose->set_cursor_pos);
3619         
3620         compose->set_cursor_pos = -1;
3621         gtk_text_buffer_place_cursor(buffer, &iter);
3622         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3623                                         G_CALLBACK(compose_changed_cb),
3624                                         compose);
3625                 
3626         UNBLOCK_WRAP();
3627 }
3628
3629 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3630 {
3631         GtkTextView *text;
3632         GtkTextBuffer *buffer;
3633         GtkTextMark *mark;
3634         GtkTextIter iter;
3635         const gchar *cur_encoding;
3636         gchar buf[BUFFSIZE];
3637         gint len;
3638         FILE *fp;
3639         gboolean prev_autowrap;
3640         GStatBuf file_stat;
3641         int ret;
3642         GString *file_contents = NULL;
3643         ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3644
3645         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3646
3647         /* get the size of the file we are about to inser