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