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