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