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