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