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