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