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