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