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