2009-12-29 [colin] 3.7.3cvs46
[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);
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                 }
1063                 /*
1064                  * CLAWS: just don't allow return receipt request, even if the user
1065                  * may want to send an email. simple but foolproof.
1066                  */
1067                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1068         }
1069         compose_add_field_list( compose, listAddress );
1070
1071         if (item && item->prefs && item->prefs->compose_with_format) {
1072                 subject_format = item->prefs->compose_subject_format;
1073                 body_format = item->prefs->compose_body_format;
1074         } else if (account->compose_with_format) {
1075                 subject_format = account->compose_subject_format;
1076                 body_format = account->compose_body_format;
1077         } else if (prefs_common.compose_with_format) {
1078                 subject_format = prefs_common.compose_subject_format;
1079                 body_format = prefs_common.compose_body_format;
1080         }
1081
1082         if (subject_format || body_format) {
1083
1084                 if ( subject_format
1085                          && *subject_format != '\0' )
1086                 {
1087                         gchar *subject = NULL;
1088                         gchar *tmp = NULL;
1089                         gchar *buf = NULL;
1090
1091                         if (!dummyinfo)
1092                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1093
1094                         /* decode \-escape sequences in the internal representation of the quote format */
1095                         tmp = malloc(strlen(subject_format)+1);
1096                         pref_get_unescaped_pref(tmp, subject_format);
1097
1098                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1099 #ifdef USE_ENCHANT
1100                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1101                                         compose->gtkaspell);
1102 #else
1103                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1104 #endif
1105                         quote_fmt_scan_string(tmp);
1106                         quote_fmt_parse();
1107
1108                         buf = quote_fmt_get_buffer();
1109                         if (buf == NULL)
1110                                 alertpanel_error(_("New message subject format error."));
1111                         else
1112                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1113                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1114                         quote_fmt_reset_vartable();
1115
1116                         g_free(subject);
1117                         g_free(tmp);
1118                         mfield = SUBJECT_FIELD_PRESENT;
1119                 }
1120
1121                 if ( body_format
1122                          && *body_format != '\0' )
1123                 {
1124                         GtkTextView *text;
1125                         GtkTextBuffer *buffer;
1126                         GtkTextIter start, end;
1127                         gchar *tmp = NULL;
1128
1129                         if (!dummyinfo)
1130                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1131
1132                         text = GTK_TEXT_VIEW(compose->text);
1133                         buffer = gtk_text_view_get_buffer(text);
1134                         gtk_text_buffer_get_start_iter(buffer, &start);
1135                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1136                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1137
1138                         compose_quote_fmt(compose, dummyinfo,
1139                                           body_format,
1140                                           NULL, tmp, FALSE, TRUE,
1141                                                   _("The body of the \"New message\" template has an error at line %d."));
1142                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1143                         quote_fmt_reset_vartable();
1144
1145                         g_free(tmp);
1146 #ifdef USE_ENCHANT
1147                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1148                                 gtkaspell_highlight_all(compose->gtkaspell);
1149 #endif
1150                         mfield = BODY_FIELD_PRESENT;
1151                 }
1152
1153         }
1154         procmsg_msginfo_free( dummyinfo );
1155
1156         if (attach_files) {
1157                 gint i;
1158                 gchar *file;
1159
1160                 for (i = 0; i < attach_files->len; i++) {
1161                         file = g_ptr_array_index(attach_files, i);
1162                         compose_attach_append(compose, file, file, NULL);
1163                 }
1164         }
1165
1166         compose_show_first_last_header(compose, TRUE);
1167
1168         /* Set save folder */
1169         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1170                 gchar *folderidentifier;
1171
1172                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1173                 folderidentifier = folder_item_get_identifier(item);
1174                 compose_set_save_to(compose, folderidentifier);
1175                 g_free(folderidentifier);
1176         }
1177
1178         /* Place cursor according to provided input (mfield) */
1179         switch (mfield) { 
1180                 case NO_FIELD_PRESENT:
1181                         if (compose->header_last)
1182                                 gtk_widget_grab_focus(compose->header_last->entry);
1183                         break;
1184                 case TO_FIELD_PRESENT:
1185                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1186                         if (buf) {
1187                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1188                                 g_free(buf);
1189                         }
1190                         gtk_widget_grab_focus(compose->subject_entry);
1191                         break;
1192                 case SUBJECT_FIELD_PRESENT:
1193                         textview = GTK_TEXT_VIEW(compose->text);
1194                         if (!textview)
1195                                 break;
1196                         textbuf = gtk_text_view_get_buffer(textview);
1197                         if (!textbuf)
1198                                 break;
1199                         mark = gtk_text_buffer_get_insert(textbuf);
1200                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1201                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1202                     /* 
1203                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1204                      * only defers where it comes to the variable body
1205                      * is not null. If no body is present compose->text
1206                      * will be null in which case you cannot place the
1207                      * cursor inside the component so. An empty component
1208                      * is therefore created before placing the cursor
1209                      */
1210                 case BODY_FIELD_PRESENT:
1211                         gtk_widget_grab_focus(compose->text);
1212                         break;
1213         }
1214
1215         undo_unblock(compose->undostruct);
1216
1217         if (prefs_common.auto_exteditor)
1218                 compose_exec_ext_editor(compose);
1219
1220         compose->draft_timeout_tag = -1;
1221         SCROLL_TO_CURSOR(compose);
1222
1223         compose->modified = FALSE;
1224         compose_set_title(compose);
1225
1226   hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1227
1228         return compose;
1229 }
1230
1231 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1232                 gboolean override_pref, const gchar *system)
1233 {
1234         const gchar *privacy = NULL;
1235
1236         cm_return_if_fail(compose != NULL);
1237         cm_return_if_fail(account != NULL);
1238
1239         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1240                 return;
1241
1242         if (system)
1243                 privacy = system;
1244         else if (account->default_privacy_system
1245         &&  strlen(account->default_privacy_system)) {
1246                 privacy = account->default_privacy_system;
1247         } else {
1248                 GSList *privacy_avail = privacy_get_system_ids();
1249                 if (privacy_avail && g_slist_length(privacy_avail)) {
1250                         privacy = (gchar *)(privacy_avail->data);
1251                 }
1252         }
1253         if (privacy != NULL) {
1254                 if (system) {
1255                         g_free(compose->privacy_system);
1256                         compose->privacy_system = NULL;
1257                 }
1258                 if (compose->privacy_system == NULL)
1259                         compose->privacy_system = g_strdup(privacy);
1260                 else if (*(compose->privacy_system) == '\0') {
1261                         g_free(compose->privacy_system);
1262                         compose->privacy_system = g_strdup(privacy);
1263                 }
1264                 compose_update_privacy_system_menu_item(compose, FALSE);
1265                 compose_use_encryption(compose, TRUE);
1266         }
1267 }       
1268
1269 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1270 {
1271         const gchar *privacy = NULL;
1272
1273         if (system)
1274                 privacy = system;
1275         else if (account->default_privacy_system
1276         &&  strlen(account->default_privacy_system)) {
1277                 privacy = account->default_privacy_system;
1278         } else {
1279                 GSList *privacy_avail = privacy_get_system_ids();
1280                 if (privacy_avail && g_slist_length(privacy_avail)) {
1281                         privacy = (gchar *)(privacy_avail->data);
1282                 }
1283         }
1284
1285         if (privacy != NULL) {
1286                 if (system) {
1287                         g_free(compose->privacy_system);
1288                         compose->privacy_system = NULL;
1289                 }
1290                 if (compose->privacy_system == NULL)
1291                         compose->privacy_system = g_strdup(privacy);
1292                 compose_update_privacy_system_menu_item(compose, FALSE);
1293                 compose_use_signing(compose, TRUE);
1294         }
1295 }       
1296
1297 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1298 {
1299         MsgInfo *msginfo;
1300         guint list_len;
1301         Compose *compose = NULL;
1302         
1303         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1304
1305         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1306         cm_return_val_if_fail(msginfo != NULL, NULL);
1307
1308         list_len = g_slist_length(msginfo_list);
1309
1310         switch (mode) {
1311         case COMPOSE_REPLY:
1312                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1313                               FALSE, prefs_common.default_reply_list, FALSE, body);
1314                 break;
1315         case COMPOSE_REPLY_WITH_QUOTE:
1316                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1317                         FALSE, prefs_common.default_reply_list, FALSE, body);
1318                 break;
1319         case COMPOSE_REPLY_WITHOUT_QUOTE:
1320                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1321                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1322                 break;
1323         case COMPOSE_REPLY_TO_SENDER:
1324                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1325                               FALSE, FALSE, TRUE, body);
1326                 break;
1327         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1328                 compose = compose_followup_and_reply_to(msginfo,
1329                                               COMPOSE_QUOTE_CHECK,
1330                                               FALSE, FALSE, body);
1331                 break;
1332         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1333                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1334                         FALSE, FALSE, TRUE, body);
1335                 break;
1336         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1337                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1338                         FALSE, FALSE, TRUE, NULL);
1339                 break;
1340         case COMPOSE_REPLY_TO_ALL:
1341                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1342                         TRUE, FALSE, FALSE, body);
1343                 break;
1344         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1345                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1346                         TRUE, FALSE, FALSE, body);
1347                 break;
1348         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1349                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1350                         TRUE, FALSE, FALSE, NULL);
1351                 break;
1352         case COMPOSE_REPLY_TO_LIST:
1353                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1354                         FALSE, TRUE, FALSE, body);
1355                 break;
1356         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1357                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1358                         FALSE, TRUE, FALSE, body);
1359                 break;
1360         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1361                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1362                         FALSE, TRUE, FALSE, NULL);
1363                 break;
1364         case COMPOSE_FORWARD:
1365                 if (prefs_common.forward_as_attachment) {
1366                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1367                         return compose;
1368                 } else {
1369                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1370                         return compose;
1371                 }
1372                 break;
1373         case COMPOSE_FORWARD_INLINE:
1374                 /* check if we reply to more than one Message */
1375                 if (list_len == 1) {
1376                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1377                         break;
1378                 } 
1379                 /* more messages FALL THROUGH */
1380         case COMPOSE_FORWARD_AS_ATTACH:
1381                 compose = compose_forward_multiple(NULL, msginfo_list);
1382                 break;
1383         case COMPOSE_REDIRECT:
1384                 compose = compose_redirect(NULL, msginfo, FALSE);
1385                 break;
1386         default:
1387                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1388         }
1389         
1390         if (compose == NULL) {
1391                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1392                 return NULL;
1393         }
1394
1395         compose->rmode = mode;
1396         switch (compose->rmode) {
1397         case COMPOSE_REPLY:
1398         case COMPOSE_REPLY_WITH_QUOTE:
1399         case COMPOSE_REPLY_WITHOUT_QUOTE:
1400         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1401                 debug_print("reply mode Normal\n");
1402                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1403                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1404                 break;
1405         case COMPOSE_REPLY_TO_SENDER:
1406         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1407         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1408                 debug_print("reply mode Sender\n");
1409                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1410                 break;
1411         case COMPOSE_REPLY_TO_ALL:
1412         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1413         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1414                 debug_print("reply mode All\n");
1415                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1416                 break;
1417         case COMPOSE_REPLY_TO_LIST:
1418         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1419         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1420                 debug_print("reply mode List\n");
1421                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1422                 break;
1423         default:
1424                 break;
1425         }
1426         return compose;
1427 }
1428
1429 static Compose *compose_reply(MsgInfo *msginfo,
1430                                    ComposeQuoteMode quote_mode,
1431                                    gboolean to_all,
1432                                    gboolean to_ml,
1433                                    gboolean to_sender, 
1434                    const gchar *body)
1435 {
1436         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1437                               to_sender, FALSE, body);
1438 }
1439
1440 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1441                                    ComposeQuoteMode quote_mode,
1442                                    gboolean to_all,
1443                                    gboolean to_sender,
1444                                    const gchar *body)
1445 {
1446         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1447                               to_sender, TRUE, body);
1448 }
1449
1450 static void compose_extract_original_charset(Compose *compose)
1451 {
1452         MsgInfo *info = NULL;
1453         if (compose->replyinfo) {
1454                 info = compose->replyinfo;
1455         } else if (compose->fwdinfo) {
1456                 info = compose->fwdinfo;
1457         } else if (compose->targetinfo) {
1458                 info = compose->targetinfo;
1459         }
1460         if (info) {
1461                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1462                 MimeInfo *partinfo = mimeinfo;
1463                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1464                         partinfo = procmime_mimeinfo_next(partinfo);
1465                 if (partinfo) {
1466                         compose->orig_charset = 
1467                                 g_strdup(procmime_mimeinfo_get_parameter(
1468                                                 partinfo, "charset"));
1469                 }
1470                 procmime_mimeinfo_free_all(mimeinfo);
1471         }
1472 }
1473
1474 #define SIGNAL_BLOCK(buffer) {                                  \
1475         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1476                                 G_CALLBACK(compose_changed_cb), \
1477                                 compose);                       \
1478         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1479                                 G_CALLBACK(text_inserted),      \
1480                                 compose);                       \
1481 }
1482
1483 #define SIGNAL_UNBLOCK(buffer) {                                \
1484         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1485                                 G_CALLBACK(compose_changed_cb), \
1486                                 compose);                       \
1487         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1488                                 G_CALLBACK(text_inserted),      \
1489                                 compose);                       \
1490 }
1491
1492 static Compose *compose_generic_reply(MsgInfo *msginfo,
1493                                   ComposeQuoteMode quote_mode,
1494                                   gboolean to_all, gboolean to_ml,
1495                                   gboolean to_sender,
1496                                   gboolean followup_and_reply_to,
1497                                   const gchar *body)
1498 {
1499         Compose *compose;
1500         PrefsAccount *account = NULL;
1501         GtkTextView *textview;
1502         GtkTextBuffer *textbuf;
1503         gboolean quote = FALSE;
1504         const gchar *qmark = NULL;
1505         const gchar *body_fmt = NULL;
1506         gchar *s_system = NULL;
1507         START_TIMING("");
1508         cm_return_val_if_fail(msginfo != NULL, NULL);
1509         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1510
1511         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1512
1513         cm_return_val_if_fail(account != NULL, NULL);
1514
1515         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1516
1517         compose->updating = TRUE;
1518
1519         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1520         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1521
1522         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1523         if (!compose->replyinfo)
1524                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1525
1526         compose_extract_original_charset(compose);
1527         
1528         if (msginfo->folder && msginfo->folder->ret_rcpt)
1529                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1530
1531         /* Set save folder */
1532         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1533                 gchar *folderidentifier;
1534
1535                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1536                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1537                 compose_set_save_to(compose, folderidentifier);
1538                 g_free(folderidentifier);
1539         }
1540
1541         if (compose_parse_header(compose, msginfo) < 0) {
1542                 compose->updating = FALSE;
1543                 compose_destroy(compose);
1544                 return NULL;
1545         }
1546
1547         /* override from name according to folder properties */
1548         if (msginfo->folder && msginfo->folder->prefs &&
1549                 msginfo->folder->prefs->reply_with_format &&
1550                 msginfo->folder->prefs->reply_override_from_format &&
1551                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1552
1553                 gchar *tmp = NULL;
1554                 gchar *buf = NULL;
1555
1556                 /* decode \-escape sequences in the internal representation of the quote format */
1557                 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1558                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1559
1560 #ifdef USE_ENCHANT
1561                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1562                                 compose->gtkaspell);
1563 #else
1564                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1565 #endif
1566                 quote_fmt_scan_string(tmp);
1567                 quote_fmt_parse();
1568
1569                 buf = quote_fmt_get_buffer();
1570                 if (buf == NULL)
1571                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1572                 else
1573                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1574                 quote_fmt_reset_vartable();
1575
1576                 g_free(tmp);
1577         }
1578
1579         textview = (GTK_TEXT_VIEW(compose->text));
1580         textbuf = gtk_text_view_get_buffer(textview);
1581         compose_create_tags(textview, compose);
1582
1583         undo_block(compose->undostruct);
1584 #ifdef USE_ENCHANT
1585                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1586 #endif
1587
1588         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1589                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1590                 /* use the reply format of folder (if enabled), or the account's one
1591                    (if enabled) or fallback to the global reply format, which is always
1592                    enabled (even if empty), and use the relevant quotemark */
1593                 quote = TRUE;
1594                 if (msginfo->folder && msginfo->folder->prefs &&
1595                                 msginfo->folder->prefs->reply_with_format) {
1596                         qmark = msginfo->folder->prefs->reply_quotemark;
1597                         body_fmt = msginfo->folder->prefs->reply_body_format;
1598
1599                 } else if (account->reply_with_format) {
1600                         qmark = account->reply_quotemark;
1601                         body_fmt = account->reply_body_format;
1602
1603                 } else {
1604                         qmark = prefs_common.quotemark;
1605                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1606                                 body_fmt = gettext(prefs_common.quotefmt);
1607                         else
1608                                 body_fmt = "";
1609                 }
1610         }
1611
1612         if (quote) {
1613                 /* empty quotemark is not allowed */
1614                 if (qmark == NULL || *qmark == '\0')
1615                         qmark = "> ";
1616                 compose_quote_fmt(compose, compose->replyinfo,
1617                                   body_fmt, qmark, body, FALSE, TRUE,
1618                                           _("The body of the \"Reply\" template has an error at line %d."));
1619                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1620                 quote_fmt_reset_vartable();
1621 #ifdef USE_ENCHANT
1622                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1623                         gtkaspell_highlight_all(compose->gtkaspell);
1624 #endif
1625         }
1626
1627         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1628                 compose_force_encryption(compose, account, FALSE, s_system);
1629         }
1630
1631         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1632         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1633                 compose_force_signing(compose, account, s_system);
1634         }
1635         g_free(s_system);
1636
1637         SIGNAL_BLOCK(textbuf);
1638         
1639         if (account->auto_sig)
1640                 compose_insert_sig(compose, FALSE);
1641
1642         compose_wrap_all(compose);
1643
1644         SIGNAL_UNBLOCK(textbuf);
1645         
1646         gtk_widget_grab_focus(compose->text);
1647
1648         undo_unblock(compose->undostruct);
1649
1650         if (prefs_common.auto_exteditor)
1651                 compose_exec_ext_editor(compose);
1652                 
1653         compose->modified = FALSE;
1654         compose_set_title(compose);
1655
1656         compose->updating = FALSE;
1657         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1658         SCROLL_TO_CURSOR(compose);
1659         
1660         if (compose->deferred_destroy) {
1661                 compose_destroy(compose);
1662                 return NULL;
1663         }
1664         END_TIMING();
1665
1666         return compose;
1667 }
1668
1669 #define INSERT_FW_HEADER(var, hdr) \
1670 if (msginfo->var && *msginfo->var) { \
1671         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1672         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1673         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1674 }
1675
1676 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1677                          gboolean as_attach, const gchar *body,
1678                          gboolean no_extedit,
1679                          gboolean batch)
1680 {
1681         Compose *compose;
1682         GtkTextView *textview;
1683         GtkTextBuffer *textbuf;
1684         GtkTextIter iter;
1685
1686         cm_return_val_if_fail(msginfo != NULL, NULL);
1687         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1688
1689         if (!account && 
1690             !(account = compose_guess_forward_account_from_msginfo
1691                                 (msginfo)))
1692                 account = cur_account;
1693
1694         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1695
1696         compose->updating = TRUE;
1697         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1698         if (!compose->fwdinfo)
1699                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1700
1701         compose_extract_original_charset(compose);
1702
1703         if (msginfo->subject && *msginfo->subject) {
1704                 gchar *buf, *buf2, *p;
1705
1706                 buf = p = g_strdup(msginfo->subject);
1707                 p += subject_get_prefix_length(p);
1708                 memmove(buf, p, strlen(p) + 1);
1709
1710                 buf2 = g_strdup_printf("Fw: %s", buf);
1711                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1712                 
1713                 g_free(buf);
1714                 g_free(buf2);
1715         }
1716
1717         /* override from name according to folder properties */
1718         if (msginfo->folder && msginfo->folder->prefs &&
1719                 msginfo->folder->prefs->forward_with_format &&
1720                 msginfo->folder->prefs->forward_override_from_format &&
1721                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1722
1723                 gchar *tmp = NULL;
1724                 gchar *buf = NULL;
1725                 MsgInfo *full_msginfo = NULL;
1726
1727                 if (!as_attach)
1728                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1729                 if (!full_msginfo)
1730                         full_msginfo = procmsg_msginfo_copy(msginfo);
1731
1732                 /* decode \-escape sequences in the internal representation of the quote format */
1733                 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1734                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1735
1736 #ifdef USE_ENCHANT
1737                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1738                                 compose->gtkaspell);
1739 #else
1740                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1741 #endif
1742                 quote_fmt_scan_string(tmp);
1743                 quote_fmt_parse();
1744
1745                 buf = quote_fmt_get_buffer();
1746                 if (buf == NULL)
1747                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1748                 else
1749                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1750                 quote_fmt_reset_vartable();
1751
1752                 g_free(tmp);
1753                 procmsg_msginfo_free(full_msginfo);
1754         }
1755
1756         textview = GTK_TEXT_VIEW(compose->text);
1757         textbuf = gtk_text_view_get_buffer(textview);
1758         compose_create_tags(textview, compose);
1759         
1760         undo_block(compose->undostruct);
1761         if (as_attach) {
1762                 gchar *msgfile;
1763
1764                 msgfile = procmsg_get_message_file(msginfo);
1765                 if (!is_file_exist(msgfile))
1766                         g_warning("%s: file not exist\n", msgfile);
1767                 else
1768                         compose_attach_append(compose, msgfile, msgfile,
1769                                               "message/rfc822");
1770
1771                 g_free(msgfile);
1772         } else {
1773                 const gchar *qmark = NULL;
1774                 const gchar *body_fmt = NULL;
1775                 MsgInfo *full_msginfo;
1776
1777                 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1778                         body_fmt = gettext(prefs_common.fw_quotefmt);
1779                 else
1780                         body_fmt = "";
1781         
1782                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1783                 if (!full_msginfo)
1784                         full_msginfo = procmsg_msginfo_copy(msginfo);
1785
1786                 /* use the forward format of folder (if enabled), or the account's one
1787                    (if enabled) or fallback to the global forward format, which is always
1788                    enabled (even if empty), and use the relevant quotemark */
1789                 if (msginfo->folder && msginfo->folder->prefs &&
1790                                 msginfo->folder->prefs->forward_with_format) {
1791                         qmark = msginfo->folder->prefs->forward_quotemark;
1792                         body_fmt = msginfo->folder->prefs->forward_body_format;
1793
1794                 } else if (account->forward_with_format) {
1795                         qmark = account->forward_quotemark;
1796                         body_fmt = account->forward_body_format;
1797
1798                 } else {
1799                         qmark = prefs_common.fw_quotemark;
1800                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1801                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1802                         else
1803                                 body_fmt = "";
1804                 }
1805
1806                 /* empty quotemark is not allowed */
1807                 if (qmark == NULL || *qmark == '\0')
1808                         qmark = "> ";
1809
1810                 compose_quote_fmt(compose, full_msginfo,
1811                                   body_fmt, qmark, body, FALSE, TRUE,
1812                                           _("The body of the \"Forward\" template has an error at line %d."));
1813                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1814                 quote_fmt_reset_vartable();
1815                 compose_attach_parts(compose, msginfo);
1816
1817                 procmsg_msginfo_free(full_msginfo);
1818 #ifdef USE_ENCHANT
1819                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1820                         gtkaspell_highlight_all(compose->gtkaspell);
1821 #endif
1822         }
1823
1824         SIGNAL_BLOCK(textbuf);
1825
1826         if (account->auto_sig)
1827                 compose_insert_sig(compose, FALSE);
1828
1829         compose_wrap_all(compose);
1830
1831         SIGNAL_UNBLOCK(textbuf);
1832         
1833         gtk_text_buffer_get_start_iter(textbuf, &iter);
1834         gtk_text_buffer_place_cursor(textbuf, &iter);
1835
1836         gtk_widget_grab_focus(compose->header_last->entry);
1837
1838         if (!no_extedit && prefs_common.auto_exteditor)
1839                 compose_exec_ext_editor(compose);
1840         
1841         /*save folder*/
1842         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1843                 gchar *folderidentifier;
1844
1845                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1846                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1847                 compose_set_save_to(compose, folderidentifier);
1848                 g_free(folderidentifier);
1849         }
1850
1851         undo_unblock(compose->undostruct);
1852         
1853         compose->modified = FALSE;
1854         compose_set_title(compose);
1855
1856         compose->updating = FALSE;
1857         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1858         SCROLL_TO_CURSOR(compose);
1859
1860         if (compose->deferred_destroy) {
1861                 compose_destroy(compose);
1862                 return NULL;
1863         }
1864
1865         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1866
1867         return compose;
1868 }
1869
1870 #undef INSERT_FW_HEADER
1871
1872 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1873 {
1874         Compose *compose;
1875         GtkTextView *textview;
1876         GtkTextBuffer *textbuf;
1877         GtkTextIter iter;
1878         GSList *msginfo;
1879         gchar *msgfile;
1880         gboolean single_mail = TRUE;
1881         
1882         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1883
1884         if (g_slist_length(msginfo_list) > 1)
1885                 single_mail = FALSE;
1886
1887         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1888                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1889                         return NULL;
1890
1891         /* guess account from first selected message */
1892         if (!account && 
1893             !(account = compose_guess_forward_account_from_msginfo
1894                                 (msginfo_list->data)))
1895                 account = cur_account;
1896
1897         cm_return_val_if_fail(account != NULL, NULL);
1898
1899         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1900                 if (msginfo->data) {
1901                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1902                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1903                 }
1904         }
1905
1906         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1907                 g_warning("no msginfo_list");
1908                 return NULL;
1909         }
1910
1911         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1912
1913         compose->updating = TRUE;
1914
1915         /* override from name according to folder properties */
1916         if (msginfo_list->data) {
1917                 MsgInfo *msginfo = msginfo_list->data;
1918
1919                 if (msginfo->folder && msginfo->folder->prefs &&
1920                         msginfo->folder->prefs->forward_with_format &&
1921                         msginfo->folder->prefs->forward_override_from_format &&
1922                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1923
1924                         gchar *tmp = NULL;
1925                         gchar *buf = NULL;
1926
1927                         /* decode \-escape sequences in the internal representation of the quote format */
1928                         tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1929                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1930
1931 #ifdef USE_ENCHANT
1932                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1933                                         compose->gtkaspell);
1934 #else
1935                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1936 #endif
1937                         quote_fmt_scan_string(tmp);
1938                         quote_fmt_parse();
1939
1940                         buf = quote_fmt_get_buffer();
1941                         if (buf == NULL)
1942                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1943                         else
1944                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1945                         quote_fmt_reset_vartable();
1946
1947                         g_free(tmp);
1948                 }
1949         }
1950
1951         textview = GTK_TEXT_VIEW(compose->text);
1952         textbuf = gtk_text_view_get_buffer(textview);
1953         compose_create_tags(textview, compose);
1954         
1955         undo_block(compose->undostruct);
1956         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1957                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1958
1959                 if (!is_file_exist(msgfile))
1960                         g_warning("%s: file not exist\n", msgfile);
1961                 else
1962                         compose_attach_append(compose, msgfile, msgfile,
1963                                 "message/rfc822");
1964                 g_free(msgfile);
1965         }
1966         
1967         if (single_mail) {
1968                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1969                 if (info->subject && *info->subject) {
1970                         gchar *buf, *buf2, *p;
1971
1972                         buf = p = g_strdup(info->subject);
1973                         p += subject_get_prefix_length(p);
1974                         memmove(buf, p, strlen(p) + 1);
1975
1976                         buf2 = g_strdup_printf("Fw: %s", buf);
1977                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1978
1979                         g_free(buf);
1980                         g_free(buf2);
1981                 }
1982         } else {
1983                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1984                         _("Fw: multiple emails"));
1985         }
1986
1987         SIGNAL_BLOCK(textbuf);
1988         
1989         if (account->auto_sig)
1990                 compose_insert_sig(compose, FALSE);
1991
1992         compose_wrap_all(compose);
1993
1994         SIGNAL_UNBLOCK(textbuf);
1995         
1996         gtk_text_buffer_get_start_iter(textbuf, &iter);
1997         gtk_text_buffer_place_cursor(textbuf, &iter);
1998
1999         gtk_widget_grab_focus(compose->header_last->entry);
2000         undo_unblock(compose->undostruct);
2001         compose->modified = FALSE;
2002         compose_set_title(compose);
2003
2004         compose->updating = FALSE;
2005         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2006         SCROLL_TO_CURSOR(compose);
2007
2008         if (compose->deferred_destroy) {
2009                 compose_destroy(compose);
2010                 return NULL;
2011         }
2012
2013         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2014
2015         return compose;
2016 }
2017
2018 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2019 {
2020         GtkTextIter start = *iter;
2021         GtkTextIter end_iter;
2022         int start_pos = gtk_text_iter_get_offset(&start);
2023         gchar *str = NULL;
2024         if (!compose->account->sig_sep)
2025                 return FALSE;
2026         
2027         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2028                 start_pos+strlen(compose->account->sig_sep));
2029
2030         /* check sig separator */
2031         str = gtk_text_iter_get_text(&start, &end_iter);
2032         if (!strcmp(str, compose->account->sig_sep)) {
2033                 gchar *tmp = NULL;
2034                 /* check end of line (\n) */
2035                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2036                         start_pos+strlen(compose->account->sig_sep));
2037                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2038                         start_pos+strlen(compose->account->sig_sep)+1);
2039                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2040                 if (!strcmp(tmp,"\n")) {
2041                         g_free(str);
2042                         g_free(tmp);
2043                         return TRUE;
2044                 }
2045                 g_free(tmp);    
2046         }
2047         g_free(str);
2048
2049         return FALSE;
2050 }
2051
2052 static void compose_colorize_signature(Compose *compose)
2053 {
2054         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2055         GtkTextIter iter;
2056         GtkTextIter end_iter;
2057         gtk_text_buffer_get_start_iter(buffer, &iter);
2058         while (gtk_text_iter_forward_line(&iter))
2059                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2060                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2061                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2062                 }
2063 }
2064
2065 #define BLOCK_WRAP() {                                                  \
2066         prev_autowrap = compose->autowrap;                              \
2067         buffer = gtk_text_view_get_buffer(                              \
2068                                         GTK_TEXT_VIEW(compose->text));  \
2069         compose->autowrap = FALSE;                                      \
2070                                                                         \
2071         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2072                                 G_CALLBACK(compose_changed_cb),         \
2073                                 compose);                               \
2074         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2075                                 G_CALLBACK(text_inserted),              \
2076                                 compose);                               \
2077 }
2078 #define UNBLOCK_WRAP() {                                                \
2079         compose->autowrap = prev_autowrap;                              \
2080         if (compose->autowrap) {                                        \
2081                 gint old = compose->draft_timeout_tag;                  \
2082                 compose->draft_timeout_tag = -2;                        \
2083                 compose_wrap_all(compose);                              \
2084                 compose->draft_timeout_tag = old;                       \
2085         }                                                               \
2086                                                                         \
2087         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2088                                 G_CALLBACK(compose_changed_cb),         \
2089                                 compose);                               \
2090         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2091                                 G_CALLBACK(text_inserted),              \
2092                                 compose);                               \
2093 }
2094
2095 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2096 {
2097         Compose *compose = NULL;
2098         PrefsAccount *account = NULL;
2099         GtkTextView *textview;
2100         GtkTextBuffer *textbuf;
2101         GtkTextMark *mark;
2102         GtkTextIter iter;
2103         FILE *fp;
2104         gchar buf[BUFFSIZE];
2105         gboolean use_signing = FALSE;
2106         gboolean use_encryption = FALSE;
2107         gchar *privacy_system = NULL;
2108         int priority = PRIORITY_NORMAL;
2109         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2110         gboolean autowrap = prefs_common.autowrap;
2111         gboolean autoindent = prefs_common.auto_indent;
2112
2113         cm_return_val_if_fail(msginfo != NULL, NULL);
2114         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2115
2116         if (compose_put_existing_to_front(msginfo)) {
2117                 return NULL;
2118         }
2119
2120         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2121             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2122             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2123                 gchar queueheader_buf[BUFFSIZE];
2124                 gint id, param;
2125
2126                 /* Select Account from queue headers */
2127                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2128                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2129                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2130                         account = account_find_from_id(id);
2131                 }
2132                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2133                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2134                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2135                         account = account_find_from_id(id);
2136                 }
2137                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2138                                              sizeof(queueheader_buf), "NAID:")) {
2139                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2140                         account = account_find_from_id(id);
2141                 }
2142                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2143                                                     sizeof(queueheader_buf), "MAID:")) {
2144                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2145                         account = account_find_from_id(id);
2146                 }
2147                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2148                                                                 sizeof(queueheader_buf), "S:")) {
2149                         account = account_find_from_address(queueheader_buf, FALSE);
2150                 }
2151                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2152                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2153                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2154                         use_signing = param;
2155                         
2156                 }
2157                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2158                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2159                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2160                         use_signing = param;
2161                         
2162                 }
2163                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2164                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2165                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2166                         use_encryption = param;
2167                 }
2168                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2169                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2170                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2171                         use_encryption = param;
2172                 }
2173                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2174                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2175                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2176                         autowrap = param;
2177                 }
2178                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2179                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2180                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2181                         autoindent = param;
2182                 }
2183                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2184                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2185                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2186                 }
2187                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2188                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2189                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2190                 }
2191                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2192                                              sizeof(queueheader_buf), "X-Priority: ")) {
2193                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2194                         priority = param;
2195                 }
2196                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2197                                              sizeof(queueheader_buf), "RMID:")) {
2198                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2199                         if (tokens[0] && tokens[1] && tokens[2]) {
2200                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2201                                 if (orig_item != NULL) {
2202                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2203                                 }
2204                         }
2205                         g_strfreev(tokens);
2206                 }
2207                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2208                                              sizeof(queueheader_buf), "FMID:")) {
2209                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2210                         if (tokens[0] && tokens[1] && tokens[2]) {
2211                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2212                                 if (orig_item != NULL) {
2213                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2214                                 }
2215                         }
2216                         g_strfreev(tokens);
2217                 }
2218         } else {
2219                 account = msginfo->folder->folder->account;
2220         }
2221
2222         if (!account && prefs_common.reedit_account_autosel) {
2223                 gchar from[BUFFSIZE];
2224                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2225                         extract_address(from);
2226                         account = account_find_from_address(from, FALSE);
2227                 }
2228         }
2229         if (!account) {
2230                 account = cur_account;
2231         }
2232         cm_return_val_if_fail(account != NULL, NULL);
2233
2234         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2235
2236         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2237         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2238         compose->autowrap = autowrap;
2239         compose->replyinfo = replyinfo;
2240         compose->fwdinfo = fwdinfo;
2241
2242         compose->updating = TRUE;
2243         compose->priority = priority;
2244
2245         if (privacy_system != NULL) {
2246                 compose->privacy_system = privacy_system;
2247                 compose_use_signing(compose, use_signing);
2248                 compose_use_encryption(compose, use_encryption);
2249                 compose_update_privacy_system_menu_item(compose, FALSE);
2250         } else {
2251                 activate_privacy_system(compose, account, FALSE);
2252         }
2253
2254         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2255
2256         compose_extract_original_charset(compose);
2257
2258         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2259             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2260             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2261                 gchar queueheader_buf[BUFFSIZE];
2262
2263                 /* Set message save folder */
2264                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2265                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2266                         compose_set_save_to(compose, &queueheader_buf[4]);
2267                 }
2268                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2269                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2270                         if (active) {
2271                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2272                         }
2273                 }
2274         }
2275         
2276         if (compose_parse_header(compose, msginfo) < 0) {
2277                 compose->updating = FALSE;
2278                 compose_destroy(compose);
2279                 return NULL;
2280         }
2281         compose_reedit_set_entry(compose, msginfo);
2282
2283         textview = GTK_TEXT_VIEW(compose->text);
2284         textbuf = gtk_text_view_get_buffer(textview);
2285         compose_create_tags(textview, compose);
2286
2287         mark = gtk_text_buffer_get_insert(textbuf);
2288         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2289
2290         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2291                                         G_CALLBACK(compose_changed_cb),
2292                                         compose);
2293         
2294         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2295                 fp = procmime_get_first_encrypted_text_content(msginfo);
2296                 if (fp) {
2297                         compose_force_encryption(compose, account, TRUE, NULL);
2298                 }
2299         } else {
2300                 fp = procmime_get_first_text_content(msginfo);
2301         }
2302         if (fp == NULL) {
2303                 g_warning("Can't get text part\n");
2304         }
2305
2306         if (fp != NULL) {
2307                 gboolean prev_autowrap = compose->autowrap;
2308                 GtkTextBuffer *buffer = textbuf;
2309                 BLOCK_WRAP();
2310                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2311                         strcrchomp(buf);
2312                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2313                 }
2314                 UNBLOCK_WRAP();
2315                 fclose(fp);
2316         }
2317         
2318         compose_attach_parts(compose, msginfo);
2319
2320         compose_colorize_signature(compose);
2321
2322         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2323                                         G_CALLBACK(compose_changed_cb),
2324                                         compose);
2325
2326         gtk_widget_grab_focus(compose->text);
2327
2328         if (prefs_common.auto_exteditor) {
2329                 compose_exec_ext_editor(compose);
2330         }
2331         compose->modified = FALSE;
2332         compose_set_title(compose);
2333
2334         compose->updating = FALSE;
2335         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2336         SCROLL_TO_CURSOR(compose);
2337
2338         if (compose->deferred_destroy) {
2339                 compose_destroy(compose);
2340                 return NULL;
2341         }
2342         
2343         compose->sig_str = account_get_signature_str(compose->account);
2344         
2345         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2346
2347         return compose;
2348 }
2349
2350 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2351                                                  gboolean batch)
2352 {
2353         Compose *compose;
2354         gchar *filename;
2355         FolderItem *item;
2356
2357         cm_return_val_if_fail(msginfo != NULL, NULL);
2358
2359         if (!account)
2360                 account = account_get_reply_account(msginfo,
2361                                         prefs_common.reply_account_autosel);
2362         cm_return_val_if_fail(account != NULL, NULL);
2363
2364         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2365
2366         compose->updating = TRUE;
2367
2368         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2369         compose->replyinfo = NULL;
2370         compose->fwdinfo = NULL;
2371
2372         compose_show_first_last_header(compose, TRUE);
2373
2374         gtk_widget_grab_focus(compose->header_last->entry);
2375
2376         filename = procmsg_get_message_file(msginfo);
2377
2378         if (filename == NULL) {
2379                 compose->updating = FALSE;
2380                 compose_destroy(compose);
2381
2382                 return NULL;
2383         }
2384
2385         compose->redirect_filename = filename;
2386         
2387         /* Set save folder */
2388         item = msginfo->folder;
2389         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2390                 gchar *folderidentifier;
2391
2392                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2393                 folderidentifier = folder_item_get_identifier(item);
2394                 compose_set_save_to(compose, folderidentifier);
2395                 g_free(folderidentifier);
2396         }
2397
2398         compose_attach_parts(compose, msginfo);
2399
2400         if (msginfo->subject)
2401                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2402                                    msginfo->subject);
2403         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2404
2405         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2406                                           _("The body of the \"Redirect\" template has an error at line %d."));
2407         quote_fmt_reset_vartable();
2408         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2409
2410         compose_colorize_signature(compose);
2411
2412         
2413         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2414         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2415         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2416
2417         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2418         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2419         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2420         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2421         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2422         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2423         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2424         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2425         
2426         if (compose->toolbar->draft_btn)
2427                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2428         if (compose->toolbar->insert_btn)
2429                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2430         if (compose->toolbar->attach_btn)
2431                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2432         if (compose->toolbar->sig_btn)
2433                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2434         if (compose->toolbar->exteditor_btn)
2435                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2436         if (compose->toolbar->linewrap_current_btn)
2437                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2438         if (compose->toolbar->linewrap_all_btn)
2439                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2440
2441         compose->modified = FALSE;
2442         compose_set_title(compose);
2443         compose->updating = FALSE;
2444         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2445         SCROLL_TO_CURSOR(compose);
2446
2447         if (compose->deferred_destroy) {
2448                 compose_destroy(compose);
2449                 return NULL;
2450         }
2451         
2452         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2453
2454         return compose;
2455 }
2456
2457 GList *compose_get_compose_list(void)
2458 {
2459         return compose_list;
2460 }
2461
2462 void compose_entry_append(Compose *compose, const gchar *address,
2463                           ComposeEntryType type, ComposePrefType pref_type)
2464 {
2465         const gchar *header;
2466         gchar *cur, *begin;
2467         gboolean in_quote = FALSE;
2468         if (!address || *address == '\0') return;
2469
2470         switch (type) {
2471         case COMPOSE_CC:
2472                 header = N_("Cc:");
2473                 break;
2474         case COMPOSE_BCC:
2475                 header = N_("Bcc:");
2476                 break;
2477         case COMPOSE_REPLYTO:
2478                 header = N_("Reply-To:");
2479                 break;
2480         case COMPOSE_NEWSGROUPS:
2481                 header = N_("Newsgroups:");
2482                 break;
2483         case COMPOSE_FOLLOWUPTO:
2484                 header = N_( "Followup-To:");
2485                 break;
2486         case COMPOSE_TO:
2487         default:
2488                 header = N_("To:");
2489                 break;
2490         }
2491         header = prefs_common_translated_header_name(header);
2492         
2493         cur = begin = (gchar *)address;
2494         
2495         /* we separate the line by commas, but not if we're inside a quoted
2496          * string */
2497         while (*cur != '\0') {
2498                 if (*cur == '"') 
2499                         in_quote = !in_quote;
2500                 if (*cur == ',' && !in_quote) {
2501                         gchar *tmp = g_strdup(begin);
2502                         gchar *o_tmp = tmp;
2503                         tmp[cur-begin]='\0';
2504                         cur++;
2505                         begin = cur;
2506                         while (*tmp == ' ' || *tmp == '\t')
2507                                 tmp++;
2508                         compose_add_header_entry(compose, header, tmp, pref_type);
2509                         g_free(o_tmp);
2510                         continue;
2511                 }
2512                 cur++;
2513         }
2514         if (begin < cur) {
2515                 gchar *tmp = g_strdup(begin);
2516                 gchar *o_tmp = tmp;
2517                 tmp[cur-begin]='\0';
2518                 cur++;
2519                 begin = cur;
2520                 while (*tmp == ' ' || *tmp == '\t')
2521                         tmp++;
2522                 compose_add_header_entry(compose, header, tmp, pref_type);
2523                 g_free(o_tmp);          
2524         }
2525 }
2526
2527 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2528 {
2529         static GdkColor yellow;
2530         static GdkColor black;
2531         static gboolean yellow_initialised = FALSE;
2532         GSList *h_list;
2533         GtkEntry *entry;
2534                 
2535         if (!yellow_initialised) {
2536                 gdk_color_parse("#f5f6be", &yellow);
2537                 gdk_color_parse("#000000", &black);
2538                 yellow_initialised = gdk_colormap_alloc_color(
2539                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2540                 yellow_initialised &= gdk_colormap_alloc_color(
2541                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2542         }
2543
2544         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2545                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2546                 if (gtk_entry_get_text(entry) && 
2547                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2548                         if (yellow_initialised) {
2549                                 gtk_widget_modify_base(
2550                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2551                                         GTK_STATE_NORMAL, &yellow);
2552                                 gtk_widget_modify_text(
2553                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2554                                         GTK_STATE_NORMAL, &black);
2555                         }
2556                 }
2557         }
2558 }
2559
2560 void compose_toolbar_cb(gint action, gpointer data)
2561 {
2562         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2563         Compose *compose = (Compose*)toolbar_item->parent;
2564         
2565         cm_return_if_fail(compose != NULL);
2566
2567         switch(action) {
2568         case A_SEND:
2569                 compose_send_cb(NULL, compose);
2570                 break;
2571         case A_SENDL:
2572                 compose_send_later_cb(NULL, compose);
2573                 break;
2574         case A_DRAFT:
2575                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2576                 break;
2577         case A_INSERT:
2578                 compose_insert_file_cb(NULL, compose);
2579                 break;
2580         case A_ATTACH:
2581                 compose_attach_cb(NULL, compose);
2582                 break;
2583         case A_SIG:
2584                 compose_insert_sig(compose, FALSE);
2585                 break;
2586         case A_EXTEDITOR:
2587                 compose_ext_editor_cb(NULL, compose);
2588                 break;
2589         case A_LINEWRAP_CURRENT:
2590                 compose_beautify_paragraph(compose, NULL, TRUE);
2591                 break;
2592         case A_LINEWRAP_ALL:
2593                 compose_wrap_all_full(compose, TRUE);
2594                 break;
2595         case A_ADDRBOOK:
2596                 compose_address_cb(NULL, compose);
2597                 break;
2598 #ifdef USE_ENCHANT
2599         case A_CHECK_SPELLING:
2600                 compose_check_all(NULL, compose);
2601                 break;
2602 #endif
2603         default:
2604                 break;
2605         }
2606 }
2607
2608 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2609 {
2610         gchar *to = NULL;
2611         gchar *cc = NULL;
2612         gchar *bcc = NULL;
2613         gchar *subject = NULL;
2614         gchar *body = NULL;
2615         gchar *temp = NULL;
2616         gsize  len = 0;
2617         gchar **attach = NULL;
2618         MailField mfield = NO_FIELD_PRESENT;
2619
2620         /* get mailto parts but skip from */
2621         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2622
2623         if (to) {
2624                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2625                 mfield = TO_FIELD_PRESENT;
2626         }
2627         if (cc)
2628                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2629         if (bcc)
2630                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2631         if (subject) {
2632                 if (!g_utf8_validate (subject, -1, NULL)) {
2633                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2634                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2635                         g_free(temp);
2636                 } else {
2637                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2638                 }
2639                 mfield = SUBJECT_FIELD_PRESENT;
2640         }
2641         if (body) {
2642                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2643                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2644                 GtkTextMark *mark;
2645                 GtkTextIter iter;
2646                 gboolean prev_autowrap = compose->autowrap;
2647
2648                 compose->autowrap = FALSE;
2649
2650                 mark = gtk_text_buffer_get_insert(buffer);
2651                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2652
2653                 if (!g_utf8_validate (body, -1, NULL)) {
2654                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2655                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2656                         g_free(temp);
2657                 } else {
2658                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2659                 }
2660                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2661
2662                 compose->autowrap = prev_autowrap;
2663                 if (compose->autowrap)
2664                         compose_wrap_all(compose);
2665                 mfield = BODY_FIELD_PRESENT;
2666         }
2667
2668         if (attach) {
2669                 gint i = 0, att = 0;
2670                 gchar *warn_files = NULL;
2671                 while (attach[i] != NULL) {
2672                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2673                         if (utf8_filename) {
2674                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2675                                         gchar *tmp = g_strdup_printf("%s%s\n",
2676                                                         warn_files?warn_files:"",
2677                                                         utf8_filename);
2678                                         g_free(warn_files);
2679                                         warn_files = tmp;
2680                                         att++;
2681                                 }
2682                                 g_free(utf8_filename);
2683                         } else {
2684                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2685                         }
2686                         i++;
2687                 }
2688                 if (warn_files) {
2689                         alertpanel_notice(ngettext(
2690                         "The following file has been attached: \n%s",
2691                         "The following files have been attached: \n%s", att), warn_files);
2692                         g_free(warn_files);
2693                 }
2694         }
2695         g_free(to);
2696         g_free(cc);
2697         g_free(bcc);
2698         g_free(subject);
2699         g_free(body);
2700         g_strfreev(attach);
2701         
2702         return mfield;
2703 }
2704
2705 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2706 {
2707         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2708                                        {"Cc:",          NULL, TRUE},
2709                                        {"References:",  NULL, FALSE},
2710                                        {"Bcc:",         NULL, TRUE},
2711                                        {"Newsgroups:",  NULL, TRUE},
2712                                        {"Followup-To:", NULL, TRUE},
2713                                        {"List-Post:",   NULL, FALSE},
2714                                        {"X-Priority:",  NULL, FALSE},
2715                                        {NULL,           NULL, FALSE}};
2716
2717         enum
2718         {
2719                 H_REPLY_TO      = 0,
2720                 H_CC            = 1,
2721                 H_REFERENCES    = 2,
2722                 H_BCC           = 3,
2723                 H_NEWSGROUPS    = 4,
2724                 H_FOLLOWUP_TO   = 5,
2725                 H_LIST_POST     = 6,
2726                 H_X_PRIORITY    = 7
2727         };
2728
2729         FILE *fp;
2730
2731         cm_return_val_if_fail(msginfo != NULL, -1);
2732
2733         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2734         procheader_get_header_fields(fp, hentry);
2735         fclose(fp);
2736
2737         if (hentry[H_REPLY_TO].body != NULL) {
2738                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2739                         compose->replyto =
2740                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2741                                                    NULL);
2742                 }
2743                 g_free(hentry[H_REPLY_TO].body);
2744                 hentry[H_REPLY_TO].body = NULL;
2745         }
2746         if (hentry[H_CC].body != NULL) {
2747                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2748                 g_free(hentry[H_CC].body);
2749                 hentry[H_CC].body = NULL;
2750         }
2751         if (hentry[H_REFERENCES].body != NULL) {
2752                 if (compose->mode == COMPOSE_REEDIT)
2753                         compose->references = hentry[H_REFERENCES].body;
2754                 else {
2755                         compose->references = compose_parse_references
2756                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2757                         g_free(hentry[H_REFERENCES].body);
2758                 }
2759                 hentry[H_REFERENCES].body = NULL;
2760         }
2761         if (hentry[H_BCC].body != NULL) {
2762                 if (compose->mode == COMPOSE_REEDIT)
2763                         compose->bcc =
2764                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2765                 g_free(hentry[H_BCC].body);
2766                 hentry[H_BCC].body = NULL;
2767         }
2768         if (hentry[H_NEWSGROUPS].body != NULL) {
2769                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2770                 hentry[H_NEWSGROUPS].body = NULL;
2771         }
2772         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2773                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2774                         compose->followup_to =
2775                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2776                                                    NULL);
2777                 }
2778                 g_free(hentry[H_FOLLOWUP_TO].body);
2779                 hentry[H_FOLLOWUP_TO].body = NULL;
2780         }
2781         if (hentry[H_LIST_POST].body != NULL) {
2782                 gchar *to = NULL, *start = NULL;
2783
2784                 extract_address(hentry[H_LIST_POST].body);
2785                 if (hentry[H_LIST_POST].body[0] != '\0') {
2786                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2787                         
2788                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2789                                         NULL, &to, NULL, NULL, NULL, NULL, NULL);
2790
2791                         if (to) {
2792                                 g_free(compose->ml_post);
2793                                 compose->ml_post = to;
2794                         }
2795                 }
2796                 g_free(hentry[H_LIST_POST].body);
2797                 hentry[H_LIST_POST].body = NULL;
2798         }
2799
2800         /* CLAWS - X-Priority */
2801         if (compose->mode == COMPOSE_REEDIT)
2802                 if (hentry[H_X_PRIORITY].body != NULL) {
2803                         gint priority;
2804                         
2805                         priority = atoi(hentry[H_X_PRIORITY].body);
2806                         g_free(hentry[H_X_PRIORITY].body);
2807                         
2808                         hentry[H_X_PRIORITY].body = NULL;
2809                         
2810                         if (priority < PRIORITY_HIGHEST || 
2811                             priority > PRIORITY_LOWEST)
2812                                 priority = PRIORITY_NORMAL;
2813                         
2814                         compose->priority =  priority;
2815                 }
2816  
2817         if (compose->mode == COMPOSE_REEDIT) {
2818                 if (msginfo->inreplyto && *msginfo->inreplyto)
2819                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2820                 return 0;
2821         }
2822
2823         if (msginfo->msgid && *msginfo->msgid)
2824                 compose->inreplyto = g_strdup(msginfo->msgid);
2825
2826         if (!compose->references) {
2827                 if (msginfo->msgid && *msginfo->msgid) {
2828                         if (msginfo->inreplyto && *msginfo->inreplyto)
2829                                 compose->references =
2830                                         g_strdup_printf("<%s>\n\t<%s>",
2831                                                         msginfo->inreplyto,
2832                                                         msginfo->msgid);
2833                         else
2834                                 compose->references =
2835                                         g_strconcat("<", msginfo->msgid, ">",
2836                                                     NULL);
2837                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2838                         compose->references =
2839                                 g_strconcat("<", msginfo->inreplyto, ">",
2840                                             NULL);
2841                 }
2842         }
2843
2844         return 0;
2845 }
2846
2847 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2848 {
2849         GSList *ref_id_list, *cur;
2850         GString *new_ref;
2851         gchar *new_ref_str;
2852
2853         ref_id_list = references_list_append(NULL, ref);
2854         if (!ref_id_list) return NULL;
2855         if (msgid && *msgid)
2856                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2857
2858         for (;;) {
2859                 gint len = 0;
2860
2861                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2862                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2863                         len += strlen((gchar *)cur->data) + 5;
2864
2865                 if (len > MAX_REFERENCES_LEN) {
2866                         /* remove second message-ID */
2867                         if (ref_id_list && ref_id_list->next &&
2868                             ref_id_list->next->next) {
2869                                 g_free(ref_id_list->next->data);
2870                                 ref_id_list = g_slist_remove
2871                                         (ref_id_list, ref_id_list->next->data);
2872                         } else {
2873                                 slist_free_strings(ref_id_list);
2874                                 g_slist_free(ref_id_list);
2875                                 return NULL;
2876                         }
2877                 } else
2878                         break;
2879         }
2880
2881         new_ref = g_string_new("");
2882         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2883                 if (new_ref->len > 0)
2884                         g_string_append(new_ref, "\n\t");
2885                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2886         }
2887
2888         slist_free_strings(ref_id_list);
2889         g_slist_free(ref_id_list);
2890
2891         new_ref_str = new_ref->str;
2892         g_string_free(new_ref, FALSE);
2893
2894         return new_ref_str;
2895 }
2896
2897 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2898                                 const gchar *fmt, const gchar *qmark,
2899                                 const gchar *body, gboolean rewrap,
2900                                 gboolean need_unescape,
2901                                 const gchar *err_msg)
2902 {
2903         MsgInfo* dummyinfo = NULL;
2904         gchar *quote_str = NULL;
2905         gchar *buf;
2906         gboolean prev_autowrap;
2907         const gchar *trimmed_body = body;
2908         gint cursor_pos = -1;
2909         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2910         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2911         GtkTextIter iter;
2912         GtkTextMark *mark;
2913         
2914
2915         SIGNAL_BLOCK(buffer);
2916
2917         if (!msginfo) {
2918                 dummyinfo = compose_msginfo_new_from_compose(compose);
2919                 msginfo = dummyinfo;
2920         }
2921
2922         if (qmark != NULL) {
2923 #ifdef USE_ENCHANT
2924                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2925                                 compose->gtkaspell);
2926 #else
2927                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2928 #endif
2929                 quote_fmt_scan_string(qmark);
2930                 quote_fmt_parse();
2931
2932                 buf = quote_fmt_get_buffer();
2933                 if (buf == NULL)
2934                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2935                 else
2936                         Xstrdup_a(quote_str, buf, goto error)
2937         }
2938
2939         if (fmt && *fmt != '\0') {
2940
2941                 if (trimmed_body)
2942                         while (*trimmed_body == '\n')
2943                                 trimmed_body++;
2944
2945 #ifdef USE_ENCHANT
2946                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2947                                 compose->gtkaspell);
2948 #else
2949                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2950 #endif
2951                 if (need_unescape) {
2952                         gchar *tmp = NULL;
2953
2954                         /* decode \-escape sequences in the internal representation of the quote format */
2955                         tmp = malloc(strlen(fmt)+1);
2956                         pref_get_unescaped_pref(tmp, fmt);
2957                         quote_fmt_scan_string(tmp);
2958                         quote_fmt_parse();
2959                         g_free(tmp);
2960                 } else {
2961                         quote_fmt_scan_string(fmt);
2962                         quote_fmt_parse();
2963                 }
2964
2965                 buf = quote_fmt_get_buffer();
2966                 if (buf == NULL) {
2967                         gint line = quote_fmt_get_line();
2968                         alertpanel_error(err_msg, line);
2969                         goto error;
2970                 }
2971         } else
2972                 buf = "";
2973
2974         prev_autowrap = compose->autowrap;
2975         compose->autowrap = FALSE;
2976
2977         mark = gtk_text_buffer_get_insert(buffer);
2978         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2979         if (g_utf8_validate(buf, -1, NULL)) { 
2980                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2981         } else {
2982                 gchar *tmpout = NULL;
2983                 tmpout = conv_codeset_strdup
2984                         (buf, conv_get_locale_charset_str_no_utf8(),
2985                          CS_INTERNAL);
2986                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2987                         g_free(tmpout);
2988                         tmpout = g_malloc(strlen(buf)*2+1);
2989                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2990                 }
2991                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2992                 g_free(tmpout);
2993         }
2994
2995         cursor_pos = quote_fmt_get_cursor_pos();
2996         if (cursor_pos == -1)
2997                 cursor_pos = gtk_text_iter_get_offset(&iter);
2998         compose->set_cursor_pos = cursor_pos;
2999
3000         gtk_text_buffer_get_start_iter(buffer, &iter);
3001         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3002         gtk_text_buffer_place_cursor(buffer, &iter);
3003
3004         compose->autowrap = prev_autowrap;
3005         if (compose->autowrap && rewrap)
3006                 compose_wrap_all(compose);
3007
3008         goto ok;
3009
3010 error:
3011         buf = NULL;
3012 ok:
3013         SIGNAL_UNBLOCK(buffer);
3014
3015         procmsg_msginfo_free( dummyinfo );
3016
3017         return buf;
3018 }
3019
3020 /* if ml_post is of type addr@host and from is of type
3021  * addr-anything@host, return TRUE
3022  */
3023 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3024 {
3025         gchar *left_ml = NULL;
3026         gchar *right_ml = NULL;
3027         gchar *left_from = NULL;
3028         gchar *right_from = NULL;
3029         gboolean result = FALSE;
3030         
3031         if (!ml_post || !from)
3032                 return FALSE;
3033         
3034         left_ml = g_strdup(ml_post);
3035         if (strstr(left_ml, "@")) {
3036                 right_ml = strstr(left_ml, "@")+1;
3037                 *(strstr(left_ml, "@")) = '\0';
3038         }
3039         
3040         left_from = g_strdup(from);
3041         if (strstr(left_from, "@")) {
3042                 right_from = strstr(left_from, "@")+1;
3043                 *(strstr(left_from, "@")) = '\0';
3044         }
3045         
3046         if (left_ml && left_from && right_ml && right_from
3047         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3048         &&  !strcmp(right_from, right_ml)) {
3049                 result = TRUE;
3050         }
3051         g_free(left_ml);
3052         g_free(left_from);
3053         
3054         return result;
3055 }
3056
3057 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3058                                     gboolean to_all, gboolean to_ml,
3059                                     gboolean to_sender,
3060                                     gboolean followup_and_reply_to)
3061 {
3062         GSList *cc_list = NULL;
3063         GSList *cur;
3064         gchar *from = NULL;
3065         gchar *replyto = NULL;
3066         gchar *ac_email = NULL;
3067
3068         gboolean reply_to_ml = FALSE;
3069         gboolean default_reply_to = FALSE;
3070
3071         cm_return_if_fail(compose->account != NULL);
3072         cm_return_if_fail(msginfo != NULL);
3073
3074         reply_to_ml = to_ml && compose->ml_post;
3075
3076         default_reply_to = msginfo->folder && 
3077                 msginfo->folder->prefs->enable_default_reply_to;
3078
3079         if (compose->account->protocol != A_NNTP) {
3080                 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3081                         if (msginfo->folder->prefs->enable_default_replyto) {
3082                                 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3083                                                         COMPOSE_REPLYTO, PREF_FOLDER);
3084                         }
3085                         if (msginfo->folder->prefs->enable_default_bcc) {
3086                                 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3087                                                         COMPOSE_BCC, PREF_FOLDER);
3088                         }
3089                         if (msginfo->folder->prefs->enable_default_cc) {
3090                                 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3091                                                         COMPOSE_CC, PREF_FOLDER);
3092                         }
3093                 }
3094                 if (reply_to_ml && !default_reply_to) {
3095                         
3096                         gboolean is_subscr = is_subscription(compose->ml_post,
3097                                                              msginfo->from);
3098                         if (!is_subscr) {
3099                                 /* normal answer to ml post with a reply-to */
3100                                 compose_entry_append(compose,
3101                                            compose->ml_post,
3102                                            COMPOSE_TO, PREF_ML);
3103                                 if (compose->replyto)
3104                                         compose_entry_append(compose,
3105                                                 compose->replyto,
3106                                                 COMPOSE_CC, PREF_ML);
3107                         } else {
3108                                 /* answer to subscription confirmation */
3109                                 if (compose->replyto)
3110                                         compose_entry_append(compose,
3111                                                 compose->replyto,
3112                                                 COMPOSE_TO, PREF_ML);
3113                                 else if (msginfo->from)
3114                                         compose_entry_append(compose,
3115                                                 msginfo->from,
3116                                                 COMPOSE_TO, PREF_ML);
3117                         }
3118                 }
3119                 else if (!(to_all || to_sender) && default_reply_to) {
3120                         compose_entry_append(compose,
3121                             msginfo->folder->prefs->default_reply_to,
3122                             COMPOSE_TO, PREF_FOLDER);
3123                         compose_entry_mark_default_to(compose,
3124                                 msginfo->folder->prefs->default_reply_to);
3125                 } else {
3126                         gchar *tmp1 = NULL;
3127                         if (!msginfo->from)
3128                                 return;
3129                         Xstrdup_a(tmp1, msginfo->from, return);
3130                         extract_address(tmp1);
3131                         if (to_all || to_sender ||
3132                             !account_find_from_address(tmp1, FALSE))
3133                                 compose_entry_append(compose,
3134                                  (compose->replyto && !to_sender)
3135                                           ? compose->replyto :
3136                                           msginfo->from ? msginfo->from : "",
3137                                           COMPOSE_TO, PREF_NONE);
3138                         else if (!to_all && !to_sender) {
3139                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3140                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3141                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3142                                         if (compose->replyto) {
3143                                                 compose_entry_append(compose,
3144                                                         compose->replyto,
3145                                                         COMPOSE_TO, PREF_NONE);
3146                                         } else {
3147                                                 compose_entry_append(compose,
3148                                                           msginfo->from ? msginfo->from : "",
3149                                                           COMPOSE_TO, PREF_NONE);
3150                                         }
3151                                 } else {
3152                                         /* replying to own mail, use original recp */
3153                                         compose_entry_append(compose,