7e97e46a3498626bd8cdded5691eba3c9ea6b9da
[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 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
537                                      gboolean respect_default_to);
538
539 static GtkActionEntry compose_popup_entries[] =
540 {
541         {"Compose",                     NULL, "Compose" },
542         {"Compose/Add",                 NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
543         {"Compose/Remove",                      NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
544         {"Compose/---",                 NULL, "---", NULL, NULL, NULL },
545         {"Compose/Properties",          NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
546 };
547
548 static GtkActionEntry compose_entries[] =
549 {
550         {"Menu",                                NULL, "Menu" },
551 /* menus */
552         {"Message",                     NULL, N_("_Message") },
553         {"Edit",                        NULL, N_("_Edit") },
554 #if USE_ENCHANT
555         {"Spelling",                    NULL, N_("_Spelling") },
556 #endif
557         {"Options",                     NULL, N_("_Options") },
558         {"Tools",                       NULL, N_("_Tools") },
559         {"Help",                        NULL, N_("_Help") },
560 /* Message menu */
561         {"Message/Send",                NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
562         {"Message/SendLater",           NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
563         {"Message/---",                 NULL, "---" },
564
565         {"Message/AttachFile",          NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
566         {"Message/InsertFile",          NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
567         {"Message/InsertSig",           NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
568         /* {"Message/---",              NULL, "---" }, */
569         {"Message/Save",                NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
570         /* {"Message/---",              NULL, "---" }, */
571         {"Message/Close",               NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
572
573 /* Edit menu */
574         {"Edit/Undo",                   NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
575         {"Edit/Redo",                   NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
576         {"Edit/---",                    NULL, "---" },
577
578         {"Edit/Cut",                    NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
579         {"Edit/Copy",                   NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
580         {"Edit/Paste",                  NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
581
582         {"Edit/SpecialPaste",           NULL, N_("Special paste") },
583         {"Edit/SpecialPaste/AsQuotation",       NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
584         {"Edit/SpecialPaste/Wrapped",   NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
585         {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
586
587         {"Edit/SelectAll",              NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
588
589         {"Edit/Advanced",               NULL, N_("A_dvanced") },
590         {"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*/
591         {"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*/
592         {"Edit/Advanced/BackWord",      NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
593         {"Edit/Advanced/ForwWord",      NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
594         {"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*/
595         {"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*/
596         {"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*/
597         {"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*/
598         {"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*/
599         {"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*/
600         {"Edit/Advanced/DelBackWord",   NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
601         {"Edit/Advanced/DelForwWord",   NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
602         {"Edit/Advanced/DelLine",       NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
603         {"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*/
604
605         /* {"Edit/---",                 NULL, "---" }, */
606         {"Edit/Find",           NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
607
608         /* {"Edit/---",                 NULL, "---" }, */
609         {"Edit/WrapPara",               NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
610         {"Edit/WrapAllLines",           NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
611         /* {"Edit/---",                 NULL, "---" }, */
612         {"Edit/ExtEditor",              NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
613 #if USE_ENCHANT
614 /* Spelling menu */
615         {"Spelling/CheckAllSel",        NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
616         {"Spelling/HighlightAll",       NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
617         {"Spelling/CheckBackwards",     NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
618         {"Spelling/ForwardNext",        NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
619
620         {"Spelling/---",                NULL, "---" },
621         {"Spelling/Options",            NULL, N_("_Options") },
622 #endif
623
624 /* Options menu */
625
626         {"Options/ReplyMode",           NULL, N_("Reply _mode") },
627         {"Options/---",                 NULL, "---" },
628         {"Options/PrivacySystem",       NULL, N_("Privacy _System") },
629         {"Options/PrivacySystem/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
630
631         /* {"Options/---",              NULL, "---" }, */
632
633         {"Options/Priority",            NULL, N_("_Priority") },
634
635         {"Options/Encoding",            NULL, N_("Character _encoding") },
636         {"Options/Encoding/---",        NULL, "---" },
637 #define ENC_ACTION(cs_char,c_char,string) \
638         { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
639
640         {"Options/Encoding/Western",    NULL, N_("Western European") },
641         {"Options/Encoding/Baltic",     NULL, N_("Baltic") },
642         {"Options/Encoding/Hebrew",     NULL, N_("Hebrew") },
643         {"Options/Encoding/Arabic",     NULL, N_("Arabic") },
644         {"Options/Encoding/Cyrillic",   NULL, N_("Cyrillic") },
645         {"Options/Encoding/Japanese",   NULL, N_("Japanese") },
646         {"Options/Encoding/Chinese",    NULL, N_("Chinese") },
647         {"Options/Encoding/Korean",     NULL, N_("Korean") },
648         {"Options/Encoding/Thai",       NULL, N_("Thai") },
649
650 /* Tools menu */
651         {"Tools/AddressBook",           NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
652
653         {"Tools/Template",      NULL, N_("_Template") },
654         {"Tools/Template/PlaceHolder",  NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
655         {"Tools/Actions",       NULL, N_("Actio_ns") },
656         {"Tools/Actions/PlaceHolder",   NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
657
658 /* Help menu */
659         {"Help/About",          NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
660 };
661
662 static GtkToggleActionEntry compose_toggle_entries[] =
663 {
664         {"Edit/AutoWrap",               NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
665         {"Edit/AutoIndent",             NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
666         {"Options/Sign",                NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
667         {"Options/Encrypt",             NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
668         {"Options/RequestRetRcpt",      NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
669         {"Options/RemoveReferences",    NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
670         {"Tools/ShowRuler",             NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
671 };
672
673 static GtkRadioActionEntry compose_radio_rm_entries[] =
674 {
675         {"Options/ReplyMode/Normal",    NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
676         {"Options/ReplyMode/All",       NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
677         {"Options/ReplyMode/Sender",    NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
678         {"Options/ReplyMode/List",      NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
679 };
680
681 static GtkRadioActionEntry compose_radio_prio_entries[] =
682 {
683         {"Options/Priority/Highest",    NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
684         {"Options/Priority/High",       NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
685         {"Options/Priority/Normal",     NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
686         {"Options/Priority/Low",        NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
687         {"Options/Priority/Lowest",     NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
688 };
689
690 static GtkRadioActionEntry compose_radio_enc_entries[] =
691 {
692         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
693         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
694         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
695         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
696         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
697         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
698         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
699         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
700         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
701         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
702         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
703         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
704         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
705         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
706         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
707         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
708         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
709         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
710         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
711         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
712         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
713         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
714         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
715         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
716         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
717         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
718         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
719         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
720         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
721         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
722         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
723         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
724 };
725
726 static GtkTargetEntry compose_mime_types[] =
727 {
728         {"text/uri-list", 0, 0},
729         {"UTF8_STRING", 0, 0},
730         {"text/plain", 0, 0}
731 };
732
733 static gboolean compose_put_existing_to_front(MsgInfo *info)
734 {
735         GList *compose_list = compose_get_compose_list();
736         GList *elem = NULL;
737         
738         if (compose_list) {
739                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
740                      elem = elem->next) {
741                         Compose *c = (Compose*)elem->data;
742
743                         if (!c->targetinfo || !c->targetinfo->msgid ||
744                             !info->msgid)
745                                 continue;
746
747                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
748                                 gtkut_window_popup(c->window);
749                                 return TRUE;
750                         }
751                 }
752         }
753         return FALSE;
754 }
755
756 static GdkColor quote_color1 = 
757         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_color2 = 
759         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760 static GdkColor quote_color3 = 
761         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
762
763 static GdkColor quote_bgcolor1 = 
764         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor2 = 
766         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767 static GdkColor quote_bgcolor3 = 
768         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
769
770 static GdkColor signature_color = {
771         (gulong)0,
772         (gushort)0x7fff,
773         (gushort)0x7fff,
774         (gushort)0x7fff
775 };
776
777 static GdkColor uri_color = {
778         (gulong)0,
779         (gushort)0,
780         (gushort)0,
781         (gushort)0
782 };
783
784 static void compose_create_tags(GtkTextView *text, Compose *compose)
785 {
786         GtkTextBuffer *buffer;
787         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
788         GdkColormap *cmap;
789         GdkColor color[8];
790         gboolean success[8];
791         int i;
792
793         buffer = gtk_text_view_get_buffer(text);
794
795         if (prefs_common.enable_color) {
796                 /* grab the quote colors, converting from an int to a GdkColor */
797                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
798                                                &quote_color1);
799                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
800                                                &quote_color2);
801                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
802                                                &quote_color3);
803                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
804                                                &quote_bgcolor1);
805                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
806                                                &quote_bgcolor2);
807                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
808                                                &quote_bgcolor3);
809                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
810                                                &signature_color);
811                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
812                                                &uri_color);
813         } else {
814                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
815                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
816         }
817
818         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
819                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820                                            "foreground-gdk", &quote_color1,
821                                            "paragraph-background-gdk", &quote_bgcolor1,
822                                            NULL);
823                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
824                                            "foreground-gdk", &quote_color2,
825                                            "paragraph-background-gdk", &quote_bgcolor2,
826                                            NULL);
827                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
828                                            "foreground-gdk", &quote_color3,
829                                            "paragraph-background-gdk", &quote_bgcolor3,
830                                            NULL);
831         } else {
832                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
833                                            "foreground-gdk", &quote_color1,
834                                            NULL);
835                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
836                                            "foreground-gdk", &quote_color2,
837                                            NULL);
838                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
839                                            "foreground-gdk", &quote_color3,
840                                            NULL);
841         }
842         
843         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
844                                    "foreground-gdk", &signature_color,
845                                    NULL);
846         
847         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
848                                         "foreground-gdk", &uri_color,
849                                          NULL);
850         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
851         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
852
853         color[0] = quote_color1;
854         color[1] = quote_color2;
855         color[2] = quote_color3;
856         color[3] = quote_bgcolor1;
857         color[4] = quote_bgcolor2;
858         color[5] = quote_bgcolor3;
859         color[6] = signature_color;
860         color[7] = uri_color;
861         cmap = gdk_drawable_get_colormap(compose->window->window);
862         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
863
864         for (i = 0; i < 8; i++) {
865                 if (success[i] == FALSE) {
866                         GtkStyle *style;
867
868                         g_warning("Compose: color allocation failed.\n");
869                         style = gtk_widget_get_style(GTK_WIDGET(text));
870                         quote_color1 = quote_color2 = quote_color3 = 
871                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
872                                 signature_color = uri_color = black;
873                 }
874         }
875 }
876
877 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
878                      GPtrArray *attach_files)
879 {
880         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
881 }
882
883 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
884 {
885         return compose_generic_new(account, mailto, item, NULL, NULL);
886 }
887
888 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
889 {
890         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
891 }
892
893 #define SCROLL_TO_CURSOR(compose) {                             \
894         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
895                 gtk_text_view_get_buffer(                       \
896                         GTK_TEXT_VIEW(compose->text)));         \
897         gtk_text_view_scroll_mark_onscreen(                     \
898                 GTK_TEXT_VIEW(compose->text),                   \
899                 cmark);                                         \
900 }
901
902 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
903 {
904         GtkEditable *entry;
905         if (folderidentifier) {
906                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
907                 prefs_common.compose_save_to_history = add_history(
908                                 prefs_common.compose_save_to_history, folderidentifier);
909                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
910                                 prefs_common.compose_save_to_history);
911         }
912
913         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
914         if (folderidentifier)
915                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
916         else
917                 gtk_entry_set_text(GTK_ENTRY(entry), "");
918 }
919
920 static gchar *compose_get_save_to(Compose *compose)
921 {
922         GtkEditable *entry;
923         gchar *result = NULL;
924         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
925         result = gtk_editable_get_chars(entry, 0, -1);
926         
927         if (result) {
928                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
929                 prefs_common.compose_save_to_history = add_history(
930                                 prefs_common.compose_save_to_history, result);
931                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
932                                 prefs_common.compose_save_to_history);
933         }
934         return result;
935 }
936
937 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
938                              GPtrArray *attach_files, GList *listAddress )
939 {
940         Compose *compose;
941         GtkTextView *textview;
942         GtkTextBuffer *textbuf;
943         GtkTextIter iter;
944         const gchar *subject_format = NULL;
945         const gchar *body_format = NULL;
946         gchar *mailto_from = NULL;
947         PrefsAccount *mailto_account = NULL;
948         MsgInfo* dummyinfo = NULL;
949         MailField mfield = NO_FIELD_PRESENT;
950         gchar* buf;
951         GtkTextMark *mark;
952
953         /* check if mailto defines a from */
954         if (mailto && *mailto != '\0') {
955                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
956                 /* mailto defines a from, check if we can get account prefs from it,
957                    if not, the account prefs will be guessed using other ways, but we'll keep
958                    the from anyway */
959                 if (mailto_from)
960                         mailto_account = account_find_from_address(mailto_from, TRUE);
961                 if (mailto_account)
962                         account = mailto_account;
963         }
964
965         /* if no account prefs set from mailto, set if from folder prefs (if any) */
966         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
967                 account = account_find_from_id(item->prefs->default_account);
968
969         /* if no account prefs set, fallback to the current one */
970         if (!account) account = cur_account;
971         cm_return_val_if_fail(account != NULL, NULL);
972
973         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
974
975         /* override from name if mailto asked for it */
976         if (mailto_from) {
977                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
978                 g_free(mailto_from);
979         } else
980                 /* override from name according to folder properties */
981                 if (item && item->prefs &&
982                         item->prefs->compose_with_format &&
983                         item->prefs->compose_override_from_format &&
984                         *item->prefs->compose_override_from_format != '\0') {
985
986                         gchar *tmp = NULL;
987                         gchar *buf = NULL;
988
989                         dummyinfo = compose_msginfo_new_from_compose(compose);
990
991                         /* decode \-escape sequences in the internal representation of the quote format */
992                         tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
993                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
994
995 #ifdef USE_ENCHANT
996                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
997                                         compose->gtkaspell);
998 #else
999                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1000 #endif
1001                         quote_fmt_scan_string(tmp);
1002                         quote_fmt_parse();
1003
1004                         buf = quote_fmt_get_buffer();
1005                         if (buf == NULL)
1006                                 alertpanel_error(_("New message From format error."));
1007                         else
1008                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1009                         quote_fmt_reset_vartable();
1010
1011                         g_free(tmp);
1012                 }
1013
1014         compose->replyinfo = NULL;
1015         compose->fwdinfo   = NULL;
1016
1017         textview = GTK_TEXT_VIEW(compose->text);
1018         textbuf = gtk_text_view_get_buffer(textview);
1019         compose_create_tags(textview, compose);
1020
1021         undo_block(compose->undostruct);
1022 #ifdef USE_ENCHANT
1023         compose_set_dictionaries_from_folder_prefs(compose, item);
1024 #endif
1025
1026         if (account->auto_sig)
1027                 compose_insert_sig(compose, FALSE);
1028         gtk_text_buffer_get_start_iter(textbuf, &iter);
1029         gtk_text_buffer_place_cursor(textbuf, &iter);
1030
1031         if (account->protocol != A_NNTP) {
1032                 if (mailto && *mailto != '\0') {
1033                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1034
1035                 } else {
1036                         compose_set_folder_prefs(compose, item, TRUE);
1037                 }
1038                 if (item && item->ret_rcpt) {
1039                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1040                 }
1041         } else {
1042                 if (mailto && *mailto != '\0') {
1043                         if (!strchr(mailto, '@'))
1044                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1045                         else
1046                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1047                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1049                         mfield = TO_FIELD_PRESENT;
1050                 }
1051                 /*
1052                  * CLAWS: just don't allow return receipt request, even if the user
1053                  * may want to send an email. simple but foolproof.
1054                  */
1055                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1056         }
1057         compose_add_field_list( compose, listAddress );
1058
1059         if (item && item->prefs && item->prefs->compose_with_format) {
1060                 subject_format = item->prefs->compose_subject_format;
1061                 body_format = item->prefs->compose_body_format;
1062         } else if (account->compose_with_format) {
1063                 subject_format = account->compose_subject_format;
1064                 body_format = account->compose_body_format;
1065         } else if (prefs_common.compose_with_format) {
1066                 subject_format = prefs_common.compose_subject_format;
1067                 body_format = prefs_common.compose_body_format;
1068         }
1069
1070         if (subject_format || body_format) {
1071
1072                 if ( subject_format
1073                          && *subject_format != '\0' )
1074                 {
1075                         gchar *subject = NULL;
1076                         gchar *tmp = NULL;
1077                         gchar *buf = NULL;
1078
1079                         if (!dummyinfo)
1080                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1081
1082                         /* decode \-escape sequences in the internal representation of the quote format */
1083                         tmp = malloc(strlen(subject_format)+1);
1084                         pref_get_unescaped_pref(tmp, subject_format);
1085
1086                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1087 #ifdef USE_ENCHANT
1088                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1089                                         compose->gtkaspell);
1090 #else
1091                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1092 #endif
1093                         quote_fmt_scan_string(tmp);
1094                         quote_fmt_parse();
1095
1096                         buf = quote_fmt_get_buffer();
1097                         if (buf == NULL)
1098                                 alertpanel_error(_("New message subject format error."));
1099                         else
1100                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102                         quote_fmt_reset_vartable();
1103
1104                         g_free(subject);
1105                         g_free(tmp);
1106                         mfield = SUBJECT_FIELD_PRESENT;
1107                 }
1108
1109                 if ( body_format
1110                          && *body_format != '\0' )
1111                 {
1112                         GtkTextView *text;
1113                         GtkTextBuffer *buffer;
1114                         GtkTextIter start, end;
1115                         gchar *tmp = NULL;
1116
1117                         if (!dummyinfo)
1118                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1119
1120                         text = GTK_TEXT_VIEW(compose->text);
1121                         buffer = gtk_text_view_get_buffer(text);
1122                         gtk_text_buffer_get_start_iter(buffer, &start);
1123                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1124                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1125
1126                         compose_quote_fmt(compose, dummyinfo,
1127                                           body_format,
1128                                           NULL, tmp, FALSE, TRUE,
1129                                                   _("The body of the \"New message\" template has an error at line %d."));
1130                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1131                         quote_fmt_reset_vartable();
1132
1133                         g_free(tmp);
1134 #ifdef USE_ENCHANT
1135                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1136                                 gtkaspell_highlight_all(compose->gtkaspell);
1137 #endif
1138                         mfield = BODY_FIELD_PRESENT;
1139                 }
1140
1141         }
1142         procmsg_msginfo_free( dummyinfo );
1143
1144         if (attach_files) {
1145                 gint i;
1146                 gchar *file;
1147
1148                 for (i = 0; i < attach_files->len; i++) {
1149                         file = g_ptr_array_index(attach_files, i);
1150                         compose_attach_append(compose, file, file, NULL);
1151                 }
1152         }
1153
1154         compose_show_first_last_header(compose, TRUE);
1155
1156         /* Set save folder */
1157         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1158                 gchar *folderidentifier;
1159
1160                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1161                 folderidentifier = folder_item_get_identifier(item);
1162                 compose_set_save_to(compose, folderidentifier);
1163                 g_free(folderidentifier);
1164         }
1165
1166         /* Place cursor according to provided input (mfield) */
1167         switch (mfield) { 
1168                 case NO_FIELD_PRESENT:
1169                         if (compose->header_last)
1170                                 gtk_widget_grab_focus(compose->header_last->entry);
1171                         break;
1172                 case TO_FIELD_PRESENT:
1173                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1174                         if (buf) {
1175                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1176                                 g_free(buf);
1177                         }
1178                         gtk_widget_grab_focus(compose->subject_entry);
1179                         break;
1180                 case SUBJECT_FIELD_PRESENT:
1181                         textview = GTK_TEXT_VIEW(compose->text);
1182                         if (!textview)
1183                                 break;
1184                         textbuf = gtk_text_view_get_buffer(textview);
1185                         if (!textbuf)
1186                                 break;
1187                         mark = gtk_text_buffer_get_insert(textbuf);
1188                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1189                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1190                     /* 
1191                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1192                      * only defers where it comes to the variable body
1193                      * is not null. If no body is present compose->text
1194                      * will be null in which case you cannot place the
1195                      * cursor inside the component so. An empty component
1196                      * is therefore created before placing the cursor
1197                      */
1198                 case BODY_FIELD_PRESENT:
1199                         gtk_widget_grab_focus(compose->text);
1200                         break;
1201         }
1202
1203         undo_unblock(compose->undostruct);
1204
1205         if (prefs_common.auto_exteditor)
1206                 compose_exec_ext_editor(compose);
1207
1208         compose->draft_timeout_tag = -1;
1209         SCROLL_TO_CURSOR(compose);
1210
1211         compose->modified = FALSE;
1212         compose_set_title(compose);
1213
1214   hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1215
1216         return compose;
1217 }
1218
1219 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1220                 gboolean override_pref, const gchar *system)
1221 {
1222         const gchar *privacy = NULL;
1223
1224         cm_return_if_fail(compose != NULL);
1225         cm_return_if_fail(account != NULL);
1226
1227         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1228                 return;
1229
1230         if (system)
1231                 privacy = system;
1232         else if (account->default_privacy_system
1233         &&  strlen(account->default_privacy_system)) {
1234                 privacy = account->default_privacy_system;
1235         } else {
1236                 GSList *privacy_avail = privacy_get_system_ids();
1237                 if (privacy_avail && g_slist_length(privacy_avail)) {
1238                         privacy = (gchar *)(privacy_avail->data);
1239                 }
1240         }
1241         if (privacy != NULL) {
1242                 if (system) {
1243                         g_free(compose->privacy_system);
1244                         compose->privacy_system = NULL;
1245                 }
1246                 if (compose->privacy_system == NULL)
1247                         compose->privacy_system = g_strdup(privacy);
1248                 else if (*(compose->privacy_system) == '\0') {
1249                         g_free(compose->privacy_system);
1250                         compose->privacy_system = g_strdup(privacy);
1251                 }
1252                 compose_update_privacy_system_menu_item(compose, FALSE);
1253                 compose_use_encryption(compose, TRUE);
1254         }
1255 }       
1256
1257 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1258 {
1259         const gchar *privacy = NULL;
1260
1261         if (system)
1262                 privacy = system;
1263         else if (account->default_privacy_system
1264         &&  strlen(account->default_privacy_system)) {
1265                 privacy = account->default_privacy_system;
1266         } else {
1267                 GSList *privacy_avail = privacy_get_system_ids();
1268                 if (privacy_avail && g_slist_length(privacy_avail)) {
1269                         privacy = (gchar *)(privacy_avail->data);
1270                 }
1271         }
1272
1273         if (privacy != NULL) {
1274                 if (system) {
1275                         g_free(compose->privacy_system);
1276                         compose->privacy_system = NULL;
1277                 }
1278                 if (compose->privacy_system == NULL)
1279                         compose->privacy_system = g_strdup(privacy);
1280                 compose_update_privacy_system_menu_item(compose, FALSE);
1281                 compose_use_signing(compose, TRUE);
1282         }
1283 }       
1284
1285 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1286 {
1287         MsgInfo *msginfo;
1288         guint list_len;
1289         Compose *compose = NULL;
1290         
1291         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1292
1293         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1294         cm_return_val_if_fail(msginfo != NULL, NULL);
1295
1296         list_len = g_slist_length(msginfo_list);
1297
1298         switch (mode) {
1299         case COMPOSE_REPLY:
1300         case COMPOSE_REPLY_TO_ADDRESS:
1301                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1302                               FALSE, prefs_common.default_reply_list, FALSE, body);
1303                 break;
1304         case COMPOSE_REPLY_WITH_QUOTE:
1305                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1306                         FALSE, prefs_common.default_reply_list, FALSE, body);
1307                 break;
1308         case COMPOSE_REPLY_WITHOUT_QUOTE:
1309                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1310                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1311                 break;
1312         case COMPOSE_REPLY_TO_SENDER:
1313                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1314                               FALSE, FALSE, TRUE, body);
1315                 break;
1316         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1317                 compose = compose_followup_and_reply_to(msginfo,
1318                                               COMPOSE_QUOTE_CHECK,
1319                                               FALSE, FALSE, body);
1320                 break;
1321         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1322                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1323                         FALSE, FALSE, TRUE, body);
1324                 break;
1325         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1326                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1327                         FALSE, FALSE, TRUE, NULL);
1328                 break;
1329         case COMPOSE_REPLY_TO_ALL:
1330                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1331                         TRUE, FALSE, FALSE, body);
1332                 break;
1333         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1334                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1335                         TRUE, FALSE, FALSE, body);
1336                 break;
1337         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1338                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1339                         TRUE, FALSE, FALSE, NULL);
1340                 break;
1341         case COMPOSE_REPLY_TO_LIST:
1342                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1343                         FALSE, TRUE, FALSE, body);
1344                 break;
1345         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1346                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1347                         FALSE, TRUE, FALSE, body);
1348                 break;
1349         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1350                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1351                         FALSE, TRUE, FALSE, NULL);
1352                 break;
1353         case COMPOSE_FORWARD:
1354                 if (prefs_common.forward_as_attachment) {
1355                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1356                         return compose;
1357                 } else {
1358                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1359                         return compose;
1360                 }
1361                 break;
1362         case COMPOSE_FORWARD_INLINE:
1363                 /* check if we reply to more than one Message */
1364                 if (list_len == 1) {
1365                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1366                         break;
1367                 } 
1368                 /* more messages FALL THROUGH */
1369         case COMPOSE_FORWARD_AS_ATTACH:
1370                 compose = compose_forward_multiple(NULL, msginfo_list);
1371                 break;
1372         case COMPOSE_REDIRECT:
1373                 compose = compose_redirect(NULL, msginfo, FALSE);
1374                 break;
1375         default:
1376                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1377         }
1378         
1379         if (compose == NULL) {
1380                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1381                 return NULL;
1382         }
1383
1384         compose->rmode = mode;
1385         switch (compose->rmode) {
1386         case COMPOSE_REPLY:
1387         case COMPOSE_REPLY_WITH_QUOTE:
1388         case COMPOSE_REPLY_WITHOUT_QUOTE:
1389         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1390                 debug_print("reply mode Normal\n");
1391                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1392                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1393                 break;
1394         case COMPOSE_REPLY_TO_SENDER:
1395         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1396         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1397                 debug_print("reply mode Sender\n");
1398                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1399                 break;
1400         case COMPOSE_REPLY_TO_ALL:
1401         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1402         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1403                 debug_print("reply mode All\n");
1404                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1405                 break;
1406         case COMPOSE_REPLY_TO_LIST:
1407         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1408         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1409                 debug_print("reply mode List\n");
1410                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1411                 break;
1412         case COMPOSE_REPLY_TO_ADDRESS:
1413                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1414                 break;
1415         default:
1416                 break;
1417         }
1418         return compose;
1419 }
1420
1421 static Compose *compose_reply(MsgInfo *msginfo,
1422                                    ComposeQuoteMode quote_mode,
1423                                    gboolean to_all,
1424                                    gboolean to_ml,
1425                                    gboolean to_sender, 
1426                                    const gchar *body)
1427 {
1428         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1429                               to_sender, FALSE, body);
1430 }
1431
1432 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1433                                    ComposeQuoteMode quote_mode,
1434                                    gboolean to_all,
1435                                    gboolean to_sender,
1436                                    const gchar *body)
1437 {
1438         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1439                               to_sender, TRUE, body);
1440 }
1441
1442 static void compose_extract_original_charset(Compose *compose)
1443 {
1444         MsgInfo *info = NULL;
1445         if (compose->replyinfo) {
1446                 info = compose->replyinfo;
1447         } else if (compose->fwdinfo) {
1448                 info = compose->fwdinfo;
1449         } else if (compose->targetinfo) {
1450                 info = compose->targetinfo;
1451         }
1452         if (info) {
1453                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1454                 MimeInfo *partinfo = mimeinfo;
1455                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1456                         partinfo = procmime_mimeinfo_next(partinfo);
1457                 if (partinfo) {
1458                         compose->orig_charset = 
1459                                 g_strdup(procmime_mimeinfo_get_parameter(
1460                                                 partinfo, "charset"));
1461                 }
1462                 procmime_mimeinfo_free_all(mimeinfo);
1463         }
1464 }
1465
1466 #define SIGNAL_BLOCK(buffer) {                                  \
1467         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1468                                 G_CALLBACK(compose_changed_cb), \
1469                                 compose);                       \
1470         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1471                                 G_CALLBACK(text_inserted),      \
1472                                 compose);                       \
1473 }
1474
1475 #define SIGNAL_UNBLOCK(buffer) {                                \
1476         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1477                                 G_CALLBACK(compose_changed_cb), \
1478                                 compose);                       \
1479         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1480                                 G_CALLBACK(text_inserted),      \
1481                                 compose);                       \
1482 }
1483
1484 static Compose *compose_generic_reply(MsgInfo *msginfo,
1485                                   ComposeQuoteMode quote_mode,
1486                                   gboolean to_all, gboolean to_ml,
1487                                   gboolean to_sender,
1488                                   gboolean followup_and_reply_to,
1489                                   const gchar *body)
1490 {
1491         Compose *compose;
1492         PrefsAccount *account = NULL;
1493         GtkTextView *textview;
1494         GtkTextBuffer *textbuf;
1495         gboolean quote = FALSE;
1496         const gchar *qmark = NULL;
1497         const gchar *body_fmt = NULL;
1498         gchar *s_system = NULL;
1499         START_TIMING("");
1500         cm_return_val_if_fail(msginfo != NULL, NULL);
1501         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1502
1503         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1504
1505         cm_return_val_if_fail(account != NULL, NULL);
1506
1507         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1508
1509         compose->updating = TRUE;
1510
1511         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1512         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1513
1514         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1515         if (!compose->replyinfo)
1516                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1517
1518         compose_extract_original_charset(compose);
1519         
1520         if (msginfo->folder && msginfo->folder->ret_rcpt)
1521                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1522
1523         /* Set save folder */
1524         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1525                 gchar *folderidentifier;
1526
1527                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1528                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1529                 compose_set_save_to(compose, folderidentifier);
1530                 g_free(folderidentifier);
1531         }
1532
1533         if (compose_parse_header(compose, msginfo) < 0) {
1534                 compose->updating = FALSE;
1535                 compose_destroy(compose);
1536                 return NULL;
1537         }
1538         if (!compose->ml_post)
1539                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode/List", FALSE);
1540         
1541         /* override from name according to folder properties */
1542         if (msginfo->folder && msginfo->folder->prefs &&
1543                 msginfo->folder->prefs->reply_with_format &&
1544                 msginfo->folder->prefs->reply_override_from_format &&
1545                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1546
1547                 gchar *tmp = NULL;
1548                 gchar *buf = NULL;
1549
1550                 /* decode \-escape sequences in the internal representation of the quote format */
1551                 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1552                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1553
1554 #ifdef USE_ENCHANT
1555                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1556                                 compose->gtkaspell);
1557 #else
1558                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1559 #endif
1560                 quote_fmt_scan_string(tmp);
1561                 quote_fmt_parse();
1562
1563                 buf = quote_fmt_get_buffer();
1564                 if (buf == NULL)
1565                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1566                 else
1567                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1568                 quote_fmt_reset_vartable();
1569
1570                 g_free(tmp);
1571         }
1572
1573         textview = (GTK_TEXT_VIEW(compose->text));
1574         textbuf = gtk_text_view_get_buffer(textview);
1575         compose_create_tags(textview, compose);
1576
1577         undo_block(compose->undostruct);
1578 #ifdef USE_ENCHANT
1579                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1580 #endif
1581
1582         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1583                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1584                 /* use the reply format of folder (if enabled), or the account's one
1585                    (if enabled) or fallback to the global reply format, which is always
1586                    enabled (even if empty), and use the relevant quotemark */
1587                 quote = TRUE;
1588                 if (msginfo->folder && msginfo->folder->prefs &&
1589                                 msginfo->folder->prefs->reply_with_format) {
1590                         qmark = msginfo->folder->prefs->reply_quotemark;
1591                         body_fmt = msginfo->folder->prefs->reply_body_format;
1592
1593                 } else if (account->reply_with_format) {
1594                         qmark = account->reply_quotemark;
1595                         body_fmt = account->reply_body_format;
1596
1597                 } else {
1598                         qmark = prefs_common.quotemark;
1599                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1600                                 body_fmt = gettext(prefs_common.quotefmt);
1601                         else
1602                                 body_fmt = "";
1603                 }
1604         }
1605
1606         if (quote) {
1607                 /* empty quotemark is not allowed */
1608                 if (qmark == NULL || *qmark == '\0')
1609                         qmark = "> ";
1610                 compose_quote_fmt(compose, compose->replyinfo,
1611                                   body_fmt, qmark, body, FALSE, TRUE,
1612                                           _("The body of the \"Reply\" template has an error at line %d."));
1613                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1614                 quote_fmt_reset_vartable();
1615 #ifdef USE_ENCHANT
1616                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1617                         gtkaspell_highlight_all(compose->gtkaspell);
1618 #endif
1619         }
1620
1621         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1622                 compose_force_encryption(compose, account, FALSE, s_system);
1623         }
1624
1625         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1626         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1627                 compose_force_signing(compose, account, s_system);
1628         }
1629         g_free(s_system);
1630
1631         SIGNAL_BLOCK(textbuf);
1632         
1633         if (account->auto_sig)
1634                 compose_insert_sig(compose, FALSE);
1635
1636         compose_wrap_all(compose);
1637
1638         SIGNAL_UNBLOCK(textbuf);
1639         
1640         gtk_widget_grab_focus(compose->text);
1641
1642         undo_unblock(compose->undostruct);
1643
1644         if (prefs_common.auto_exteditor)
1645                 compose_exec_ext_editor(compose);
1646                 
1647         compose->modified = FALSE;
1648         compose_set_title(compose);
1649
1650         compose->updating = FALSE;
1651         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1652         SCROLL_TO_CURSOR(compose);
1653         
1654         if (compose->deferred_destroy) {
1655                 compose_destroy(compose);
1656                 return NULL;
1657         }
1658         END_TIMING();
1659
1660         return compose;
1661 }
1662
1663 #define INSERT_FW_HEADER(var, hdr) \
1664 if (msginfo->var && *msginfo->var) { \
1665         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1666         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1667         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1668 }
1669
1670 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1671                          gboolean as_attach, const gchar *body,
1672                          gboolean no_extedit,
1673                          gboolean batch)
1674 {
1675         Compose *compose;
1676         GtkTextView *textview;
1677         GtkTextBuffer *textbuf;
1678         GtkTextIter iter;
1679
1680         cm_return_val_if_fail(msginfo != NULL, NULL);
1681         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1682
1683         if (!account && 
1684             !(account = compose_guess_forward_account_from_msginfo
1685                                 (msginfo)))
1686                 account = cur_account;
1687
1688         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1689
1690         compose->updating = TRUE;
1691         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1692         if (!compose->fwdinfo)
1693                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1694
1695         compose_extract_original_charset(compose);
1696
1697         if (msginfo->subject && *msginfo->subject) {
1698                 gchar *buf, *buf2, *p;
1699
1700                 buf = p = g_strdup(msginfo->subject);
1701                 p += subject_get_prefix_length(p);
1702                 memmove(buf, p, strlen(p) + 1);
1703
1704                 buf2 = g_strdup_printf("Fw: %s", buf);
1705                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1706                 
1707                 g_free(buf);
1708                 g_free(buf2);
1709         }
1710
1711         /* override from name according to folder properties */
1712         if (msginfo->folder && msginfo->folder->prefs &&
1713                 msginfo->folder->prefs->forward_with_format &&
1714                 msginfo->folder->prefs->forward_override_from_format &&
1715                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1716
1717                 gchar *tmp = NULL;
1718                 gchar *buf = NULL;
1719                 MsgInfo *full_msginfo = NULL;
1720
1721                 if (!as_attach)
1722                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1723                 if (!full_msginfo)
1724                         full_msginfo = procmsg_msginfo_copy(msginfo);
1725
1726                 /* decode \-escape sequences in the internal representation of the quote format */
1727                 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1728                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1729
1730 #ifdef USE_ENCHANT
1731                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1732                                 compose->gtkaspell);
1733 #else
1734                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1735 #endif
1736                 quote_fmt_scan_string(tmp);
1737                 quote_fmt_parse();
1738
1739                 buf = quote_fmt_get_buffer();
1740                 if (buf == NULL)
1741                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1742                 else
1743                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1744                 quote_fmt_reset_vartable();
1745
1746                 g_free(tmp);
1747                 procmsg_msginfo_free(full_msginfo);
1748         }
1749
1750         textview = GTK_TEXT_VIEW(compose->text);
1751         textbuf = gtk_text_view_get_buffer(textview);
1752         compose_create_tags(textview, compose);
1753         
1754         undo_block(compose->undostruct);
1755         if (as_attach) {
1756                 gchar *msgfile;
1757
1758                 msgfile = procmsg_get_message_file(msginfo);
1759                 if (!is_file_exist(msgfile))
1760                         g_warning("%s: file not exist\n", msgfile);
1761                 else
1762                         compose_attach_append(compose, msgfile, msgfile,
1763                                               "message/rfc822");
1764
1765                 g_free(msgfile);
1766         } else {
1767                 const gchar *qmark = NULL;
1768                 const gchar *body_fmt = NULL;
1769                 MsgInfo *full_msginfo;
1770
1771                 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1772                         body_fmt = gettext(prefs_common.fw_quotefmt);
1773                 else
1774                         body_fmt = "";
1775         
1776                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1777                 if (!full_msginfo)
1778                         full_msginfo = procmsg_msginfo_copy(msginfo);
1779
1780                 /* use the forward format of folder (if enabled), or the account's one
1781                    (if enabled) or fallback to the global forward format, which is always
1782                    enabled (even if empty), and use the relevant quotemark */
1783                 if (msginfo->folder && msginfo->folder->prefs &&
1784                                 msginfo->folder->prefs->forward_with_format) {
1785                         qmark = msginfo->folder->prefs->forward_quotemark;
1786                         body_fmt = msginfo->folder->prefs->forward_body_format;
1787
1788                 } else if (account->forward_with_format) {
1789                         qmark = account->forward_quotemark;
1790                         body_fmt = account->forward_body_format;
1791
1792                 } else {
1793                         qmark = prefs_common.fw_quotemark;
1794                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1795                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1796                         else
1797                                 body_fmt = "";
1798                 }
1799
1800                 /* empty quotemark is not allowed */
1801                 if (qmark == NULL || *qmark == '\0')
1802                         qmark = "> ";
1803
1804                 compose_quote_fmt(compose, full_msginfo,
1805                                   body_fmt, qmark, body, FALSE, TRUE,
1806                                           _("The body of the \"Forward\" template has an error at line %d."));
1807                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1808                 quote_fmt_reset_vartable();
1809                 compose_attach_parts(compose, msginfo);
1810
1811                 procmsg_msginfo_free(full_msginfo);
1812 #ifdef USE_ENCHANT
1813                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1814                         gtkaspell_highlight_all(compose->gtkaspell);
1815 #endif
1816         }
1817
1818         SIGNAL_BLOCK(textbuf);
1819
1820         if (account->auto_sig)
1821                 compose_insert_sig(compose, FALSE);
1822
1823         compose_wrap_all(compose);
1824
1825         SIGNAL_UNBLOCK(textbuf);
1826         
1827         gtk_text_buffer_get_start_iter(textbuf, &iter);
1828         gtk_text_buffer_place_cursor(textbuf, &iter);
1829
1830         gtk_widget_grab_focus(compose->header_last->entry);
1831
1832         if (!no_extedit && prefs_common.auto_exteditor)
1833                 compose_exec_ext_editor(compose);
1834         
1835         /*save folder*/
1836         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1837                 gchar *folderidentifier;
1838
1839                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1840                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1841                 compose_set_save_to(compose, folderidentifier);
1842                 g_free(folderidentifier);
1843         }
1844
1845         undo_unblock(compose->undostruct);
1846         
1847         compose->modified = FALSE;
1848         compose_set_title(compose);
1849
1850         compose->updating = FALSE;
1851         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1852         SCROLL_TO_CURSOR(compose);
1853
1854         if (compose->deferred_destroy) {
1855                 compose_destroy(compose);
1856                 return NULL;
1857         }
1858
1859         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1860
1861         return compose;
1862 }
1863
1864 #undef INSERT_FW_HEADER
1865
1866 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1867 {
1868         Compose *compose;
1869         GtkTextView *textview;
1870         GtkTextBuffer *textbuf;
1871         GtkTextIter iter;
1872         GSList *msginfo;
1873         gchar *msgfile;
1874         gboolean single_mail = TRUE;
1875         
1876         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1877
1878         if (g_slist_length(msginfo_list) > 1)
1879                 single_mail = FALSE;
1880
1881         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1882                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1883                         return NULL;
1884
1885         /* guess account from first selected message */
1886         if (!account && 
1887             !(account = compose_guess_forward_account_from_msginfo
1888                                 (msginfo_list->data)))
1889                 account = cur_account;
1890
1891         cm_return_val_if_fail(account != NULL, NULL);
1892
1893         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1894                 if (msginfo->data) {
1895                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1896                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1897                 }
1898         }
1899
1900         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1901                 g_warning("no msginfo_list");
1902                 return NULL;
1903         }
1904
1905         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1906
1907         compose->updating = TRUE;
1908
1909         /* override from name according to folder properties */
1910         if (msginfo_list->data) {
1911                 MsgInfo *msginfo = msginfo_list->data;
1912
1913                 if (msginfo->folder && msginfo->folder->prefs &&
1914                         msginfo->folder->prefs->forward_with_format &&
1915                         msginfo->folder->prefs->forward_override_from_format &&
1916                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1917
1918                         gchar *tmp = NULL;
1919                         gchar *buf = NULL;
1920
1921                         /* decode \-escape sequences in the internal representation of the quote format */
1922                         tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1923                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1924
1925 #ifdef USE_ENCHANT
1926                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1927                                         compose->gtkaspell);
1928 #else
1929                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1930 #endif
1931                         quote_fmt_scan_string(tmp);
1932                         quote_fmt_parse();
1933
1934                         buf = quote_fmt_get_buffer();
1935                         if (buf == NULL)
1936                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1937                         else
1938                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1939                         quote_fmt_reset_vartable();
1940
1941                         g_free(tmp);
1942                 }
1943         }
1944
1945         textview = GTK_TEXT_VIEW(compose->text);
1946         textbuf = gtk_text_view_get_buffer(textview);
1947         compose_create_tags(textview, compose);
1948         
1949         undo_block(compose->undostruct);
1950         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1951                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1952
1953                 if (!is_file_exist(msgfile))
1954                         g_warning("%s: file not exist\n", msgfile);
1955                 else
1956                         compose_attach_append(compose, msgfile, msgfile,
1957                                 "message/rfc822");
1958                 g_free(msgfile);
1959         }
1960         
1961         if (single_mail) {
1962                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1963                 if (info->subject && *info->subject) {
1964                         gchar *buf, *buf2, *p;
1965
1966                         buf = p = g_strdup(info->subject);
1967                         p += subject_get_prefix_length(p);
1968                         memmove(buf, p, strlen(p) + 1);
1969
1970                         buf2 = g_strdup_printf("Fw: %s", buf);
1971                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1972
1973                         g_free(buf);
1974                         g_free(buf2);
1975                 }
1976         } else {
1977                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1978                         _("Fw: multiple emails"));
1979         }
1980
1981         SIGNAL_BLOCK(textbuf);
1982         
1983         if (account->auto_sig)
1984                 compose_insert_sig(compose, FALSE);
1985
1986         compose_wrap_all(compose);
1987
1988         SIGNAL_UNBLOCK(textbuf);
1989         
1990         gtk_text_buffer_get_start_iter(textbuf, &iter);
1991         gtk_text_buffer_place_cursor(textbuf, &iter);
1992
1993         gtk_widget_grab_focus(compose->header_last->entry);
1994         undo_unblock(compose->undostruct);
1995         compose->modified = FALSE;
1996         compose_set_title(compose);
1997
1998         compose->updating = FALSE;
1999         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2000         SCROLL_TO_CURSOR(compose);
2001
2002         if (compose->deferred_destroy) {
2003                 compose_destroy(compose);
2004                 return NULL;
2005         }
2006
2007         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2008
2009         return compose;
2010 }
2011
2012 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2013 {
2014         GtkTextIter start = *iter;
2015         GtkTextIter end_iter;
2016         int start_pos = gtk_text_iter_get_offset(&start);
2017         gchar *str = NULL;
2018         if (!compose->account->sig_sep)
2019                 return FALSE;
2020         
2021         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2022                 start_pos+strlen(compose->account->sig_sep));
2023
2024         /* check sig separator */
2025         str = gtk_text_iter_get_text(&start, &end_iter);
2026         if (!strcmp(str, compose->account->sig_sep)) {
2027                 gchar *tmp = NULL;
2028                 /* check end of line (\n) */
2029                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2030                         start_pos+strlen(compose->account->sig_sep));
2031                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2032                         start_pos+strlen(compose->account->sig_sep)+1);
2033                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2034                 if (!strcmp(tmp,"\n")) {
2035                         g_free(str);
2036                         g_free(tmp);
2037                         return TRUE;
2038                 }
2039                 g_free(tmp);    
2040         }
2041         g_free(str);
2042
2043         return FALSE;
2044 }
2045
2046 static void compose_colorize_signature(Compose *compose)
2047 {
2048         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2049         GtkTextIter iter;
2050         GtkTextIter end_iter;
2051         gtk_text_buffer_get_start_iter(buffer, &iter);
2052         while (gtk_text_iter_forward_line(&iter))
2053                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2054                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2055                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2056                 }
2057 }
2058
2059 #define BLOCK_WRAP() {                                                  \
2060         prev_autowrap = compose->autowrap;                              \
2061         buffer = gtk_text_view_get_buffer(                              \
2062                                         GTK_TEXT_VIEW(compose->text));  \
2063         compose->autowrap = FALSE;                                      \
2064                                                                         \
2065         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2066                                 G_CALLBACK(compose_changed_cb),         \
2067                                 compose);                               \
2068         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2069                                 G_CALLBACK(text_inserted),              \
2070                                 compose);                               \
2071 }
2072 #define UNBLOCK_WRAP() {                                                \
2073         compose->autowrap = prev_autowrap;                              \
2074         if (compose->autowrap) {                                        \
2075                 gint old = compose->draft_timeout_tag;                  \
2076                 compose->draft_timeout_tag = -2;                        \
2077                 compose_wrap_all(compose);                              \
2078                 compose->draft_timeout_tag = old;                       \
2079         }                                                               \
2080                                                                         \
2081         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2082                                 G_CALLBACK(compose_changed_cb),         \
2083                                 compose);                               \
2084         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2085                                 G_CALLBACK(text_inserted),              \
2086                                 compose);                               \
2087 }
2088
2089 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2090 {
2091         Compose *compose = NULL;
2092         PrefsAccount *account = NULL;
2093         GtkTextView *textview;
2094         GtkTextBuffer *textbuf;
2095         GtkTextMark *mark;
2096         GtkTextIter iter;
2097         FILE *fp;
2098         gchar buf[BUFFSIZE];
2099         gboolean use_signing = FALSE;
2100         gboolean use_encryption = FALSE;
2101         gchar *privacy_system = NULL;
2102         int priority = PRIORITY_NORMAL;
2103         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2104         gboolean autowrap = prefs_common.autowrap;
2105         gboolean autoindent = prefs_common.auto_indent;
2106
2107         cm_return_val_if_fail(msginfo != NULL, NULL);
2108         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2109
2110         if (compose_put_existing_to_front(msginfo)) {
2111                 return NULL;
2112         }
2113
2114         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2115             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2116             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2117                 gchar queueheader_buf[BUFFSIZE];
2118                 gint id, param;
2119
2120                 /* Select Account from queue headers */
2121                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2122                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2123                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2124                         account = account_find_from_id(id);
2125                 }
2126                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2127                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2128                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2129                         account = account_find_from_id(id);
2130                 }
2131                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2132                                              sizeof(queueheader_buf), "NAID:")) {
2133                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2134                         account = account_find_from_id(id);
2135                 }
2136                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2137                                                     sizeof(queueheader_buf), "MAID:")) {
2138                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2139                         account = account_find_from_id(id);
2140                 }
2141                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2142                                                                 sizeof(queueheader_buf), "S:")) {
2143                         account = account_find_from_address(queueheader_buf, FALSE);
2144                 }
2145                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2146                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2147                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2148                         use_signing = param;
2149                         
2150                 }
2151                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2152                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2153                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2154                         use_signing = param;
2155                         
2156                 }
2157                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2158                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2159                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2160                         use_encryption = param;
2161                 }
2162                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2163                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2164                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2165                         use_encryption = param;
2166                 }
2167                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2168                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2169                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2170                         autowrap = param;
2171                 }
2172                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2173                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2174                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2175                         autoindent = param;
2176                 }
2177                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2178                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2179                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2180                 }
2181                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2182                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2183                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2184                 }
2185                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2186                                              sizeof(queueheader_buf), "X-Priority: ")) {
2187                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2188                         priority = param;
2189                 }
2190                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2191                                              sizeof(queueheader_buf), "RMID:")) {
2192                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2193                         if (tokens[0] && tokens[1] && tokens[2]) {
2194                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2195                                 if (orig_item != NULL) {
2196                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2197                                 }
2198                         }
2199                         g_strfreev(tokens);
2200                 }
2201                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2202                                              sizeof(queueheader_buf), "FMID:")) {
2203                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2204                         if (tokens[0] && tokens[1] && tokens[2]) {
2205                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2206                                 if (orig_item != NULL) {
2207                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2208                                 }
2209                         }
2210                         g_strfreev(tokens);
2211                 }
2212         } else {
2213                 account = msginfo->folder->folder->account;
2214         }
2215
2216         if (!account && prefs_common.reedit_account_autosel) {
2217                 gchar from[BUFFSIZE];
2218                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2219                         extract_address(from);
2220                         account = account_find_from_address(from, FALSE);
2221                 }
2222         }
2223         if (!account) {
2224                 account = cur_account;
2225         }
2226         cm_return_val_if_fail(account != NULL, NULL);
2227
2228         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2229
2230         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2231         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2232         compose->autowrap = autowrap;
2233         compose->replyinfo = replyinfo;
2234         compose->fwdinfo = fwdinfo;
2235
2236         compose->updating = TRUE;
2237         compose->priority = priority;
2238
2239         if (privacy_system != NULL) {
2240                 compose->privacy_system = privacy_system;
2241                 compose_use_signing(compose, use_signing);
2242                 compose_use_encryption(compose, use_encryption);
2243                 compose_update_privacy_system_menu_item(compose, FALSE);
2244         } else {
2245                 activate_privacy_system(compose, account, FALSE);
2246         }
2247
2248         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2249
2250         compose_extract_original_charset(compose);
2251
2252         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2253             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2254             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2255                 gchar queueheader_buf[BUFFSIZE];
2256
2257                 /* Set message save folder */
2258                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2259                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2260                         compose_set_save_to(compose, &queueheader_buf[4]);
2261                 }
2262                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2263                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2264                         if (active) {
2265                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2266                         }
2267                 }
2268         }
2269         
2270         if (compose_parse_header(compose, msginfo) < 0) {
2271                 compose->updating = FALSE;
2272                 compose_destroy(compose);
2273                 return NULL;
2274         }
2275         compose_reedit_set_entry(compose, msginfo);
2276
2277         textview = GTK_TEXT_VIEW(compose->text);
2278         textbuf = gtk_text_view_get_buffer(textview);
2279         compose_create_tags(textview, compose);
2280
2281         mark = gtk_text_buffer_get_insert(textbuf);
2282         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2283
2284         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2285                                         G_CALLBACK(compose_changed_cb),
2286                                         compose);
2287         
2288         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2289                 fp = procmime_get_first_encrypted_text_content(msginfo);
2290                 if (fp) {
2291                         compose_force_encryption(compose, account, TRUE, NULL);
2292                 }
2293         } else {
2294                 fp = procmime_get_first_text_content(msginfo);
2295         }
2296         if (fp == NULL) {
2297                 g_warning("Can't get text part\n");
2298         }
2299
2300         if (fp != NULL) {
2301                 gboolean prev_autowrap = compose->autowrap;
2302                 GtkTextBuffer *buffer = textbuf;
2303                 BLOCK_WRAP();
2304                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2305                         strcrchomp(buf);
2306                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2307                 }
2308                 UNBLOCK_WRAP();
2309                 fclose(fp);
2310         }
2311         
2312         compose_attach_parts(compose, msginfo);
2313
2314         compose_colorize_signature(compose);
2315
2316         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2317                                         G_CALLBACK(compose_changed_cb),
2318                                         compose);
2319
2320         gtk_widget_grab_focus(compose->text);
2321
2322         if (prefs_common.auto_exteditor) {
2323                 compose_exec_ext_editor(compose);
2324         }
2325         compose->modified = FALSE;
2326         compose_set_title(compose);
2327
2328         compose->updating = FALSE;
2329         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2330         SCROLL_TO_CURSOR(compose);
2331
2332         if (compose->deferred_destroy) {
2333                 compose_destroy(compose);
2334                 return NULL;
2335         }
2336         
2337         compose->sig_str = account_get_signature_str(compose->account);
2338         
2339         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2340
2341         return compose;
2342 }
2343
2344 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2345                                                  gboolean batch)
2346 {
2347         Compose *compose;
2348         gchar *filename;
2349         FolderItem *item;
2350
2351         cm_return_val_if_fail(msginfo != NULL, NULL);
2352
2353         if (!account)
2354                 account = account_get_reply_account(msginfo,
2355                                         prefs_common.reply_account_autosel);
2356         cm_return_val_if_fail(account != NULL, NULL);
2357
2358         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2359
2360         compose->updating = TRUE;
2361
2362         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2363         compose->replyinfo = NULL;
2364         compose->fwdinfo = NULL;
2365
2366         compose_show_first_last_header(compose, TRUE);
2367
2368         gtk_widget_grab_focus(compose->header_last->entry);
2369
2370         filename = procmsg_get_message_file(msginfo);
2371
2372         if (filename == NULL) {
2373                 compose->updating = FALSE;
2374                 compose_destroy(compose);
2375
2376                 return NULL;
2377         }
2378
2379         compose->redirect_filename = filename;
2380         
2381         /* Set save folder */
2382         item = msginfo->folder;
2383         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2384                 gchar *folderidentifier;
2385
2386                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2387                 folderidentifier = folder_item_get_identifier(item);
2388                 compose_set_save_to(compose, folderidentifier);
2389                 g_free(folderidentifier);
2390         }
2391
2392         compose_attach_parts(compose, msginfo);
2393
2394         if (msginfo->subject)
2395                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2396                                    msginfo->subject);
2397         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2398
2399         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2400                                           _("The body of the \"Redirect\" template has an error at line %d."));
2401         quote_fmt_reset_vartable();
2402         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2403
2404         compose_colorize_signature(compose);
2405
2406         
2407         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2408         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2409         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2410
2411         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2412         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2413         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2414         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2415         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2416         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2417         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2418         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2419         
2420         if (compose->toolbar->draft_btn)
2421                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2422         if (compose->toolbar->insert_btn)
2423                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2424         if (compose->toolbar->attach_btn)
2425                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2426         if (compose->toolbar->sig_btn)
2427                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2428         if (compose->toolbar->exteditor_btn)
2429                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2430         if (compose->toolbar->linewrap_current_btn)
2431                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2432         if (compose->toolbar->linewrap_all_btn)
2433                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2434
2435         compose->modified = FALSE;
2436         compose_set_title(compose);
2437         compose->updating = FALSE;
2438         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2439         SCROLL_TO_CURSOR(compose);
2440
2441         if (compose->deferred_destroy) {
2442                 compose_destroy(compose);
2443                 return NULL;
2444         }
2445         
2446         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2447
2448         return compose;
2449 }
2450
2451 GList *compose_get_compose_list(void)
2452 {
2453         return compose_list;
2454 }
2455
2456 void compose_entry_append(Compose *compose, const gchar *address,
2457                           ComposeEntryType type, ComposePrefType pref_type)
2458 {
2459         const gchar *header;
2460         gchar *cur, *begin;
2461         gboolean in_quote = FALSE;
2462         if (!address || *address == '\0') return;
2463
2464         switch (type) {
2465         case COMPOSE_CC:
2466                 header = N_("Cc:");
2467                 break;
2468         case COMPOSE_BCC:
2469                 header = N_("Bcc:");
2470                 break;
2471         case COMPOSE_REPLYTO:
2472                 header = N_("Reply-To:");
2473                 break;
2474         case COMPOSE_NEWSGROUPS:
2475                 header = N_("Newsgroups:");
2476                 break;
2477         case COMPOSE_FOLLOWUPTO:
2478                 header = N_( "Followup-To:");
2479                 break;
2480         case COMPOSE_INREPLYTO:
2481                 header = N_( "In-Reply-To:");
2482                 break;
2483         case COMPOSE_TO:
2484         default:
2485                 header = N_("To:");
2486                 break;
2487         }
2488         header = prefs_common_translated_header_name(header);
2489         
2490         cur = begin = (gchar *)address;
2491         
2492         /* we separate the line by commas, but not if we're inside a quoted
2493          * string */
2494         while (*cur != '\0') {
2495                 if (*cur == '"') 
2496                         in_quote = !in_quote;
2497                 if (*cur == ',' && !in_quote) {
2498                         gchar *tmp = g_strdup(begin);
2499                         gchar *o_tmp = tmp;
2500                         tmp[cur-begin]='\0';
2501                         cur++;
2502                         begin = cur;
2503                         while (*tmp == ' ' || *tmp == '\t')
2504                                 tmp++;
2505                         compose_add_header_entry(compose, header, tmp, pref_type);
2506                         g_free(o_tmp);
2507                         continue;
2508                 }
2509                 cur++;
2510         }
2511         if (begin < cur) {
2512                 gchar *tmp = g_strdup(begin);
2513                 gchar *o_tmp = tmp;
2514                 tmp[cur-begin]='\0';
2515                 cur++;
2516                 begin = cur;
2517                 while (*tmp == ' ' || *tmp == '\t')
2518                         tmp++;
2519                 compose_add_header_entry(compose, header, tmp, pref_type);
2520                 g_free(o_tmp);          
2521         }
2522 }
2523
2524 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2525 {
2526         static GdkColor yellow;
2527         static GdkColor black;
2528         static gboolean yellow_initialised = FALSE;
2529         GSList *h_list;
2530         GtkEntry *entry;
2531                 
2532         if (!yellow_initialised) {
2533                 gdk_color_parse("#f5f6be", &yellow);
2534                 gdk_color_parse("#000000", &black);
2535                 yellow_initialised = gdk_colormap_alloc_color(
2536                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2537                 yellow_initialised &= gdk_colormap_alloc_color(
2538                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2539         }
2540
2541         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2542                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2543                 if (gtk_entry_get_text(entry) && 
2544                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2545                         if (yellow_initialised) {
2546                                 gtk_widget_modify_base(
2547                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2548                                         GTK_STATE_NORMAL, &yellow);
2549                                 gtk_widget_modify_text(
2550                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2551                                         GTK_STATE_NORMAL, &black);
2552                         }
2553                 }
2554         }
2555 }
2556
2557 void compose_toolbar_cb(gint action, gpointer data)
2558 {
2559         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2560         Compose *compose = (Compose*)toolbar_item->parent;
2561         
2562         cm_return_if_fail(compose != NULL);
2563
2564         switch(action) {
2565         case A_SEND:
2566                 compose_send_cb(NULL, compose);
2567                 break;
2568         case A_SENDL:
2569                 compose_send_later_cb(NULL, compose);
2570                 break;
2571         case A_DRAFT:
2572                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2573                 break;
2574         case A_INSERT:
2575                 compose_insert_file_cb(NULL, compose);
2576                 break;
2577         case A_ATTACH:
2578                 compose_attach_cb(NULL, compose);
2579                 break;
2580         case A_SIG:
2581                 compose_insert_sig(compose, FALSE);
2582                 break;
2583         case A_EXTEDITOR:
2584                 compose_ext_editor_cb(NULL, compose);
2585                 break;
2586         case A_LINEWRAP_CURRENT:
2587                 compose_beautify_paragraph(compose, NULL, TRUE);
2588                 break;
2589         case A_LINEWRAP_ALL:
2590                 compose_wrap_all_full(compose, TRUE);
2591                 break;
2592         case A_ADDRBOOK:
2593                 compose_address_cb(NULL, compose);
2594                 break;
2595 #ifdef USE_ENCHANT
2596         case A_CHECK_SPELLING:
2597                 compose_check_all(NULL, compose);
2598                 break;
2599 #endif
2600         default:
2601                 break;
2602         }
2603 }
2604
2605 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2606 {
2607         gchar *to = NULL;
2608         gchar *cc = NULL;
2609         gchar *bcc = NULL;
2610         gchar *subject = NULL;
2611         gchar *body = NULL;
2612         gchar *temp = NULL;
2613         gsize  len = 0;
2614         gchar **attach = NULL;
2615         gchar *inreplyto = NULL;
2616         MailField mfield = NO_FIELD_PRESENT;
2617
2618         /* get mailto parts but skip from */
2619         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2620
2621         if (to) {
2622                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2623                 mfield = TO_FIELD_PRESENT;
2624         }
2625         if (cc)
2626                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2627         if (bcc)
2628                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2629         if (subject) {
2630                 if (!g_utf8_validate (subject, -1, NULL)) {
2631                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2632                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2633                         g_free(temp);
2634                 } else {
2635                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2636                 }
2637                 mfield = SUBJECT_FIELD_PRESENT;
2638         }
2639         if (body) {
2640                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2641                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2642                 GtkTextMark *mark;
2643                 GtkTextIter iter;
2644                 gboolean prev_autowrap = compose->autowrap;
2645
2646                 compose->autowrap = FALSE;
2647
2648                 mark = gtk_text_buffer_get_insert(buffer);
2649                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2650
2651                 if (!g_utf8_validate (body, -1, NULL)) {
2652                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2653                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2654                         g_free(temp);
2655                 } else {
2656                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2657                 }
2658                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2659
2660                 compose->autowrap = prev_autowrap;
2661                 if (compose->autowrap)
2662                         compose_wrap_all(compose);
2663                 mfield = BODY_FIELD_PRESENT;
2664         }
2665
2666         if (attach) {
2667                 gint i = 0, att = 0;
2668                 gchar *warn_files = NULL;
2669                 while (attach[i] != NULL) {
2670                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2671                         if (utf8_filename) {
2672                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2673                                         gchar *tmp = g_strdup_printf("%s%s\n",
2674                                                         warn_files?warn_files:"",
2675                                                         utf8_filename);
2676                                         g_free(warn_files);
2677                                         warn_files = tmp;
2678                                         att++;
2679                                 }
2680                                 g_free(utf8_filename);
2681                         } else {
2682                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2683                         }
2684                         i++;
2685                 }
2686                 if (warn_files) {
2687                         alertpanel_notice(ngettext(
2688                         "The following file has been attached: \n%s",
2689                         "The following files have been attached: \n%s", att), warn_files);
2690                         g_free(warn_files);
2691                 }
2692         }
2693         if (inreplyto)
2694                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
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         g_free(inreplyto);
2703         
2704         return mfield;
2705 }
2706
2707 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2708 {
2709         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2710                                        {"Cc:",          NULL, TRUE},
2711                                        {"References:",  NULL, FALSE},
2712                                        {"Bcc:",         NULL, TRUE},
2713                                        {"Newsgroups:",  NULL, TRUE},
2714                                        {"Followup-To:", NULL, TRUE},
2715                                        {"List-Post:",   NULL, FALSE},
2716                                        {"X-Priority:",  NULL, FALSE},
2717                                        {NULL,           NULL, FALSE}};
2718
2719         enum
2720         {
2721                 H_REPLY_TO      = 0,
2722                 H_CC            = 1,
2723                 H_REFERENCES    = 2,
2724                 H_BCC           = 3,
2725                 H_NEWSGROUPS    = 4,
2726                 H_FOLLOWUP_TO   = 5,
2727                 H_LIST_POST     = 6,
2728                 H_X_PRIORITY    = 7
2729         };
2730
2731         FILE *fp;
2732
2733         cm_return_val_if_fail(msginfo != NULL, -1);
2734
2735         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2736         procheader_get_header_fields(fp, hentry);
2737         fclose(fp);
2738
2739         if (hentry[H_REPLY_TO].body != NULL) {
2740                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2741                         compose->replyto =
2742                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2743                                                    NULL);
2744                 }
2745                 g_free(hentry[H_REPLY_TO].body);
2746                 hentry[H_REPLY_TO].body = NULL;
2747         }
2748         if (hentry[H_CC].body != NULL) {
2749                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2750                 g_free(hentry[H_CC].body);
2751                 hentry[H_CC].body = NULL;
2752         }
2753         if (hentry[H_REFERENCES].body != NULL) {
2754                 if (compose->mode == COMPOSE_REEDIT)
2755                         compose->references = hentry[H_REFERENCES].body;
2756                 else {
2757                         compose->references = compose_parse_references
2758                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2759                         g_free(hentry[H_REFERENCES].body);
2760                 }
2761                 hentry[H_REFERENCES].body = NULL;
2762         }
2763         if (hentry[H_BCC].body != NULL) {
2764                 if (compose->mode == COMPOSE_REEDIT)
2765                         compose->bcc =
2766                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2767                 g_free(hentry[H_BCC].body);
2768                 hentry[H_BCC].body = NULL;
2769         }
2770         if (hentry[H_NEWSGROUPS].body != NULL) {
2771                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2772                 hentry[H_NEWSGROUPS].body = NULL;
2773         }
2774         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2775                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2776                         compose->followup_to =
2777                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2778                                                    NULL);
2779                 }
2780                 g_free(hentry[H_FOLLOWUP_TO].body);
2781                 hentry[H_FOLLOWUP_TO].body = NULL;
2782         }
2783         if (hentry[H_LIST_POST].body != NULL) {
2784                 gchar *to = NULL, *start = NULL;
2785
2786                 extract_address(hentry[H_LIST_POST].body);
2787                 if (hentry[H_LIST_POST].body[0] != '\0') {
2788                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2789                         
2790                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2791                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2792
2793                         if (to) {
2794                                 g_free(compose->ml_post);
2795                                 compose->ml_post = to;
2796                         }
2797                 }
2798                 g_free(hentry[H_LIST_POST].body);
2799                 hentry[H_LIST_POST].body = NULL;
2800         }
2801
2802         /* CLAWS - X-Priority */
2803         if (compose->mode == COMPOSE_REEDIT)
2804                 if (hentry[H_X_PRIORITY].body != NULL) {
2805                         gint priority;
2806                         
2807                         priority = atoi(hentry[H_X_PRIORITY].body);
2808                         g_free(hentry[H_X_PRIORITY].body);
2809                         
2810                         hentry[H_X_PRIORITY].body = NULL;
2811                         
2812                         if (priority < PRIORITY_HIGHEST || 
2813                             priority > PRIORITY_LOWEST)
2814                                 priority = PRIORITY_NORMAL;
2815                         
2816                         compose->priority =  priority;
2817                 }
2818  
2819         if (compose->mode == COMPOSE_REEDIT) {
2820                 if (msginfo->inreplyto && *msginfo->inreplyto)
2821                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2822                 return 0;
2823         }
2824
2825         if (msginfo->msgid && *msginfo->msgid)
2826                 compose->inreplyto = g_strdup(msginfo->msgid);
2827
2828         if (!compose->references) {
2829                 if (msginfo->msgid && *msginfo->msgid) {
2830                         if (msginfo->inreplyto && *msginfo->inreplyto)
2831                                 compose->references =
2832                                         g_strdup_printf("<%s>\n\t<%s>",
2833                                                         msginfo->inreplyto,
2834                                                         msginfo->msgid);
2835                         else
2836                                 compose->references =
2837                                         g_strconcat("<", msginfo->msgid, ">",
2838                                                     NULL);
2839                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2840                         compose->references =
2841                                 g_strconcat("<", msginfo->inreplyto, ">",
2842                                             NULL);
2843                 }
2844         }
2845
2846         return 0;
2847 }
2848
2849 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2850 {
2851         GSList *ref_id_list, *cur;
2852         GString *new_ref;
2853         gchar *new_ref_str;
2854
2855         ref_id_list = references_list_append(NULL, ref);
2856         if (!ref_id_list) return NULL;
2857         if (msgid && *msgid)
2858                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2859
2860         for (;;) {
2861                 gint len = 0;
2862
2863                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2864                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2865                         len += strlen((gchar *)cur->data) + 5;
2866
2867                 if (len > MAX_REFERENCES_LEN) {
2868                         /* remove second message-ID */
2869                         if (ref_id_list && ref_id_list->next &&
2870                             ref_id_list->next->next) {
2871                                 g_free(ref_id_list->next->data);
2872                                 ref_id_list = g_slist_remove
2873                                         (ref_id_list, ref_id_list->next->data);
2874                         } else {
2875                                 slist_free_strings(ref_id_list);
2876                                 g_slist_free(ref_id_list);
2877                                 return NULL;
2878                         }
2879                 } else
2880                         break;
2881         }
2882
2883         new_ref = g_string_new("");
2884         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2885                 if (new_ref->len > 0)
2886                         g_string_append(new_ref, "\n\t");
2887                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2888         }
2889
2890         slist_free_strings(ref_id_list);
2891         g_slist_free(ref_id_list);
2892
2893         new_ref_str = new_ref->str;
2894         g_string_free(new_ref, FALSE);
2895
2896         return new_ref_str;
2897 }
2898
2899 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2900                                 const gchar *fmt, const gchar *qmark,
2901                                 const gchar *body, gboolean rewrap,
2902                                 gboolean need_unescape,
2903                                 const gchar *err_msg)
2904 {
2905         MsgInfo* dummyinfo = NULL;
2906         gchar *quote_str = NULL;
2907         gchar *buf;
2908         gboolean prev_autowrap;
2909         const gchar *trimmed_body = body;
2910         gint cursor_pos = -1;
2911         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2912         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2913         GtkTextIter iter;
2914         GtkTextMark *mark;
2915         
2916
2917         SIGNAL_BLOCK(buffer);
2918
2919         if (!msginfo) {
2920                 dummyinfo = compose_msginfo_new_from_compose(compose);
2921                 msginfo = dummyinfo;
2922         }
2923
2924         if (qmark != NULL) {
2925 #ifdef USE_ENCHANT
2926                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2927                                 compose->gtkaspell);
2928 #else
2929                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2930 #endif
2931                 quote_fmt_scan_string(qmark);
2932                 quote_fmt_parse();
2933
2934                 buf = quote_fmt_get_buffer();
2935                 if (buf == NULL)
2936                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2937                 else
2938                         Xstrdup_a(quote_str, buf, goto error)
2939         }
2940
2941         if (fmt && *fmt != '\0') {
2942
2943                 if (trimmed_body)
2944                         while (*trimmed_body == '\n')
2945                                 trimmed_body++;
2946
2947 #ifdef USE_ENCHANT
2948                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2949                                 compose->gtkaspell);
2950 #else
2951                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2952 #endif
2953                 if (need_unescape) {
2954                         gchar *tmp = NULL;
2955
2956                         /* decode \-escape sequences in the internal representation of the quote format */
2957                         tmp = malloc(strlen(fmt)+1);
2958                         pref_get_unescaped_pref(tmp, fmt);
2959                         quote_fmt_scan_string(tmp);
2960                         quote_fmt_parse();
2961                         g_free(tmp);
2962                 } else {
2963                         quote_fmt_scan_string(fmt);
2964                         quote_fmt_parse();
2965                 }
2966
2967                 buf = quote_fmt_get_buffer();
2968                 if (buf == NULL) {
2969                         gint line = quote_fmt_get_line();
2970                         alertpanel_error(err_msg, line);
2971                         goto error;
2972                 }
2973         } else
2974                 buf = "";
2975
2976         prev_autowrap = compose->autowrap;
2977         compose->autowrap = FALSE;
2978
2979         mark = gtk_text_buffer_get_insert(buffer);
2980         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2981         if (g_utf8_validate(buf, -1, NULL)) { 
2982                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2983         } else {
2984                 gchar *tmpout = NULL;
2985                 tmpout = conv_codeset_strdup
2986                         (buf, conv_get_locale_charset_str_no_utf8(),
2987                          CS_INTERNAL);
2988                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2989                         g_free(tmpout);
2990                         tmpout = g_malloc(strlen(buf)*2+1);
2991                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2992                 }
2993                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2994                 g_free(tmpout);
2995         }
2996
2997         cursor_pos = quote_fmt_get_cursor_pos();
2998         if (cursor_pos == -1)
2999                 cursor_pos = gtk_text_iter_get_offset(&iter);
3000         compose->set_cursor_pos = cursor_pos;
3001
3002         gtk_text_buffer_get_start_iter(buffer, &iter);
3003         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3004         gtk_text_buffer_place_cursor(buffer, &iter);
3005
3006         compose->autowrap = prev_autowrap;
3007         if (compose->autowrap && rewrap)
3008                 compose_wrap_all(compose);
3009
3010         goto ok;
3011
3012 error:
3013         buf = NULL;
3014 ok:
3015         SIGNAL_UNBLOCK(buffer);
3016
3017         procmsg_msginfo_free( dummyinfo );
3018
3019         return buf;
3020 }
3021
3022 /* if ml_post is of type addr@host and from is of type
3023  * addr-anything@host, return TRUE
3024  */
3025 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3026 {
3027         gchar *left_ml = NULL;
3028         gchar *right_ml = NULL;
3029         gchar *left_from = NULL;
3030         gchar *right_from = NULL;
3031         gboolean result = FALSE;
3032         
3033         if (!ml_post || !from)
3034                 return FALSE;
3035         
3036         left_ml = g_strdup(ml_post);
3037         if (strstr(left_ml, "@")) {
3038                 right_ml = strstr(left_ml, "@")+1;
3039                 *(strstr(left_ml, "@")) = '\0';
3040         }
3041         
3042         left_from = g_strdup(from);
3043         if (strstr(left_from, "@")) {
3044                 right_from = strstr(left_from, "@")+1;
3045                 *(strstr(left_from, "@")) = '\0';
3046         }
3047         
3048         if (left_ml && left_from && right_ml && right_from
3049         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3050         &&  !strcmp(right_from, right_ml)) {
3051                 result = TRUE;
3052         }
3053         g_free(left_ml);
3054         g_free(left_from);
3055         
3056         return result;
3057 }
3058
3059 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3060                                      gboolean respect_default_to)
3061 {
3062         if (!compose)
3063                 return;
3064         if (!folder || !folder->prefs)
3065                 return;
3066
3067         if (respect_default_to && folder->prefs->enable_default_to) {
3068                 compose_entry_append(compose, folder->prefs->default_to,
3069                                         COMPOSE_TO, PREF_FOLDER);
3070                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3071         }
3072         if (folder->prefs->enable_default_cc)
3073                 compose_entry_append(compose, folder->prefs->default_cc,
3074                                         COMPOSE_CC, PREF_FOLDER);
3075         if (folder->prefs->enable_default_bcc)
3076                 compose_entry_append(compose, folder->prefs->default_bcc,
3077                                         COMPOSE_BCC, PREF_FOLDER);
3078         if (folder->prefs->enable_default_replyto)
3079                 compose_entry_append(compose, folder->prefs->default_replyto,
3080                                         COMPOSE_REPLYTO, PREF_FOLDER);
3081 }
3082
3083 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3084 {
3085         gchar *buf, *buf2;
3086         gchar *p;
3087         
3088         if (!compose || !msginfo)
3089                 return;
3090
3091         if (msginfo->subject && *msginfo->subject) {
3092                 buf = p = g_strdup(msginfo->subject);
3093                 p += subject_get_prefix_length(p);
3094                 memmove(buf, p, strlen(p) + 1);
3095
3096                 buf2 = g_strdup_printf("Re: %s", buf);
3097                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3098
3099                 g_free(buf2);
3100                 g_free(buf);
3101         } else
3102                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3103 }
3104
3105 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3106                                     gboolean to_all, gboolean to_ml,
3107                                     gboolean to_sender,
3108                                     gboolean followup_and_reply_to)
3109 {
3110         GSList *cc_list = NULL;
3111         GSList *cur;
3112         gchar *from = NULL;
3113         gchar *replyto = NULL;
3114         gchar *ac_email = NULL;
3115
3116         gboolean reply_to_ml = FALSE;
3117         gboolean default_reply_to = FALSE;
3118
3119         cm_return_if_fail(compose->account != NULL);
3120         cm_return_if_fail(msginfo != NULL);
3121
3122         reply_to_ml = to_ml && compose->ml_post;
3123
3124         default_reply_to = msginfo->folder && 
3125                 msginfo->folder->prefs->enable_default_reply_to;
3126
3127         if (compose->account->protocol != A_NNTP) {
3128                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3129
3130                 if (reply_to_ml && !default_reply_to) {
3131                         
3132                         gboolean is_subscr = is_subscription(compose->ml_post,
3133                                                              msginfo->from);
3134                         if (!is_subscr) {
3135                                 /* normal answer to ml post with a reply-to */
3136                                 compose_entry_append(compose,
3137                                            compose->ml_post,
3138                                            COMPOSE_TO, PREF_ML);
3139                                 if (compose->replyto)
3140                                         compose_entry_append(compose,
3141                                                 compose->replyto,
3142                                                 COMPOSE_CC, PREF_ML);
3143                         } else {
3144                                 /* answer to subscription confirmation */
3145                                 if (compose->replyto)
3146                                         compose_entry_append(compose,
3147                                                 compose->replyto,
3148                                                 COMPOSE_TO, PREF_ML);
3149                                 else if (msginfo->from)
3150                                         compose_entry_append(compose,
3151                                                 msginfo->from,
3152                                                 COMPOSE_TO, PREF_ML);
3153                         }
3154                 }
3155                 else if (!(to_all || to_sender) && default_reply_to) {
3156                         compose_entry_append(compose,
3157                             msginfo->folder->prefs->default_reply_to,
3158                             COMPOSE_TO, PREF_FOLDER);
3159                         compose_entry_mark_default_to(compose,
3160                                 msginfo->folder->prefs->default_reply_to);
3161                 } else {
3162                         gchar *tmp1 = NULL;
3163                         if (!msginfo->from)
3164                                 return;
3165                         Xstrdup_a(tmp1, msginfo->from, return);
3166                         extract_address(tmp1);
3167                         if (to_all || to_sender ||
3168                             !account_find_from_address(tmp1, FALSE))
3169                                 compose_entry_append(compose,
3170                                  (compose->replyto && !to_sender)
3171                                           ? compose->replyto :
3172                                           msginfo->from ? msginfo->from : "",
3173                                           COMPOSE_TO, PREF_NONE);
3174                         else if (!to_all && !to_sender) {
3175                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3176                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&