2010-01-03 [mir] 3.7.3cvs51
[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                         mfield = TO_FIELD_PRESENT;
1063                 }
1064                 /*
1065                  * CLAWS: just don't allow return receipt request, even if the user
1066                  * may want to send an email. simple but foolproof.
1067                  */
1068                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1069         }
1070         compose_add_field_list( compose, listAddress );
1071
1072         if (item && item->prefs && item->prefs->compose_with_format) {
1073                 subject_format = item->prefs->compose_subject_format;
1074                 body_format = item->prefs->compose_body_format;
1075         } else if (account->compose_with_format) {
1076                 subject_format = account->compose_subject_format;
1077                 body_format = account->compose_body_format;
1078         } else if (prefs_common.compose_with_format) {
1079                 subject_format = prefs_common.compose_subject_format;
1080                 body_format = prefs_common.compose_body_format;
1081         }
1082
1083         if (subject_format || body_format) {
1084
1085                 if ( subject_format
1086                          && *subject_format != '\0' )
1087                 {
1088                         gchar *subject = NULL;
1089                         gchar *tmp = NULL;
1090                         gchar *buf = NULL;
1091
1092                         if (!dummyinfo)
1093                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1094
1095                         /* decode \-escape sequences in the internal representation of the quote format */
1096                         tmp = malloc(strlen(subject_format)+1);
1097                         pref_get_unescaped_pref(tmp, subject_format);
1098
1099                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1100 #ifdef USE_ENCHANT
1101                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1102                                         compose->gtkaspell);
1103 #else
1104                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1105 #endif
1106                         quote_fmt_scan_string(tmp);
1107                         quote_fmt_parse();
1108
1109                         buf = quote_fmt_get_buffer();
1110                         if (buf == NULL)
1111                                 alertpanel_error(_("New message subject format error."));
1112                         else
1113                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1114                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1115                         quote_fmt_reset_vartable();
1116
1117                         g_free(subject);
1118                         g_free(tmp);
1119                         mfield = SUBJECT_FIELD_PRESENT;
1120                 }
1121
1122                 if ( body_format
1123                          && *body_format != '\0' )
1124                 {
1125                         GtkTextView *text;
1126                         GtkTextBuffer *buffer;
1127                         GtkTextIter start, end;
1128                         gchar *tmp = NULL;
1129
1130                         if (!dummyinfo)
1131                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1132
1133                         text = GTK_TEXT_VIEW(compose->text);
1134                         buffer = gtk_text_view_get_buffer(text);
1135                         gtk_text_buffer_get_start_iter(buffer, &start);
1136                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1137                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1138
1139                         compose_quote_fmt(compose, dummyinfo,
1140                                           body_format,
1141                                           NULL, tmp, FALSE, TRUE,
1142                                                   _("The body of the \"New message\" template has an error at line %d."));
1143                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1144                         quote_fmt_reset_vartable();
1145
1146                         g_free(tmp);
1147 #ifdef USE_ENCHANT
1148                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1149                                 gtkaspell_highlight_all(compose->gtkaspell);
1150 #endif
1151                         mfield = BODY_FIELD_PRESENT;
1152                 }
1153
1154         }
1155         procmsg_msginfo_free( dummyinfo );
1156
1157         if (attach_files) {
1158                 gint i;
1159                 gchar *file;
1160
1161                 for (i = 0; i < attach_files->len; i++) {
1162                         file = g_ptr_array_index(attach_files, i);
1163                         compose_attach_append(compose, file, file, NULL);
1164                 }
1165         }
1166
1167         compose_show_first_last_header(compose, TRUE);
1168
1169         /* Set save folder */
1170         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1171                 gchar *folderidentifier;
1172
1173                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1174                 folderidentifier = folder_item_get_identifier(item);
1175                 compose_set_save_to(compose, folderidentifier);
1176                 g_free(folderidentifier);
1177         }
1178
1179         /* Place cursor according to provided input (mfield) */
1180         switch (mfield) { 
1181                 case NO_FIELD_PRESENT:
1182                         if (compose->header_last)
1183                                 gtk_widget_grab_focus(compose->header_last->entry);
1184                         break;
1185                 case TO_FIELD_PRESENT:
1186                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1187                         if (buf) {
1188                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1189                                 g_free(buf);
1190                         }
1191                         gtk_widget_grab_focus(compose->subject_entry);
1192                         break;
1193                 case SUBJECT_FIELD_PRESENT:
1194                         textview = GTK_TEXT_VIEW(compose->text);
1195                         if (!textview)
1196                                 break;
1197                         textbuf = gtk_text_view_get_buffer(textview);
1198                         if (!textbuf)
1199                                 break;
1200                         mark = gtk_text_buffer_get_insert(textbuf);
1201                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1202                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1203                     /* 
1204                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1205                      * only defers where it comes to the variable body
1206                      * is not null. If no body is present compose->text
1207                      * will be null in which case you cannot place the
1208                      * cursor inside the component so. An empty component
1209                      * is therefore created before placing the cursor
1210                      */
1211                 case BODY_FIELD_PRESENT:
1212                         gtk_widget_grab_focus(compose->text);
1213                         break;
1214         }
1215
1216         undo_unblock(compose->undostruct);
1217
1218         if (prefs_common.auto_exteditor)
1219                 compose_exec_ext_editor(compose);
1220
1221         compose->draft_timeout_tag = -1;
1222         SCROLL_TO_CURSOR(compose);
1223
1224         compose->modified = FALSE;
1225         compose_set_title(compose);
1226
1227   hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1228
1229         return compose;
1230 }
1231
1232 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1233                 gboolean override_pref, const gchar *system)
1234 {
1235         const gchar *privacy = NULL;
1236
1237         cm_return_if_fail(compose != NULL);
1238         cm_return_if_fail(account != NULL);
1239
1240         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1241                 return;
1242
1243         if (system)
1244                 privacy = system;
1245         else if (account->default_privacy_system
1246         &&  strlen(account->default_privacy_system)) {
1247                 privacy = account->default_privacy_system;
1248         } else {
1249                 GSList *privacy_avail = privacy_get_system_ids();
1250                 if (privacy_avail && g_slist_length(privacy_avail)) {
1251                         privacy = (gchar *)(privacy_avail->data);
1252                 }
1253         }
1254         if (privacy != NULL) {
1255                 if (system) {
1256                         g_free(compose->privacy_system);
1257                         compose->privacy_system = NULL;
1258                 }
1259                 if (compose->privacy_system == NULL)
1260                         compose->privacy_system = g_strdup(privacy);
1261                 else if (*(compose->privacy_system) == '\0') {
1262                         g_free(compose->privacy_system);
1263                         compose->privacy_system = g_strdup(privacy);
1264                 }
1265                 compose_update_privacy_system_menu_item(compose, FALSE);
1266                 compose_use_encryption(compose, TRUE);
1267         }
1268 }       
1269
1270 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1271 {
1272         const gchar *privacy = NULL;
1273
1274         if (system)
1275                 privacy = system;
1276         else if (account->default_privacy_system
1277         &&  strlen(account->default_privacy_system)) {
1278                 privacy = account->default_privacy_system;
1279         } else {
1280                 GSList *privacy_avail = privacy_get_system_ids();
1281                 if (privacy_avail && g_slist_length(privacy_avail)) {
1282                         privacy = (gchar *)(privacy_avail->data);
1283                 }
1284         }
1285
1286         if (privacy != NULL) {
1287                 if (system) {
1288                         g_free(compose->privacy_system);
1289                         compose->privacy_system = NULL;
1290                 }
1291                 if (compose->privacy_system == NULL)
1292                         compose->privacy_system = g_strdup(privacy);
1293                 compose_update_privacy_system_menu_item(compose, FALSE);
1294                 compose_use_signing(compose, TRUE);
1295         }
1296 }       
1297
1298 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1299 {
1300         MsgInfo *msginfo;
1301         guint list_len;
1302         Compose *compose = NULL;
1303         
1304         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1305
1306         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1307         cm_return_val_if_fail(msginfo != NULL, NULL);
1308
1309         list_len = g_slist_length(msginfo_list);
1310
1311         switch (mode) {
1312         case COMPOSE_REPLY:
1313                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1314                               FALSE, prefs_common.default_reply_list, FALSE, body);
1315                 break;
1316         case COMPOSE_REPLY_WITH_QUOTE:
1317                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1318                         FALSE, prefs_common.default_reply_list, FALSE, body);
1319                 break;
1320         case COMPOSE_REPLY_WITHOUT_QUOTE:
1321                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1322                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1323                 break;
1324         case COMPOSE_REPLY_TO_SENDER:
1325                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1326                               FALSE, FALSE, TRUE, body);
1327                 break;
1328         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1329                 compose = compose_followup_and_reply_to(msginfo,
1330                                               COMPOSE_QUOTE_CHECK,
1331                                               FALSE, FALSE, body);
1332                 break;
1333         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1334                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1335                         FALSE, FALSE, TRUE, body);
1336                 break;
1337         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1338                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1339                         FALSE, FALSE, TRUE, NULL);
1340                 break;
1341         case COMPOSE_REPLY_TO_ALL:
1342                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1343                         TRUE, FALSE, FALSE, body);
1344                 break;
1345         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1346                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1347                         TRUE, FALSE, FALSE, body);
1348                 break;
1349         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1350                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1351                         TRUE, FALSE, FALSE, NULL);
1352                 break;
1353         case COMPOSE_REPLY_TO_LIST:
1354                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1355                         FALSE, TRUE, FALSE, body);
1356                 break;
1357         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1358                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1359                         FALSE, TRUE, FALSE, body);
1360                 break;
1361         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1362                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1363                         FALSE, TRUE, FALSE, NULL);
1364                 break;
1365         case COMPOSE_FORWARD:
1366                 if (prefs_common.forward_as_attachment) {
1367                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1368                         return compose;
1369                 } else {
1370                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1371                         return compose;
1372                 }
1373                 break;
1374         case COMPOSE_FORWARD_INLINE:
1375                 /* check if we reply to more than one Message */
1376                 if (list_len == 1) {
1377                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1378                         break;
1379                 } 
1380                 /* more messages FALL THROUGH */
1381         case COMPOSE_FORWARD_AS_ATTACH:
1382                 compose = compose_forward_multiple(NULL, msginfo_list);
1383                 break;
1384         case COMPOSE_REDIRECT:
1385                 compose = compose_redirect(NULL, msginfo, FALSE);
1386                 break;
1387         default:
1388                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1389         }
1390         
1391         if (compose == NULL) {
1392                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1393                 return NULL;
1394         }
1395
1396         compose->rmode = mode;
1397         switch (compose->rmode) {
1398         case COMPOSE_REPLY:
1399         case COMPOSE_REPLY_WITH_QUOTE:
1400         case COMPOSE_REPLY_WITHOUT_QUOTE:
1401         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1402                 debug_print("reply mode Normal\n");
1403                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1404                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1405                 break;
1406         case COMPOSE_REPLY_TO_SENDER:
1407         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1408         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1409                 debug_print("reply mode Sender\n");
1410                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1411                 break;
1412         case COMPOSE_REPLY_TO_ALL:
1413         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1414         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1415                 debug_print("reply mode All\n");
1416                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1417                 break;
1418         case COMPOSE_REPLY_TO_LIST:
1419         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1420         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1421                 debug_print("reply mode List\n");
1422                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1423                 break;
1424         default:
1425                 break;
1426         }
1427         return compose;
1428 }
1429
1430 static Compose *compose_reply(MsgInfo *msginfo,
1431                                    ComposeQuoteMode quote_mode,
1432                                    gboolean to_all,
1433                                    gboolean to_ml,
1434                                    gboolean to_sender, 
1435                    const gchar *body)
1436 {
1437         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1438                               to_sender, FALSE, body);
1439 }
1440
1441 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1442                                    ComposeQuoteMode quote_mode,
1443                                    gboolean to_all,
1444                                    gboolean to_sender,
1445                                    const gchar *body)
1446 {
1447         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1448                               to_sender, TRUE, body);
1449 }
1450
1451 static void compose_extract_original_charset(Compose *compose)
1452 {
1453         MsgInfo *info = NULL;
1454         if (compose->replyinfo) {
1455                 info = compose->replyinfo;
1456         } else if (compose->fwdinfo) {
1457                 info = compose->fwdinfo;
1458         } else if (compose->targetinfo) {
1459                 info = compose->targetinfo;
1460         }
1461         if (info) {
1462                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1463                 MimeInfo *partinfo = mimeinfo;
1464                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1465                         partinfo = procmime_mimeinfo_next(partinfo);
1466                 if (partinfo) {
1467                         compose->orig_charset = 
1468                                 g_strdup(procmime_mimeinfo_get_parameter(
1469                                                 partinfo, "charset"));
1470                 }
1471                 procmime_mimeinfo_free_all(mimeinfo);
1472         }
1473 }
1474
1475 #define SIGNAL_BLOCK(buffer) {                                  \
1476         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1477                                 G_CALLBACK(compose_changed_cb), \
1478                                 compose);                       \
1479         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1480                                 G_CALLBACK(text_inserted),      \
1481                                 compose);                       \
1482 }
1483
1484 #define SIGNAL_UNBLOCK(buffer) {                                \
1485         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1486                                 G_CALLBACK(compose_changed_cb), \
1487                                 compose);                       \
1488         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1489                                 G_CALLBACK(text_inserted),      \
1490                                 compose);                       \
1491 }
1492
1493 static Compose *compose_generic_reply(MsgInfo *msginfo,
1494                                   ComposeQuoteMode quote_mode,
1495                                   gboolean to_all, gboolean to_ml,
1496                                   gboolean to_sender,
1497                                   gboolean followup_and_reply_to,
1498                                   const gchar *body)
1499 {
1500         Compose *compose;
1501         PrefsAccount *account = NULL;
1502         GtkTextView *textview;
1503         GtkTextBuffer *textbuf;
1504         gboolean quote = FALSE;
1505         const gchar *qmark = NULL;
1506         const gchar *body_fmt = NULL;
1507         gchar *s_system = NULL;
1508         START_TIMING("");
1509         cm_return_val_if_fail(msginfo != NULL, NULL);
1510         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1511
1512         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1513
1514         cm_return_val_if_fail(account != NULL, NULL);
1515
1516         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1517
1518         compose->updating = TRUE;
1519
1520         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1521         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1522
1523         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1524         if (!compose->replyinfo)
1525                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1526
1527         compose_extract_original_charset(compose);
1528         
1529         if (msginfo->folder && msginfo->folder->ret_rcpt)
1530                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1531
1532         /* Set save folder */
1533         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1534                 gchar *folderidentifier;
1535
1536                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1537                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1538                 compose_set_save_to(compose, folderidentifier);
1539                 g_free(folderidentifier);
1540         }
1541
1542         if (compose_parse_header(compose, msginfo) < 0) {
1543                 compose->updating = FALSE;
1544                 compose_destroy(compose);
1545                 return NULL;
1546         }
1547
1548         /* override from name according to folder properties */
1549         if (msginfo->folder && msginfo->folder->prefs &&
1550                 msginfo->folder->prefs->reply_with_format &&
1551                 msginfo->folder->prefs->reply_override_from_format &&
1552                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1553
1554                 gchar *tmp = NULL;
1555                 gchar *buf = NULL;
1556
1557                 /* decode \-escape sequences in the internal representation of the quote format */
1558                 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1559                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1560
1561 #ifdef USE_ENCHANT
1562                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1563                                 compose->gtkaspell);
1564 #else
1565                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1566 #endif
1567                 quote_fmt_scan_string(tmp);
1568                 quote_fmt_parse();
1569
1570                 buf = quote_fmt_get_buffer();
1571                 if (buf == NULL)
1572                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1573                 else
1574                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1575                 quote_fmt_reset_vartable();
1576
1577                 g_free(tmp);
1578         }
1579
1580         textview = (GTK_TEXT_VIEW(compose->text));
1581         textbuf = gtk_text_view_get_buffer(textview);
1582         compose_create_tags(textview, compose);
1583
1584         undo_block(compose->undostruct);
1585 #ifdef USE_ENCHANT
1586                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1587 #endif
1588
1589         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1590                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1591                 /* use the reply format of folder (if enabled), or the account's one
1592                    (if enabled) or fallback to the global reply format, which is always
1593                    enabled (even if empty), and use the relevant quotemark */
1594                 quote = TRUE;
1595                 if (msginfo->folder && msginfo->folder->prefs &&
1596                                 msginfo->folder->prefs->reply_with_format) {
1597                         qmark = msginfo->folder->prefs->reply_quotemark;
1598                         body_fmt = msginfo->folder->prefs->reply_body_format;
1599
1600                 } else if (account->reply_with_format) {
1601                         qmark = account->reply_quotemark;
1602                         body_fmt = account->reply_body_format;
1603
1604                 } else {
1605                         qmark = prefs_common.quotemark;
1606                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1607                                 body_fmt = gettext(prefs_common.quotefmt);
1608                         else
1609                                 body_fmt = "";
1610                 }
1611         }
1612
1613         if (quote) {
1614                 /* empty quotemark is not allowed */
1615                 if (qmark == NULL || *qmark == '\0')
1616                         qmark = "> ";
1617                 compose_quote_fmt(compose, compose->replyinfo,
1618                                   body_fmt, qmark, body, FALSE, TRUE,
1619                                           _("The body of the \"Reply\" template has an error at line %d."));
1620                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1621                 quote_fmt_reset_vartable();
1622 #ifdef USE_ENCHANT
1623                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1624                         gtkaspell_highlight_all(compose->gtkaspell);
1625 #endif
1626         }
1627
1628         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1629                 compose_force_encryption(compose, account, FALSE, s_system);
1630         }
1631
1632         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1633         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1634                 compose_force_signing(compose, account, s_system);
1635         }
1636         g_free(s_system);
1637
1638         SIGNAL_BLOCK(textbuf);
1639         
1640         if (account->auto_sig)
1641                 compose_insert_sig(compose, FALSE);
1642
1643         compose_wrap_all(compose);
1644
1645         SIGNAL_UNBLOCK(textbuf);
1646         
1647         gtk_widget_grab_focus(compose->text);
1648
1649         undo_unblock(compose->undostruct);
1650
1651         if (prefs_common.auto_exteditor)
1652                 compose_exec_ext_editor(compose);
1653                 
1654         compose->modified = FALSE;
1655         compose_set_title(compose);
1656
1657         compose->updating = FALSE;
1658         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1659         SCROLL_TO_CURSOR(compose);
1660         
1661         if (compose->deferred_destroy) {
1662                 compose_destroy(compose);
1663                 return NULL;
1664         }
1665         END_TIMING();
1666
1667         return compose;
1668 }
1669
1670 #define INSERT_FW_HEADER(var, hdr) \
1671 if (msginfo->var && *msginfo->var) { \
1672         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1673         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1674         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1675 }
1676
1677 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1678                          gboolean as_attach, const gchar *body,
1679                          gboolean no_extedit,
1680                          gboolean batch)
1681 {
1682         Compose *compose;
1683         GtkTextView *textview;
1684         GtkTextBuffer *textbuf;
1685         GtkTextIter iter;
1686
1687         cm_return_val_if_fail(msginfo != NULL, NULL);
1688         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1689
1690         if (!account && 
1691             !(account = compose_guess_forward_account_from_msginfo
1692                                 (msginfo)))
1693                 account = cur_account;
1694
1695         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1696
1697         compose->updating = TRUE;
1698         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1699         if (!compose->fwdinfo)
1700                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1701
1702         compose_extract_original_charset(compose);
1703
1704         if (msginfo->subject && *msginfo->subject) {
1705                 gchar *buf, *buf2, *p;
1706
1707                 buf = p = g_strdup(msginfo->subject);
1708                 p += subject_get_prefix_length(p);
1709                 memmove(buf, p, strlen(p) + 1);
1710
1711                 buf2 = g_strdup_printf("Fw: %s", buf);
1712                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1713                 
1714                 g_free(buf);
1715                 g_free(buf2);
1716         }
1717
1718         /* override from name according to folder properties */
1719         if (msginfo->folder && msginfo->folder->prefs &&
1720                 msginfo->folder->prefs->forward_with_format &&
1721                 msginfo->folder->prefs->forward_override_from_format &&
1722                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1723
1724                 gchar *tmp = NULL;
1725                 gchar *buf = NULL;
1726                 MsgInfo *full_msginfo = NULL;
1727
1728                 if (!as_attach)
1729                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1730                 if (!full_msginfo)
1731                         full_msginfo = procmsg_msginfo_copy(msginfo);
1732
1733                 /* decode \-escape sequences in the internal representation of the quote format */
1734                 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1735                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1736
1737 #ifdef USE_ENCHANT
1738                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1739                                 compose->gtkaspell);
1740 #else
1741                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1742 #endif
1743                 quote_fmt_scan_string(tmp);
1744                 quote_fmt_parse();
1745
1746                 buf = quote_fmt_get_buffer();
1747                 if (buf == NULL)
1748                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1749                 else
1750                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1751                 quote_fmt_reset_vartable();
1752
1753                 g_free(tmp);
1754                 procmsg_msginfo_free(full_msginfo);
1755         }
1756
1757         textview = GTK_TEXT_VIEW(compose->text);
1758         textbuf = gtk_text_view_get_buffer(textview);
1759         compose_create_tags(textview, compose);
1760         
1761         undo_block(compose->undostruct);
1762         if (as_attach) {
1763                 gchar *msgfile;
1764
1765                 msgfile = procmsg_get_message_file(msginfo);
1766                 if (!is_file_exist(msgfile))
1767                         g_warning("%s: file not exist\n", msgfile);
1768                 else
1769                         compose_attach_append(compose, msgfile, msgfile,
1770                                               "message/rfc822");
1771
1772                 g_free(msgfile);
1773         } else {
1774                 const gchar *qmark = NULL;
1775                 const gchar *body_fmt = NULL;
1776                 MsgInfo *full_msginfo;
1777
1778                 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1779                         body_fmt = gettext(prefs_common.fw_quotefmt);
1780                 else
1781                         body_fmt = "";
1782         
1783                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1784                 if (!full_msginfo)
1785                         full_msginfo = procmsg_msginfo_copy(msginfo);
1786
1787                 /* use the forward format of folder (if enabled), or the account's one
1788                    (if enabled) or fallback to the global forward format, which is always
1789                    enabled (even if empty), and use the relevant quotemark */
1790                 if (msginfo->folder && msginfo->folder->prefs &&
1791                                 msginfo->folder->prefs->forward_with_format) {
1792                         qmark = msginfo->folder->prefs->forward_quotemark;
1793                         body_fmt = msginfo->folder->prefs->forward_body_format;
1794
1795                 } else if (account->forward_with_format) {
1796                         qmark = account->forward_quotemark;
1797                         body_fmt = account->forward_body_format;
1798
1799                 } else {
1800                         qmark = prefs_common.fw_quotemark;
1801                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1802                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1803                         else
1804                                 body_fmt = "";
1805                 }
1806
1807                 /* empty quotemark is not allowed */
1808                 if (qmark == NULL || *qmark == '\0')
1809                         qmark = "> ";
1810
1811                 compose_quote_fmt(compose, full_msginfo,
1812                                   body_fmt, qmark, body, FALSE, TRUE,
1813                                           _("The body of the \"Forward\" template has an error at line %d."));
1814                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1815                 quote_fmt_reset_vartable();
1816                 compose_attach_parts(compose, msginfo);
1817
1818                 procmsg_msginfo_free(full_msginfo);
1819 #ifdef USE_ENCHANT
1820                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1821                         gtkaspell_highlight_all(compose->gtkaspell);
1822 #endif
1823         }
1824
1825         SIGNAL_BLOCK(textbuf);
1826
1827         if (account->auto_sig)
1828                 compose_insert_sig(compose, FALSE);
1829
1830         compose_wrap_all(compose);
1831
1832         SIGNAL_UNBLOCK(textbuf);
1833         
1834         gtk_text_buffer_get_start_iter(textbuf, &iter);
1835         gtk_text_buffer_place_cursor(textbuf, &iter);
1836
1837         gtk_widget_grab_focus(compose->header_last->entry);
1838
1839         if (!no_extedit && prefs_common.auto_exteditor)
1840                 compose_exec_ext_editor(compose);
1841         
1842         /*save folder*/
1843         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1844                 gchar *folderidentifier;
1845
1846                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1847                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1848                 compose_set_save_to(compose, folderidentifier);
1849                 g_free(folderidentifier);
1850         }
1851
1852         undo_unblock(compose->undostruct);
1853         
1854         compose->modified = FALSE;
1855         compose_set_title(compose);
1856
1857         compose->updating = FALSE;
1858         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859         SCROLL_TO_CURSOR(compose);
1860
1861         if (compose->deferred_destroy) {
1862                 compose_destroy(compose);
1863                 return NULL;
1864         }
1865
1866         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1867
1868         return compose;
1869 }
1870
1871 #undef INSERT_FW_HEADER
1872
1873 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1874 {
1875         Compose *compose;
1876         GtkTextView *textview;
1877         GtkTextBuffer *textbuf;
1878         GtkTextIter iter;
1879         GSList *msginfo;
1880         gchar *msgfile;
1881         gboolean single_mail = TRUE;
1882         
1883         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1884
1885         if (g_slist_length(msginfo_list) > 1)
1886                 single_mail = FALSE;
1887
1888         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1889                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1890                         return NULL;
1891
1892         /* guess account from first selected message */
1893         if (!account && 
1894             !(account = compose_guess_forward_account_from_msginfo
1895                                 (msginfo_list->data)))
1896                 account = cur_account;
1897
1898         cm_return_val_if_fail(account != NULL, NULL);
1899
1900         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1901                 if (msginfo->data) {
1902                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1903                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1904                 }
1905         }
1906
1907         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1908                 g_warning("no msginfo_list");
1909                 return NULL;
1910         }
1911
1912         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1913
1914         compose->updating = TRUE;
1915
1916         /* override from name according to folder properties */
1917         if (msginfo_list->data) {
1918                 MsgInfo *msginfo = msginfo_list->data;
1919
1920                 if (msginfo->folder && msginfo->folder->prefs &&
1921                         msginfo->folder->prefs->forward_with_format &&
1922                         msginfo->folder->prefs->forward_override_from_format &&
1923                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1924
1925                         gchar *tmp = NULL;
1926                         gchar *buf = NULL;
1927
1928                         /* decode \-escape sequences in the internal representation of the quote format */
1929                         tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1930                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1931
1932 #ifdef USE_ENCHANT
1933                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1934                                         compose->gtkaspell);
1935 #else
1936                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1937 #endif
1938                         quote_fmt_scan_string(tmp);
1939                         quote_fmt_parse();
1940
1941                         buf = quote_fmt_get_buffer();
1942                         if (buf == NULL)
1943                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1944                         else
1945                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1946                         quote_fmt_reset_vartable();
1947
1948                         g_free(tmp);
1949                 }
1950         }
1951
1952         textview = GTK_TEXT_VIEW(compose->text);
1953         textbuf = gtk_text_view_get_buffer(textview);
1954         compose_create_tags(textview, compose);
1955         
1956         undo_block(compose->undostruct);
1957         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1958                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1959
1960                 if (!is_file_exist(msgfile))
1961                         g_warning("%s: file not exist\n", msgfile);
1962                 else
1963                         compose_attach_append(compose, msgfile, msgfile,
1964                                 "message/rfc822");
1965                 g_free(msgfile);
1966         }
1967         
1968         if (single_mail) {
1969                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1970                 if (info->subject && *info->subject) {
1971                         gchar *buf, *buf2, *p;
1972
1973                         buf = p = g_strdup(info->subject);
1974                         p += subject_get_prefix_length(p);
1975                         memmove(buf, p, strlen(p) + 1);
1976
1977                         buf2 = g_strdup_printf("Fw: %s", buf);
1978                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1979
1980                         g_free(buf);
1981                         g_free(buf2);
1982                 }
1983         } else {
1984                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1985                         _("Fw: multiple emails"));
1986         }
1987
1988         SIGNAL_BLOCK(textbuf);
1989         
1990         if (account->auto_sig)
1991                 compose_insert_sig(compose, FALSE);
1992
1993         compose_wrap_all(compose);
1994
1995         SIGNAL_UNBLOCK(textbuf);
1996         
1997         gtk_text_buffer_get_start_iter(textbuf, &iter);
1998         gtk_text_buffer_place_cursor(textbuf, &iter);
1999
2000         gtk_widget_grab_focus(compose->header_last->entry);
2001         undo_unblock(compose->undostruct);
2002         compose->modified = FALSE;
2003         compose_set_title(compose);
2004
2005         compose->updating = FALSE;
2006         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2007         SCROLL_TO_CURSOR(compose);
2008
2009         if (compose->deferred_destroy) {
2010                 compose_destroy(compose);
2011                 return NULL;
2012         }
2013
2014         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2015
2016         return compose;
2017 }
2018
2019 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2020 {
2021         GtkTextIter start = *iter;
2022         GtkTextIter end_iter;
2023         int start_pos = gtk_text_iter_get_offset(&start);
2024         gchar *str = NULL;
2025         if (!compose->account->sig_sep)
2026                 return FALSE;
2027         
2028         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2029                 start_pos+strlen(compose->account->sig_sep));
2030
2031         /* check sig separator */
2032         str = gtk_text_iter_get_text(&start, &end_iter);
2033         if (!strcmp(str, compose->account->sig_sep)) {
2034                 gchar *tmp = NULL;
2035                 /* check end of line (\n) */
2036                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2037                         start_pos+strlen(compose->account->sig_sep));
2038                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2039                         start_pos+strlen(compose->account->sig_sep)+1);
2040                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2041                 if (!strcmp(tmp,"\n")) {
2042                         g_free(str);
2043                         g_free(tmp);
2044                         return TRUE;
2045                 }
2046                 g_free(tmp);    
2047         }
2048         g_free(str);
2049
2050         return FALSE;
2051 }
2052
2053 static void compose_colorize_signature(Compose *compose)
2054 {
2055         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2056         GtkTextIter iter;
2057         GtkTextIter end_iter;
2058         gtk_text_buffer_get_start_iter(buffer, &iter);
2059         while (gtk_text_iter_forward_line(&iter))
2060                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2061                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2062                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2063                 }
2064 }
2065
2066 #define BLOCK_WRAP() {                                                  \
2067         prev_autowrap = compose->autowrap;                              \
2068         buffer = gtk_text_view_get_buffer(                              \
2069                                         GTK_TEXT_VIEW(compose->text));  \
2070         compose->autowrap = FALSE;                                      \
2071                                                                         \
2072         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2073                                 G_CALLBACK(compose_changed_cb),         \
2074                                 compose);                               \
2075         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2076                                 G_CALLBACK(text_inserted),              \
2077                                 compose);                               \
2078 }
2079 #define UNBLOCK_WRAP() {                                                \
2080         compose->autowrap = prev_autowrap;                              \
2081         if (compose->autowrap) {                                        \
2082                 gint old = compose->draft_timeout_tag;                  \
2083                 compose->draft_timeout_tag = -2;                        \
2084                 compose_wrap_all(compose);                              \
2085                 compose->draft_timeout_tag = old;                       \
2086         }                                                               \
2087                                                                         \
2088         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2089                                 G_CALLBACK(compose_changed_cb),         \
2090                                 compose);                               \
2091         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2092                                 G_CALLBACK(text_inserted),              \
2093                                 compose);                               \
2094 }
2095
2096 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2097 {
2098         Compose *compose = NULL;
2099         PrefsAccount *account = NULL;
2100         GtkTextView *textview;
2101         GtkTextBuffer *textbuf;
2102         GtkTextMark *mark;
2103         GtkTextIter iter;
2104         FILE *fp;
2105         gchar buf[BUFFSIZE];
2106         gboolean use_signing = FALSE;
2107         gboolean use_encryption = FALSE;
2108         gchar *privacy_system = NULL;
2109         int priority = PRIORITY_NORMAL;
2110         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2111         gboolean autowrap = prefs_common.autowrap;
2112         gboolean autoindent = prefs_common.auto_indent;
2113
2114         cm_return_val_if_fail(msginfo != NULL, NULL);
2115         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2116
2117         if (compose_put_existing_to_front(msginfo)) {
2118                 return NULL;
2119         }
2120
2121         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2122             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2123             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2124                 gchar queueheader_buf[BUFFSIZE];
2125                 gint id, param;
2126
2127                 /* Select Account from queue headers */
2128                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2129                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2130                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2131                         account = account_find_from_id(id);
2132                 }
2133                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2134                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2135                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2136                         account = account_find_from_id(id);
2137                 }
2138                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2139                                              sizeof(queueheader_buf), "NAID:")) {
2140                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2141                         account = account_find_from_id(id);
2142                 }
2143                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2144                                                     sizeof(queueheader_buf), "MAID:")) {
2145                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2146                         account = account_find_from_id(id);
2147                 }
2148                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2149                                                                 sizeof(queueheader_buf), "S:")) {
2150                         account = account_find_from_address(queueheader_buf, FALSE);
2151                 }
2152                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2153                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2154                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2155                         use_signing = param;
2156                         
2157                 }
2158                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2159                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2160                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2161                         use_signing = param;
2162                         
2163                 }
2164                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2165                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2166                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2167                         use_encryption = param;
2168                 }
2169                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2170                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2171                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2172                         use_encryption = param;
2173                 }
2174                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2175                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2176                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2177                         autowrap = param;
2178                 }
2179                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2180                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2181                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2182                         autoindent = param;
2183                 }
2184                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2185                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2186                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2187                 }
2188                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2189                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2190                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2191                 }
2192                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2193                                              sizeof(queueheader_buf), "X-Priority: ")) {
2194                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2195                         priority = param;
2196                 }
2197                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2198                                              sizeof(queueheader_buf), "RMID:")) {
2199                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2200                         if (tokens[0] && tokens[1] && tokens[2]) {
2201                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2202                                 if (orig_item != NULL) {
2203                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2204                                 }
2205                         }
2206                         g_strfreev(tokens);
2207                 }
2208                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2209                                              sizeof(queueheader_buf), "FMID:")) {
2210                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2211                         if (tokens[0] && tokens[1] && tokens[2]) {
2212                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2213                                 if (orig_item != NULL) {
2214                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2215                                 }
2216                         }
2217                         g_strfreev(tokens);
2218                 }
2219         } else {
2220                 account = msginfo->folder->folder->account;
2221         }
2222
2223         if (!account && prefs_common.reedit_account_autosel) {
2224                 gchar from[BUFFSIZE];
2225                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2226                         extract_address(from);
2227                         account = account_find_from_address(from, FALSE);
2228                 }
2229         }
2230         if (!account) {
2231                 account = cur_account;
2232         }
2233         cm_return_val_if_fail(account != NULL, NULL);
2234
2235         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2236
2237         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2238         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2239         compose->autowrap = autowrap;
2240         compose->replyinfo = replyinfo;
2241         compose->fwdinfo = fwdinfo;
2242
2243         compose->updating = TRUE;
2244         compose->priority = priority;
2245
2246         if (privacy_system != NULL) {
2247                 compose->privacy_system = privacy_system;
2248                 compose_use_signing(compose, use_signing);
2249                 compose_use_encryption(compose, use_encryption);
2250                 compose_update_privacy_system_menu_item(compose, FALSE);
2251         } else {
2252                 activate_privacy_system(compose, account, FALSE);
2253         }
2254
2255         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2256
2257         compose_extract_original_charset(compose);
2258
2259         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2260             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2261             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2262                 gchar queueheader_buf[BUFFSIZE];
2263
2264                 /* Set message save folder */
2265                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2266                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2267                         compose_set_save_to(compose, &queueheader_buf[4]);
2268                 }
2269                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2270                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2271                         if (active) {
2272                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2273                         }
2274                 }
2275         }
2276         
2277         if (compose_parse_header(compose, msginfo) < 0) {
2278                 compose->updating = FALSE;
2279                 compose_destroy(compose);
2280                 return NULL;
2281         }
2282         compose_reedit_set_entry(compose, msginfo);
2283
2284         textview = GTK_TEXT_VIEW(compose->text);
2285         textbuf = gtk_text_view_get_buffer(textview);
2286         compose_create_tags(textview, compose);
2287
2288         mark = gtk_text_buffer_get_insert(textbuf);
2289         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2290
2291         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2292                                         G_CALLBACK(compose_changed_cb),
2293                                         compose);
2294         
2295         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2296                 fp = procmime_get_first_encrypted_text_content(msginfo);
2297                 if (fp) {
2298                         compose_force_encryption(compose, account, TRUE, NULL);
2299                 }
2300         } else {
2301                 fp = procmime_get_first_text_content(msginfo);
2302         }
2303         if (fp == NULL) {
2304                 g_warning("Can't get text part\n");
2305         }
2306
2307         if (fp != NULL) {
2308                 gboolean prev_autowrap = compose->autowrap;
2309                 GtkTextBuffer *buffer = textbuf;
2310                 BLOCK_WRAP();
2311                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2312                         strcrchomp(buf);
2313                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2314                 }
2315                 UNBLOCK_WRAP();
2316                 fclose(fp);
2317         }
2318         
2319         compose_attach_parts(compose, msginfo);
2320
2321         compose_colorize_signature(compose);
2322
2323         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2324                                         G_CALLBACK(compose_changed_cb),
2325                                         compose);
2326
2327         gtk_widget_grab_focus(compose->text);
2328
2329         if (prefs_common.auto_exteditor) {
2330                 compose_exec_ext_editor(compose);
2331         }
2332         compose->modified = FALSE;
2333         compose_set_title(compose);
2334
2335         compose->updating = FALSE;
2336         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2337         SCROLL_TO_CURSOR(compose);
2338
2339         if (compose->deferred_destroy) {
2340                 compose_destroy(compose);
2341                 return NULL;
2342         }
2343         
2344         compose->sig_str = account_get_signature_str(compose->account);
2345         
2346         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2347
2348         return compose;
2349 }
2350
2351 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2352                                                  gboolean batch)
2353 {
2354         Compose *compose;
2355         gchar *filename;
2356         FolderItem *item;
2357
2358         cm_return_val_if_fail(msginfo != NULL, NULL);
2359
2360         if (!account)
2361                 account = account_get_reply_account(msginfo,
2362                                         prefs_common.reply_account_autosel);
2363         cm_return_val_if_fail(account != NULL, NULL);
2364
2365         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2366
2367         compose->updating = TRUE;
2368
2369         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2370         compose->replyinfo = NULL;
2371         compose->fwdinfo = NULL;
2372
2373         compose_show_first_last_header(compose, TRUE);
2374
2375         gtk_widget_grab_focus(compose->header_last->entry);
2376
2377         filename = procmsg_get_message_file(msginfo);
2378
2379         if (filename == NULL) {
2380                 compose->updating = FALSE;
2381                 compose_destroy(compose);
2382
2383                 return NULL;
2384         }
2385
2386         compose->redirect_filename = filename;
2387         
2388         /* Set save folder */
2389         item = msginfo->folder;
2390         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2391                 gchar *folderidentifier;
2392
2393                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2394                 folderidentifier = folder_item_get_identifier(item);
2395                 compose_set_save_to(compose, folderidentifier);
2396                 g_free(folderidentifier);
2397         }
2398
2399         compose_attach_parts(compose, msginfo);
2400
2401         if (msginfo->subject)
2402                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2403                                    msginfo->subject);
2404         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2405
2406         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2407                                           _("The body of the \"Redirect\" template has an error at line %d."));
2408         quote_fmt_reset_vartable();
2409         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2410
2411         compose_colorize_signature(compose);
2412
2413         
2414         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2415         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2416         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2417
2418         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2419         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2420         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2421         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2422         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2423         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2424         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2425         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2426         
2427         if (compose->toolbar->draft_btn)
2428                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2429         if (compose->toolbar->insert_btn)
2430                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2431         if (compose->toolbar->attach_btn)
2432                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2433         if (compose->toolbar->sig_btn)
2434                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2435         if (compose->toolbar->exteditor_btn)
2436                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2437         if (compose->toolbar->linewrap_current_btn)
2438                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2439         if (compose->toolbar->linewrap_all_btn)
2440                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2441
2442         compose->modified = FALSE;
2443         compose_set_title(compose);
2444         compose->updating = FALSE;
2445         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2446         SCROLL_TO_CURSOR(compose);
2447
2448         if (compose->deferred_destroy) {
2449                 compose_destroy(compose);
2450                 return NULL;
2451         }
2452         
2453         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2454
2455         return compose;
2456 }
2457
2458 GList *compose_get_compose_list(void)
2459 {
2460         return compose_list;
2461 }
2462
2463 void compose_entry_append(Compose *compose, const gchar *address,
2464                           ComposeEntryType type, ComposePrefType pref_type)
2465 {
2466         const gchar *header;
2467         gchar *cur, *begin;
2468         gboolean in_quote = FALSE;
2469         if (!address || *address == '\0') return;
2470
2471         switch (type) {
2472         case COMPOSE_CC:
2473                 header = N_("Cc:");
2474                 break;
2475         case COMPOSE_BCC:
2476                 header = N_("Bcc:");
2477                 break;
2478         case COMPOSE_REPLYTO:
2479                 header = N_("Reply-To:");
2480                 break;
2481         case COMPOSE_NEWSGROUPS:
2482                 header = N_("Newsgroups:");
2483                 break;
2484         case COMPOSE_FOLLOWUPTO:
2485                 header = N_( "Followup-To:");
2486                 break;
2487         case COMPOSE_TO:
2488         default:
2489                 header = N_("To:");
2490                 break;
2491         }
2492         header = prefs_common_translated_header_name(header);
2493         
2494         cur = begin = (gchar *)address;
2495         
2496         /* we separate the line by commas, but not if we're inside a quoted
2497          * string */
2498         while (*cur != '\0') {
2499                 if (*cur == '"') 
2500                         in_quote = !in_quote;
2501                 if (*cur == ',' && !in_quote) {
2502                         gchar *tmp = g_strdup(begin);
2503                         gchar *o_tmp = tmp;
2504                         tmp[cur-begin]='\0';
2505                         cur++;
2506                         begin = cur;
2507                         while (*tmp == ' ' || *tmp == '\t')
2508                                 tmp++;
2509                         compose_add_header_entry(compose, header, tmp, pref_type);
2510                         g_free(o_tmp);
2511                         continue;
2512                 }
2513                 cur++;
2514         }
2515         if (begin < cur) {
2516                 gchar *tmp = g_strdup(begin);
2517                 gchar *o_tmp = tmp;
2518                 tmp[cur-begin]='\0';
2519                 cur++;
2520                 begin = cur;
2521                 while (*tmp == ' ' || *tmp == '\t')
2522                         tmp++;
2523                 compose_add_header_entry(compose, header, tmp, pref_type);
2524                 g_free(o_tmp);          
2525         }
2526 }
2527
2528 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2529 {
2530         static GdkColor yellow;
2531         static GdkColor black;
2532         static gboolean yellow_initialised = FALSE;
2533         GSList *h_list;
2534         GtkEntry *entry;
2535                 
2536         if (!yellow_initialised) {
2537                 gdk_color_parse("#f5f6be", &yellow);
2538                 gdk_color_parse("#000000", &black);
2539                 yellow_initialised = gdk_colormap_alloc_color(
2540                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2541                 yellow_initialised &= gdk_colormap_alloc_color(
2542                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2543         }
2544
2545         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2546                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2547                 if (gtk_entry_get_text(entry) && 
2548                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2549                         if (yellow_initialised) {
2550                                 gtk_widget_modify_base(
2551                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2552                                         GTK_STATE_NORMAL, &yellow);
2553                                 gtk_widget_modify_text(
2554                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2555                                         GTK_STATE_NORMAL, &black);
2556                         }
2557                 }
2558         }
2559 }
2560
2561 void compose_toolbar_cb(gint action, gpointer data)
2562 {
2563         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2564         Compose *compose = (Compose*)toolbar_item->parent;
2565         
2566         cm_return_if_fail(compose != NULL);
2567
2568         switch(action) {
2569         case A_SEND:
2570                 compose_send_cb(NULL, compose);
2571                 break;
2572         case A_SENDL:
2573                 compose_send_later_cb(NULL, compose);
2574                 break;
2575         case A_DRAFT:
2576                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2577                 break;
2578         case A_INSERT:
2579                 compose_insert_file_cb(NULL, compose);
2580                 break;
2581         case A_ATTACH:
2582                 compose_attach_cb(NULL, compose);
2583                 break;
2584         case A_SIG:
2585                 compose_insert_sig(compose, FALSE);
2586                 break;
2587         case A_EXTEDITOR:
2588                 compose_ext_editor_cb(NULL, compose);
2589                 break;
2590         case A_LINEWRAP_CURRENT:
2591                 compose_beautify_paragraph(compose, NULL, TRUE);
2592                 break;
2593         case A_LINEWRAP_ALL:
2594                 compose_wrap_all_full(compose, TRUE);
2595                 break;
2596         case A_ADDRBOOK:
2597                 compose_address_cb(NULL, compose);
2598                 break;
2599 #ifdef USE_ENCHANT
2600         case A_CHECK_SPELLING:
2601                 compose_check_all(NULL, compose);
2602                 break;
2603 #endif
2604         default:
2605                 break;
2606         }
2607 }
2608
2609 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2610 {
2611         gchar *to = NULL;
2612         gchar *cc = NULL;
2613         gchar *bcc = NULL;
2614         gchar *subject = NULL;
2615         gchar *body = NULL;
2616         gchar *temp = NULL;
2617         gsize  len = 0;
2618         gchar **attach = NULL;
2619         MailField mfield = NO_FIELD_PRESENT;
2620
2621         /* get mailto parts but skip from */
2622         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2623
2624         if (to) {
2625                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2626                 mfield = TO_FIELD_PRESENT;
2627         }
2628         if (cc)
2629                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2630         if (bcc)
2631                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2632         if (subject) {
2633                 if (!g_utf8_validate (subject, -1, NULL)) {
2634                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2635                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2636                         g_free(temp);
2637                 } else {
2638                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2639                 }
2640                 mfield = SUBJECT_FIELD_PRESENT;
2641         }
2642         if (body) {
2643                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2644                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2645                 GtkTextMark *mark;
2646                 GtkTextIter iter;
2647                 gboolean prev_autowrap = compose->autowrap;
2648
2649                 compose->autowrap = FALSE;
2650
2651                 mark = gtk_text_buffer_get_insert(buffer);
2652                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2653
2654                 if (!g_utf8_validate (body, -1, NULL)) {
2655                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2656                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2657                         g_free(temp);
2658                 } else {
2659                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2660                 }
2661                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2662
2663                 compose->autowrap = prev_autowrap;
2664                 if (compose->autowrap)
2665                         compose_wrap_all(compose);
2666                 mfield = BODY_FIELD_PRESENT;
2667         }
2668
2669         if (attach) {
2670                 gint i = 0, att = 0;
2671                 gchar *warn_files = NULL;
2672                 while (attach[i] != NULL) {
2673                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2674                         if (utf8_filename) {
2675                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2676                                         gchar *tmp = g_strdup_printf("%s%s\n",
2677                                                         warn_files?warn_files:"",
2678                                                         utf8_filename);
2679                                         g_free(warn_files);
2680                                         warn_files = tmp;
2681                                         att++;
2682                                 }
2683                                 g_free(utf8_filename);
2684                         } else {
2685                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2686                         }
2687                         i++;
2688                 }
2689                 if (warn_files) {
2690                         alertpanel_notice(ngettext(
2691                         "The following file has been attached: \n%s",
2692                         "The following files have been attached: \n%s", att), warn_files);
2693                         g_free(warn_files);
2694                 }
2695         }
2696         g_free(to);
2697         g_free(cc);
2698         g_free(bcc);
2699         g_free(subject);
2700         g_free(body);
2701         g_strfreev(attach);
2702         
2703         return mfield;
2704 }
2705
2706 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2707 {
2708         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2709                                        {"Cc:",          NULL, TRUE},
2710                                        {"References:",  NULL, FALSE},
2711                                        {"Bcc:",         NULL, TRUE},
2712                                        {"Newsgroups:",  NULL, TRUE},
2713                                        {"Followup-To:", NULL, TRUE},
2714                                        {"List-Post:",   NULL, FALSE},
2715                                        {"X-Priority:",  NULL, FALSE},
2716                                        {NULL,           NULL, FALSE}};
2717
2718         enum
2719         {
2720                 H_REPLY_TO      = 0,
2721                 H_CC            = 1,
2722                 H_REFERENCES    = 2,
2723                 H_BCC           = 3,
2724                 H_NEWSGROUPS    = 4,
2725                 H_FOLLOWUP_TO   = 5,
2726                 H_LIST_POST     = 6,
2727                 H_X_PRIORITY    = 7
2728         };
2729
2730         FILE *fp;
2731
2732         cm_return_val_if_fail(msginfo != NULL, -1);
2733
2734         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2735         procheader_get_header_fields(fp, hentry);
2736         fclose(fp);
2737
2738         if (hentry[H_REPLY_TO].body != NULL) {
2739                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2740                         compose->replyto =
2741                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2742                                                    NULL);
2743                 }
2744                 g_free(hentry[H_REPLY_TO].body);
2745                 hentry[H_REPLY_TO].body = NULL;
2746         }
2747         if (hentry[H_CC].body != NULL) {
2748                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2749                 g_free(hentry[H_CC].body);
2750                 hentry[H_CC].body = NULL;
2751         }
2752         if (hentry[H_REFERENCES].body != NULL) {
2753                 if (compose->mode == COMPOSE_REEDIT)
2754                         compose->references = hentry[H_REFERENCES].body;
2755                 else {
2756                         compose->references = compose_parse_references
2757                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2758                         g_free(hentry[H_REFERENCES].body);
2759                 }
2760                 hentry[H_REFERENCES].body = NULL;
2761         }
2762         if (hentry[H_BCC].body != NULL) {
2763                 if (compose->mode == COMPOSE_REEDIT)
2764                         compose->bcc =
2765                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2766                 g_free(hentry[H_BCC].body);
2767                 hentry[H_BCC].body = NULL;
2768         }
2769         if (hentry[H_NEWSGROUPS].body != NULL) {
2770                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2771                 hentry[H_NEWSGROUPS].body = NULL;
2772         }
2773         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2774                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2775                         compose->followup_to =
2776                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2777                                                    NULL);
2778                 }
2779                 g_free(hentry[H_FOLLOWUP_TO].body);
2780                 hentry[H_FOLLOWUP_TO].body = NULL;
2781         }
2782         if (hentry[H_LIST_POST].body != NULL) {
2783                 gchar *to = NULL, *start = NULL;
2784
2785                 extract_address(hentry[H_LIST_POST].body);
2786                 if (hentry[H_LIST_POST].body[0] != '\0') {
2787                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2788                         
2789                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2790                                         NULL, &to, NULL, NULL, NULL, NULL, NULL);
2791
2792                         if (to) {
2793                                 g_free(compose->ml_post);
2794                                 compose->ml_post = to;
2795                         }
2796                 }
2797                 g_free(hentry[H_LIST_POST].body);
2798                 hentry[H_LIST_POST].body = NULL;
2799         }
2800
2801         /* CLAWS - X-Priority */
2802         if (compose->mode == COMPOSE_REEDIT)
2803                 if (hentry[H_X_PRIORITY].body != NULL) {
2804                         gint priority;
2805                         
2806                         priority = atoi(hentry[H_X_PRIORITY].body);
2807                         g_free(hentry[H_X_PRIORITY].body);
2808                         
2809                         hentry[H_X_PRIORITY].body = NULL;
2810                         
2811                         if (priority < PRIORITY_HIGHEST || 
2812                             priority > PRIORITY_LOWEST)
2813                                 priority = PRIORITY_NORMAL;
2814                         
2815                         compose->priority =  priority;
2816                 }
2817  
2818         if (compose->mode == COMPOSE_REEDIT) {
2819                 if (msginfo->inreplyto && *msginfo->inreplyto)
2820                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2821                 return 0;
2822         }
2823
2824         if (msginfo->msgid && *msginfo->msgid)
2825                 compose->inreplyto = g_strdup(msginfo->msgid);
2826
2827         if (!compose->references) {
2828                 if (msginfo->msgid && *msginfo->msgid) {
2829                         if (msginfo->inreplyto && *msginfo->inreplyto)
2830                                 compose->references =
2831                                         g_strdup_printf("<%s>\n\t<%s>",
2832                                                         msginfo->inreplyto,
2833                                                         msginfo->msgid);
2834                         else
2835                                 compose->references =
2836                                         g_strconcat("<", msginfo->msgid, ">",
2837                                                     NULL);
2838                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2839                         compose->references =
2840                                 g_strconcat("<", msginfo->inreplyto, ">",
2841                                             NULL);
2842                 }
2843         }
2844
2845         return 0;
2846 }
2847
2848 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2849 {
2850         GSList *ref_id_list, *cur;
2851         GString *new_ref;
2852         gchar *new_ref_str;
2853
2854         ref_id_list = references_list_append(NULL, ref);
2855         if (!ref_id_list) return NULL;
2856         if (msgid && *msgid)
2857                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2858
2859         for (;;) {
2860                 gint len = 0;
2861
2862                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2863                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2864                         len += strlen((gchar *)cur->data) + 5;
2865
2866                 if (len > MAX_REFERENCES_LEN) {
2867                         /* remove second message-ID */
2868                         if (ref_id_list && ref_id_list->next &&
2869                             ref_id_list->next->next) {
2870                                 g_free(ref_id_list->next->data);
2871                                 ref_id_list = g_slist_remove
2872                                         (ref_id_list, ref_id_list->next->data);
2873                         } else {
2874                                 slist_free_strings(ref_id_list);
2875                                 g_slist_free(ref_id_list);
2876                                 return NULL;
2877                         }
2878                 } else
2879                         break;
2880         }
2881
2882         new_ref = g_string_new("");
2883         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2884                 if (new_ref->len > 0)
2885                         g_string_append(new_ref, "\n\t");
2886                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2887         }
2888
2889         slist_free_strings(ref_id_list);
2890         g_slist_free(ref_id_list);
2891
2892         new_ref_str = new_ref->str;
2893         g_string_free(new_ref, FALSE);
2894
2895         return new_ref_str;
2896 }
2897
2898 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2899                                 const gchar *fmt, const gchar *qmark,
2900                                 const gchar *body, gboolean rewrap,
2901                                 gboolean need_unescape,
2902                                 const gchar *err_msg)
2903 {
2904         MsgInfo* dummyinfo = NULL;
2905         gchar *quote_str = NULL;
2906         gchar *buf;
2907         gboolean prev_autowrap;
2908         const gchar *trimmed_body = body;
2909         gint cursor_pos = -1;
2910         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2911         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2912         GtkTextIter iter;
2913         GtkTextMark *mark;
2914         
2915
2916         SIGNAL_BLOCK(buffer);
2917
2918         if (!msginfo) {
2919                 dummyinfo = compose_msginfo_new_from_compose(compose);
2920                 msginfo = dummyinfo;
2921         }
2922
2923         if (qmark != NULL) {
2924 #ifdef USE_ENCHANT
2925                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2926                                 compose->gtkaspell);
2927 #else
2928                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2929 #endif
2930                 quote_fmt_scan_string(qmark);
2931                 quote_fmt_parse();
2932
2933                 buf = quote_fmt_get_buffer();
2934                 if (buf == NULL)
2935                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2936                 else
2937                         Xstrdup_a(quote_str, buf, goto error)
2938         }
2939
2940         if (fmt && *fmt != '\0') {
2941
2942                 if (trimmed_body)
2943                         while (*trimmed_body == '\n')
2944                                 trimmed_body++;
2945
2946 #ifdef USE_ENCHANT
2947                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2948                                 compose->gtkaspell);
2949 #else
2950                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2951 #endif
2952                 if (need_unescape) {
2953                         gchar *tmp = NULL;
2954
2955                         /* decode \-escape sequences in the internal representation of the quote format */
2956                         tmp = malloc(strlen(fmt)+1);
2957                         pref_get_unescaped_pref(tmp, fmt);
2958                         quote_fmt_scan_string(tmp);
2959                         quote_fmt_parse();
2960                         g_free(tmp);
2961                 } else {
2962                         quote_fmt_scan_string(fmt);
2963                         quote_fmt_parse();
2964                 }
2965
2966                 buf = quote_fmt_get_buffer();
2967                 if (buf == NULL) {
2968                         gint line = quote_fmt_get_line();
2969                         alertpanel_error(err_msg, line);
2970                         goto error;
2971                 }
2972         } else
2973                 buf = "";
2974
2975         prev_autowrap = compose->autowrap;
2976         compose->autowrap = FALSE;
2977
2978         mark = gtk_text_buffer_get_insert(buffer);
2979         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2980         if (g_utf8_validate(buf, -1, NULL)) { 
2981                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2982         } else {
2983                 gchar *tmpout = NULL;
2984                 tmpout = conv_codeset_strdup
2985                         (buf, conv_get_locale_charset_str_no_utf8(),
2986                          CS_INTERNAL);
2987                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2988                         g_free(tmpout);
2989                         tmpout = g_malloc(strlen(buf)*2+1);
2990                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2991                 }
2992                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2993                 g_free(tmpout);
2994         }
2995
2996         cursor_pos = quote_fmt_get_cursor_pos();
2997         if (cursor_pos == -1)
2998                 cursor_pos = gtk_text_iter_get_offset(&iter);
2999         compose->set_cursor_pos = cursor_pos;
3000
3001         gtk_text_buffer_get_start_iter(buffer, &iter);
3002         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3003         gtk_text_buffer_place_cursor(buffer, &iter);
3004
3005         compose->autowrap = prev_autowrap;
3006         if (compose->autowrap && rewrap)
3007                 compose_wrap_all(compose);
3008
3009         goto ok;
3010
3011 error:
3012         buf = NULL;
3013 ok:
3014         SIGNAL_UNBLOCK(buffer);
3015
3016         procmsg_msginfo_free( dummyinfo );
3017
3018         return buf;
3019 }
3020
3021 /* if ml_post is of type addr@host and from is of type
3022  * addr-anything@host, return TRUE
3023  */
3024 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3025 {
3026         gchar *left_ml = NULL;
3027         gchar *right_ml = NULL;
3028         gchar *left_from = NULL;
3029         gchar *right_from = NULL;
3030         gboolean result = FALSE;
3031         
3032         if (!ml_post || !from)
3033                 return FALSE;
3034         
3035         left_ml = g_strdup(ml_post);
3036         if (strstr(left_ml, "@")) {
3037                 right_ml = strstr(left_ml, "@")+1;
3038                 *(strstr(left_ml, "@")) = '\0';
3039         }
3040         
3041         left_from = g_strdup(from);
3042         if (strstr(left_from, "@")) {
3043                 right_from = strstr(left_from, "@")+1;
3044                 *(strstr(left_from, "@")) = '\0';
3045         }
3046         
3047         if (left_ml && left_from && right_ml && right_from
3048         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3049         &&  !strcmp(right_from, right_ml)) {
3050                 result = TRUE;
3051         }
3052         g_free(left_ml);
3053         g_free(left_from);
3054         
3055         return result;
3056 }
3057
3058 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3059                                     gboolean to_all, gboolean to_ml,
3060                                     gboolean to_sender,
3061                                     gboolean followup_and_reply_to)
3062 {
3063         GSList *cc_list = NULL;
3064         GSList *cur;
3065         gchar *from = NULL;
3066         gchar *replyto = NULL;
3067         gchar *ac_email = NULL;
3068
3069         gboolean reply_to_ml = FALSE;
3070         gboolean default_reply_to = FALSE;
3071
3072         cm_return_if_fail(compose->account != NULL);
3073         cm_return_if_fail(msginfo != NULL);
3074
3075         reply_to_ml = to_ml && compose->ml_post;
3076
3077         default_reply_to = msginfo->folder && 
3078                 msginfo->folder->prefs->enable_default_reply_to;
3079
3080         if (compose->account->protocol != A_NNTP) {
3081                 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3082                         if (msginfo->folder->prefs->enable_default_replyto) {
3083                                 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3084                                                         COMPOSE_REPLYTO, PREF_FOLDER);
3085                         }
3086                         if (msginfo->folder->prefs->enable_default_bcc) {
3087                                 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3088                                                         COMPOSE_BCC, PREF_FOLDER);
3089                         }
3090                         if (msginfo->folder->prefs->enable_default_cc) {
3091                                 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3092                                                         COMPOSE_CC, PREF_FOLDER);
3093                         }
3094                 }
3095                 if (reply_to_ml && !default_reply_to) {
3096                         
3097                         gboolean is_subscr = is_subscription(compose->ml_post,
3098                                                              msginfo->from);
3099                         if (!is_subscr) {
3100                                 /* normal answer to ml post with a reply-to */
3101                                 compose_entry_append(compose,
3102                                            compose->ml_post,
3103                                            COMPOSE_TO, PREF_ML);
3104                                 if (compose->replyto)
3105                                         compose_entry_append(compose,
3106                                                 compose->replyto,
3107                                                 COMPOSE_CC, PREF_ML);
3108                         } else {
3109                                 /* answer to subscription confirmation */
3110                                 if (compose->replyto)
3111                                         compose_entry_append(compose,
3112                                                 compose->replyto,
3113                                                 COMPOSE_TO, PREF_ML);
3114                                 else if (msginfo->from)
3115                                         compose_entry_append(compose,
3116                                                 msginfo->from,
3117                                                 COMPOSE_TO, PREF_ML);
3118                         }
3119                 }
3120                 else if (!(to_all || to_sender) && default_reply_to) {
3121                         compose_entry_append(compose,
3122                             msginfo->folder->prefs->default_reply_to,
3123                             COMPOSE_TO, PREF_FOLDER);
3124                         compose_entry_mark_default_to(compose,
3125                                 msginfo->folder->prefs->default_reply_to);
3126                 } else {
3127                         gchar *tmp1 = NULL;
3128                         if (!msginfo->from)
3129                                 return;
3130                         Xstrdup_a(tmp1, msginfo->from, return);
3131                         extract_address(tmp1);
3132                         if (to_all || to_sender ||
3133                             !account_find_from_address(tmp1, FALSE))
3134                                 compose_entry_append(compose,
3135                                  (compose->replyto && !to_sender)
3136                                           ? compose->replyto :
3137                                           msginfo->from ? msginfo->from : "",
3138                                           COMPOSE_TO, PREF_NONE);
3139                         else if (!to_all && !to_sender) {
3140                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3141                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3142                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3143                                         if (compose->replyto) {
3144                                                 compose_entry_append(compose,
3145                                                         compose->replyto,
3146                                                         COMPOSE_TO, PREF_NONE);
3147                                         } else {
3148                                                 compose_entry_append(compose,
3149                                                           msginfo->from ? msginfo->from : "",
3150                                                           COMPOSE_TO, PREF_NONE);
3151                                         }
3152                                 } else {
3153                                         /* replying to own mail, use original recp */