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