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