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