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