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