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