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