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