Fix for bug #4300 again. Initially committer Andrej Kacian <ticho@claws-mail.org>
[claws.git] / src / compose.c
1 /*
2  * Claws Mail -- a GTK based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2022 the Claws Mail team and Hiroyuki Yamamoto
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 #ifdef HAVE_CONFIG_H
20 #  include "config.h"
21 #include "claws-features.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 #ifdef GDK_WINDOWING_X11
35 #include <gtk/gtkx.h>
36 #endif
37
38 #include <pango/pango-break.h>
39 #include <stdio.h>
40 #include <stdlib.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <sys/types.h>
44 #include <sys/stat.h>
45 #include <unistd.h>
46 #include <time.h>
47 #if HAVE_SYS_WAIT_H
48 #  include <sys/wait.h>
49 #endif
50 #include <signal.h>
51 #include <errno.h>
52 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
53 #include <libgen.h>
54 #endif
55 #ifdef G_OS_WIN32
56 #include <windows.h>
57 #endif
58
59 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
60 #  include <wchar.h>
61 #  include <wctype.h>
62 #endif
63
64 #include "claws.h"
65 #include "main.h"
66 #include "mainwindow.h"
67 #include "compose.h"
68 #ifndef USE_ALT_ADDRBOOK
69         #include "addressbook.h"
70 #else
71         #include "addressbook-dbus.h"
72         #include "addressadd.h"
73 #endif
74 #include "folderview.h"
75 #include "procmsg.h"
76 #include "menu.h"
77 #include "stock_pixmap.h"
78 #include "send_message.h"
79 #include "imap.h"
80 #include "news.h"
81 #include "customheader.h"
82 #include "prefs_common.h"
83 #include "prefs_account.h"
84 #include "action.h"
85 #include "account.h"
86 #include "filesel.h"
87 #include "procheader.h"
88 #include "procmime.h"
89 #include "statusbar.h"
90 #include "about.h"
91 #include "quoted-printable.h"
92 #include "codeconv.h"
93 #include "utils.h"
94 #include "gtkutils.h"
95 #include "gtkshruler.h"
96 #include "socket.h"
97 #include "alertpanel.h"
98 #include "manage_window.h"
99 #include "folder.h"
100 #include "folder_item_prefs.h"
101 #include "addr_compl.h"
102 #include "quote_fmt.h"
103 #include "undo.h"
104 #include "foldersel.h"
105 #include "toolbar.h"
106 #include "inc.h"
107 #include "message_search.h"
108 #include "combobox.h"
109 #include "hooks.h"
110 #include "privacy.h"
111 #include "timing.h"
112 #include "autofaces.h"
113 #include "spell_entry.h"
114 #include "headers.h"
115 #include "file-utils.h"
116
117 #ifdef USE_LDAP
118 #include "password.h"
119 #include "ldapserver.h"
120 #endif
121
122 enum
123 {
124         COL_MIMETYPE = 0,
125         COL_SIZE     = 1,
126         COL_NAME     = 2,
127         COL_CHARSET  = 3,
128         COL_DATA     = 4,
129         COL_AUTODATA = 5,
130         N_COL_COLUMNS
131 };
132
133 #define N_ATTACH_COLS   (N_COL_COLUMNS)
134
135 typedef enum
136 {
137         COMPOSE_CALL_ADVANCED_ACTION_UNDEFINED = -1,
138         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE = 0,
139         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
140         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
141         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
142         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
143         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
144         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
145         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
146         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
147         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
148         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
149         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
150         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
151         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
152 } ComposeCallAdvancedAction;
153
154 typedef enum
155 {
156         PRIORITY_HIGHEST = 1,
157         PRIORITY_HIGH,
158         PRIORITY_NORMAL,
159         PRIORITY_LOW,
160         PRIORITY_LOWEST
161 } PriorityLevel;
162
163 typedef enum
164 {
165         COMPOSE_INSERT_SUCCESS,
166         COMPOSE_INSERT_READ_ERROR,
167         COMPOSE_INSERT_INVALID_CHARACTER,
168         COMPOSE_INSERT_NO_FILE
169 } ComposeInsertResult;
170
171 typedef enum
172 {
173         COMPOSE_WRITE_FOR_SEND,
174         COMPOSE_WRITE_FOR_STORE
175 } ComposeWriteType;
176
177 typedef enum
178 {
179         COMPOSE_QUOTE_FORCED,
180         COMPOSE_QUOTE_CHECK,
181         COMPOSE_QUOTE_SKIP
182 } ComposeQuoteMode;
183
184 typedef enum {
185     TO_FIELD_PRESENT,
186     SUBJECT_FIELD_PRESENT,
187     BODY_FIELD_PRESENT,
188     NO_FIELD_PRESENT
189 } MailField;
190
191 #define B64_LINE_SIZE           57
192 #define B64_BUFFSIZE            77
193
194 #define MAX_REFERENCES_LEN      999
195
196 #define COMPOSE_DRAFT_TIMEOUT_UNSET -1
197 #define COMPOSE_DRAFT_TIMEOUT_FORBIDDEN -2
198
199 #define COMPOSE_PRIVACY_WARNING() {                                                     \
200         alertpanel_error(_("You have opted to sign and/or encrypt this "                \
201                            "message but have not selected a privacy system.\n\n"        \
202                            "Signing and encrypting have been disabled for this "        \
203                            "message."));                                                \
204 }
205
206 #ifdef G_OS_WIN32
207 #define INVALID_PID INVALID_HANDLE_VALUE
208 #else
209 #define INVALID_PID -1
210 #endif
211
212 static GdkRGBA default_header_bgcolor =
213         {0, 0, 0, 1};
214
215 static GdkRGBA default_header_color =
216         {0, 0, 0, 1};
217
218 static GList *compose_list = NULL;
219 static GSList *extra_headers = NULL;
220
221 static Compose *compose_generic_new                     (PrefsAccount   *account,
222                                                  const gchar    *to,
223                                                  FolderItem     *item,
224                                                  GList          *attach_files,
225                                                  GList          *listAddress );
226
227 static Compose *compose_create                  (PrefsAccount   *account,
228                                                  FolderItem              *item,
229                                                  ComposeMode     mode,
230                                                  gboolean batch);
231
232 static void compose_entry_indicate      (Compose          *compose,
233                                          const gchar      *address);
234 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
235                                          ComposeQuoteMode        quote_mode,
236                                          gboolean        to_all,
237                                          gboolean        to_sender,
238                                          const gchar    *body);
239 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
240                                          GSList         *msginfo_list);
241 static Compose *compose_reply                   (MsgInfo        *msginfo,
242                                          ComposeQuoteMode        quote_mode,
243                                          gboolean        to_all,
244                                          gboolean        to_ml,
245                                          gboolean        to_sender,
246                                          const gchar    *body);
247 static Compose *compose_reply_mode              (ComposeMode     mode, 
248                                          GSList         *msginfo_list, 
249                                          gchar          *body);
250 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
251 static void compose_update_privacy_systems_menu(Compose *compose);
252
253 static GtkWidget *compose_account_option_menu_create
254                                                 (Compose        *compose);
255 static void compose_set_out_encoding            (Compose        *compose);
256 static void compose_set_template_menu           (Compose        *compose);
257 static void compose_destroy                     (Compose        *compose);
258
259 static MailField compose_entries_set            (Compose        *compose,
260                                                  const gchar    *mailto,
261                                                  ComposeEntryType to_type);
262 static gint compose_parse_header                (Compose        *compose,
263                                                  MsgInfo        *msginfo);
264 static gint compose_parse_manual_headers        (Compose        *compose,
265                                                  MsgInfo        *msginfo,
266                                                  HeaderEntry    *entries);
267 static gchar *compose_parse_references          (const gchar    *ref,
268                                                  const gchar    *msgid);
269
270 static gchar *compose_quote_fmt                 (Compose        *compose,
271                                                  MsgInfo        *msginfo,
272                                                  const gchar    *fmt,
273                                                  const gchar    *qmark,
274                                                  const gchar    *body,
275                                                  gboolean        rewrap,
276                                                  gboolean        need_unescape,
277                                                  const gchar *err_msg);
278
279 static void compose_reply_set_entry             (Compose        *compose,
280                                                  MsgInfo        *msginfo,
281                                                  gboolean        to_all,
282                                                  gboolean        to_ml,
283                                                  gboolean        to_sender,
284                                                  gboolean
285                                                  followup_and_reply_to);
286 static void compose_reedit_set_entry            (Compose        *compose,
287                                                  MsgInfo        *msginfo);
288
289 static void compose_insert_sig                  (Compose        *compose,
290                                                  gboolean        replace);
291 static ComposeInsertResult compose_insert_file  (Compose        *compose,
292                                                  const gchar    *file);
293
294 static gboolean compose_attach_append           (Compose        *compose,
295                                                  const gchar    *file,
296                                                  const gchar    *type,
297                                                  const gchar    *content_type,
298                                                  const gchar    *charset);
299 static void compose_attach_parts                (Compose        *compose,
300                                                  MsgInfo        *msginfo);
301
302 static gboolean compose_beautify_paragraph      (Compose        *compose,
303                                                  GtkTextIter    *par_iter,
304                                                  gboolean        force);
305 static void compose_wrap_all                    (Compose        *compose);
306 static void compose_wrap_all_full               (Compose        *compose,
307                                                  gboolean        autowrap);
308
309 static void compose_set_title                   (Compose        *compose);
310 static void compose_select_account              (Compose        *compose,
311                                                  PrefsAccount   *account,
312                                                  gboolean        init);
313
314 static PrefsAccount *compose_current_mail_account(void);
315 /* static gint compose_send                     (Compose        *compose); */
316 static gboolean compose_check_for_valid_recipient
317                                                 (Compose        *compose);
318 static gboolean compose_check_entries           (Compose        *compose,
319                                                  gboolean       check_everything);
320 static gint compose_write_to_file               (Compose        *compose,
321                                                  FILE           *fp,
322                                                  gint            action,
323                                                  gboolean        attach_parts);
324 static gint compose_write_body_to_file          (Compose        *compose,
325                                                  const gchar    *file);
326 static gint compose_remove_reedit_target        (Compose        *compose,
327                                                  gboolean        force);
328 static void compose_remove_draft                        (Compose        *compose);
329 static ComposeQueueResult compose_queue_sub                     (Compose        *compose,
330                                                  gint           *msgnum,
331                                                  FolderItem     **item,
332                                                  gchar          **msgpath,
333                                                  gboolean       perform_checks,
334                                                  gboolean       remove_reedit_target);
335 static int compose_add_attachments              (Compose        *compose,
336                                                  MimeInfo       *parent);
337 static gchar *compose_get_header                (Compose        *compose);
338 static gchar *compose_get_manual_headers_info   (Compose        *compose);
339
340 static void compose_convert_header              (Compose        *compose,
341                                                  gchar          *dest,
342                                                  gint            len,
343                                                  gchar          *src,
344                                                  gint            header_len,
345                                                  gboolean        addr_field);
346
347 static void compose_attach_info_free            (AttachInfo     *ainfo);
348 static void compose_attach_remove_selected      (GtkAction      *action,
349                                                  gpointer        data);
350
351 static void compose_template_apply              (Compose        *compose,
352                                                  Template       *tmpl,
353                                                  gboolean        replace);
354 static void compose_attach_property             (GtkAction      *action,
355                                                  gpointer        data);
356 static void compose_attach_property_create      (gboolean       *cancelled);
357 static void attach_property_ok                  (GtkWidget      *widget,
358                                                  gboolean       *cancelled);
359 static void attach_property_cancel              (GtkWidget      *widget,
360                                                  gboolean       *cancelled);
361 static gint attach_property_delete_event        (GtkWidget      *widget,
362                                                  GdkEventAny    *event,
363                                                  gboolean       *cancelled);
364 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
365                                                  GdkEventKey    *event,
366                                                  gboolean       *cancelled);
367
368 static void compose_exec_ext_editor             (Compose        *compose);
369 static gboolean compose_ext_editor_kill         (Compose        *compose);
370 static void compose_ext_editor_closed_cb        (GPid            pid,
371                                                  gint            exit_status,
372                                                  gpointer        data);
373 static void compose_set_ext_editor_sensitive    (Compose        *compose,
374                                                  gboolean        sensitive);
375 static gboolean compose_get_ext_editor_cmd_valid();
376 static gboolean compose_get_ext_editor_uses_socket();
377 #ifndef G_OS_WIN32
378 static gboolean compose_ext_editor_plug_removed_cb
379                                                 (GtkSocket      *socket,
380                                                  Compose        *compose);
381 #endif /* G_OS_WIN32 */
382
383 static void compose_undo_state_changed          (UndoMain       *undostruct,
384                                                  gint            undo_state,
385                                                  gint            redo_state,
386                                                  gpointer        data);
387
388 static void compose_create_header_entry (Compose *compose);
389 static void compose_add_header_entry    (Compose *compose, const gchar *header,
390                                          gchar *text, ComposePrefType pref_type);
391 static void compose_remove_header_entries(Compose *compose);
392
393 static void compose_update_priority_menu_item(Compose * compose);
394 #if USE_ENCHANT
395 static void compose_spell_menu_changed  (void *data);
396 static void compose_dict_changed        (void *data);
397 #endif
398 static void compose_add_field_list      ( Compose *compose,
399                                           GList *listAddress );
400
401 /* callback functions */
402
403 static void compose_notebook_size_alloc (GtkNotebook *notebook,
404                                          GtkAllocation *allocation,
405                                          GtkPaned *paned);
406 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
407                                          GtkAllocation  *allocation,
408                                          GtkSHRuler     *shruler);
409 static void account_activated           (GtkComboBox *optmenu,
410                                          gpointer        data);
411 static void attach_selected             (GtkTreeView    *tree_view, 
412                                          GtkTreePath    *tree_path,
413                                          GtkTreeViewColumn *column, 
414                                          Compose *compose);
415 static gboolean attach_button_pressed   (GtkWidget      *widget,
416                                          GdkEventButton *event,
417                                          gpointer        data);
418 static gboolean attach_key_pressed      (GtkWidget      *widget,
419                                          GdkEventKey    *event,
420                                          gpointer        data);
421 static void compose_send_cb             (GtkAction      *action, gpointer data);
422 static void compose_send_later_cb       (GtkAction      *action, gpointer data);
423
424 static void compose_save_cb             (GtkAction      *action,
425                                          gpointer        data);
426
427 static void compose_attach_cb           (GtkAction      *action,
428                                          gpointer        data);
429 static void compose_insert_file_cb      (GtkAction      *action,
430                                          gpointer        data);
431 static void compose_insert_sig_cb       (GtkAction      *action,
432                                          gpointer        data);
433 static void compose_replace_sig_cb      (GtkAction      *action,
434                                          gpointer        data);
435
436 static void compose_close_cb            (GtkAction      *action,
437                                          gpointer        data);
438 static void compose_print_cb            (GtkAction      *action,
439                                          gpointer        data);
440
441 static void compose_set_encoding_cb     (GtkAction      *action, GtkRadioAction *current, gpointer data);
442
443 static void compose_address_cb          (GtkAction      *action,
444                                          gpointer        data);
445 static void about_show_cb               (GtkAction      *action,
446                                          gpointer        data);
447 static void compose_template_activate_cb(GtkWidget      *widget,
448                                          gpointer        data);
449
450 static void compose_ext_editor_cb       (GtkAction      *action,
451                                          gpointer        data);
452
453 static gint compose_delete_cb           (GtkWidget      *widget,
454                                          GdkEventAny    *event,
455                                          gpointer        data);
456
457 static void compose_undo_cb             (GtkAction      *action,
458                                          gpointer        data);
459 static void compose_redo_cb             (GtkAction      *action,
460                                          gpointer        data);
461 static void compose_cut_cb              (GtkAction      *action,
462                                          gpointer        data);
463 static void compose_copy_cb             (GtkAction      *action,
464                                          gpointer        data);
465 static void compose_paste_cb            (GtkAction      *action,
466                                          gpointer        data);
467 static void compose_paste_as_quote_cb   (GtkAction      *action,
468                                          gpointer        data);
469 static void compose_paste_no_wrap_cb    (GtkAction      *action,
470                                          gpointer        data);
471 static void compose_paste_wrap_cb       (GtkAction      *action,
472                                          gpointer        data);
473 static void compose_allsel_cb           (GtkAction      *action,
474                                          gpointer        data);
475
476 static void compose_advanced_action_cb  (GtkAction      *action,
477                                          gpointer        data);
478
479 static void compose_grab_focus_cb       (GtkWidget      *widget,
480                                          Compose        *compose);
481
482 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
483                                          Compose        *compose);
484
485 static void compose_wrap_cb             (GtkAction      *action,
486                                          gpointer        data);
487 static void compose_wrap_all_cb         (GtkAction      *action,
488                                          gpointer        data);
489 static void compose_find_cb             (GtkAction      *action,
490                                          gpointer        data);
491 static void compose_toggle_autowrap_cb  (GtkToggleAction *action,
492                                          gpointer        data);
493 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
494                                          gpointer        data);
495
496 static void compose_toggle_ruler_cb     (GtkToggleAction *action,
497                                          gpointer        data);
498 static void compose_toggle_sign_cb      (GtkToggleAction *action,
499                                          gpointer        data);
500 static void compose_toggle_encrypt_cb   (GtkToggleAction *action,
501                                          gpointer        data);
502 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
503 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
504 static void compose_activate_privacy_system     (Compose *compose, 
505                                          PrefsAccount *account,
506                                          gboolean warn);
507 static void compose_apply_folder_privacy_settings(Compose *compose, FolderItem *folder_item);
508 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
509                                          gpointer        data);
510 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
511                                          gpointer        data);
512 static void compose_set_priority_cb     (GtkAction *action, GtkRadioAction *current, gpointer data);
513 static void compose_reply_change_mode   (Compose *compose, ComposeMode action);
514 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
515
516 static void compose_attach_drag_received_cb (GtkWidget          *widget,
517                                              GdkDragContext     *drag_context,
518                                              gint                x,
519                                              gint                y,
520                                              GtkSelectionData   *data,
521                                              guint               info,
522                                              guint               time,
523                                              gpointer            user_data);
524 static void compose_insert_drag_received_cb (GtkWidget          *widget,
525                                              GdkDragContext     *drag_context,
526                                              gint                x,
527                                              gint                y,
528                                              GtkSelectionData   *data,
529                                              guint               info,
530                                              guint               time,
531                                              gpointer            user_data);
532 static void compose_header_drag_received_cb (GtkWidget          *widget,
533                                              GdkDragContext     *drag_context,
534                                              gint                x,
535                                              gint                y,
536                                              GtkSelectionData   *data,
537                                              guint               info,
538                                              guint               time,
539                                              gpointer            user_data);
540
541 static gboolean compose_drag_drop           (GtkWidget *widget,
542                                              GdkDragContext *drag_context,
543                                              gint x, gint y,
544                                              guint time, gpointer user_data);
545 static gboolean completion_set_focus_to_subject
546                                         (GtkWidget    *widget,
547                                          GdkEventKey  *event,
548                                          Compose      *user_data);
549
550 static void text_inserted               (GtkTextBuffer  *buffer,
551                                          GtkTextIter    *iter,
552                                          const gchar    *text,
553                                          gint            len,
554                                          Compose        *compose);
555 static Compose *compose_generic_reply(MsgInfo *msginfo,
556                                   ComposeQuoteMode quote_mode,
557                                   gboolean to_all,
558                                   gboolean to_ml,
559                                   gboolean to_sender,
560                                   gboolean followup_and_reply_to,
561                                   const gchar *body);
562
563 static void compose_headerentry_changed_cb         (GtkWidget          *entry,
564                                             ComposeHeaderEntry *headerentry);
565 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
566                                             GdkEventKey        *event,
567                                             ComposeHeaderEntry *headerentry);
568 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
569                                         ComposeHeaderEntry *headerentry);
570
571 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
572
573 static void compose_allow_user_actions (Compose *compose, gboolean allow);
574
575 static void compose_nothing_cb             (GtkAction *action, gpointer data)
576 {
577
578 }
579
580 #if USE_ENCHANT
581 static void compose_check_all              (GtkAction *action, gpointer data);
582 static void compose_highlight_all          (GtkAction *action, gpointer data);
583 static void compose_check_backwards        (GtkAction *action, gpointer data);
584 static void compose_check_forwards_go      (GtkAction *action, gpointer data);
585 #endif
586
587 static PrefsAccount *compose_find_account       (MsgInfo *msginfo);
588
589 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
590
591 #ifdef USE_ENCHANT
592 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
593                                                 FolderItem *folder_item);
594 #endif
595 static void compose_attach_update_label(Compose *compose);
596 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
597                                      gboolean respect_default_to);
598 static void compose_subject_entry_activated(GtkWidget *widget, gpointer data);
599 static void from_name_activate_cb(GtkWidget *widget, gpointer data);
600
601 static GtkActionEntry compose_popup_entries[] =
602 {
603         {"Compose",            NULL, "Compose", NULL, NULL, NULL },
604         {"Compose/Add",        NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
605         {"Compose/Remove",     NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
606         {"Compose/---",        NULL, "---", NULL, NULL, NULL },
607         {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
608 };
609
610 static GtkActionEntry compose_entries[] =
611 {
612         {"Menu",                          NULL, "Menu", NULL, NULL, NULL },
613 /* menus */
614         {"Message",                       NULL, N_("_Message"), NULL, NULL, NULL },
615         {"Edit",                          NULL, N_("_Edit"), NULL, NULL, NULL },
616 #if USE_ENCHANT
617         {"Spelling",                      NULL, N_("_Spelling"), NULL, NULL, NULL },
618 #endif
619         {"Options",                       NULL, N_("_Options"), NULL, NULL, NULL },
620         {"Tools",                         NULL, N_("_Tools"), NULL, NULL, NULL },
621         {"Help",                          NULL, N_("_Help"), NULL, NULL, NULL },
622 /* Message menu */
623         {"Message/Send",                  NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
624         {"Message/SendLater",             NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
625         {"Message/---",                   NULL, "---", NULL, NULL, NULL },
626
627         {"Message/AttachFile",            NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
628         {"Message/InsertFile",            NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
629         {"Message/InsertSig",             NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
630         {"Message/ReplaceSig",            NULL, N_("_Replace signature"), NULL, NULL, G_CALLBACK(compose_replace_sig_cb) },
631         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
632         {"Message/Save",                  NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
633         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
634         {"Message/Print",                 NULL, N_("_Print"), NULL, NULL, G_CALLBACK(compose_print_cb) },
635         /* {"Message/---",                NULL, "---", NULL, NULL, NULL }, */
636         {"Message/Close",                 NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
637
638 /* Edit menu */
639         {"Edit/Undo",                     NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
640         {"Edit/Redo",                     NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
641         {"Edit/---",                      NULL, "---", NULL, NULL, NULL },
642
643         {"Edit/Cut",                      NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
644         {"Edit/Copy",                     NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
645         {"Edit/Paste",                    NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
646
647         {"Edit/SpecialPaste",             NULL, N_("_Special paste"), NULL, NULL, NULL },
648         {"Edit/SpecialPaste/AsQuotation", NULL, N_("As _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
649         {"Edit/SpecialPaste/Wrapped",     NULL, N_("_Wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
650         {"Edit/SpecialPaste/Unwrapped",   NULL, N_("_Unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
651
652         {"Edit/SelectAll",                NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
653
654         {"Edit/Advanced",                 NULL, N_("A_dvanced"), NULL, NULL, NULL },
655         {"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*/
656         {"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*/
657         {"Edit/Advanced/BackWord",        NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
658         {"Edit/Advanced/ForwWord",        NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
659         {"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*/
660         {"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*/
661         {"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*/
662         {"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*/
663         {"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*/
664         {"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*/
665         {"Edit/Advanced/DelBackWord",     NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
666         {"Edit/Advanced/DelForwWord",     NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
667         {"Edit/Advanced/DelLine",         NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
668         {"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*/
669
670         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
671         {"Edit/Find",                     NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
672
673         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
674         {"Edit/WrapPara",                 NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
675         {"Edit/WrapAllLines",             NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
676         /* {"Edit/---",                   NULL, "---", NULL, NULL, NULL }, */
677         {"Edit/ExtEditor",                NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
678 #if USE_ENCHANT
679 /* Spelling menu */
680         {"Spelling/CheckAllSel",          NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
681         {"Spelling/HighlightAll",         NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
682         {"Spelling/CheckBackwards",       NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
683         {"Spelling/ForwardNext",          NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
684
685         {"Spelling/---",                  NULL, "---", NULL, NULL, NULL },
686         {"Spelling/Options",              NULL, N_("_Options"), NULL, NULL, NULL },
687 #endif
688
689 /* Options menu */
690         {"Options/ReplyMode",                 NULL, N_("Reply _mode"), NULL, NULL, NULL },
691         {"Options/---",                       NULL, "---", NULL, NULL, NULL },
692         {"Options/PrivacySystem",             NULL, N_("Privacy _System"), NULL, NULL, NULL },
693         {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
694
695         /* {"Options/---",                NULL, "---", NULL, NULL, NULL }, */
696         {"Options/Priority",              NULL, N_("_Priority"), NULL, NULL, NULL },
697
698         {"Options/Encoding",              NULL, N_("Character _encoding"), NULL, NULL, NULL },
699         {"Options/Encoding/---",          NULL, "---", NULL, NULL, NULL },
700 #define ENC_ACTION(cs_char,c_char,string) \
701         {"Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
702
703         {"Options/Encoding/Western",      NULL, N_("Western European"), NULL, NULL, NULL },
704         {"Options/Encoding/Baltic",       NULL, N_("Baltic"), NULL, NULL, NULL },
705         {"Options/Encoding/Hebrew",       NULL, N_("Hebrew"), NULL, NULL, NULL },
706         {"Options/Encoding/Arabic",       NULL, N_("Arabic"), NULL, NULL, NULL },
707         {"Options/Encoding/Cyrillic",     NULL, N_("Cyrillic"), NULL, NULL, NULL },
708         {"Options/Encoding/Japanese",     NULL, N_("Japanese"), NULL, NULL, NULL },
709         {"Options/Encoding/Chinese",      NULL, N_("Chinese"), NULL, NULL, NULL },
710         {"Options/Encoding/Korean",       NULL, N_("Korean"), NULL, NULL, NULL },
711         {"Options/Encoding/Thai",         NULL, N_("Thai"), NULL, NULL, NULL },
712
713 /* Tools menu */
714         {"Tools/AddressBook",             NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) }, 
715
716         {"Tools/Template",                NULL, N_("_Template"), NULL, NULL, NULL },
717         {"Tools/Template/PlaceHolder",    NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
718         {"Tools/Actions",                 NULL, N_("Actio_ns"), NULL, NULL, NULL },
719         {"Tools/Actions/PlaceHolder",     NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
720
721 /* Help menu */
722         {"Help/About",                    NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) }, 
723 };
724
725 static GtkToggleActionEntry compose_toggle_entries[] =
726 {
727         {"Edit/AutoWrap",            NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb), FALSE }, /* Toggle */
728         {"Edit/AutoIndent",          NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb), FALSE }, /* Toggle */
729         {"Options/Sign",             NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb), FALSE }, /* Toggle */
730         {"Options/Encrypt",          NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb), FALSE }, /* Toggle */
731         {"Options/RequestRetRcpt",   NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb), FALSE }, /* Toggle */
732         {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb), FALSE }, /* Toggle */
733         {"Tools/ShowRuler",          NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb), FALSE }, /* Toggle */
734 };
735
736 static GtkRadioActionEntry compose_radio_rm_entries[] =
737 {
738         {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
739         {"Options/ReplyMode/All",    NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
740         {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
741         {"Options/ReplyMode/List",   NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
742 };
743
744 static GtkRadioActionEntry compose_radio_prio_entries[] =
745 {
746         {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
747         {"Options/Priority/High",    NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
748         {"Options/Priority/Normal",  NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
749         {"Options/Priority/Low",     NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
750         {"Options/Priority/Lowest",  NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
751 };
752
753 static GtkRadioActionEntry compose_radio_enc_entries[] =
754 {
755         ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
756         ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
757         ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
758         ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
759         ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
760         ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
761         ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
762         ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
763         ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
764         ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
765         ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
766         ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
767         ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
768         ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
769         ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
770         ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
771         ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
772         ENC_ACTION("Cyrillic/"CS_MACCYR, C_MACCYR, "_Mac-Cyrillic"), /* RADIO compose_set_encoding_cb */
773         ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
774         ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
775         ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
776         ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
777         ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
778         ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
779         ENC_ACTION("Chinese/"CS_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
780         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
781         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
782         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
783         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
784         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
785         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
786         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
787         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
788 };
789
790 static GtkTargetEntry compose_mime_types[] =
791 {
792         {"text/uri-list", 0, 0},
793         {"UTF8_STRING", 0, 0},
794         {"text/plain", 0, 0}
795 };
796
797 static gboolean compose_put_existing_to_front(MsgInfo *info)
798 {
799         const GList *compose_list = compose_get_compose_list();
800         const GList *elem = NULL;
801         
802         if (compose_list) {
803                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
804                      elem = elem->next) {
805                         Compose *c = (Compose*)elem->data;
806
807                         if (!c->targetinfo || !c->targetinfo->msgid ||
808                             !info->msgid)
809                                 continue;
810
811                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
812                                 gtkut_window_popup(c->window);
813                                 return TRUE;
814                         }
815                 }
816         }
817         return FALSE;
818 }
819
820 static GdkRGBA quote_color1 =
821         {0, 0, 0, 1};
822 static GdkRGBA quote_color2 =
823         {0, 0, 0, 1};
824 static GdkRGBA quote_color3 =
825         {0, 0, 0, 1};
826
827 static GdkRGBA quote_bgcolor1 =
828         {0, 0, 0, 1};
829 static GdkRGBA quote_bgcolor2 =
830         {0, 0, 0, 1};
831 static GdkRGBA quote_bgcolor3 =
832         {0, 0, 0, 1};
833
834 static GdkRGBA signature_color =
835         {0.5, 0.5, 0.5, 1};
836
837 static GdkRGBA uri_color =
838         {0, 0, 0, 1};
839
840 static void compose_create_tags(GtkTextView *text, Compose *compose)
841 {
842         GtkTextBuffer *buffer;
843         GdkRGBA black = { 0, 0, 0, 1 };
844
845         buffer = gtk_text_view_get_buffer(text);
846
847         if (prefs_common.enable_color) {
848                 /* grab the quote colors, converting from an int to a GdkColor */
849                 quote_color1 = prefs_common.color[COL_QUOTE_LEVEL1];
850                 quote_color2 = prefs_common.color[COL_QUOTE_LEVEL2];
851                 quote_color3 = prefs_common.color[COL_QUOTE_LEVEL3];
852                 quote_bgcolor1 = prefs_common.color[COL_QUOTE_LEVEL1_BG];
853                 quote_bgcolor2 = prefs_common.color[COL_QUOTE_LEVEL2_BG];
854                 quote_bgcolor3 = prefs_common.color[COL_QUOTE_LEVEL3_BG];
855                 signature_color = prefs_common.color[COL_SIGNATURE];
856                 uri_color = prefs_common.color[COL_URI];
857         } else {
858                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
859                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
860         }
861
862         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
863                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
864                                            "foreground-rgba", &quote_color1,
865                                            "paragraph-background-rgba", &quote_bgcolor1,
866                                            NULL);
867                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
868                                            "foreground-rgba", &quote_color2,
869                                            "paragraph-background-rgba", &quote_bgcolor2,
870                                            NULL);
871                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
872                                            "foreground-rgba", &quote_color3,
873                                            "paragraph-background-rgba", &quote_bgcolor3,
874                                            NULL);
875         } else {
876                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
877                                            "foreground-rgba", &quote_color1,
878                                            NULL);
879                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
880                                            "foreground-rgba", &quote_color2,
881                                            NULL);
882                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
883                                            "foreground-rgba", &quote_color3,
884                                            NULL);
885         }
886         
887         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
888                                    "foreground-rgba", &signature_color,
889                                    NULL);
890         
891         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
892                                         "foreground-rgba", &uri_color,
893                                          NULL);
894         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
895         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
896 }
897
898 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
899                      GList *attach_files)
900 {
901         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
902 }
903
904 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
905 {
906         return compose_generic_new(account, mailto, item, NULL, NULL);
907 }
908
909 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
910 {
911         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
912 }
913
914 #define SCROLL_TO_CURSOR(compose) {                             \
915         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
916                 gtk_text_view_get_buffer(                       \
917                         GTK_TEXT_VIEW(compose->text)));         \
918         gtk_text_view_scroll_mark_onscreen(                     \
919                 GTK_TEXT_VIEW(compose->text),                   \
920                 cmark);                                         \
921 }
922
923 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
924 {
925         GtkEditable *entry;
926         if (folderidentifier) {
927                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
928                 prefs_common.compose_save_to_history = add_history(
929                                 prefs_common.compose_save_to_history, folderidentifier);
930                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
931                                 prefs_common.compose_save_to_history);
932         }
933
934         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
935         if (folderidentifier)
936                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
937         else
938                 gtk_entry_set_text(GTK_ENTRY(entry), "");
939 }
940
941 static gchar *compose_get_save_to(Compose *compose)
942 {
943         GtkEditable *entry;
944         gchar *result = NULL;
945         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
946         result = gtk_editable_get_chars(entry, 0, -1);
947         
948         if (result) {
949                 combobox_unset_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo));
950                 prefs_common.compose_save_to_history = add_history(
951                                 prefs_common.compose_save_to_history, result);
952                 combobox_set_popdown_strings(GTK_COMBO_BOX_TEXT(compose->savemsg_combo),
953                                 prefs_common.compose_save_to_history);
954         }
955         return result;
956 }
957
958 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
959                              GList *attach_files, GList *listAddress )
960 {
961         Compose *compose;
962         GtkTextView *textview;
963         GtkTextBuffer *textbuf;
964         GtkTextIter iter;
965         const gchar *subject_format = NULL;
966         const gchar *body_format = NULL;
967         gchar *mailto_from = NULL;
968         PrefsAccount *mailto_account = NULL;
969         MsgInfo* dummyinfo = NULL;
970         gint cursor_pos = -1;
971         MailField mfield = NO_FIELD_PRESENT;
972         gchar* buf;
973         GtkTextMark *mark;
974
975         /* check if mailto defines a from */
976         if (mailto && *mailto != '\0') {
977                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
978                 /* mailto defines a from, check if we can get account prefs from it,
979                    if not, the account prefs will be guessed using other ways, but we'll keep
980                    the from anyway */
981                 if (mailto_from) {
982                         mailto_account = account_find_from_address(mailto_from, TRUE);
983                         if (mailto_account == NULL) {
984                                 gchar *tmp_from;
985                                 Xstrdup_a(tmp_from, mailto_from, return NULL);
986                                 extract_address(tmp_from);
987                                 mailto_account = account_find_from_address(tmp_from, TRUE);
988                         }
989                 }
990                 if (mailto_account)
991                         account = mailto_account;
992         }
993
994         /* if no account prefs set from mailto, set if from folder prefs (if any) */
995         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
996                 account = account_find_from_id(item->prefs->default_account);
997
998         /* if no account prefs set, fallback to the current one */
999         if (!account) account = cur_account;
1000         cm_return_val_if_fail(account != NULL, NULL);
1001
1002         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1003         compose_apply_folder_privacy_settings(compose, item);
1004
1005         if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1006             (account->default_encrypt || account->default_sign))
1007                 COMPOSE_PRIVACY_WARNING();
1008
1009         /* override from name if mailto asked for it */
1010         if (mailto_from) {
1011                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1012                 g_free(mailto_from);
1013         } else
1014                 /* override from name according to folder properties */
1015                 if (item && item->prefs &&
1016                         item->prefs->compose_with_format &&
1017                         item->prefs->compose_override_from_format &&
1018                         *item->prefs->compose_override_from_format != '\0') {
1019
1020                         gchar *tmp = NULL;
1021                         gchar *buf = NULL;
1022
1023                         dummyinfo = compose_msginfo_new_from_compose(compose);
1024
1025                         /* decode \-escape sequences in the internal representation of the quote format */
1026                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1027                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1028
1029 #ifdef USE_ENCHANT
1030                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1031                                         compose->gtkaspell);
1032 #else
1033                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1034 #endif
1035                         quote_fmt_scan_string(tmp);
1036                         quote_fmt_parse();
1037
1038                         buf = quote_fmt_get_buffer();
1039                         if (buf == NULL)
1040                                 alertpanel_error(_("New message From format error."));
1041                         else
1042                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1043                         quote_fmt_reset_vartable();
1044                         quote_fmtlex_destroy();
1045
1046                         g_free(tmp);
1047                 }
1048
1049         compose->replyinfo = NULL;
1050         compose->fwdinfo   = NULL;
1051
1052         textview = GTK_TEXT_VIEW(compose->text);
1053         textbuf = gtk_text_view_get_buffer(textview);
1054         compose_create_tags(textview, compose);
1055
1056         undo_block(compose->undostruct);
1057 #ifdef USE_ENCHANT
1058         compose_set_dictionaries_from_folder_prefs(compose, item);
1059 #endif
1060
1061         if (account->auto_sig)
1062                 compose_insert_sig(compose, FALSE);
1063         gtk_text_buffer_get_start_iter(textbuf, &iter);
1064         gtk_text_buffer_place_cursor(textbuf, &iter);
1065
1066         if (account->protocol != A_NNTP) {
1067                 if (mailto && *mailto != '\0') {
1068                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1069
1070                 } else {
1071                         compose_set_folder_prefs(compose, item, TRUE);
1072                 }
1073                 if (item && item->ret_rcpt) {
1074                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1075                 }
1076         } else {
1077                 if (mailto && *mailto != '\0') {
1078                         if (!strchr(mailto, '@'))
1079                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1080                         else
1081                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1082                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1083                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1084                         mfield = TO_FIELD_PRESENT;
1085                 }
1086                 /*
1087                  * CLAWS: just don't allow return receipt request, even if the user
1088                  * may want to send an email. simple but foolproof.
1089                  */
1090                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1091         }
1092         compose_add_field_list( compose, listAddress );
1093
1094         if (item && item->prefs && item->prefs->compose_with_format) {
1095                 subject_format = item->prefs->compose_subject_format;
1096                 body_format = item->prefs->compose_body_format;
1097         } else if (account->compose_with_format) {
1098                 subject_format = account->compose_subject_format;
1099                 body_format = account->compose_body_format;
1100         } else if (prefs_common.compose_with_format) {
1101                 subject_format = prefs_common.compose_subject_format;
1102                 body_format = prefs_common.compose_body_format;
1103         }
1104
1105         if (subject_format || body_format) {
1106
1107                 if ( subject_format
1108                          && *subject_format != '\0' )
1109                 {
1110                         gchar *subject = NULL;
1111                         gchar *tmp = NULL;
1112                         gchar *buf = NULL;
1113
1114                         if (!dummyinfo)
1115                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1116
1117                         /* decode \-escape sequences in the internal representation of the quote format */
1118                         tmp = g_malloc(strlen(subject_format)+1);
1119                         pref_get_unescaped_pref(tmp, subject_format);
1120
1121                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1122 #ifdef USE_ENCHANT
1123                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1124                                         compose->gtkaspell);
1125 #else
1126                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1127 #endif
1128                         quote_fmt_scan_string(tmp);
1129                         quote_fmt_parse();
1130
1131                         buf = quote_fmt_get_buffer();
1132                         if (buf == NULL)
1133                                 alertpanel_error(_("New message subject format error."));
1134                         else
1135                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1136                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1137                         quote_fmt_reset_vartable();
1138                         quote_fmtlex_destroy();
1139
1140                         g_free(subject);
1141                         g_free(tmp);
1142                         mfield = SUBJECT_FIELD_PRESENT;
1143                 }
1144
1145                 if ( body_format
1146                          && *body_format != '\0' )
1147                 {
1148                         GtkTextView *text;
1149                         GtkTextBuffer *buffer;
1150                         GtkTextIter start, end;
1151                         gchar *tmp = NULL;
1152
1153                         if (!dummyinfo)
1154                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1155
1156                         text = GTK_TEXT_VIEW(compose->text);
1157                         buffer = gtk_text_view_get_buffer(text);
1158                         gtk_text_buffer_get_start_iter(buffer, &start);
1159                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1160                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1161
1162                         compose_quote_fmt(compose, dummyinfo,
1163                                           body_format,
1164                                           NULL, tmp, FALSE, TRUE,
1165                                                   _("The body of the \"New message\" template has an error at line %d."));
1166                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1167                         quote_fmt_reset_vartable();
1168
1169                         g_free(tmp);
1170 #ifdef USE_ENCHANT
1171                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1172                                 gtkaspell_highlight_all(compose->gtkaspell);
1173 #endif
1174                         mfield = BODY_FIELD_PRESENT;
1175                 }
1176
1177         }
1178         procmsg_msginfo_free( &dummyinfo );
1179
1180         if (attach_files) {
1181                 GList *curr;
1182                 AttachInfo *ainfo;
1183
1184                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1185                         ainfo = (AttachInfo *) curr->data;
1186                         if (ainfo->insert)
1187                                 compose_insert_file(compose, ainfo->file);
1188                         else
1189                                 compose_attach_append(compose, ainfo->file, ainfo->file,
1190                                         ainfo->content_type, ainfo->charset);
1191                 }
1192         }
1193
1194         compose_show_first_last_header(compose, TRUE);
1195
1196         /* Set save folder */
1197         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1198                 gchar *folderidentifier;
1199
1200                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1201                 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1202                 folderidentifier = folder_item_get_identifier(item);
1203                 compose_set_save_to(compose, folderidentifier);
1204                 g_free(folderidentifier);
1205         }
1206
1207         /* Place cursor according to provided input (mfield) */
1208         switch (mfield) { 
1209                 case NO_FIELD_PRESENT:
1210                         if (compose->header_last)
1211                                 gtk_widget_grab_focus(compose->header_last->entry);
1212                         break;
1213                 case TO_FIELD_PRESENT:
1214                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1215                         if (buf) {
1216                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1217                                 g_free(buf);
1218                         }
1219                         gtk_widget_grab_focus(compose->subject_entry);
1220                         break;
1221                 case SUBJECT_FIELD_PRESENT:
1222                         textview = GTK_TEXT_VIEW(compose->text);
1223                         if (!textview)
1224                                 break;
1225                         textbuf = gtk_text_view_get_buffer(textview);
1226                         if (!textbuf)
1227                                 break;
1228                         mark = gtk_text_buffer_get_insert(textbuf);
1229                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1230                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1231                     /* 
1232                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1233                      * only defers where it comes to the variable body
1234                      * is not null. If no body is present compose->text
1235                      * will be null in which case you cannot place the
1236                      * cursor inside the component so. An empty component
1237                      * is therefore created before placing the cursor
1238                      */
1239                 case BODY_FIELD_PRESENT:
1240                         cursor_pos = quote_fmt_get_cursor_pos();
1241                         if (cursor_pos == -1)
1242                                 gtk_widget_grab_focus(compose->header_last->entry);
1243                         else
1244                                 gtk_widget_grab_focus(compose->text);
1245                         break;
1246         }
1247
1248         undo_unblock(compose->undostruct);
1249
1250         if (prefs_common.auto_exteditor)
1251                 compose_exec_ext_editor(compose);
1252
1253         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET;
1254
1255         SCROLL_TO_CURSOR(compose);
1256
1257         compose->modified = FALSE;
1258         compose_set_title(compose);
1259
1260         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1261
1262         return compose;
1263 }
1264
1265 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1266                 gboolean override_pref, const gchar *system)
1267 {
1268         const gchar *privacy = NULL;
1269
1270         cm_return_if_fail(compose != NULL);
1271         cm_return_if_fail(account != NULL);
1272
1273         if (privacy_system_can_encrypt(compose->privacy_system) == FALSE ||
1274             (override_pref == FALSE && account->default_encrypt_reply == FALSE))
1275                 return;
1276
1277         if (account->default_privacy_system && strlen(account->default_privacy_system))
1278                 privacy = account->default_privacy_system;
1279         else if (system)
1280                 privacy = system;
1281         else {
1282                 GSList *privacy_avail = privacy_get_system_ids();
1283                 if (privacy_avail && g_slist_length(privacy_avail)) {
1284                         privacy = (gchar *)(privacy_avail->data);
1285                 }
1286                 g_slist_free_full(privacy_avail, g_free);
1287         }
1288         if (privacy != NULL) {
1289                 if (system) {
1290                         g_free(compose->privacy_system);
1291                         compose->privacy_system = NULL;
1292                         g_free(compose->encdata);
1293                         compose->encdata = NULL;
1294                 }
1295                 if (compose->privacy_system == NULL)
1296                         compose->privacy_system = g_strdup(privacy);
1297                 else if (*(compose->privacy_system) == '\0') {
1298                         g_free(compose->privacy_system);
1299                         g_free(compose->encdata);
1300                         compose->encdata = NULL;
1301                         compose->privacy_system = g_strdup(privacy);
1302                 }
1303                 compose_update_privacy_system_menu_item(compose, FALSE);
1304                 compose_use_encryption(compose, TRUE);
1305         }
1306 }       
1307
1308 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1309 {
1310         const gchar *privacy = NULL;
1311         if (privacy_system_can_sign(compose->privacy_system) == FALSE)
1312                 return;
1313
1314         if (account->default_privacy_system && strlen(account->default_privacy_system))
1315                 privacy = account->default_privacy_system;
1316         else if (system)
1317                 privacy = system;
1318         else {
1319                 GSList *privacy_avail = privacy_get_system_ids();
1320                 if (privacy_avail && g_slist_length(privacy_avail)) {
1321                         privacy = (gchar *)(privacy_avail->data);
1322                 }
1323         }
1324
1325         if (privacy != NULL) {
1326                 if (system) {
1327                         g_free(compose->privacy_system);
1328                         compose->privacy_system = NULL;
1329                         g_free(compose->encdata);
1330                         compose->encdata = NULL;
1331                 }
1332                 if (compose->privacy_system == NULL)
1333                         compose->privacy_system = g_strdup(privacy);
1334                 compose_update_privacy_system_menu_item(compose, FALSE);
1335                 compose_use_signing(compose, TRUE);
1336         }
1337 }       
1338
1339 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1340 {
1341         MsgInfo *msginfo;
1342         guint list_len;
1343         Compose *compose = NULL;
1344         
1345         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1346
1347         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1348         cm_return_val_if_fail(msginfo != NULL, NULL);
1349
1350         list_len = g_slist_length(msginfo_list);
1351
1352         switch (mode) {
1353         case COMPOSE_REPLY:
1354         case COMPOSE_REPLY_TO_ADDRESS:
1355                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1356                               FALSE, prefs_common.default_reply_list, FALSE, body);
1357                 break;
1358         case COMPOSE_REPLY_WITH_QUOTE:
1359                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1360                         FALSE, prefs_common.default_reply_list, FALSE, body);
1361                 break;
1362         case COMPOSE_REPLY_WITHOUT_QUOTE:
1363                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1364                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1365                 break;
1366         case COMPOSE_REPLY_TO_SENDER:
1367                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1368                               FALSE, FALSE, TRUE, body);
1369                 break;
1370         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1371                 compose = compose_followup_and_reply_to(msginfo,
1372                                               COMPOSE_QUOTE_CHECK,
1373                                               FALSE, FALSE, body);
1374                 break;
1375         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1376                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1377                         FALSE, FALSE, TRUE, body);
1378                 break;
1379         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1380                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1381                         FALSE, FALSE, TRUE, NULL);
1382                 break;
1383         case COMPOSE_REPLY_TO_ALL:
1384                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1385                         TRUE, FALSE, FALSE, body);
1386                 break;
1387         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1388                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1389                         TRUE, FALSE, FALSE, body);
1390                 break;
1391         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1392                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1393                         TRUE, FALSE, FALSE, NULL);
1394                 break;
1395         case COMPOSE_REPLY_TO_LIST:
1396                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1397                         FALSE, TRUE, FALSE, body);
1398                 break;
1399         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1400                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1401                         FALSE, TRUE, FALSE, body);
1402                 break;
1403         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1404                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1405                         FALSE, TRUE, FALSE, NULL);
1406                 break;
1407         case COMPOSE_FORWARD:
1408                 if (prefs_common.forward_as_attachment) {
1409                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1410                         return compose;
1411                 } else {
1412                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1413                         return compose;
1414                 }
1415                 break;
1416         case COMPOSE_FORWARD_INLINE:
1417                 /* check if we reply to more than one Message */
1418                 if (list_len == 1) {
1419                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1420                         break;
1421                 } 
1422                 /* more messages FALL THROUGH */
1423         case COMPOSE_FORWARD_AS_ATTACH:
1424                 compose = compose_forward_multiple(NULL, msginfo_list);
1425                 break;
1426         case COMPOSE_REDIRECT:
1427                 compose = compose_redirect(NULL, msginfo, FALSE);
1428                 break;
1429         default:
1430                 g_warning("compose_reply_mode(): invalid Compose Mode: %d", mode);
1431         }
1432         
1433         if (compose == NULL) {
1434                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1435                 return NULL;
1436         }
1437
1438         compose->rmode = mode;
1439         switch (compose->rmode) {
1440         case COMPOSE_REPLY:
1441         case COMPOSE_REPLY_WITH_QUOTE:
1442         case COMPOSE_REPLY_WITHOUT_QUOTE:
1443         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1444                 debug_print("reply mode Normal\n");
1445                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1446                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1447                 break;
1448         case COMPOSE_REPLY_TO_SENDER:
1449         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1450         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1451                 debug_print("reply mode Sender\n");
1452                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1453                 break;
1454         case COMPOSE_REPLY_TO_ALL:
1455         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1456         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1457                 debug_print("reply mode All\n");
1458                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1459                 break;
1460         case COMPOSE_REPLY_TO_LIST:
1461         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1462         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1463                 debug_print("reply mode List\n");
1464                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1465                 break;
1466         case COMPOSE_REPLY_TO_ADDRESS:
1467                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1468                 break;
1469         default:
1470                 break;
1471         }
1472         return compose;
1473 }
1474
1475 static Compose *compose_reply(MsgInfo *msginfo,
1476                                    ComposeQuoteMode quote_mode,
1477                                    gboolean to_all,
1478                                    gboolean to_ml,
1479                                    gboolean to_sender, 
1480                                    const gchar *body)
1481 {
1482         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1483                               to_sender, FALSE, body);
1484 }
1485
1486 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1487                                    ComposeQuoteMode quote_mode,
1488                                    gboolean to_all,
1489                                    gboolean to_sender,
1490                                    const gchar *body)
1491 {
1492         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1493                               to_sender, TRUE, body);
1494 }
1495
1496 static void compose_extract_original_charset(Compose *compose)
1497 {
1498         MsgInfo *info = NULL;
1499         if (compose->replyinfo) {
1500                 info = compose->replyinfo;
1501         } else if (compose->fwdinfo) {
1502                 info = compose->fwdinfo;
1503         } else if (compose->targetinfo) {
1504                 info = compose->targetinfo;
1505         }
1506         if (info) {
1507                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1508                 MimeInfo *partinfo = mimeinfo;
1509                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1510                         partinfo = procmime_mimeinfo_next(partinfo);
1511                 if (partinfo) {
1512                         compose->orig_charset = 
1513                                 g_strdup(procmime_mimeinfo_get_parameter(
1514                                                 partinfo, "charset"));
1515                 }
1516                 procmime_mimeinfo_free_all(&mimeinfo);
1517         }
1518 }
1519
1520 #define SIGNAL_BLOCK(buffer) {                                  \
1521         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1522                                 G_CALLBACK(compose_changed_cb), \
1523                                 compose);                       \
1524         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1525                                 G_CALLBACK(text_inserted),      \
1526                                 compose);                       \
1527 }
1528
1529 #define SIGNAL_UNBLOCK(buffer) {                                \
1530         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1531                                 G_CALLBACK(compose_changed_cb), \
1532                                 compose);                       \
1533         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1534                                 G_CALLBACK(text_inserted),      \
1535                                 compose);                       \
1536 }
1537
1538 static Compose *compose_generic_reply(MsgInfo *msginfo,
1539                                   ComposeQuoteMode quote_mode,
1540                                   gboolean to_all, gboolean to_ml,
1541                                   gboolean to_sender,
1542                                   gboolean followup_and_reply_to,
1543                                   const gchar *body)
1544 {
1545         Compose *compose;
1546         PrefsAccount *account = NULL;
1547         GtkTextView *textview;
1548         GtkTextBuffer *textbuf;
1549         gboolean quote = FALSE;
1550         const gchar *qmark = NULL;
1551         const gchar *body_fmt = NULL;
1552         gchar *s_system = NULL;
1553         START_TIMING("");
1554         cm_return_val_if_fail(msginfo != NULL, NULL);
1555         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1556
1557         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1558
1559         cm_return_val_if_fail(account != NULL, NULL);
1560
1561         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1562         compose_apply_folder_privacy_settings(compose, msginfo->folder);
1563
1564         compose->updating = TRUE;
1565
1566         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1567         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1568
1569         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1570         if (!compose->replyinfo)
1571                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1572
1573         compose_extract_original_charset(compose);
1574         
1575         if (msginfo->folder && msginfo->folder->ret_rcpt)
1576                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1577
1578         /* Set save folder */
1579         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1580                 gchar *folderidentifier;
1581
1582                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1583                 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1584                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1585                 compose_set_save_to(compose, folderidentifier);
1586                 g_free(folderidentifier);
1587         }
1588
1589         if (compose_parse_header(compose, msginfo) < 0) {
1590                 compose->updating = FALSE;
1591                 compose_destroy(compose);
1592                 return NULL;
1593         }
1594
1595         /* override from name according to folder properties */
1596         if (msginfo->folder && msginfo->folder->prefs &&
1597                 msginfo->folder->prefs->reply_with_format &&
1598                 msginfo->folder->prefs->reply_override_from_format &&
1599                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1600
1601                 gchar *tmp = NULL;
1602                 gchar *buf = NULL;
1603
1604                 /* decode \-escape sequences in the internal representation of the quote format */
1605                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1606                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1607
1608 #ifdef USE_ENCHANT
1609                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1610                                 compose->gtkaspell);
1611 #else
1612                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1613 #endif
1614                 quote_fmt_scan_string(tmp);
1615                 quote_fmt_parse();
1616
1617                 buf = quote_fmt_get_buffer();
1618                 if (buf == NULL)
1619                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1620                 else
1621                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1622                 quote_fmt_reset_vartable();
1623                 quote_fmtlex_destroy();
1624
1625                 g_free(tmp);
1626         }
1627
1628         textview = (GTK_TEXT_VIEW(compose->text));
1629         textbuf = gtk_text_view_get_buffer(textview);
1630         compose_create_tags(textview, compose);
1631
1632         undo_block(compose->undostruct);
1633 #ifdef USE_ENCHANT
1634         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1635         gtkaspell_block_check(compose->gtkaspell);
1636 #endif
1637
1638         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1639                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1640                 /* use the reply format of folder (if enabled), or the account's one
1641                    (if enabled) or fallback to the global reply format, which is always
1642                    enabled (even if empty), and use the relevant quotemark */
1643                 quote = TRUE;
1644                 if (msginfo->folder && msginfo->folder->prefs &&
1645                                 msginfo->folder->prefs->reply_with_format) {
1646                         qmark = msginfo->folder->prefs->reply_quotemark;
1647                         body_fmt = msginfo->folder->prefs->reply_body_format;
1648
1649                 } else if (account->reply_with_format) {
1650                         qmark = account->reply_quotemark;
1651                         body_fmt = account->reply_body_format;
1652
1653                 } else {
1654                         qmark = prefs_common.quotemark;
1655                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1656                                 body_fmt = gettext(prefs_common.quotefmt);
1657                         else
1658                                 body_fmt = "";
1659                 }
1660         }
1661
1662         if (quote) {
1663                 /* empty quotemark is not allowed */
1664                 if (qmark == NULL || *qmark == '\0')
1665                         qmark = "> ";
1666                 compose_quote_fmt(compose, compose->replyinfo,
1667                                   body_fmt, qmark, body, FALSE, TRUE,
1668                                           _("The body of the \"Reply\" template has an error at line %d."));
1669                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1670                 quote_fmt_reset_vartable();
1671         }
1672
1673         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1674                 compose_force_encryption(compose, account, FALSE, s_system);
1675         }
1676
1677         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1678         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1679                 compose_force_signing(compose, account, s_system);
1680         }
1681         g_free(s_system);
1682
1683         if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1684             ((account->default_encrypt || account->default_sign) ||
1685              (account->default_encrypt_reply && MSG_IS_ENCRYPTED(compose->replyinfo->flags)) ||
1686              (account->default_sign_reply && MSG_IS_SIGNED(compose->replyinfo->flags))))
1687                 COMPOSE_PRIVACY_WARNING();
1688
1689         SIGNAL_BLOCK(textbuf);
1690         
1691         if (account->auto_sig)
1692                 compose_insert_sig(compose, FALSE);
1693
1694         compose_wrap_all(compose);
1695
1696 #ifdef USE_ENCHANT
1697         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1698                 gtkaspell_highlight_all(compose->gtkaspell);
1699         gtkaspell_unblock_check(compose->gtkaspell);
1700 #endif
1701         SIGNAL_UNBLOCK(textbuf);
1702         
1703         gtk_widget_grab_focus(compose->text);
1704
1705         undo_unblock(compose->undostruct);
1706
1707         if (prefs_common.auto_exteditor)
1708                 compose_exec_ext_editor(compose);
1709                 
1710         compose->modified = FALSE;
1711         compose_set_title(compose);
1712
1713         compose->updating = FALSE;
1714         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1715         SCROLL_TO_CURSOR(compose);
1716         
1717         if (compose->deferred_destroy) {
1718                 compose_destroy(compose);
1719                 return NULL;
1720         }
1721         END_TIMING();
1722
1723         return compose;
1724 }
1725
1726 #define INSERT_FW_HEADER(var, hdr) \
1727 if (msginfo->var && *msginfo->var) { \
1728         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1729         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1730         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1731 }
1732
1733 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1734                          gboolean as_attach, const gchar *body,
1735                          gboolean no_extedit,
1736                          gboolean batch)
1737 {
1738         Compose *compose;
1739         GtkTextView *textview;
1740         GtkTextBuffer *textbuf;
1741         gint cursor_pos = -1;
1742         ComposeMode mode;
1743
1744         cm_return_val_if_fail(msginfo != NULL, NULL);
1745         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1746
1747         if (!account && !(account = compose_find_account(msginfo)))
1748                 account = cur_account;
1749
1750         if (!prefs_common.forward_as_attachment)
1751                 mode = COMPOSE_FORWARD_INLINE;
1752         else
1753                 mode = COMPOSE_FORWARD;
1754         compose = compose_create(account, msginfo->folder, mode, batch);
1755         compose_apply_folder_privacy_settings(compose, msginfo->folder);
1756
1757         compose->updating = TRUE;
1758         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1759         if (!compose->fwdinfo)
1760                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1761
1762         compose_extract_original_charset(compose);
1763
1764         if (msginfo->subject && *msginfo->subject) {
1765                 gchar *buf, *buf2, *p;
1766
1767                 buf = p = g_strdup(msginfo->subject);
1768                 p += subject_get_prefix_length(p);
1769                 memmove(buf, p, strlen(p) + 1);
1770
1771                 buf2 = g_strdup_printf("Fw: %s", buf);
1772                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1773                 
1774                 g_free(buf);
1775                 g_free(buf2);
1776         }
1777
1778         /* override from name according to folder properties */
1779         if (msginfo->folder && msginfo->folder->prefs &&
1780                 msginfo->folder->prefs->forward_with_format &&
1781                 msginfo->folder->prefs->forward_override_from_format &&
1782                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1783
1784                 gchar *tmp = NULL;
1785                 gchar *buf = NULL;
1786                 MsgInfo *full_msginfo = NULL;
1787
1788                 if (!as_attach)
1789                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1790                 if (!full_msginfo)
1791                         full_msginfo = procmsg_msginfo_copy(msginfo);
1792
1793                 /* decode \-escape sequences in the internal representation of the quote format */
1794                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1795                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1796
1797 #ifdef USE_ENCHANT
1798                 gtkaspell_block_check(compose->gtkaspell);
1799                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1800                                 compose->gtkaspell);
1801 #else
1802                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1803 #endif
1804                 quote_fmt_scan_string(tmp);
1805                 quote_fmt_parse();
1806
1807                 buf = quote_fmt_get_buffer();
1808                 if (buf == NULL)
1809                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1810                 else
1811                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1812                 quote_fmt_reset_vartable();
1813                 quote_fmtlex_destroy();
1814
1815                 g_free(tmp);
1816                 procmsg_msginfo_free(&full_msginfo);
1817         }
1818
1819         textview = GTK_TEXT_VIEW(compose->text);
1820         textbuf = gtk_text_view_get_buffer(textview);
1821         compose_create_tags(textview, compose);
1822         
1823         undo_block(compose->undostruct);
1824         if (as_attach) {
1825                 gchar *msgfile;
1826
1827                 msgfile = procmsg_get_message_file(msginfo);
1828                 if (!is_file_exist(msgfile))
1829                         g_warning("%s: file does not exist", msgfile);
1830                 else
1831                         compose_attach_append(compose, msgfile, msgfile,
1832                                               "message/rfc822", NULL);
1833
1834                 g_free(msgfile);
1835         } else {
1836                 const gchar *qmark = NULL;
1837                 const gchar *body_fmt = NULL;
1838                 MsgInfo *full_msginfo;
1839
1840                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1841                 if (!full_msginfo)
1842                         full_msginfo = procmsg_msginfo_copy(msginfo);
1843
1844                 /* use the forward format of folder (if enabled), or the account's one
1845                    (if enabled) or fallback to the global forward format, which is always
1846                    enabled (even if empty), and use the relevant quotemark */
1847                 if (msginfo->folder && msginfo->folder->prefs &&
1848                                 msginfo->folder->prefs->forward_with_format) {
1849                         qmark = msginfo->folder->prefs->forward_quotemark;
1850                         body_fmt = msginfo->folder->prefs->forward_body_format;
1851
1852                 } else if (account->forward_with_format) {
1853                         qmark = account->forward_quotemark;
1854                         body_fmt = account->forward_body_format;
1855
1856                 } else {
1857                         qmark = prefs_common.fw_quotemark;
1858                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1859                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1860                         else
1861                                 body_fmt = "";
1862                 }
1863
1864                 /* empty quotemark is not allowed */
1865                 if (qmark == NULL || *qmark == '\0')
1866                         qmark = "> ";
1867
1868                 compose_quote_fmt(compose, full_msginfo,
1869                                   body_fmt, qmark, body, FALSE, TRUE,
1870                                           _("The body of the \"Forward\" template has an error at line %d."));
1871                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1872                 quote_fmt_reset_vartable();
1873                 compose_attach_parts(compose, msginfo);
1874
1875                 procmsg_msginfo_free(&full_msginfo);
1876         }
1877
1878         SIGNAL_BLOCK(textbuf);
1879
1880         if (account->auto_sig)
1881                 compose_insert_sig(compose, FALSE);
1882
1883         compose_wrap_all(compose);
1884
1885 #ifdef USE_ENCHANT
1886         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1887                 gtkaspell_highlight_all(compose->gtkaspell);
1888         gtkaspell_unblock_check(compose->gtkaspell);
1889 #endif
1890         SIGNAL_UNBLOCK(textbuf);
1891         
1892         if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1893             (account->default_encrypt || account->default_sign))
1894                 COMPOSE_PRIVACY_WARNING();
1895
1896         cursor_pos = quote_fmt_get_cursor_pos();
1897         if (cursor_pos == -1)
1898                 gtk_widget_grab_focus(compose->header_last->entry);
1899         else
1900                 gtk_widget_grab_focus(compose->text);
1901
1902         if (!no_extedit && prefs_common.auto_exteditor)
1903                 compose_exec_ext_editor(compose);
1904         
1905         /*save folder*/
1906         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1907                 gchar *folderidentifier;
1908
1909                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1910                 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
1911                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1912                 compose_set_save_to(compose, folderidentifier);
1913                 g_free(folderidentifier);
1914         }
1915
1916         undo_unblock(compose->undostruct);
1917         
1918         compose->modified = FALSE;
1919         compose_set_title(compose);
1920
1921         compose->updating = FALSE;
1922         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
1923         SCROLL_TO_CURSOR(compose);
1924
1925         if (compose->deferred_destroy) {
1926                 compose_destroy(compose);
1927                 return NULL;
1928         }
1929
1930         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1931
1932         return compose;
1933 }
1934
1935 #undef INSERT_FW_HEADER
1936
1937 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1938 {
1939         Compose *compose;
1940         GtkTextView *textview;
1941         GtkTextBuffer *textbuf;
1942         GtkTextIter iter;
1943         GSList *msginfo;
1944         gchar *msgfile;
1945         gboolean single_mail = TRUE;
1946         
1947         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1948
1949         if (g_slist_length(msginfo_list) > 1)
1950                 single_mail = FALSE;
1951
1952         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1953                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1954                         return NULL;
1955
1956         /* guess account from first selected message */
1957         if (!account && 
1958             !(account = compose_find_account(msginfo_list->data)))
1959                 account = cur_account;
1960
1961         cm_return_val_if_fail(account != NULL, NULL);
1962
1963         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1964                 if (msginfo->data) {
1965                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1966                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1967                 }
1968         }
1969
1970         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1971                 g_warning("no msginfo_list");
1972                 return NULL;
1973         }
1974
1975         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1976         compose_apply_folder_privacy_settings(compose, ((MsgInfo *)msginfo_list->data)->folder);
1977         if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
1978             (account->default_encrypt || account->default_sign))
1979                 COMPOSE_PRIVACY_WARNING();
1980
1981         compose->updating = TRUE;
1982
1983         /* override from name according to folder properties */
1984         if (msginfo_list->data) {
1985                 MsgInfo *msginfo = msginfo_list->data;
1986
1987                 if (msginfo->folder && msginfo->folder->prefs &&
1988                         msginfo->folder->prefs->forward_with_format &&
1989                         msginfo->folder->prefs->forward_override_from_format &&
1990                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1991
1992                         gchar *tmp = NULL;
1993                         gchar *buf = NULL;
1994
1995                         /* decode \-escape sequences in the internal representation of the quote format */
1996                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1997                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1998
1999 #ifdef USE_ENCHANT
2000                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2001                                         compose->gtkaspell);
2002 #else
2003                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2004 #endif
2005                         quote_fmt_scan_string(tmp);
2006                         quote_fmt_parse();
2007
2008                         buf = quote_fmt_get_buffer();
2009                         if (buf == NULL)
2010                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
2011                         else
2012                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
2013                         quote_fmt_reset_vartable();
2014                         quote_fmtlex_destroy();
2015
2016                         g_free(tmp);
2017                 }
2018         }
2019
2020         textview = GTK_TEXT_VIEW(compose->text);
2021         textbuf = gtk_text_view_get_buffer(textview);
2022         compose_create_tags(textview, compose);
2023         
2024         undo_block(compose->undostruct);
2025         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
2026                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
2027
2028                 if (!is_file_exist(msgfile))
2029                         g_warning("%s: file does not exist", msgfile);
2030                 else
2031                         compose_attach_append(compose, msgfile, msgfile,
2032                                 "message/rfc822", NULL);
2033                 g_free(msgfile);
2034         }
2035         
2036         if (single_mail) {
2037                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
2038                 if (info->subject && *info->subject) {
2039                         gchar *buf, *buf2, *p;
2040
2041                         buf = p = g_strdup(info->subject);
2042                         p += subject_get_prefix_length(p);
2043                         memmove(buf, p, strlen(p) + 1);
2044
2045                         buf2 = g_strdup_printf("Fw: %s", buf);
2046                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2047
2048                         g_free(buf);
2049                         g_free(buf2);
2050                 }
2051         } else {
2052                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2053                         _("Fw: multiple emails"));
2054         }
2055
2056         SIGNAL_BLOCK(textbuf);
2057         
2058         if (account->auto_sig)
2059                 compose_insert_sig(compose, FALSE);
2060
2061         compose_wrap_all(compose);
2062
2063         SIGNAL_UNBLOCK(textbuf);
2064         
2065         gtk_text_buffer_get_start_iter(textbuf, &iter);
2066         gtk_text_buffer_place_cursor(textbuf, &iter);
2067
2068         if (prefs_common.auto_exteditor)
2069                 compose_exec_ext_editor(compose);
2070
2071         gtk_widget_grab_focus(compose->header_last->entry);
2072         undo_unblock(compose->undostruct);
2073         compose->modified = FALSE;
2074         compose_set_title(compose);
2075
2076         compose->updating = FALSE;
2077         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2078         SCROLL_TO_CURSOR(compose);
2079
2080         if (compose->deferred_destroy) {
2081                 compose_destroy(compose);
2082                 return NULL;
2083         }
2084
2085         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2086
2087         return compose;
2088 }
2089
2090 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2091 {
2092         GtkTextIter start = *iter;
2093         GtkTextIter end_iter;
2094         int start_pos = gtk_text_iter_get_offset(&start);
2095         gchar *str = NULL;
2096         if (!compose->account->sig_sep)
2097                 return FALSE;
2098         
2099         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2100                 start_pos+strlen(compose->account->sig_sep));
2101
2102         /* check sig separator */
2103         str = gtk_text_iter_get_text(&start, &end_iter);
2104         if (!strcmp(str, compose->account->sig_sep)) {
2105                 gchar *tmp = NULL;
2106                 /* check end of line (\n) */
2107                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2108                         start_pos+strlen(compose->account->sig_sep));
2109                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2110                         start_pos+strlen(compose->account->sig_sep)+1);
2111                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2112                 if (!strcmp(tmp,"\n")) {
2113                         g_free(str);
2114                         g_free(tmp);
2115                         return TRUE;
2116                 }
2117                 g_free(tmp);    
2118         }
2119         g_free(str);
2120
2121         return FALSE;
2122 }
2123
2124 static gboolean compose_update_folder_hook(gpointer source, gpointer data)
2125 {
2126         FolderUpdateData *hookdata = (FolderUpdateData *)source;
2127         Compose *compose = (Compose *)data;
2128         FolderItem *old_item = NULL;
2129         FolderItem *new_item = NULL;
2130         gchar *old_id, *new_id;
2131
2132         if (!(hookdata->update_flags & FOLDER_REMOVE_FOLDERITEM)
2133          && !(hookdata->update_flags & FOLDER_MOVE_FOLDERITEM))
2134                 return FALSE;
2135
2136         old_item = hookdata->item;
2137         new_item = hookdata->item2;
2138
2139         old_id = folder_item_get_identifier(old_item);
2140         new_id = new_item ? folder_item_get_identifier(new_item) : g_strdup("NULL");
2141
2142         if (compose->targetinfo && compose->targetinfo->folder == old_item) {
2143                 debug_print("updating targetinfo folder: %s -> %s\n", old_id, new_id);
2144                 compose->targetinfo->folder = new_item;
2145         }
2146
2147         if (compose->replyinfo && compose->replyinfo->folder == old_item) {
2148                 debug_print("updating replyinfo folder: %s -> %s\n", old_id, new_id);
2149                 compose->replyinfo->folder = new_item;
2150         }
2151
2152         if (compose->fwdinfo && compose->fwdinfo->folder == old_item) {
2153                 debug_print("updating fwdinfo folder: %s -> %s\n", old_id, new_id);
2154                 compose->fwdinfo->folder = new_item;
2155         }
2156
2157         g_free(old_id);
2158         g_free(new_id);
2159         return FALSE;
2160 }
2161
2162 static void compose_colorize_signature(Compose *compose)
2163 {
2164         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2165         GtkTextIter iter;
2166         GtkTextIter end_iter;
2167         gtk_text_buffer_get_start_iter(buffer, &iter);
2168         while (gtk_text_iter_forward_line(&iter))
2169                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2170                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2171                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2172                 }
2173 }
2174
2175 #define BLOCK_WRAP() {                                                  \
2176         prev_autowrap = compose->autowrap;                              \
2177         buffer = gtk_text_view_get_buffer(                              \
2178                                         GTK_TEXT_VIEW(compose->text));  \
2179         compose->autowrap = FALSE;                                      \
2180                                                                         \
2181         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2182                                 G_CALLBACK(compose_changed_cb),         \
2183                                 compose);                               \
2184         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2185                                 G_CALLBACK(text_inserted),              \
2186                                 compose);                               \
2187 }
2188 #define UNBLOCK_WRAP() {                                                        \
2189         compose->autowrap = prev_autowrap;                                      \
2190         if (compose->autowrap) {                                                \
2191                 gint old = compose->draft_timeout_tag;                          \
2192                 compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_FORBIDDEN;   \
2193                 compose_wrap_all(compose);                                      \
2194                 compose->draft_timeout_tag = old;                               \
2195         }                                                                       \
2196                                                                                 \
2197         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2198                                 G_CALLBACK(compose_changed_cb),                 \
2199                                 compose);                                       \
2200         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),                     \
2201                                 G_CALLBACK(text_inserted),                      \
2202                                 compose);                                       \
2203 }
2204
2205 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2206 {
2207         Compose *compose = NULL;
2208         PrefsAccount *account = NULL;
2209         GtkTextView *textview;
2210         GtkTextBuffer *textbuf;
2211         GtkTextMark *mark;
2212         GtkTextIter iter;
2213         FILE *fp;
2214         gboolean use_signing = FALSE;
2215         gboolean use_encryption = FALSE;
2216         gchar *privacy_system = NULL;
2217         int priority = PRIORITY_NORMAL;
2218         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2219         gboolean autowrap = prefs_common.autowrap;
2220         gboolean autoindent = prefs_common.auto_indent;
2221         HeaderEntry *manual_headers = NULL;
2222
2223         cm_return_val_if_fail(msginfo != NULL, NULL);
2224         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2225
2226         if (compose_put_existing_to_front(msginfo)) {
2227                 return NULL;
2228         }
2229
2230         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2231             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2232             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2233                 gchar *queueheader_buf = NULL;
2234                 gint id, param;
2235
2236                 /* Select Account from queue headers */
2237                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2238                                                                                         "X-Claws-Account-Id:")) {
2239                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2240                         account = account_find_from_id(id);
2241                         g_free(queueheader_buf);
2242                 }
2243                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2244                                                                                         "X-Sylpheed-Account-Id:")) {
2245                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2246                         account = account_find_from_id(id);
2247                         g_free(queueheader_buf);
2248                 }
2249                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2250                                                                                         "NAID:")) {
2251                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2252                         account = account_find_from_id(id);
2253                         g_free(queueheader_buf);
2254                 }
2255                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2256                                                                                         "MAID:")) {
2257                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2258                         account = account_find_from_id(id);
2259                         g_free(queueheader_buf);
2260                 }
2261                 if (!account && !procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2262                                                                                         "S:")) {
2263                         account = account_find_from_address(queueheader_buf, FALSE);
2264                         g_free(queueheader_buf);
2265                 }
2266                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2267                                                                                 "X-Claws-Sign:")) {
2268                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2269                         use_signing = param;
2270                         g_free(queueheader_buf);
2271                 }
2272                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2273                                                                                 "X-Sylpheed-Sign:")) {
2274                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2275                         use_signing = param;
2276                         g_free(queueheader_buf);
2277                 }
2278                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2279                                                                                 "X-Claws-Encrypt:")) {
2280                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2281                         use_encryption = param;
2282                         g_free(queueheader_buf);
2283                 }
2284                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2285                                                                                 "X-Sylpheed-Encrypt:")) {
2286                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2287                         use_encryption = param;
2288                         g_free(queueheader_buf);
2289                 }
2290                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2291                                                                                 "X-Claws-Auto-Wrapping:")) {
2292                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2293                         autowrap = param;
2294                         g_free(queueheader_buf);
2295                 }
2296                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2297                                                                                 "X-Claws-Auto-Indent:")) {
2298                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2299                         autoindent = param;
2300                         g_free(queueheader_buf);
2301                 }
2302                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2303                                         "X-Claws-Privacy-System:")) {
2304                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2305                         g_free(queueheader_buf);
2306                 }
2307                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2308                                         "X-Sylpheed-Privacy-System:")) {
2309                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2310                         g_free(queueheader_buf);
2311                 }
2312                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2313                                                                                 "X-Priority: ")) {
2314                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2315                         priority = param;
2316                         g_free(queueheader_buf);
2317                 }
2318                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2319                                                                                         "RMID:")) {
2320                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2321                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2322                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2323                                 if (orig_item != NULL) {
2324                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2325                                 }
2326                         }
2327                         if (tokens)
2328                                 g_strfreev(tokens);
2329                         g_free(queueheader_buf);
2330                 }
2331                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, 
2332                                                                                 "FMID:")) {
2333                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2334                         if (tokens && tokens[0] && tokens[1] && tokens[2]) {
2335                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2336                                 if (orig_item != NULL) {
2337                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2338                                 }
2339                         }
2340                         if (tokens)
2341                                 g_strfreev(tokens);
2342                         g_free(queueheader_buf);
2343                 }
2344                 /* Get manual headers */
2345                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf,
2346                                                                                         "X-Claws-Manual-Headers:")) {
2347                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2348                         if (listmh && *listmh != '\0') {
2349                                 debug_print("Got manual headers: %s\n", listmh);
2350                                 manual_headers = procheader_entries_from_str(listmh);
2351                         }
2352                         if (listmh)
2353                                 g_free(listmh);
2354                         g_free(queueheader_buf);
2355                 }
2356         } else {
2357                 account = msginfo->folder->folder->account;
2358         }
2359
2360         if (!account && prefs_common.reedit_account_autosel) {
2361                 gchar *from = NULL;
2362                 if (!procheader_get_header_from_msginfo(msginfo, &from, "FROM:")) {
2363                         extract_address(from);
2364                         account = account_find_from_address(from, FALSE);
2365                 }
2366                 if (from)
2367                         g_free(from);
2368         }
2369         if (!account) {
2370                 account = cur_account;
2371         }
2372         if (!account) {
2373                 g_warning("can't select account");
2374                 if (manual_headers)
2375                         procheader_entries_free(manual_headers);
2376                 return NULL;
2377         }
2378
2379         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2380
2381         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
2382         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
2383         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2384         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2385         compose->autowrap = autowrap;
2386         compose->replyinfo = replyinfo;
2387         compose->fwdinfo = fwdinfo;
2388
2389         compose->updating = TRUE;
2390         compose->priority = priority;
2391
2392         if (privacy_system != NULL) {
2393                 compose->privacy_system = privacy_system;
2394                 compose_use_signing(compose, use_signing);
2395                 compose_use_encryption(compose, use_encryption);
2396                 compose_update_privacy_system_menu_item(compose, FALSE);
2397         } else {
2398                 compose_activate_privacy_system(compose, account, FALSE);
2399         }
2400         compose_apply_folder_privacy_settings(compose, msginfo->folder);
2401         if (privacy_system_can_sign(compose->privacy_system) == FALSE &&
2402             (account->default_encrypt || account->default_sign))
2403                 COMPOSE_PRIVACY_WARNING();
2404
2405         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2406         compose->targetinfo->tags = g_slist_copy(msginfo->tags);
2407
2408         compose_extract_original_charset(compose);
2409
2410         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2411             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2412             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2413                 gchar *queueheader_buf = NULL;
2414
2415                 /* Set message save folder */
2416                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "SCF:")) {
2417                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2418                         gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2419                         compose_set_save_to(compose, &queueheader_buf[4]);
2420                         g_free(queueheader_buf);
2421                 }
2422                 if (!procheader_get_header_from_msginfo(msginfo, &queueheader_buf, "RRCPT:")) {
2423                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2424                         if (active) {
2425                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2426                         }
2427                         g_free(queueheader_buf);
2428                 }
2429         }
2430         
2431         if (compose_parse_header(compose, msginfo) < 0) {
2432                 compose->updating = FALSE;
2433                 compose_destroy(compose);
2434                 if (manual_headers)
2435                         procheader_entries_free(manual_headers);
2436                 return NULL;
2437         }
2438         compose_reedit_set_entry(compose, msginfo);
2439
2440         textview = GTK_TEXT_VIEW(compose->text);
2441         textbuf = gtk_text_view_get_buffer(textview);
2442         compose_create_tags(textview, compose);
2443
2444         mark = gtk_text_buffer_get_insert(textbuf);
2445         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2446
2447         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2448                                         G_CALLBACK(compose_changed_cb),
2449                                         compose);
2450         
2451         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2452                 fp = procmime_get_first_encrypted_text_content(msginfo);
2453                 if (fp) {
2454                         compose_force_encryption(compose, account, TRUE, NULL);
2455                 }
2456         } else {
2457                 fp = procmime_get_first_text_content(msginfo);
2458         }
2459         if (fp == NULL) {
2460                 g_warning("can't get text part");
2461         }
2462
2463         if (fp != NULL) {
2464                 gchar buf[BUFFSIZE];
2465                 gboolean prev_autowrap;
2466                 GtkTextBuffer *buffer;
2467                 BLOCK_WRAP();
2468                 while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
2469                         strcrchomp(buf);
2470                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2471                 }
2472                 UNBLOCK_WRAP();
2473                 claws_fclose(fp);
2474         }
2475         
2476         compose_attach_parts(compose, msginfo);
2477
2478         compose_colorize_signature(compose);
2479
2480         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2481                                         G_CALLBACK(compose_changed_cb),
2482                                         compose);
2483
2484         if (manual_headers != NULL) {
2485                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2486                         procheader_entries_free(manual_headers);
2487                         compose->updating = FALSE;
2488                         compose_destroy(compose);
2489                         return NULL;
2490                 }
2491                 procheader_entries_free(manual_headers);
2492         }
2493
2494         gtk_widget_grab_focus(compose->text);
2495
2496         if (prefs_common.auto_exteditor) {
2497                 compose_exec_ext_editor(compose);
2498         }
2499         compose->modified = FALSE;
2500         compose_set_title(compose);
2501
2502         compose->updating = FALSE;
2503         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2504         SCROLL_TO_CURSOR(compose);
2505
2506         if (compose->deferred_destroy) {
2507                 compose_destroy(compose);
2508                 return NULL;
2509         }
2510         
2511         compose->sig_str = account_get_signature_str(compose->account);
2512         
2513         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2514
2515         return compose;
2516 }
2517
2518 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2519                                                  gboolean batch)
2520 {
2521         Compose *compose;
2522         gchar *filename;
2523         FolderItem *item;
2524
2525         cm_return_val_if_fail(msginfo != NULL, NULL);
2526
2527         if (!account)
2528                 account = account_get_reply_account(msginfo,
2529                                         prefs_common.reply_account_autosel);
2530         cm_return_val_if_fail(account != NULL, NULL);
2531
2532         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2533
2534         compose->updating = TRUE;
2535
2536         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2537         compose->replyinfo = NULL;
2538         compose->fwdinfo = NULL;
2539
2540         compose_show_first_last_header(compose, TRUE);
2541
2542         gtk_widget_grab_focus(compose->header_last->entry);
2543
2544         filename = procmsg_get_message_file(msginfo);
2545
2546         if (filename == NULL) {
2547                 compose->updating = FALSE;
2548                 compose_destroy(compose);
2549
2550                 return NULL;
2551         }
2552
2553         compose->redirect_filename = filename;
2554         
2555         /* Set save folder */
2556         item = msginfo->folder;
2557         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2558                 gchar *folderidentifier;
2559
2560                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2561                 gtk_widget_set_sensitive(GTK_WIDGET(compose->savemsg_combo), TRUE);
2562                 folderidentifier = folder_item_get_identifier(item);
2563                 compose_set_save_to(compose, folderidentifier);
2564                 g_free(folderidentifier);
2565         }
2566
2567         compose_attach_parts(compose, msginfo);
2568
2569         if (msginfo->subject)
2570                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2571                                    msginfo->subject);
2572         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2573
2574         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2575                                           _("The body of the \"Redirect\" template has an error at line %d."));
2576         quote_fmt_reset_vartable();
2577         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2578
2579         compose_colorize_signature(compose);
2580
2581         
2582         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2583         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2584         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2585
2586         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2587         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2588         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2589         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2590         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/ReplaceSig", FALSE);
2591         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2592         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2593         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2594         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2595         
2596         if (compose->toolbar->draft_btn)
2597                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2598         if (compose->toolbar->insert_btn)
2599                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2600         if (compose->toolbar->attach_btn)
2601                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2602         if (compose->toolbar->sig_btn)
2603                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2604         if (compose->toolbar->exteditor_btn)
2605                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2606         if (compose->toolbar->linewrap_current_btn)
2607                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2608         if (compose->toolbar->linewrap_all_btn)
2609                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2610         if (compose->toolbar->privacy_sign_btn)
2611                 gtk_widget_set_sensitive(compose->toolbar->privacy_sign_btn, FALSE);
2612         if (compose->toolbar->privacy_encrypt_btn)
2613                 gtk_widget_set_sensitive(compose->toolbar->privacy_encrypt_btn, FALSE);
2614
2615         compose->modified = FALSE;
2616         compose_set_title(compose);
2617         compose->updating = FALSE;
2618         compose->draft_timeout_tag = COMPOSE_DRAFT_TIMEOUT_UNSET; /* desinhibit auto-drafting after loading */
2619         SCROLL_TO_CURSOR(compose);
2620
2621         if (compose->deferred_destroy) {
2622                 compose_destroy(compose);
2623                 return NULL;
2624         }
2625         
2626         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2627
2628         return compose;
2629 }
2630
2631 const GList *compose_get_compose_list(void)
2632 {
2633         return compose_list;
2634 }
2635
2636 void compose_entry_append(Compose *compose, const gchar *address,
2637                           ComposeEntryType type, ComposePrefType pref_type)
2638 {
2639         const gchar *header;
2640         gchar *cur, *begin;
2641         gboolean in_quote = FALSE;
2642         if (!address || *address == '\0') return;
2643
2644         switch (type) {
2645         case COMPOSE_CC:
2646                 header = N_("Cc:");
2647                 break;
2648         case COMPOSE_BCC:
2649                 header = N_("Bcc:");
2650                 break;
2651         case COMPOSE_REPLYTO:
2652                 header = N_("Reply-To:");
2653                 break;
2654         case COMPOSE_NEWSGROUPS:
2655                 header = N_("Newsgroups:");
2656                 break;
2657         case COMPOSE_FOLLOWUPTO:
2658                 header = N_( "Followup-To:");
2659                 break;
2660         case COMPOSE_INREPLYTO:
2661                 header = N_( "In-Reply-To:");
2662                 break;
2663         case COMPOSE_TO:
2664         default:
2665                 header = N_("To:");
2666                 break;
2667         }
2668         header = prefs_common_translated_header_name(header);
2669         
2670         cur = begin = (gchar *)address;
2671         
2672         /* we separate the line by commas, but not if we're inside a quoted
2673          * string */
2674         while (*cur != '\0') {
2675                 if (*cur == '"') 
2676                         in_quote = !in_quote;
2677                 if (*cur == ',' && !in_quote) {
2678                         gchar *tmp = g_strdup(begin);
2679                         gchar *o_tmp = tmp;
2680                         tmp[cur-begin]='\0';
2681                         cur++;
2682                         begin = cur;
2683                         while (*tmp == ' ' || *tmp == '\t')
2684                                 tmp++;
2685                         compose_add_header_entry(compose, header, tmp, pref_type);
2686                         compose_entry_indicate(compose, tmp);
2687                         g_free(o_tmp);
2688                         continue;
2689                 }
2690                 cur++;
2691         }
2692         if (begin < cur) {
2693                 gchar *tmp = g_strdup(begin);
2694                 gchar *o_tmp = tmp;
2695                 tmp[cur-begin]='\0';
2696                 while (*tmp == ' ' || *tmp == '\t')
2697                         tmp++;
2698                 compose_add_header_entry(compose, header, tmp, pref_type);
2699                 compose_entry_indicate(compose, tmp);
2700                 g_free(o_tmp);          
2701         }
2702 }
2703
2704 static void compose_entry_indicate(Compose *compose, const gchar *mailto)
2705 {
2706         GSList *h_list;
2707         GtkEntry *entry;
2708         GdkColor color;
2709                 
2710         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2711                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2712                 if (gtk_entry_get_text(entry) && 
2713                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2714                         /* Modify background color */
2715                         GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_bgcolor, color);
2716                         gtk_widget_modify_base(
2717                                 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2718                                 GTK_STATE_NORMAL, &color);
2719
2720                         /* Modify foreground color */
2721                         GTKUT_GDKRGBA_TO_GDKCOLOR(default_header_color, color);
2722                         gtk_widget_modify_text(
2723                                 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2724                                 GTK_STATE_NORMAL, &color);
2725                 }
2726         }
2727 }
2728
2729 void compose_toolbar_cb(gint action, gpointer data)
2730 {
2731         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2732         Compose *compose = (Compose*)toolbar_item->parent;
2733         
2734         cm_return_if_fail(compose != NULL);
2735
2736         switch(action) {
2737         case A_SEND:
2738                 compose_send_cb(NULL, compose);
2739                 break;
2740         case A_SEND_LATER:
2741                 compose_send_later_cb(NULL, compose);
2742                 break;
2743         case A_DRAFT:
2744                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2745                 break;
2746         case A_INSERT:
2747                 compose_insert_file_cb(NULL, compose);
2748                 break;
2749         case A_ATTACH:
2750                 compose_attach_cb(NULL, compose);
2751                 break;
2752         case A_SIG:
2753                 compose_insert_sig(compose, FALSE);
2754                 break;
2755         case A_REP_SIG:
2756                 compose_insert_sig(compose, TRUE);
2757                 break;
2758         case A_EXTEDITOR:
2759                 compose_ext_editor_cb(NULL, compose);
2760                 break;
2761         case A_LINEWRAP_CURRENT:
2762                 compose_beautify_paragraph(compose, NULL, TRUE);
2763                 break;
2764         case A_LINEWRAP_ALL:
2765                 compose_wrap_all_full(compose, TRUE);
2766                 break;
2767         case A_ADDRBOOK:
2768                 compose_address_cb(NULL, compose);
2769                 break;
2770 #ifdef USE_ENCHANT
2771         case A_CHECK_SPELLING:
2772                 compose_check_all(NULL, compose);
2773                 break;
2774 #endif
2775         case A_PRIVACY_SIGN:
2776                 break;
2777         case A_PRIVACY_ENCRYPT:
2778                 break;
2779         default:
2780                 break;
2781         }
2782 }
2783
2784 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2785 {
2786         gchar *to = NULL;
2787         gchar *cc = NULL;
2788         gchar *bcc = NULL;
2789         gchar *subject = NULL;
2790         gchar *body = NULL;
2791         gchar *temp = NULL;
2792         gsize  len = 0;
2793         gchar **attach = NULL;
2794         gchar *inreplyto = NULL;
2795         MailField mfield = NO_FIELD_PRESENT;
2796
2797         /* get mailto parts but skip from */
2798         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2799
2800         if (to) {
2801                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2802                 mfield = TO_FIELD_PRESENT;
2803         }
2804         if (cc)
2805                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2806         if (bcc)
2807                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2808         if (subject) {
2809                 if (!g_utf8_validate (subject, -1, NULL)) {
2810                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2811                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2812                         g_free(temp);
2813                 } else {
2814                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2815                 }
2816                 mfield = SUBJECT_FIELD_PRESENT;
2817         }
2818         if (body) {
2819                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2820                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2821                 GtkTextMark *mark;
2822                 GtkTextIter iter;
2823                 gboolean prev_autowrap = compose->autowrap;
2824
2825                 compose->autowrap = FALSE;
2826
2827                 mark = gtk_text_buffer_get_insert(buffer);
2828                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2829
2830                 if (!g_utf8_validate (body, -1, NULL)) {
2831                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2832                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2833                         g_free(temp);
2834                 } else {
2835                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2836                 }
2837                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2838
2839                 compose->autowrap = prev_autowrap;
2840                 if (compose->autowrap)
2841                         compose_wrap_all(compose);
2842                 mfield = BODY_FIELD_PRESENT;
2843         }
2844
2845         if (attach) {
2846                 gint i = 0, att = 0;
2847                 gchar *warn_files = NULL;
2848                 while (attach[i] != NULL) {
2849                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2850                         if (utf8_filename) {
2851                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2852                                         gchar *tmp = g_strdup_printf("%s%s\n",
2853                                                         warn_files?warn_files:"",
2854                                                         utf8_filename);
2855                                         g_free(warn_files);
2856                                         warn_files = tmp;
2857                                         att++;
2858                                 }
2859                                 g_free(utf8_filename);
2860                         } else {
2861                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2862                         }
2863                         i++;
2864                 }
2865                 if (warn_files) {
2866                         alertpanel_notice(ngettext(
2867                         "The following file has been attached: \n%s",
2868                         "The following files have been attached: \n%s", att), warn_files);
2869                         g_free(warn_files);
2870                 }
2871         }
2872         if (inreplyto)
2873                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2874
2875         g_free(to);
2876         g_free(cc);
2877         g_free(bcc);
2878         g_free(subject);
2879         g_free(body);
2880         g_strfreev(attach);
2881         g_free(inreplyto);
2882         
2883         return mfield;
2884 }
2885
2886 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2887 {
2888         static HeaderEntry hentry[] = {
2889                                        {"Reply-To:",    NULL, TRUE },
2890                                        {"Cc:",          NULL, TRUE },
2891                                        {"References:",  NULL, FALSE },
2892                                        {"Bcc:",         NULL, TRUE },
2893                                        {"Newsgroups:",  NULL, TRUE },
2894                                        {"Followup-To:", NULL, TRUE },
2895                                        {"List-Post:",   NULL, FALSE },
2896                                        {"X-Priority:",  NULL, FALSE },
2897                                        {NULL,           NULL, FALSE }
2898         };
2899
2900         enum
2901         {
2902                 H_REPLY_TO    = 0,
2903                 H_CC          = 1,
2904                 H_REFERENCES  = 2,
2905                 H_BCC         = 3,
2906                 H_NEWSGROUPS  = 4,
2907                 H_FOLLOWUP_TO = 5,
2908                 H_LIST_POST   = 6,
2909                 H_X_PRIORITY  = 7
2910         };
2911
2912         FILE *fp;
2913
2914         cm_return_val_if_fail(msginfo != NULL, -1);
2915
2916         if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL) return -1;
2917         procheader_get_header_fields(fp, hentry);
2918         claws_fclose(fp);
2919
2920         if (hentry[H_REPLY_TO].body != NULL) {
2921                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2922                         compose->replyto =
2923                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2924                                                    NULL, TRUE);
2925                 }
2926                 g_free(hentry[H_REPLY_TO].body);
2927                 hentry[H_REPLY_TO].body = NULL;
2928         }
2929         if (hentry[H_CC].body != NULL) {
2930                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2931                 g_free(hentry[H_CC].body);
2932                 hentry[H_CC].body = NULL;
2933         }
2934         if (hentry[H_REFERENCES].body != NULL) {
2935                 if (compose->mode == COMPOSE_REEDIT)
2936                         compose->references = hentry[H_REFERENCES].body;
2937                 else {
2938                         compose->references = compose_parse_references
2939                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2940                         g_free(hentry[H_REFERENCES].body);
2941                 }
2942                 hentry[H_REFERENCES].body = NULL;
2943         }
2944         if (hentry[H_BCC].body != NULL) {
2945                 if (compose->mode == COMPOSE_REEDIT)
2946                         compose->bcc =
2947                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2948                 g_free(hentry[H_BCC].body);
2949                 hentry[H_BCC].body = NULL;
2950         }
2951         if (hentry[H_NEWSGROUPS].body != NULL) {
2952                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2953                 hentry[H_NEWSGROUPS].body = NULL;
2954         }
2955         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2956                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2957                         compose->followup_to =
2958                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2959                                                    NULL, TRUE);
2960                 }
2961                 g_free(hentry[H_FOLLOWUP_TO].body);
2962                 hentry[H_FOLLOWUP_TO].body = NULL;
2963         }
2964         if (hentry[H_LIST_POST].body != NULL) {
2965                 gchar *to = NULL, *start = NULL;
2966
2967                 extract_address(hentry[H_LIST_POST].body);
2968                 if (hentry[H_LIST_POST].body[0] != '\0') {
2969                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2970                         
2971                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2972                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2973
2974                         if (to) {
2975                                 g_free(compose->ml_post);
2976                                 compose->ml_post = to;
2977                         }
2978                 }
2979                 g_free(hentry[H_LIST_POST].body);
2980                 hentry[H_LIST_POST].body = NULL;
2981         }
2982
2983         /* CLAWS - X-Priority */
2984         if (compose->mode == COMPOSE_REEDIT)
2985                 if (hentry[H_X_PRIORITY].body != NULL) {
2986                         gint priority;
2987                         
2988                         priority = atoi(hentry[H_X_PRIORITY].body);
2989                         g_free(hentry[H_X_PRIORITY].body);
2990                         
2991                         hentry[H_X_PRIORITY].body = NULL;
2992                         
2993                         if (priority < PRIORITY_HIGHEST || 
2994                             priority > PRIORITY_LOWEST)
2995                                 priority = PRIORITY_NORMAL;
2996                         
2997                         compose->priority =  priority;
2998                 }
2999  
3000         if (compose->mode == COMPOSE_REEDIT) {
3001                 if (msginfo->inreplyto && *msginfo->inreplyto)
3002                         compose->inreplyto = g_strdup(msginfo->inreplyto);
3003
3004                 if (msginfo->msgid && *msginfo->msgid &&
3005                                 compose->folder != NULL &&
3006                                 compose->folder->stype ==  F_DRAFT)
3007                         compose->msgid = g_strdup(msginfo->msgid);
3008         } else {
3009                 if (msginfo->msgid && *msginfo->msgid)
3010                         compose->inreplyto = g_strdup(msginfo->msgid);
3011
3012                 if (!compose->references) {
3013                         if (msginfo->msgid && *msginfo->msgid) {
3014                                 if (msginfo->inreplyto && *msginfo->inreplyto)
3015                                         compose->references =
3016                                                 g_strdup_printf("<%s>\n\t<%s>",
3017                                                                 msginfo->inreplyto,
3018                                                                 msginfo->msgid);
3019                                 else
3020                                         compose->references =
3021                                                 g_strconcat("<", msginfo->msgid, ">",
3022                                                             NULL);
3023                         } else if (msginfo->inreplyto && *msginfo->inreplyto) {
3024                                 compose->references =
3025                                         g_strconcat("<", msginfo->inreplyto, ">",
3026                                                     NULL);
3027                         }
3028                 }
3029         }
3030
3031         return 0;
3032 }
3033
3034 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
3035 {
3036         FILE *fp;
3037         HeaderEntry *he;
3038
3039         cm_return_val_if_fail(msginfo != NULL, -1);
3040
3041         if ((fp = procmsg_open_message(msginfo, FALSE)) == NULL) return -1;
3042         procheader_get_header_fields(fp, entries);
3043         claws_fclose(fp);
3044
3045         he = entries;
3046         while (he != NULL && he->name != NULL) {
3047                 GtkTreeIter iter;
3048                 GtkListStore *model = NULL;
3049
3050                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
3051                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
3052                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
3053                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
3054                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
3055                 ++he;
3056         }
3057
3058         return 0;
3059 }
3060
3061 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
3062 {
3063         GSList *ref_id_list, *cur;
3064         GString *new_ref;
3065         gchar *new_ref_str;
3066
3067         ref_id_list = references_list_append(NULL, ref);
3068         if (!ref_id_list) return NULL;
3069         if (msgid && *msgid)
3070                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
3071
3072         for (;;) {
3073                 gint len = 0;
3074
3075                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
3076                         /* "<" + Message-ID + ">" + CR+LF+TAB */
3077                         len += strlen((gchar *)cur->data) + 5;
3078
3079                 if (len > MAX_REFERENCES_LEN) {
3080                         /* remove second message-ID */
3081                         if (ref_id_list && ref_id_list->next &&
3082                             ref_id_list->next->next) {
3083                                 g_free(ref_id_list->next->data);
3084                                 ref_id_list = g_slist_remove
3085                                         (ref_id_list, ref_id_list->next->data);
3086                         } else {
3087                                 slist_free_strings_full(ref_id_list);
3088                                 return NULL;
3089                         }
3090                 } else
3091                         break;
3092         }
3093
3094         new_ref = g_string_new("");
3095         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
3096                 if (new_ref->len > 0)
3097                         g_string_append(new_ref, "\n\t");
3098                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
3099         }
3100
3101         slist_free_strings_full(ref_id_list);
3102
3103         new_ref_str = new_ref->str;
3104         g_string_free(new_ref, FALSE);
3105
3106         return new_ref_str;
3107 }
3108
3109 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
3110                                 const gchar *fmt, const gchar *qmark,
3111                                 const gchar *body, gboolean rewrap,
3112                                 gboolean need_unescape,
3113                                 const gchar *err_msg)
3114 {
3115         MsgInfo* dummyinfo = NULL;
3116         gchar *quote_str = NULL;
3117         gchar *buf;
3118         gboolean prev_autowrap;
3119         const gchar *trimmed_body = body;
3120         gint cursor_pos = -1;
3121         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3122         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3123         GtkTextIter iter;
3124         GtkTextMark *mark;
3125         
3126
3127         SIGNAL_BLOCK(buffer);
3128
3129         if (!msginfo) {
3130                 dummyinfo = compose_msginfo_new_from_compose(compose);
3131                 msginfo = dummyinfo;
3132         }
3133
3134         if (qmark != NULL) {
3135 #ifdef USE_ENCHANT
3136                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3137                                 compose->gtkaspell);
3138 #else
3139                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3140 #endif
3141                 quote_fmt_scan_string(qmark);
3142                 quote_fmt_parse();
3143
3144                 buf = quote_fmt_get_buffer();
3145
3146                 if (buf == NULL)
3147                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3148                 else
3149                         Xstrdup_a(quote_str, buf, goto error)
3150         }
3151
3152         if (fmt && *fmt != '\0') {
3153
3154                 if (trimmed_body)
3155                         while (*trimmed_body == '\n')
3156                                 trimmed_body++;
3157
3158 #ifdef USE_ENCHANT
3159                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3160                                 compose->gtkaspell);
3161 #else
3162                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3163 #endif
3164                 if (need_unescape) {
3165                         gchar *tmp = NULL;
3166
3167                         /* decode \-escape sequences in the internal representation of the quote format */
3168                         tmp = g_malloc(strlen(fmt)+1);
3169                         pref_get_unescaped_pref(tmp, fmt);
3170                         quote_fmt_scan_string(tmp);
3171                         quote_fmt_parse();
3172                         g_free(tmp);
3173                 } else {
3174                         quote_fmt_scan_string(fmt);
3175                         quote_fmt_parse();
3176                 }
3177
3178                 buf = quote_fmt_get_buffer();
3179
3180                 if (buf == NULL) {
3181                         gint line = quote_fmt_get_line();
3182                         alertpanel_error(err_msg, line);
3183
3184                         goto error;
3185                 }
3186
3187         } else
3188                 buf = "";
3189
3190         prev_autowrap = compose->autowrap;
3191         compose->autowrap = FALSE;
3192
3193         mark = gtk_text_buffer_get_insert(buffer);
3194         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3195         if (g_utf8_validate(buf, -1, NULL)) { 
3196                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3197         } else {
3198                 gchar *tmpout = NULL;
3199                 tmpout = conv_codeset_strdup
3200                         (buf, conv_get_locale_charset_str_no_utf8(),
3201                          CS_INTERNAL);
3202                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3203                         g_free(tmpout);
3204                         tmpout = g_malloc(strlen(buf)*2+1);
3205                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3206                 }
3207                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3208                 g_free(tmpout);
3209         }
3210
3211         cursor_pos = quote_fmt_get_cursor_pos();
3212         if (cursor_pos == -1)
3213                 cursor_pos = gtk_text_iter_get_offset(&iter);
3214         compose->set_cursor_pos = cursor_pos;
3215
3216         gtk_text_buffer_get_start_iter(buffer, &iter);
3217         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3218         gtk_text_buffer_place_cursor(buffer, &iter);
3219
3220         compose->autowrap = prev_autowrap;
3221         if (compose->autowrap && rewrap)
3222                 compose_wrap_all(compose);
3223
3224         goto ok;
3225
3226 error:
3227         buf = NULL;
3228 ok:
3229         SIGNAL_UNBLOCK(buffer);
3230
3231         procmsg_msginfo_free( &dummyinfo );
3232
3233         return buf;
3234 }
3235
3236 /* if ml_post is of type addr@host and from is of type
3237  * addr-anything@host, return TRUE
3238  */
3239 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3240 {
3241         gchar *left_ml = NULL;
3242         gchar *right_ml = NULL;
3243         gchar *left_from = NULL;
3244         gchar *right_from = NULL;
3245         gboolean result = FALSE;
3246         
3247         if (!ml_post || !from)
3248                 return FALSE;
3249         
3250         left_ml = g_strdup(ml_post);
3251         if (strstr(left_ml, "@")) {
3252                 right_ml = strstr(left_ml, "@")+1;
3253                 *(strstr(left_ml, "@")) = '\0';
3254         }
3255         
3256         left_from = g_strdup(from);
3257         if (strstr(left_from, "@")) {
3258                 right_from = strstr(left_from, "@")+1;
3259                 *(strstr(left_from, "@")) = '\0';
3260         }
3261         
3262         if (right_ml && right_from
3263         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3264         &&  !strcmp(right_from, right_ml)) {
3265                 result = TRUE;
3266         }
3267         g_free(left_ml);
3268         g_free(left_from);
3269         
3270         return result;
3271 }
3272
3273 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3274                                      gboolean respect_default_to)
3275 {
3276         if (!compose)
3277                 return;
3278         if (!folder || !folder->prefs)
3279                 return;
3280
3281         if (folder->prefs->enable_default_from) {
3282                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), folder->prefs->default_from);
3283                 compose_entry_indicate(compose, folder->prefs->default_from);
3284         }
3285         if (respect_default_to && folder->prefs->enable_default_to) {
3286                 compose_entry_append(compose, folder->prefs->default_to,
3287                                         COMPOSE_TO, PREF_FOLDER);
3288                 compose_entry_indicate(compose, folder->prefs->default_to);
3289         }
3290         if (folder->prefs->enable_default_cc) {
3291                 compose_entry_append(compose, folder->prefs->default_cc,
3292                                         COMPOSE_CC, PREF_FOLDER);
3293                 compose_entry_indicate(compose, folder->prefs->default_cc);
3294         }
3295         if (folder->prefs->enable_default_bcc) {
3296                 compose_entry_append(compose, folder->prefs->default_bcc,
3297                                         COMPOSE_BCC, PREF_FOLDER);
3298                 compose_entry_indicate(compose, folder->prefs->default_bcc);
3299         }
3300         if (folder->prefs->enable_default_replyto) {
3301                 compose_entry_append(compose, folder->prefs->default_replyto,
3302                                         COMPOSE_REPLYTO, PREF_FOLDER);
3303                 compose_entry_indicate(compose, folder->prefs->default_replyto);
3304         }
3305 }
3306
3307 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3308 {
3309         gchar *buf, *buf2;
3310         gchar *p;
3311         
3312         if (!compose || !msginfo)
3313                 return;
3314
3315         if (msginfo->subject && *msginfo->subject) {
3316                 buf = p = g_strdup(msginfo->subject);
3317                 p += subject_get_prefix_length(p);
3318                 memmove(buf, p, strlen(p) + 1);
3319
3320                 buf2 = g_strdup_printf("Re: %s", buf);
3321                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3322
3323                 g_free(buf2);
3324                 g_free(buf);
3325         } else
3326                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3327 }
3328
3329 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3330                                     gboolean to_all, gboolean to_ml,
3331                                     gboolean to_sender,
3332                                     gboolean followup_and_reply_to)
3333 {
3334         GSList *cc_list = NULL;
3335         GSList *cur;
3336         gchar *from = NULL;
3337         gchar *replyto = NULL;
3338         gchar *ac_email = NULL;
3339
3340         gboolean reply_to_ml = FALSE;
3341         gboolean default_reply_to = FALSE;
3342
3343         cm_return_if_fail(compose->account != NULL);
3344         cm_return_if_fail(msginfo != NULL);
3345
3346         reply_to_ml = to_ml && compose->ml_post;
3347
3348         default_reply_to = msginfo->folder && 
3349                 msginfo->folder->prefs->enable_default_reply_to;
3350
3351         if (compose->account->protocol != A_NNTP) {
3352                 compose_set_folder_prefs(compose, msginfo->folder, FALSE);
3353
3354                 if (reply_to_ml && !default_reply_to) {
3355                         
3356                         gboolean is_subscr = is_subscription(compose->ml_post,
3357                                                              msginfo->from);
3358                         if (!is_subscr) {
3359                                 /* normal answer to ml post with a reply-to */
3360                                 compose_entry_append(compose,
3361                                            compose->ml_post,
3362                                            COMPOSE_TO, PREF_ML);
3363                                 if (compose->replyto)
3364                                         compose_entry_append(compose,
3365                                                 compose->replyto,
3366                                                 COMPOSE_CC, PREF_ML);
3367                         } else {
3368                                 /* answer to subscription confirmation */
3369                                 if (compose->replyto)
3370                                         compose_entry_append(compose,
3371                                                 compose->replyto,
3372                                                 COMPOSE_TO, PREF_ML);
3373                                 else if (msginfo->from)
3374                                         compose_entry_append(compose,
3375                                                 msginfo->from,
3376                                                 COMPOSE_TO, PREF_ML);
3377                         }
3378                 }
3379                 else if (!(to_all || to_sender) && default_reply_to) {
3380                         compose_entry_append(compose,
3381                             msginfo->folder->prefs->default_reply_to,
3382                             COMPOSE_TO, PREF_FOLDER);
3383                         compose_entry_indicate(compose,
3384                                 msginfo->folder->prefs->default_reply_to);
3385                 } else {
3386                         gchar *tmp1 = NULL;
3387                         if (!msginfo->from)
3388                                 return;
3389                         if (to_sender)
3390                                 compose_entry_append(compose, msginfo->from,
3391                                                      COMPOSE_TO, PREF_NONE);
3392                         else if (to_all) {
3393                                 Xstrdup_a(tmp1, msginfo->from, return);
3394                                 extract_address(tmp1);
3395                                 compose_entry_append(compose,
3396                                  (!account_find_from_address(tmp1, FALSE))
3397                                           ? msginfo->from :
3398                                           msginfo->to,
3399                                           COMPOSE_TO, PREF_NONE);
3400                                 if (compose->replyto)
3401                                                 compose_entry_append(compose,
3402                                                         compose->replyto,
3403                                                         COMPOSE_CC, PREF_NONE);
3404                         } else {
3405                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3406                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3407                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3408                                         if (compose->replyto) {
3409                                                 compose_entry_append(compose,
3410                                                         compose->replyto,
3411                                                         COMPOSE_TO, PREF_NONE);
3412                                         } else {
3413                                                 compose_entry_append(compose,
3414                                                           msginfo->from ? msginfo->from : "",
3415                                                           COMPOSE_TO, PREF_NONE);
3416                                         }
3417                                 } else {
3418                                         /* replying to own mail, use original recp */
3419                                         compose_entry_append(compose,
3420                                                   msginfo->to ? msginfo->to : "",
3421                                                   COMPOSE_TO, PREF_NONE);
3422                                         compose_entry_append(compose,
3423                                                   msginfo->cc ? msginfo->cc : "",
3424                                                   COMPOSE_CC, PREF_NONE);
3425                                 }
3426                         }
3427                 }
3428         } else {
3429                 if (to_sender || (compose->followup_to && 
3430                         !strncmp(compose->followup_to, "poster", 6)))
3431                         compose_entry_append
3432                                 (compose, 
3433                                  (compose->replyto ? compose->replyto :
3434                                         msginfo->from ? msginfo->from : ""),
3435                                  COMPOSE_TO, PREF_NONE);
3436                                  
3437                 else if (followup_and_reply_to || to_all) {
3438                         compose_entry_append
3439                                 (compose,
3440                                  (compose->replyto ? compose->replyto :
3441                                  msginfo->from ? msginfo->from : ""),
3442                                  COMPOSE_TO, PREF_NONE);                                
3443                 
3444                         compose_entry_append
3445                                 (compose,
3446                                  compose->followup_to ? compose->followup_to :
3447                                  compose->newsgroups ? compose->newsgroups : "",
3448                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3449
3450                         compose_entry_append
3451                                 (compose,
3452                                  msginfo->cc ? msginfo->cc : "",
3453                                  COMPOSE_CC, PREF_NONE);
3454                 } 
3455                 else 
3456                         compose_entry_append
3457                                 (compose,
3458                                  compose->followup_to ? compose->followup_to :
3459                                  compose->newsgroups ? compose->newsgroups : "",
3460                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3461         }
3462         compose_reply_set_subject(compose, msginfo);
3463
3464         if (to_ml && compose->ml_post) return;
3465         if (!to_all || compose->account->protocol == A_NNTP) return;
3466
3467         if (compose->replyto) {
3468                 Xstrdup_a(replyto, compose->replyto, return);
3469                 extract_address(replyto);
3470         }
3471         if (msginfo->from) {
3472                 Xstrdup_a(from, msginfo->from, return);
3473                 extract_address(from);
3474         }
3475
3476         if (replyto && from)
3477                 cc_list = address_list_append_with_comments(cc_list, from);
3478         if (to_all && msginfo->folder && 
3479             msginfo->folder->prefs->enable_default_reply_to)
3480                 cc_list = address_list_append_with_comments(cc_list,
3481                                 msginfo->folder->prefs->default_reply_to);
3482         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3483         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3484
3485         ac_email = g_utf8_strdown(compose->account->address, -1);
3486
3487         if (cc_list) {
3488                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3489                         gchar *addr = g_utf8_strdown(cur->data, -1);
3490                         extract_address(addr);
3491                 
3492                         if (strcmp(ac_email, addr))
3493                                 compose_entry_append(compose, (gchar *)cur->data,
3494                                                      COMPOSE_CC, PREF_NONE);
3495                         else
3496                                 debug_print("Cc address same as compose account's, ignoring\n");
3497
3498                         g_free(addr);
3499                 }
3500                 
3501                 slist_free_strings_full(cc_list);
3502         }
3503         
3504         g_free(ac_email);
3505 }
3506
3507 #define SET_ENTRY(entry, str) \
3508 { \
3509         if (str && *str) \
3510                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3511 }
3512
3513 #define SET_ADDRESS(type, str) \
3514 { \
3515         if (str && *str) \
3516                 compose_entry_append(compose, str, type, PREF_NONE); \
3517 }
3518
3519 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3520 {
3521         cm_return_if_fail(msginfo != NULL);
3522
3523         SET_ENTRY(subject_entry, msginfo->subject);
3524         SET_ENTRY(from_name, msginfo->from);
3525         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3526         SET_ADDRESS(COMPOSE_CC, compose->cc);
3527         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3528         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3529         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3530         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3531
3532         compose_update_priority_menu_item(compose);
3533         compose_update_privacy_system_menu_item(compose, FALSE);
3534         compose_show_first_last_header(compose, TRUE);
3535 }
3536
3537 #undef SET_ENTRY
3538 #undef SET_ADDRESS
3539
3540 static void compose_insert_sig(Compose *compose, gboolean replace)
3541 {
3542         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3543         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3544         GtkTextMark *mark;
3545         GtkTextIter iter, iter_end;
3546         gint cur_pos, ins_pos;
3547         gboolean prev_autowrap;
3548         gboolean found = FALSE;
3549         gboolean exists = FALSE;
3550         
3551         cm_return_if_fail(compose->account != NULL);
3552
3553         BLOCK_WRAP();
3554
3555         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3556                                         G_CALLBACK(compose_changed_cb),
3557                                         compose);
3558         
3559         mark = gtk_text_buffer_get_insert(buffer);
3560         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3561         cur_pos = gtk_text_iter_get_offset (&iter);
3562         ins_pos = cur_pos;
3563
3564         gtk_text_buffer_get_end_iter(buffer, &iter);
3565
3566         exists = (compose->sig_str != NULL);
3567
3568         if (replace) {
3569                 GtkTextIter first_iter, start_iter, end_iter;
3570
3571                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3572
3573                 if (!exists || compose->sig_str[0] == '\0')
3574                         found = FALSE;
3575                 else
3576                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3577                                         compose->signature_tag);
3578
3579                 if (found) {
3580                         /* include previous \n\n */
3581                         gtk_text_iter_backward_chars(&first_iter, 1);
3582                         start_iter = first_iter;
3583                         end_iter = first_iter;
3584                         /* skip re-start */
3585                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3586                                         compose->signature_tag);
3587                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3588                                         compose->signature_tag);
3589                         if (found) {
3590                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3591                                 iter = start_iter;
3592                         }
3593                 } 
3594         } 
3595
3596         g_free(compose->sig_str);
3597         compose->sig_str = account_get_signature_str(compose->account);
3598
3599         cur_pos = gtk_text_iter_get_offset(&iter);
3600
3601         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3602                 g_free(compose->sig_str);
3603                 compose->sig_str = NULL;
3604         } else {
3605                 if (compose->sig_inserted == FALSE)
3606                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3607                 compose->sig_inserted = TRUE;
3608
3609                 cur_pos = gtk_text_iter_get_offset(&iter);
3610                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3611                 /* remove \n\n */
3612                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3613                 gtk_text_iter_forward_chars(&iter, 1);
3614                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3615                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3616
3617                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3618                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3619         }
3620
3621         /* put the cursor where it should be 
3622          * either where the quote_fmt says, either where it was */
3623         if (compose->set_cursor_pos < 0)
3624                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3625         else
3626                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3627                         compose->set_cursor_pos);
3628         
3629         compose->set_cursor_pos = -1;
3630         gtk_text_buffer_place_cursor(buffer, &iter);
3631         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3632                                         G_CALLBACK(compose_changed_cb),
3633                                         compose);
3634                 
3635         UNBLOCK_WRAP();
3636 }
3637
3638 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3639 {
3640         GtkTextView *text;
3641         GtkTextBuffer *buffer;
3642         GtkTextMark *mark;
3643         GtkTextIter iter;
3644         const gchar *cur_encoding;
3645         gchar buf[BUFFSIZE];
3646         gint len;
3647         FILE *fp;
3648         gboolean prev_autowrap;
3649 #ifdef G_OS_WIN32
3650         GFile *f;
3651         GFileInfo *fi;
3652         GError *error = NULL;
3653 #else
3654         GStatBuf file_stat;
3655 #endif
3656         int ret;
3657         goffset size;
3658         GString *file_contents = NULL;
3659         ComposeInsertResult result = COMPOSE_INSERT_SUCCESS;
3660
3661         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3662
3663         /* get the size of the file we are about to insert */
3664 #ifdef G_OS_WIN32
3665         f = g_file_new_for_path(file);
3666         fi = g_file_query_info(f, "standard::size",
3667                         G_FILE_QUERY_INFO_NONE, NULL, &error);
3668         ret = 0;
3669         if (error != NULL) {
3670                 g_warning(error->message);
3671                 ret = 1;
3672                 g_error_free(error);
3673                 g_object_unref(f);
3674         }
3675 #else
3676         ret = g_stat(file, &file_stat);
3677 #endif
3678         if (ret != 0) {
3679                 gchar *shortfile = g_path_get_basename(file);
3680                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3681                 g_free(shortfile);
3682                 return COMPOSE_INSERT_NO_FILE;
3683         } else if (prefs_common.warn_large_insert == TRUE) {
3684 #ifdef G_OS_WIN32
3685                 size = g_file_info_get_size(fi);
3686                 g_object_unref(fi);
3687                 g_object_unref(f);
3688 #else
3689                 size = file_stat.st_size;
3690 #endif
3691
3692                 /* ask user for confirmation if the file is large */
3693                 if (prefs_common.warn_large_insert_size < 0 ||
3694                     size > ((goffset) prefs_common.warn_large_insert_size * 1024)) {
3695                         AlertValue aval;
3696                         gchar *msg;
3697
3698                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3699                                                 "in the message body. Are you sure you want to do that?"),
3700                                                 to_human_readable(size));
3701                         aval = alertpanel_full(_("Are you sure?"), msg, NULL, _("_Cancel"),
3702                                         NULL, _("_Insert"), NULL, NULL, ALERTFOCUS_SECOND, TRUE,
3703                                         NULL, ALERT_QUESTION);
3704                         g_free(msg);
3705
3706                         /* do we ask for confirmation next time? */
3707                         if (aval & G_ALERTDISABLE) {
3708                                 /* no confirmation next time, disable feature in preferences */
3709                                 aval &= ~G_ALERTDISABLE;
3710                                 prefs_common.warn_large_insert = FALSE;
3711                         }
3712
3713                         /* abort file insertion if user canceled action */
3714                         if (aval != G_ALERTALTERNATE) {
3715                                 return COMPOSE_INSERT_NO_FILE;
3716                         }
3717                 }
3718         }
3719
3720
3721         if ((fp = claws_fopen(file, "rb")) == NULL) {
3722                 FILE_OP_ERROR(file, "claws_fopen");
3723                 return COMPOSE_INSERT_READ_ERROR;
3724         }
3725
3726         prev_autowrap = compose->autowrap;
3727         compose->autowrap = FALSE;
3728
3729         text = GTK_TEXT_VIEW(compose->text);
3730         buffer = gtk_text_view_get_buffer(text);
3731         mark = gtk_text_buffer_get_insert(buffer);
3732         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3733
3734         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3735                                         G_CALLBACK(text_inserted),
3736                                         compose);
3737
3738         cur_encoding = conv_get_locale_charset_str_no_utf8();
3739
3740         file_contents = g_string_new("");
3741         while (claws_fgets(buf, sizeof(buf), fp) != NULL) {
3742                 gchar *str;
3743
3744                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3745                         str = g_strdup(buf);
3746                 else {
3747                         codeconv_set_strict(TRUE);
3748                         str = conv_codeset_strdup
3749                                 (buf, cur_encoding, CS_INTERNAL);
3750                         codeconv_set_strict(FALSE);
3751
3752                         if (!str) {
3753                                 result = COMPOSE_INSERT_INVALID_CHARACTER;
3754                                 break;
3755                         }
3756                 }
3757                 if (!str) continue;
3758
3759                 /* strip <CR> if DOS/Windows file,
3760                    replace <CR> with <LF> if Macintosh file. */
3761                 strcrchomp(str);
3762                 len = strlen(str);
3763                 if (len > 0 && str[len - 1] != '\n') {
3764                         while (--len >= 0)
3765                                 if (str[len] == '\r') str[len] = '\n';
3766                 }
3767
3768                 file_contents = g_string_append(file_contents, str);
3769                 g_free(str);
3770         }
3771
3772         if (result == COMPOSE_INSERT_SUCCESS) {
3773                 gtk_text_buffer_insert(buffer, &iter, file_contents->str, -1);
3774
3775                 compose_changed_cb(NULL, compose);
3776                 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3777                                                   G_CALLBACK(text_inserted),
3778                                                   compose);
3779                 compose->autowrap = prev_autowrap;
3780                 if (compose->autowrap)
3781                         compose_wrap_all(compose);
3782         }
3783
3784         g_string_free(file_contents, TRUE);
3785         claws_fclose(fp);
3786
3787         return result;
3788 }
3789
3790 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3791                                   const gchar *filename,
3792                                   const gchar *content_type,
3793                                   const gchar *charset)
3794 {
3795         AttachInfo *ainfo;
3796         GtkTreeIter iter;
3797         FILE *fp;
3798         off_t size;
3799         GAuto *auto_ainfo;
3800         gchar *size_text;
3801         GtkListStore *store;
3802         gchar *name;
3803         gboolean has_binary = FALSE;
3804
3805         if (!is_file_exist(file)) {
3806                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3807                 gboolean result = FALSE;
3808                 if (file_from_uri && is_file_exist(file_from_uri)) {
3809                         result = compose_attach_append(
3810                                                 compose, file_from_uri,
3811                                                 filename, content_type,
3812                                                 charset);
3813                 }
3814                 g_free(file_from_uri);
3815                 if (result)
3816                         return TRUE;
3817                 alertpanel_error("File %s doesn't exist or permission denied\n", filename);
3818                 return FALSE;
3819         }
3820         if ((size = get_file_size(file)) < 0) {
3821                 alertpanel_error("Can't get file size of %s\n", filename);
3822                 return FALSE;
3823         }
3824
3825         /* In batch mode, we allow 0-length files to be attached no questions asked */
3826         if (size == 0 && !compose->batch) {
3827                 gchar * msg = g_strdup_printf(_("File %s is empty."), filename);
3828                 AlertValue aval = alertpanel_full(_("Empty file"), msg, 
3829                                                   NULL, _("_Cancel"),  NULL, _("_Attach anyway"),
3830                                                   NULL, NULL, ALERTFOCUS_SECOND, FALSE, NULL, ALERT_WARNING);
3831                 g_free(msg);
3832
3833                 if (aval != G_ALERTALTERNATE) {
3834                         return FALSE;
3835                 }
3836         }
3837         if ((fp = claws_fopen(file, "rb")) == NULL) {
3838                 alertpanel_error(_("Can't read %s."), filename);
3839                 return FALSE;
3840         }
3841         claws_fclose(fp);
3842
3843         ainfo = g_new0(AttachInfo, 1);
3844         auto_ainfo = g_auto_pointer_new_with_free
3845                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3846         ainfo->file = g_strdup(file);
3847
3848         if (content_type) {
3849                 ainfo->content_type = g_strdup(content_type);
3850                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3851                         MsgInfo *msginfo;
3852                         MsgFlags flags = {0, 0};
3853
3854                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3855                                 ainfo->encoding = ENC_7BIT;
3856                         else
3857                                 ainfo->encoding = ENC_8BIT;
3858
3859                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3860                         if (msginfo && msginfo->subject)
3861                                 name = g_strdup(msginfo->subject);
3862                         else
3863                                 name = g_path_get_basename(filename ? filename : file);
3864
3865                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3866
3867                         procmsg_msginfo_free(&msginfo);
3868                 } else {
3869                         if (!g_ascii_strncasecmp(content_type, "text/", 5)) {
3870                                 ainfo->charset = g_strdup(charset);
3871                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3872                         } else {
3873                                 ainfo->encoding = ENC_BASE64;
3874                         }
3875                         name = g_path_get_basename(filename ? filename : file);
3876                         ainfo->name = g_strdup(name);
3877                 }
3878                 g_free(name);
3879         } else {
3880                 ainfo->content_type = procmime_get_mime_type(file);
3881                 if (!ainfo->content_type) {
3882                         ainfo->content_type =
3883                                 g_strdup("application/octet-stream");
3884                         ainfo->encoding = ENC_BASE64;
3885                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text/", 5))
3886                         ainfo->encoding =
3887                                 procmime_get_encoding_for_text_file(file, &has_binary);
3888                 else
3889                         ainfo->encoding = ENC_BASE64;
3890                 name = g_path_get_basename(filename ? filename : file);
3891                 ainfo->name = g_strdup(name);   
3892                 g_free(name);
3893         }
3894
3895         if (ainfo->name != NULL
3896         &&  !strcmp(ainfo->name, ".")) {
3897                 g_free(ainfo->name);
3898                 ainfo->name = NULL;
3899         }
3900
3901         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3902                 g_free(ainfo->content_type);
3903                 ainfo->content_type = g_strdup("application/octet-stream");
3904                 g_free(ainfo->charset);
3905                 ainfo->charset = NULL;
3906         }
3907
3908         ainfo->size = (goffset)size;
3909         size_text = to_human_readable((goffset)size);
3910
3911         store = GTK_LIST_STORE(gtk_tree_view_get_model
3912                         (GTK_TREE_VIEW(compose->attach_clist)));
3913                 
3914         gtk_list_store_append(store, &iter);
3915         gtk_list_store_set(store, &iter, 
3916                            COL_MIMETYPE, ainfo->content_type,
3917                            COL_SIZE, size_text,
3918                            COL_NAME, ainfo->name,
3919                            COL_CHARSET, ainfo->charset,
3920                            COL_DATA, ainfo,
3921                            COL_AUTODATA, auto_ainfo,
3922                            -1);
3923         
3924         g_auto_pointer_free(auto_ainfo);
3925         compose_attach_update_label(compose);
3926         return TRUE;
3927 }
3928
3929 void compose_use_signing(Compose *compose, gboolean use_signing)
3930 {
3931         compose->use_signing = use_signing;
3932         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3933 }
3934
3935 void compose_use_encryption(Compose *compose, gboolean use_encryption)
3936 {
3937         compose->use_encryption = use_encryption;
3938         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3939 }
3940
3941 #define NEXT_PART_NOT_CHILD(info)  \
3942 {  \
3943         node = info->node;  \
3944         while (node->children)  \
3945                 node = g_node_last_child(node);  \
3946         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3947 }
3948
3949 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3950 {
3951         MimeInfo *mimeinfo;
3952         MimeInfo *child;
3953         MimeInfo *firsttext = NULL;
3954         MimeInfo *encrypted = NULL;
3955         GNode    *node;
3956         gchar *outfile;
3957         const gchar *partname = NULL;
3958
3959         mimeinfo = procmime_scan_message(msginfo);
3960         if (!mimeinfo) return;
3961
3962         if (mimeinfo->node->children == NULL) {
3963                 procmime_mimeinfo_free_all(&mimeinfo);
3964                 return;
3965         }
3966
3967         /* find first content part */
3968         child = (MimeInfo *) mimeinfo->node->children->data;
3969         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3970                 child = (MimeInfo *)child->node->children->data;
3971
3972         if (child) {
3973                 if (child->type == MIMETYPE_TEXT) {
3974                         firsttext = child;
3975                         debug_print("First text part found\n");
3976                 } else if (compose->mode == COMPOSE_REEDIT &&
3977                          child->type == MIMETYPE_APPLICATION &&
3978                          !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3979                         encrypted = (MimeInfo *)child->node->parent->data;
3980                 }
3981         }
3982         child = (MimeInfo *) mimeinfo->node->children->data;
3983         while (child != NULL) {
3984                 gint err;
3985
3986                 if (child == encrypted) {
3987                         /* skip this part of tree */
3988                         NEXT_PART_NOT_CHILD(child);
3989                         continue;
3990                 }
3991
3992                 if (child->type == MIMETYPE_MULTIPART) {
3993                         /* get the actual content */
3994                         child = procmime_mimeinfo_next(child);
3995                         continue;
3996                 }
3997                     
3998                 if (child == firsttext) {
3999                         child = procmime_mimeinfo_next(child);
4000                         continue;
4001                 }
4002
4003                 outfile = procmime_get_tmp_file_name(child);
4004                 if ((err = procmime_get_part(outfile, child)) < 0)
4005                         g_warning("can't get the part of multipart message. (%s)", g_strerror(-err));
4006                 else {
4007                         gchar *content_type;
4008
4009                         content_type = procmime_get_content_type_str(child->type, child->subtype);
4010
4011                         /* if we meet a pgp signature, we don't attach it, but
4012                          * we force signing. */
4013                         if ((strcmp(content_type, "application/pgp-signature") &&
4014                             strcmp(content_type, "application/pkcs7-signature") &&
4015                             strcmp(content_type, "application/x-pkcs7-signature"))
4016                             || compose->mode == COMPOSE_REDIRECT) {
4017                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
4018                                 if (partname == NULL)
4019                                         partname = procmime_mimeinfo_get_parameter(child, "name");
4020                                 if (partname == NULL)
4021                                         partname = "";
4022                                 compose_attach_append(compose, outfile, 
4023                                                       partname, content_type,
4024                                                       procmime_mimeinfo_get_parameter(child, "charset"));
4025                         } else {
4026                                 compose_force_signing(compose, compose->account, NULL);
4027                         }
4028                         g_free(content_type);
4029                 }
4030                 g_free(outfile);
4031                 NEXT_PART_NOT_CHILD(child);
4032         }
4033         procmime_mimeinfo_free_all(&mimeinfo);
4034 }
4035
4036 #undef NEXT_PART_NOT_CHILD
4037
4038
4039
4040 typedef enum {
4041         WAIT_FOR_INDENT_CHAR,
4042         WAIT_FOR_INDENT_CHAR_OR_SPACE,
4043 } IndentState;
4044
4045 /* return indent length, we allow:
4046    indent characters followed by indent characters or spaces/tabs,
4047    alphabets and numbers immediately followed by indent characters,
4048    and the repeating sequences of the above
4049    If quote ends with multiple spaces, only the first one is included. */
4050 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
4051                                     const GtkTextIter *start, gint *len)
4052 {
4053         GtkTextIter iter = *start;
4054         gunichar wc;
4055         gchar ch[6];
4056         gint clen;
4057         IndentState state = WAIT_FOR_INDENT_CHAR;
4058         gboolean is_space;
4059         gboolean is_indent;
4060         gint alnum_count = 0;
4061         gint space_count = 0;
4062         gint quote_len = 0;
4063
4064         if (prefs_common.quote_chars == NULL) {
4065                 return 0 ;
4066         }
4067
4068         while (!gtk_text_iter_ends_line(&iter)) {
4069                 wc = gtk_text_iter_get_char(&iter);
4070                 if (g_unichar_iswide(wc))
4071                         break;
4072                 clen = g_unichar_to_utf8(wc, ch);
4073                 if (clen != 1)
4074                         break;
4075
4076                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
4077                 is_space = g_unichar_isspace(wc);
4078
4079                 if (state == WAIT_FOR_INDENT_CHAR) {
4080                         if (!is_indent && !g_unichar_isalnum(wc))
4081                                 break;
4082                         if (is_indent) {
4083                                 quote_len += alnum_count + space_count + 1;
4084                                 alnum_count = space_count = 0;
4085                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
4086                         } else
4087                                 alnum_count++;
4088                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
4089                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
4090                                 break;
4091                         if (is_space)
4092                                 space_count++;
4093                         else if (is_indent) {
4094                                 quote_len += alnum_count + space_count + 1;
4095                                 alnum_count = space_count = 0;
4096                         } else {
4097                                 alnum_count++;
4098                                 state = WAIT_FOR_INDENT_CHAR;
4099                         }
4100                 }
4101
4102                 gtk_text_iter_forward_char(&iter);
4103         }
4104
4105         if (quote_len > 0 && space_count > 0)
4106                 quote_len++;
4107
4108         if (len)
4109                 *len = quote_len;
4110
4111         if (quote_len > 0) {
4112                 iter = *start;
4113                 gtk_text_iter_forward_chars(&iter, quote_len);
4114                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
4115         }
4116
4117         return NULL;
4118 }
4119
4120 /* return >0 if the line is itemized */
4121 static int compose_itemized_length(GtkTextBuffer *buffer,
4122                                     const GtkTextIter *start)
4123 {
4124         GtkTextIter iter = *start;
4125         gunichar wc;
4126         gchar ch[6];
4127         gint clen;
4128         gint len = 0;
4129         if (gtk_text_iter_ends_line(&iter))
4130                 return 0;
4131
4132         while (1) {
4133                 len++;
4134                 wc = gtk_text_iter_get_char(&iter);
4135                 if (!g_unichar_isspace(wc))
4136                         break;
4137                 gtk_text_iter_forward_char(&iter);
4138                 if (gtk_text_iter_ends_line(&iter))
4139                         return 0;
4140         }
4141
4142         clen = g_unichar_to_utf8(wc, ch);
4143         if (!((clen == 1 && strchr("*-+", ch[0])) ||
4144             (clen == 3 && (
4145                 wc == 0x2022 || /* BULLET */
4146                 wc == 0x2023 || /* TRIANGULAR BULLET */
4147                 wc == 0x2043 || /* HYPHEN BULLET */
4148                 wc == 0x204c || /* BLACK LEFTWARDS BULLET */
4149                 wc == 0x204d || /* BLACK RIGHTWARDS BULLET */
4150                 wc == 0x2219 || /* BULLET OPERATOR */
4151                 wc == 0x25d8 || /* INVERSE BULLET */
4152                 wc == 0x25e6 || /* WHITE BULLET */
4153                 wc == 0x2619 || /* REVERSED ROTATED FLORAL HEART BULLET */
4154                 wc == 0x2765 || /* ROTATED HEAVY BLACK HEART BULLET */
4155                 wc == 0x2767 || /* ROTATED FLORAL HEART BULLET */
4156                 wc == 0x29be || /* CIRCLED WHITE BULLET */
4157                 wc == 0x29bf    /* CIRCLED BULLET */
4158                 ))))
4159                 return 0;
4160
4161         gtk_text_iter_forward_char(&iter);
4162         if (gtk_text_iter_ends_line(&iter))
4163                 return 0;
4164         wc = gtk_text_iter_get_char(&iter);
4165         if (g_unichar_isspace(wc)) {
4166                 return len+1;
4167   &nb