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