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