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