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