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