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