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