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