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