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