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