2011-08-06 [paul] 3.7.9cvs42
[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         gint cursor_pos = -1;
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         cursor_pos = quote_fmt_get_cursor_pos();
1837         if (cursor_pos == -1)
1838                 gtk_widget_grab_focus(compose->header_last->entry);
1839         else
1840                 gtk_widget_grab_focus(compose->text);
1841
1842         if (!no_extedit && prefs_common.auto_exteditor)
1843                 compose_exec_ext_editor(compose);
1844         
1845         /*save folder*/
1846         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1847                 gchar *folderidentifier;
1848
1849                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1850                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1851                 compose_set_save_to(compose, folderidentifier);
1852                 g_free(folderidentifier);
1853         }
1854
1855         undo_unblock(compose->undostruct);
1856         
1857         compose->modified = FALSE;
1858         compose_set_title(compose);
1859
1860         compose->updating = FALSE;
1861         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1862         SCROLL_TO_CURSOR(compose);
1863
1864         if (compose->deferred_destroy) {
1865                 compose_destroy(compose);
1866                 return NULL;
1867         }
1868
1869         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1870
1871         return compose;
1872 }
1873
1874 #undef INSERT_FW_HEADER
1875
1876 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1877 {
1878         Compose *compose;
1879         GtkTextView *textview;
1880         GtkTextBuffer *textbuf;
1881         GtkTextIter iter;
1882         GSList *msginfo;
1883         gchar *msgfile;
1884         gboolean single_mail = TRUE;
1885         
1886         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1887
1888         if (g_slist_length(msginfo_list) > 1)
1889                 single_mail = FALSE;
1890
1891         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1892                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1893                         return NULL;
1894
1895         /* guess account from first selected message */
1896         if (!account && 
1897             !(account = compose_guess_forward_account_from_msginfo
1898                                 (msginfo_list->data)))
1899                 account = cur_account;
1900
1901         cm_return_val_if_fail(account != NULL, NULL);
1902
1903         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1904                 if (msginfo->data) {
1905                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1906                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1907                 }
1908         }
1909
1910         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1911                 g_warning("no msginfo_list");
1912                 return NULL;
1913         }
1914
1915         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1916
1917         compose->updating = TRUE;
1918
1919         /* override from name according to folder properties */
1920         if (msginfo_list->data) {
1921                 MsgInfo *msginfo = msginfo_list->data;
1922
1923                 if (msginfo->folder && msginfo->folder->prefs &&
1924                         msginfo->folder->prefs->forward_with_format &&
1925                         msginfo->folder->prefs->forward_override_from_format &&
1926                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1927
1928                         gchar *tmp = NULL;
1929                         gchar *buf = NULL;
1930
1931                         /* decode \-escape sequences in the internal representation of the quote format */
1932                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1933                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1934
1935 #ifdef USE_ENCHANT
1936                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1937                                         compose->gtkaspell);
1938 #else
1939                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1940 #endif
1941                         quote_fmt_scan_string(tmp);
1942                         quote_fmt_parse();
1943
1944                         buf = quote_fmt_get_buffer();
1945                         if (buf == NULL)
1946                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1947                         else
1948                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1949                         quote_fmt_reset_vartable();
1950
1951                         g_free(tmp);
1952                 }
1953         }
1954
1955         textview = GTK_TEXT_VIEW(compose->text);
1956         textbuf = gtk_text_view_get_buffer(textview);
1957         compose_create_tags(textview, compose);
1958         
1959         undo_block(compose->undostruct);
1960         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1961                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1962
1963                 if (!is_file_exist(msgfile))
1964                         g_warning("%s: file not exist\n", msgfile);
1965                 else
1966                         compose_attach_append(compose, msgfile, msgfile,
1967                                 "message/rfc822", NULL);
1968                 g_free(msgfile);
1969         }
1970         
1971         if (single_mail) {
1972                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1973                 if (info->subject && *info->subject) {
1974                         gchar *buf, *buf2, *p;
1975
1976                         buf = p = g_strdup(info->subject);
1977                         p += subject_get_prefix_length(p);
1978                         memmove(buf, p, strlen(p) + 1);
1979
1980                         buf2 = g_strdup_printf("Fw: %s", buf);
1981                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1982
1983                         g_free(buf);
1984                         g_free(buf2);
1985                 }
1986         } else {
1987                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1988                         _("Fw: multiple emails"));
1989         }
1990
1991         SIGNAL_BLOCK(textbuf);
1992         
1993         if (account->auto_sig)
1994                 compose_insert_sig(compose, FALSE);
1995
1996         compose_wrap_all(compose);
1997
1998         SIGNAL_UNBLOCK(textbuf);
1999         
2000         gtk_text_buffer_get_start_iter(textbuf, &iter);
2001         gtk_text_buffer_place_cursor(textbuf, &iter);
2002
2003         gtk_widget_grab_focus(compose->header_last->entry);
2004         undo_unblock(compose->undostruct);
2005         compose->modified = FALSE;
2006         compose_set_title(compose);
2007
2008         compose->updating = FALSE;
2009         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2010         SCROLL_TO_CURSOR(compose);
2011
2012         if (compose->deferred_destroy) {
2013                 compose_destroy(compose);
2014                 return NULL;
2015         }
2016
2017         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2018
2019         return compose;
2020 }
2021
2022 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2023 {
2024         GtkTextIter start = *iter;
2025         GtkTextIter end_iter;
2026         int start_pos = gtk_text_iter_get_offset(&start);
2027         gchar *str = NULL;
2028         if (!compose->account->sig_sep)
2029                 return FALSE;
2030         
2031         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2032                 start_pos+strlen(compose->account->sig_sep));
2033
2034         /* check sig separator */
2035         str = gtk_text_iter_get_text(&start, &end_iter);
2036         if (!strcmp(str, compose->account->sig_sep)) {
2037                 gchar *tmp = NULL;
2038                 /* check end of line (\n) */
2039                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2040                         start_pos+strlen(compose->account->sig_sep));
2041                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2042                         start_pos+strlen(compose->account->sig_sep)+1);
2043                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2044                 if (!strcmp(tmp,"\n")) {
2045                         g_free(str);
2046                         g_free(tmp);
2047                         return TRUE;
2048                 }
2049                 g_free(tmp);    
2050         }
2051         g_free(str);
2052
2053         return FALSE;
2054 }
2055
2056 static void compose_colorize_signature(Compose *compose)
2057 {
2058         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2059         GtkTextIter iter;
2060         GtkTextIter end_iter;
2061         gtk_text_buffer_get_start_iter(buffer, &iter);
2062         while (gtk_text_iter_forward_line(&iter))
2063                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2064                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2065                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2066                 }
2067 }
2068
2069 #define BLOCK_WRAP() {                                                  \
2070         prev_autowrap = compose->autowrap;                              \
2071         buffer = gtk_text_view_get_buffer(                              \
2072                                         GTK_TEXT_VIEW(compose->text));  \
2073         compose->autowrap = FALSE;                                      \
2074                                                                         \
2075         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2076                                 G_CALLBACK(compose_changed_cb),         \
2077                                 compose);                               \
2078         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2079                                 G_CALLBACK(text_inserted),              \
2080                                 compose);                               \
2081 }
2082 #define UNBLOCK_WRAP() {                                                \
2083         compose->autowrap = prev_autowrap;                              \
2084         if (compose->autowrap) {                                        \
2085                 gint old = compose->draft_timeout_tag;                  \
2086                 compose->draft_timeout_tag = -2;                        \
2087                 compose_wrap_all(compose);                              \
2088                 compose->draft_timeout_tag = old;                       \
2089         }                                                               \
2090                                                                         \
2091         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2092                                 G_CALLBACK(compose_changed_cb),         \
2093                                 compose);                               \
2094         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2095                                 G_CALLBACK(text_inserted),              \
2096                                 compose);                               \
2097 }
2098
2099 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2100 {
2101         Compose *compose = NULL;
2102         PrefsAccount *account = NULL;
2103         GtkTextView *textview;
2104         GtkTextBuffer *textbuf;
2105         GtkTextMark *mark;
2106         GtkTextIter iter;
2107         FILE *fp;
2108         gchar buf[BUFFSIZE];
2109         gboolean use_signing = FALSE;
2110         gboolean use_encryption = FALSE;
2111         gchar *privacy_system = NULL;
2112         int priority = PRIORITY_NORMAL;
2113         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2114         gboolean autowrap = prefs_common.autowrap;
2115         gboolean autoindent = prefs_common.auto_indent;
2116
2117         cm_return_val_if_fail(msginfo != NULL, NULL);
2118         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2119
2120         if (compose_put_existing_to_front(msginfo)) {
2121                 return NULL;
2122         }
2123
2124         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2125             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2126             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2127                 gchar queueheader_buf[BUFFSIZE];
2128                 gint id, param;
2129
2130                 /* Select Account from queue headers */
2131                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2132                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2133                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2134                         account = account_find_from_id(id);
2135                 }
2136                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2137                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2138                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2139                         account = account_find_from_id(id);
2140                 }
2141                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2142                                              sizeof(queueheader_buf), "NAID:")) {
2143                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2144                         account = account_find_from_id(id);
2145                 }
2146                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2147                                                     sizeof(queueheader_buf), "MAID:")) {
2148                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2149                         account = account_find_from_id(id);
2150                 }
2151                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2152                                                                 sizeof(queueheader_buf), "S:")) {
2153                         account = account_find_from_address(queueheader_buf, FALSE);
2154                 }
2155                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2156                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2157                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2158                         use_signing = param;
2159                         
2160                 }
2161                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2162                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2163                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2164                         use_signing = param;
2165                         
2166                 }
2167                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2168                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2169                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2170                         use_encryption = param;
2171                 }
2172                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2173                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2174                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2175                         use_encryption = param;
2176                 }
2177                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2178                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2179                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2180                         autowrap = param;
2181                 }
2182                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2183                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2184                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2185                         autoindent = param;
2186                 }
2187                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2188                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2189                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2190                 }
2191                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2192                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2193                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2194                 }
2195                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2196                                              sizeof(queueheader_buf), "X-Priority: ")) {
2197                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2198                         priority = param;
2199                 }
2200                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2201                                              sizeof(queueheader_buf), "RMID:")) {
2202                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2203                         if (tokens[0] && tokens[1] && tokens[2]) {
2204                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2205                                 if (orig_item != NULL) {
2206                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2207                                 }
2208                         }
2209                         g_strfreev(tokens);
2210                 }
2211                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2212                                              sizeof(queueheader_buf), "FMID:")) {
2213                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2214                         if (tokens[0] && tokens[1] && tokens[2]) {
2215                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2216                                 if (orig_item != NULL) {
2217                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2218                                 }
2219                         }
2220                         g_strfreev(tokens);
2221                 }
2222         } else {
2223                 account = msginfo->folder->folder->account;
2224         }
2225
2226         if (!account && prefs_common.reedit_account_autosel) {
2227                 gchar from[BUFFSIZE];
2228                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2229                         extract_address(from);
2230                         account = account_find_from_address(from, FALSE);
2231                 }
2232         }
2233         if (!account) {
2234                 account = cur_account;
2235         }
2236         cm_return_val_if_fail(account != NULL, NULL);
2237
2238         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2239
2240         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2241         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2242         compose->autowrap = autowrap;
2243         compose->replyinfo = replyinfo;
2244         compose->fwdinfo = fwdinfo;
2245
2246         compose->updating = TRUE;
2247         compose->priority = priority;
2248
2249         if (privacy_system != NULL) {
2250                 compose->privacy_system = privacy_system;
2251                 compose_use_signing(compose, use_signing);
2252                 compose_use_encryption(compose, use_encryption);
2253                 compose_update_privacy_system_menu_item(compose, FALSE);
2254         } else {
2255                 activate_privacy_system(compose, account, FALSE);
2256         }
2257
2258         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2259
2260         compose_extract_original_charset(compose);
2261
2262         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2263             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2264             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2265                 gchar queueheader_buf[BUFFSIZE];
2266
2267                 /* Set message save folder */
2268                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2269                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2270                         compose_set_save_to(compose, &queueheader_buf[4]);
2271                 }
2272                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2273                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2274                         if (active) {
2275                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2276                         }
2277                 }
2278         }
2279         
2280         if (compose_parse_header(compose, msginfo) < 0) {
2281                 compose->updating = FALSE;
2282                 compose_destroy(compose);
2283                 return NULL;
2284         }
2285         compose_reedit_set_entry(compose, msginfo);
2286
2287         textview = GTK_TEXT_VIEW(compose->text);
2288         textbuf = gtk_text_view_get_buffer(textview);
2289         compose_create_tags(textview, compose);
2290
2291         mark = gtk_text_buffer_get_insert(textbuf);
2292         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2293
2294         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2295                                         G_CALLBACK(compose_changed_cb),
2296                                         compose);
2297         
2298         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2299                 fp = procmime_get_first_encrypted_text_content(msginfo);
2300                 if (fp) {
2301                         compose_force_encryption(compose, account, TRUE, NULL);
2302                 }
2303         } else {
2304                 fp = procmime_get_first_text_content(msginfo);
2305         }
2306         if (fp == NULL) {
2307                 g_warning("Can't get text part\n");
2308         }
2309
2310         if (fp != NULL) {
2311                 gboolean prev_autowrap = compose->autowrap;
2312                 GtkTextBuffer *buffer = textbuf;
2313                 BLOCK_WRAP();
2314                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2315                         strcrchomp(buf);
2316                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2317                 }
2318                 UNBLOCK_WRAP();
2319                 fclose(fp);
2320         }
2321         
2322         compose_attach_parts(compose, msginfo);
2323
2324         compose_colorize_signature(compose);
2325
2326         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2327                                         G_CALLBACK(compose_changed_cb),
2328                                         compose);
2329
2330         gtk_widget_grab_focus(compose->text);
2331
2332         if (prefs_common.auto_exteditor) {
2333                 compose_exec_ext_editor(compose);
2334         }
2335         compose->modified = FALSE;
2336         compose_set_title(compose);
2337
2338         compose->updating = FALSE;
2339         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2340         SCROLL_TO_CURSOR(compose);
2341
2342         if (compose->deferred_destroy) {
2343                 compose_destroy(compose);
2344                 return NULL;
2345         }
2346         
2347         compose->sig_str = account_get_signature_str(compose->account);
2348         
2349         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2350
2351         return compose;
2352 }
2353
2354 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2355                                                  gboolean batch)
2356 {
2357         Compose *compose;
2358         gchar *filename;
2359         FolderItem *item;
2360
2361         cm_return_val_if_fail(msginfo != NULL, NULL);
2362
2363         if (!account)
2364                 account = account_get_reply_account(msginfo,
2365                                         prefs_common.reply_account_autosel);
2366         cm_return_val_if_fail(account != NULL, NULL);
2367
2368         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2369
2370         compose->updating = TRUE;
2371
2372         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2373         compose->replyinfo = NULL;
2374         compose->fwdinfo = NULL;
2375
2376         compose_show_first_last_header(compose, TRUE);
2377
2378         gtk_widget_grab_focus(compose->header_last->entry);
2379
2380         filename = procmsg_get_message_file(msginfo);
2381
2382         if (filename == NULL) {
2383                 compose->updating = FALSE;
2384                 compose_destroy(compose);
2385
2386                 return NULL;
2387         }
2388
2389         compose->redirect_filename = filename;
2390         
2391         /* Set save folder */
2392         item = msginfo->folder;
2393         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2394                 gchar *folderidentifier;
2395
2396                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2397                 folderidentifier = folder_item_get_identifier(item);
2398                 compose_set_save_to(compose, folderidentifier);
2399                 g_free(folderidentifier);
2400         }
2401
2402         compose_attach_parts(compose, msginfo);
2403
2404         if (msginfo->subject)
2405                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2406                                    msginfo->subject);
2407         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2408
2409         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2410                                           _("The body of the \"Redirect\" template has an error at line %d."));
2411         quote_fmt_reset_vartable();
2412         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2413
2414         compose_colorize_signature(compose);
2415
2416         
2417         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2418         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2419         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2420
2421         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2422         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2423         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2424         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2425         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2426         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2427         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2428         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2429         
2430         if (compose->toolbar->draft_btn)
2431                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2432         if (compose->toolbar->insert_btn)
2433                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2434         if (compose->toolbar->attach_btn)
2435                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2436         if (compose->toolbar->sig_btn)
2437                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2438         if (compose->toolbar->exteditor_btn)
2439                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2440         if (compose->toolbar->linewrap_current_btn)
2441                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2442         if (compose->toolbar->linewrap_all_btn)
2443                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2444
2445         compose->modified = FALSE;
2446         compose_set_title(compose);
2447         compose->updating = FALSE;
2448         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2449         SCROLL_TO_CURSOR(compose);
2450
2451         if (compose->deferred_destroy) {
2452                 compose_destroy(compose);
2453                 return NULL;
2454         }
2455         
2456         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2457
2458         return compose;
2459 }
2460
2461 GList *compose_get_compose_list(void)
2462 {
2463         return compose_list;
2464 }
2465
2466 void compose_entry_append(Compose *compose, const gchar *address,
2467                           ComposeEntryType type, ComposePrefType pref_type)
2468 {
2469         const gchar *header;
2470         gchar *cur, *begin;
2471         gboolean in_quote = FALSE;
2472         if (!address || *address == '\0') return;
2473
2474         switch (type) {
2475         case COMPOSE_CC:
2476                 header = N_("Cc:");
2477                 break;
2478         case COMPOSE_BCC:
2479                 header = N_("Bcc:");
2480                 break;
2481         case COMPOSE_REPLYTO:
2482                 header = N_("Reply-To:");
2483                 break;
2484         case COMPOSE_NEWSGROUPS:
2485                 header = N_("Newsgroups:");
2486                 break;
2487         case COMPOSE_FOLLOWUPTO:
2488                 header = N_( "Followup-To:");
2489                 break;
2490         case COMPOSE_INREPLYTO:
2491                 header = N_( "In-Reply-To:");
2492                 break;
2493         case COMPOSE_TO:
2494         default:
2495                 header = N_("To:");
2496                 break;
2497         }
2498         header = prefs_common_translated_header_name(header);
2499         
2500         cur = begin = (gchar *)address;
2501         
2502         /* we separate the line by commas, but not if we're inside a quoted
2503          * string */
2504         while (*cur != '\0') {
2505                 if (*cur == '"') 
2506                         in_quote = !in_quote;
2507                 if (*cur == ',' && !in_quote) {
2508                         gchar *tmp = g_strdup(begin);
2509                         gchar *o_tmp = tmp;
2510                         tmp[cur-begin]='\0';
2511                         cur++;
2512                         begin = cur;
2513                         while (*tmp == ' ' || *tmp == '\t')
2514                                 tmp++;
2515                         compose_add_header_entry(compose, header, tmp, pref_type);
2516                         g_free(o_tmp);
2517                         continue;
2518                 }
2519                 cur++;
2520         }
2521         if (begin < cur) {
2522                 gchar *tmp = g_strdup(begin);
2523                 gchar *o_tmp = tmp;
2524                 tmp[cur-begin]='\0';
2525                 cur++;
2526                 begin = cur;
2527                 while (*tmp == ' ' || *tmp == '\t')
2528                         tmp++;
2529                 compose_add_header_entry(compose, header, tmp, pref_type);
2530                 g_free(o_tmp);          
2531         }
2532 }
2533
2534 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2535 {
2536         static GdkColor yellow;
2537         static GdkColor black;
2538         static gboolean yellow_initialised = FALSE;
2539         GSList *h_list;
2540         GtkEntry *entry;
2541                 
2542         if (!yellow_initialised) {
2543                 gdk_color_parse("#f5f6be", &yellow);
2544                 gdk_color_parse("#000000", &black);
2545                 yellow_initialised = gdk_colormap_alloc_color(
2546                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2547                 yellow_initialised &= gdk_colormap_alloc_color(
2548                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2549         }
2550
2551         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2552                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2553                 if (gtk_entry_get_text(entry) && 
2554                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2555                         if (yellow_initialised) {
2556                                 gtk_widget_modify_base(
2557                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2558                                         GTK_STATE_NORMAL, &yellow);
2559                                 gtk_widget_modify_text(
2560                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2561                                         GTK_STATE_NORMAL, &black);
2562                         }
2563                 }
2564         }
2565 }
2566
2567 void compose_toolbar_cb(gint action, gpointer data)
2568 {
2569         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2570         Compose *compose = (Compose*)toolbar_item->parent;
2571         
2572         cm_return_if_fail(compose != NULL);
2573
2574         switch(action) {
2575         case A_SEND:
2576                 compose_send_cb(NULL, compose);
2577                 break;
2578         case A_SENDL:
2579                 compose_send_later_cb(NULL, compose);
2580                 break;
2581         case A_DRAFT:
2582                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2583                 break;
2584         case A_INSERT:
2585                 compose_insert_file_cb(NULL, compose);
2586                 break;
2587         case A_ATTACH:
2588                 compose_attach_cb(NULL, compose);
2589                 break;
2590         case A_SIG:
2591                 compose_insert_sig(compose, FALSE);
2592                 break;
2593         case A_EXTEDITOR:
2594                 compose_ext_editor_cb(NULL, compose);
2595                 break;
2596         case A_LINEWRAP_CURRENT:
2597                 compose_beautify_paragraph(compose, NULL, TRUE);
2598                 break;
2599         case A_LINEWRAP_ALL:
2600                 compose_wrap_all_full(compose, TRUE);
2601                 break;
2602         case A_ADDRBOOK:
2603                 compose_address_cb(NULL, compose);
2604                 break;
2605 #ifdef USE_ENCHANT
2606         case A_CHECK_SPELLING:
2607                 compose_check_all(NULL, compose);
2608                 break;
2609 #endif
2610         default:
2611                 break;
2612         }
2613 }
2614
2615 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2616 {
2617         gchar *to = NULL;
2618         gchar *cc = NULL;
2619         gchar *bcc = NULL;
2620         gchar *subject = NULL;
2621         gchar *body = NULL;
2622         gchar *temp = NULL;
2623         gsize  len = 0;
2624         gchar **attach = NULL;
2625         gchar *inreplyto = NULL;
2626         MailField mfield = NO_FIELD_PRESENT;
2627
2628         /* get mailto parts but skip from */
2629         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2630
2631         if (to) {
2632                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2633                 mfield = TO_FIELD_PRESENT;
2634         }
2635         if (cc)
2636                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2637         if (bcc)
2638                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2639         if (subject) {
2640                 if (!g_utf8_validate (subject, -1, NULL)) {
2641                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2642                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2643                         g_free(temp);
2644                 } else {
2645                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2646                 }
2647                 mfield = SUBJECT_FIELD_PRESENT;
2648         }
2649         if (body) {
2650                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2651                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2652                 GtkTextMark *mark;
2653                 GtkTextIter iter;
2654                 gboolean prev_autowrap = compose->autowrap;
2655
2656                 compose->autowrap = FALSE;
2657
2658                 mark = gtk_text_buffer_get_insert(buffer);
2659                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2660
2661                 if (!g_utf8_validate (body, -1, NULL)) {
2662                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2663                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2664                         g_free(temp);
2665                 } else {
2666                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2667                 }
2668                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2669
2670                 compose->autowrap = prev_autowrap;
2671                 if (compose->autowrap)
2672                         compose_wrap_all(compose);
2673                 mfield = BODY_FIELD_PRESENT;
2674         }
2675
2676         if (attach) {
2677                 gint i = 0, att = 0;
2678                 gchar *warn_files = NULL;
2679                 while (attach[i] != NULL) {
2680                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2681                         if (utf8_filename) {
2682                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2683                                         gchar *tmp = g_strdup_printf("%s%s\n",
2684                                                         warn_files?warn_files:"",
2685                                                         utf8_filename);
2686                                         g_free(warn_files);
2687                                         warn_files = tmp;
2688                                         att++;
2689                                 }
2690                                 g_free(utf8_filename);
2691                         } else {
2692                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2693                         }
2694                         i++;
2695                 }
2696                 if (warn_files) {
2697                         alertpanel_notice(ngettext(
2698                         "The following file has been attached: \n%s",
2699                         "The following files have been attached: \n%s", att), warn_files);
2700                         g_free(warn_files);
2701                 }
2702         }
2703         if (inreplyto)
2704                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2705
2706         g_free(to);
2707         g_free(cc);
2708         g_free(bcc);
2709         g_free(subject);
2710         g_free(body);
2711         g_strfreev(attach);
2712         g_free(inreplyto);
2713         
2714         return mfield;
2715 }
2716
2717 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2718 {
2719         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2720                                        {"Cc:",          NULL, TRUE},
2721                                        {"References:",  NULL, FALSE},
2722                                        {"Bcc:",         NULL, TRUE},
2723                                        {"Newsgroups:",  NULL, TRUE},
2724                                        {"Followup-To:", NULL, TRUE},
2725                                        {"List-Post:",   NULL, FALSE},
2726                                        {"X-Priority:",  NULL, FALSE},
2727                                        {NULL,           NULL, FALSE}};
2728
2729         enum
2730         {
2731                 H_REPLY_TO      = 0,
2732                 H_CC            = 1,
2733                 H_REFERENCES    = 2,
2734                 H_BCC           = 3,
2735                 H_NEWSGROUPS    = 4,
2736                 H_FOLLOWUP_TO   = 5,
2737                 H_LIST_POST     = 6,
2738                 H_X_PRIORITY    = 7
2739         };
2740
2741         FILE *fp;
2742
2743         cm_return_val_if_fail(msginfo != NULL, -1);
2744
2745         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2746         procheader_get_header_fields(fp, hentry);
2747         fclose(fp);
2748
2749         if (hentry[H_REPLY_TO].body != NULL) {
2750                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2751                         compose->replyto =
2752                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2753                                                    NULL, TRUE);
2754                 }
2755                 g_free(hentry[H_REPLY_TO].body);
2756                 hentry[H_REPLY_TO].body = NULL;
2757         }
2758         if (hentry[H_CC].body != NULL) {
2759                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2760                 g_free(hentry[H_CC].body);
2761                 hentry[H_CC].body = NULL;
2762         }
2763         if (hentry[H_REFERENCES].body != NULL) {
2764                 if (compose->mode == COMPOSE_REEDIT)
2765                         compose->references = hentry[H_REFERENCES].body;
2766                 else {
2767                         compose->references = compose_parse_references
2768                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2769                         g_free(hentry[H_REFERENCES].body);
2770                 }
2771                 hentry[H_REFERENCES].body = NULL;
2772         }
2773         if (hentry[H_BCC].body != NULL) {
2774                 if (compose->mode == COMPOSE_REEDIT)
2775                         compose->bcc =
2776                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2777                 g_free(hentry[H_BCC].body);
2778                 hentry[H_BCC].body = NULL;
2779         }
2780         if (hentry[H_NEWSGROUPS].body != NULL) {
2781                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2782                 hentry[H_NEWSGROUPS].body = NULL;
2783         }
2784         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2785                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2786                         compose->followup_to =
2787                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2788                                                    NULL, TRUE);
2789                 }
2790                 g_free(hentry[H_FOLLOWUP_TO].body);
2791                 hentry[H_FOLLOWUP_TO].body = NULL;
2792         }
2793         if (hentry[H_LIST_POST].body != NULL) {
2794                 gchar *to = NULL, *start = NULL;
2795
2796                 extract_address(hentry[H_LIST_POST].body);
2797                 if (hentry[H_LIST_POST].body[0] != '\0') {
2798                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2799                         
2800                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2801                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2802
2803                         if (to) {
2804                                 g_free(compose->ml_post);
2805                                 compose->ml_post = to;
2806                         }
2807                 }
2808                 g_free(hentry[H_LIST_POST].body);
2809                 hentry[H_LIST_POST].body = NULL;
2810         }
2811
2812         /* CLAWS - X-Priority */
2813         if (compose->mode == COMPOSE_REEDIT)
2814                 if (hentry[H_X_PRIORITY].body != NULL) {
2815                         gint priority;
2816                         
2817                         priority = atoi(hentry[H_X_PRIORITY].body);
2818                         g_free(hentry[H_X_PRIORITY].body);
2819                         
2820                         hentry[H_X_PRIORITY].body = NULL;
2821                         
2822                         if (priority < PRIORITY_HIGHEST || 
2823                             priority > PRIORITY_LOWEST)
2824                                 priority = PRIORITY_NORMAL;
2825                         
2826                         compose->priority =  priority;
2827                 }
2828  
2829         if (compose->mode == COMPOSE_REEDIT) {
2830                 if (msginfo->inreplyto && *msginfo->inreplyto)
2831                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2832                 return 0;
2833         }
2834
2835         if (msginfo->msgid && *msginfo->msgid)
2836                 compose->inreplyto = g_strdup(msginfo->msgid);
2837
2838         if (!compose->references) {
2839                 if (msginfo->msgid && *msginfo->msgid) {
2840                         if (msginfo->inreplyto && *msginfo->inreplyto)
2841                                 compose->references =
2842                                         g_strdup_printf("<%s>\n\t<%s>",
2843                                                         msginfo->inreplyto,
2844                                                         msginfo->msgid);
2845                         else
2846                                 compose->references =
2847                                         g_strconcat("<", msginfo->msgid, ">",
2848                                                     NULL);
2849                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2850                         compose->references =
2851                                 g_strconcat("<", msginfo->inreplyto, ">",
2852                                             NULL);
2853                 }
2854         }
2855
2856         return 0;
2857 }
2858
2859 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2860 {
2861         GSList *ref_id_list, *cur;
2862         GString *new_ref;
2863         gchar *new_ref_str;
2864
2865         ref_id_list = references_list_append(NULL, ref);
2866         if (!ref_id_list) return NULL;
2867         if (msgid && *msgid)
2868                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2869
2870         for (;;) {
2871                 gint len = 0;
2872
2873                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2874                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2875                         len += strlen((gchar *)cur->data) + 5;
2876
2877                 if (len > MAX_REFERENCES_LEN) {
2878                         /* remove second message-ID */
2879                         if (ref_id_list && ref_id_list->next &&
2880                             ref_id_list->next->next) {
2881                                 g_free(ref_id_list->next->data);
2882                                 ref_id_list = g_slist_remove
2883                                         (ref_id_list, ref_id_list->next->data);
2884                         } else {
2885                                 slist_free_strings(ref_id_list);
2886                                 g_slist_free(ref_id_list);
2887                                 return NULL;
2888                         }
2889                 } else
2890                         break;
2891         }
2892
2893         new_ref = g_string_new("");
2894         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2895                 if (new_ref->len > 0)
2896                         g_string_append(new_ref, "\n\t");
2897                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2898         }
2899
2900         slist_free_strings(ref_id_list);
2901         g_slist_free(ref_id_list);
2902
2903         new_ref_str = new_ref->str;
2904         g_string_free(new_ref, FALSE);
2905
2906         return new_ref_str;
2907 }
2908
2909 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2910                                 const gchar *fmt, const gchar *qmark,
2911                                 const gchar *body, gboolean rewrap,
2912                                 gboolean need_unescape,
2913                                 const gchar *err_msg)
2914 {
2915         MsgInfo* dummyinfo = NULL;
2916         gchar *quote_str = NULL;
2917         gchar *buf;
2918         gboolean prev_autowrap;
2919         const gchar *trimmed_body = body;
2920         gint cursor_pos = -1;
2921         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2922         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2923         GtkTextIter iter;
2924         GtkTextMark *mark;
2925         
2926
2927         SIGNAL_BLOCK(buffer);
2928
2929         if (!msginfo) {
2930                 dummyinfo = compose_msginfo_new_from_compose(compose);
2931                 msginfo = dummyinfo;
2932         }
2933
2934         if (qmark != NULL) {
2935 #ifdef USE_ENCHANT
2936                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2937                                 compose->gtkaspell);
2938 #else
2939                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2940 #endif
2941                 quote_fmt_scan_string(qmark);
2942                 quote_fmt_parse();
2943
2944                 buf = quote_fmt_get_buffer();
2945                 if (buf == NULL)
2946                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2947                 else
2948                         Xstrdup_a(quote_str, buf, goto error)
2949         }
2950
2951         if (fmt && *fmt != '\0') {
2952
2953                 if (trimmed_body)
2954                         while (*trimmed_body == '\n')
2955                                 trimmed_body++;
2956
2957 #ifdef USE_ENCHANT
2958                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2959                                 compose->gtkaspell);
2960 #else
2961                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2962 #endif
2963                 if (need_unescape) {
2964                         gchar *tmp = NULL;
2965
2966                         /* decode \-escape sequences in the internal representation of the quote format */
2967                         tmp = g_malloc(strlen(fmt)+1);
2968                         pref_get_unescaped_pref(tmp, fmt);
2969                         quote_fmt_scan_string(tmp);
2970                         quote_fmt_parse();
2971                         g_free(tmp);
2972                 } else {
2973                         quote_fmt_scan_string(fmt);
2974                         quote_fmt_parse();
2975                 }
2976
2977                 buf = quote_fmt_get_buffer();
2978                 if (buf == NULL) {
2979                         gint line = quote_fmt_get_line();
2980                         alertpanel_error(err_msg, line);
2981                         goto error;
2982                 }
2983         } else
2984                 buf = "";
2985
2986         prev_autowrap = compose->autowrap;
2987         compose->autowrap = FALSE;
2988
2989         mark = gtk_text_buffer_get_insert(buffer);
2990         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2991         if (g_utf8_validate(buf, -1, NULL)) { 
2992                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2993         } else {
2994                 gchar *tmpout = NULL;
2995                 tmpout = conv_codeset_strdup
2996                         (buf, conv_get_locale_charset_str_no_utf8(),
2997                          CS_INTERNAL);
2998                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2999                         g_free(tmpout);
3000                         tmpout = g_malloc(strlen(buf)*2+1);
3001                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3002                 }
3003                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3004                 g_free(tmpout);
3005         }
3006
3007         cursor_pos = quote_fmt_get_cursor_pos();
3008         if (cursor_pos == -1)
3009                 cursor_pos = gtk_text_iter_get_offset(&iter);
3010         compose->set_cursor_pos = cursor_pos;
3011
3012         gtk_text_buffer_get_start_iter(buffer, &iter);
3013         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3014         gtk_text_buffer_place_cursor(buffer, &iter);
3015
3016         compose->autowrap = prev_autowrap;
3017         if (compose->autowrap && rewrap)
3018                 compose_wrap_all(compose);
3019
3020         goto ok;
3021
3022 error:
3023         buf = NULL;
3024 ok:
3025         SIGNAL_UNBLOCK(buffer);
3026
3027         procmsg_msginfo_free( dummyinfo );
3028
3029         return buf;
3030 }
3031
3032 /* if ml_post is of type addr@host and from is of type
3033  * addr-anything@host, return TRUE
3034  */
3035 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3036 {
3037         gchar *left_ml = NULL;
3038         gchar *right_ml = NULL;
3039         gchar *left_from = NULL;
3040         gchar *right_from = NULL;
3041         gboolean result = FALSE;
3042         
3043         if (!ml_post || !from)
3044                 return FALSE;
3045         
3046         left_ml = g_strdup(ml_post);
3047         if (strstr(left_ml, "@")) {
3048                 right_ml = strstr(left_ml, "@")+1;
3049                 *(strstr(left_ml, "@")) = '\0';
3050         }
3051         
3052         left_from = g_strdup(from);
3053         if (strstr(left_from, "@")) {
3054                 right_from = strstr(left_from, "@")+1;
3055                 *(strstr(left_from, "@")) = '\0';
3056         }
3057         
3058         if (left_ml && left_from && right_ml && right_from
3059         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3060         &&  !strcmp(right_from, right_ml)) {
3061                 result = TRUE;
3062         }
3063         g_free(left_ml);
3064         g_free(left_from);
3065         
3066         return result;
3067 }
3068
3069 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3070                                      gboolean respect_default_to)
3071 {
3072         if (!compose)
3073                 return;
3074         if (!folder || !folder->prefs)
3075                 return;
3076
3077         if (respect_default_to && folder->prefs->enable_default_to) {
3078                 compose_entry_append(compose, folder->prefs->default_to,
3079                                         COMPOSE_TO, PREF_FOLDER);
3080                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3081         }
3082         if (folder->prefs->enable_default_cc)
3083                 compose_entry_append(compose, folder->prefs->default_cc,
3084                                         COMPOSE_CC, PREF_FOLDER);
3085         if (folder->prefs->enable_default_bcc)
3086                 compose_entry_append(compose, folder->prefs->default_bcc,
3087                                         COMPOSE_BCC, PREF_FOLDER);
3088         if (folder->prefs->enable_default_replyto)
3089                 compose_entry_append(compose, folder->prefs->default_replyto,
3090                                         COMPOSE_REPLYTO, PREF_FOLDER);
3091 }
3092
3093 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3094 {
3095         gchar *buf, *buf2;
3096         gchar *p;
3097         
3098         if (!compose || !msginfo)
3099                 return;
3100
3101         if (msginfo->subject && *msginfo->subject) {
3102                 buf = p = g_strdup(msginfo->subject);
3103                 p += subject_get_prefix_length(p);
3104                 memmove(buf, p, strlen(p) + 1);
3105
3106                 buf2 = g_strdup_printf("Re: %s", buf);
3107                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3108
3109                 g_free(buf2);
3110                 g_free(buf);
3111         } else
3112                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3113 }
3114
3115 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3116                                     gboolean to_all, gboolean to_ml,
3117                                     gboolean to_sender,
3118                                     gboolean followup_and_reply_to)
3119 {
3120         GSList *cc_list = NULL;
3121         GSList *cur;
3122         gchar *from = NULL;
3123         gchar *replyto = NULL;
3124         gchar *ac_email = NULL;
3125
3126         gboolean reply_to_ml = FALSE;
3127         gboolean default_reply_to = FALSE;
3128
3129         cm_return_if_fail(compose->account != NULL);
3130         cm_return_if_fail(msginfo != NULL);
3131
3132         reply_to_ml = to_ml && compose->ml_post;
3133
3134         default_reply_to = msginfo->folder && 
3135                 msginfo->folder->prefs->enable_default_reply_to;
3136
3137         if (compose->account->protocol != A_NNTP) {
3138                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3139
3140                 if (reply_to_ml && !default_reply_to) {
3141                         
3142                         gboolean is_subscr = is_subscription(compose->ml_post,
3143                                                              msginfo->from);
3144                         if (!is_subscr) {
3145                                 /* normal answer to ml post with a reply-to */
3146                                 compose_entry_append(compose,
3147                                            compose->ml_post,
3148                                            COMPOSE_TO, PREF_ML);
3149                                 if (compose->replyto)
3150                                         compose_entry_append(compose,
3151                                                 compose->replyto,
3152                                                 COMPOSE_CC, PREF_ML);
3153                         } else {
3154                                 /* answer to subscription confirmation */
3155                                 if (compose->replyto)
3156                                         compose_entry_append(compose,
3157                                                 compose->replyto,
3158                                                 COMPOSE_TO, PREF_ML);
3159                                 else if (msginfo->from)
3160                                         compose_entry_append(compose,
3161                                                 msginfo->from,
3162                                                 COMPOSE_TO, PREF_ML);
3163                         }
3164                 }
3165                 else if (!(to_all || to_sender) && default_reply_to) {
3166                         compose_entry_append(compose,
3167                             msginfo->folder->prefs->default_reply_to,
3168                             COMPOSE_TO, PREF_FOLDER);
3169                         compose_entry_mark_default_to(compose,
3170                                 msginfo->folder->prefs->default_reply_to);
3171                 } else {
3172                         gchar *tmp1 = NULL;
3173                         if (!msginfo->from)
3174                                 return;
3175                         Xstrdup_a(tmp1, msginfo->from, return);
3176                        &n