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