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