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