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