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