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