2012-09-16 [mones] 3.8.1cvs60
[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         GdkColor color[8];
816 #endif
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 #if !GTK_CHECK_VERSION(2, 24, 0)
879         color[0] = quote_color1;
880         color[1] = quote_color2;
881         color[2] = quote_color3;
882         color[3] = quote_bgcolor1;
883         color[4] = quote_bgcolor2;
884         color[5] = quote_bgcolor3;
885         color[6] = signature_color;
886         color[7] = uri_color;
887
888         cmap = gdk_drawable_get_colormap(gtk_widget_get_window(compose->window));
889         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
890
891         for (i = 0; i < 8; i++) {
892                 if (success[i] == FALSE) {
893                         g_warning("Compose: color allocation failed.\n");
894                         quote_color1 = quote_color2 = quote_color3 = 
895                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
896                                 signature_color = uri_color = black;
897                 }
898         }
899 #endif
900 }
901
902 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
903                      GList *attach_files)
904 {
905         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
906 }
907
908 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
909 {
910         return compose_generic_new(account, mailto, item, NULL, NULL);
911 }
912
913 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
914 {
915         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
916 }
917
918 #define SCROLL_TO_CURSOR(compose) {                             \
919         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
920                 gtk_text_view_get_buffer(                       \
921                         GTK_TEXT_VIEW(compose->text)));         \
922         gtk_text_view_scroll_mark_onscreen(                     \
923                 GTK_TEXT_VIEW(compose->text),                   \
924                 cmark);                                         \
925 }
926
927 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
928 {
929         GtkEditable *entry;
930         if (folderidentifier) {
931                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
932                 prefs_common.compose_save_to_history = add_history(
933                                 prefs_common.compose_save_to_history, folderidentifier);
934                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
935                                 prefs_common.compose_save_to_history);
936         }
937
938         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
939         if (folderidentifier)
940                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
941         else
942                 gtk_entry_set_text(GTK_ENTRY(entry), "");
943 }
944
945 static gchar *compose_get_save_to(Compose *compose)
946 {
947         GtkEditable *entry;
948         gchar *result = NULL;
949         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
950         result = gtk_editable_get_chars(entry, 0, -1);
951         
952         if (result) {
953                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
954                 prefs_common.compose_save_to_history = add_history(
955                                 prefs_common.compose_save_to_history, result);
956                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
957                                 prefs_common.compose_save_to_history);
958         }
959         return result;
960 }
961
962 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
963                              GList *attach_files, GList *listAddress )
964 {
965         Compose *compose;
966         GtkTextView *textview;
967         GtkTextBuffer *textbuf;
968         GtkTextIter iter;
969         const gchar *subject_format = NULL;
970         const gchar *body_format = NULL;
971         gchar *mailto_from = NULL;
972         PrefsAccount *mailto_account = NULL;
973         MsgInfo* dummyinfo = NULL;
974         gint cursor_pos = -1;
975         MailField mfield = NO_FIELD_PRESENT;
976         gchar* buf;
977         GtkTextMark *mark;
978
979         /* check if mailto defines a from */
980         if (mailto && *mailto != '\0') {
981                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
982                 /* mailto defines a from, check if we can get account prefs from it,
983                    if not, the account prefs will be guessed using other ways, but we'll keep
984                    the from anyway */
985                 if (mailto_from)
986                         mailto_account = account_find_from_address(mailto_from, TRUE);
987                 if (mailto_account)
988                         account = mailto_account;
989         }
990
991         /* if no account prefs set from mailto, set if from folder prefs (if any) */
992         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
993                 account = account_find_from_id(item->prefs->default_account);
994
995         /* if no account prefs set, fallback to the current one */
996         if (!account) account = cur_account;
997         cm_return_val_if_fail(account != NULL, NULL);
998
999         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1000
1001         /* override from name if mailto asked for it */
1002         if (mailto_from) {
1003                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
1004                 g_free(mailto_from);
1005         } else
1006                 /* override from name according to folder properties */
1007                 if (item && item->prefs &&
1008                         item->prefs->compose_with_format &&
1009                         item->prefs->compose_override_from_format &&
1010                         *item->prefs->compose_override_from_format != '\0') {
1011
1012                         gchar *tmp = NULL;
1013                         gchar *buf = NULL;
1014
1015                         dummyinfo = compose_msginfo_new_from_compose(compose);
1016
1017                         /* decode \-escape sequences in the internal representation of the quote format */
1018                         tmp = g_malloc(strlen(item->prefs->compose_override_from_format)+1);
1019                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
1020
1021 #ifdef USE_ENCHANT
1022                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1023                                         compose->gtkaspell);
1024 #else
1025                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1026 #endif
1027                         quote_fmt_scan_string(tmp);
1028                         quote_fmt_parse();
1029
1030                         buf = quote_fmt_get_buffer();
1031                         if (buf == NULL)
1032                                 alertpanel_error(_("New message From format error."));
1033                         else
1034                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1035                         quote_fmt_reset_vartable();
1036
1037                         g_free(tmp);
1038                 }
1039
1040         compose->replyinfo = NULL;
1041         compose->fwdinfo   = NULL;
1042
1043         textview = GTK_TEXT_VIEW(compose->text);
1044         textbuf = gtk_text_view_get_buffer(textview);
1045         compose_create_tags(textview, compose);
1046
1047         undo_block(compose->undostruct);
1048 #ifdef USE_ENCHANT
1049         compose_set_dictionaries_from_folder_prefs(compose, item);
1050 #endif
1051
1052         if (account->auto_sig)
1053                 compose_insert_sig(compose, FALSE);
1054         gtk_text_buffer_get_start_iter(textbuf, &iter);
1055         gtk_text_buffer_place_cursor(textbuf, &iter);
1056
1057         if (account->protocol != A_NNTP) {
1058                 if (mailto && *mailto != '\0') {
1059                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1060
1061                 } else {
1062                         compose_set_folder_prefs(compose, item, TRUE);
1063                 }
1064                 if (item && item->ret_rcpt) {
1065                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1066                 }
1067         } else {
1068                 if (mailto && *mailto != '\0') {
1069                         if (!strchr(mailto, '@'))
1070                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1071                         else
1072                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1073                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1074                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1075                         mfield = TO_FIELD_PRESENT;
1076                 }
1077                 /*
1078                  * CLAWS: just don't allow return receipt request, even if the user
1079                  * may want to send an email. simple but foolproof.
1080                  */
1081                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1082         }
1083         compose_add_field_list( compose, listAddress );
1084
1085         if (item && item->prefs && item->prefs->compose_with_format) {
1086                 subject_format = item->prefs->compose_subject_format;
1087                 body_format = item->prefs->compose_body_format;
1088         } else if (account->compose_with_format) {
1089                 subject_format = account->compose_subject_format;
1090                 body_format = account->compose_body_format;
1091         } else if (prefs_common.compose_with_format) {
1092                 subject_format = prefs_common.compose_subject_format;
1093                 body_format = prefs_common.compose_body_format;
1094         }
1095
1096         if (subject_format || body_format) {
1097
1098                 if ( subject_format
1099                          && *subject_format != '\0' )
1100                 {
1101                         gchar *subject = NULL;
1102                         gchar *tmp = NULL;
1103                         gchar *buf = NULL;
1104
1105                         if (!dummyinfo)
1106                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1107
1108                         /* decode \-escape sequences in the internal representation of the quote format */
1109                         tmp = g_malloc(strlen(subject_format)+1);
1110                         pref_get_unescaped_pref(tmp, subject_format);
1111
1112                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1113 #ifdef USE_ENCHANT
1114                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1115                                         compose->gtkaspell);
1116 #else
1117                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1118 #endif
1119                         quote_fmt_scan_string(tmp);
1120                         quote_fmt_parse();
1121
1122                         buf = quote_fmt_get_buffer();
1123                         if (buf == NULL)
1124                                 alertpanel_error(_("New message subject format error."));
1125                         else
1126                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1127                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1128                         quote_fmt_reset_vartable();
1129
1130                         g_free(subject);
1131                         g_free(tmp);
1132                         mfield = SUBJECT_FIELD_PRESENT;
1133                 }
1134
1135                 if ( body_format
1136                          && *body_format != '\0' )
1137                 {
1138                         GtkTextView *text;
1139                         GtkTextBuffer *buffer;
1140                         GtkTextIter start, end;
1141                         gchar *tmp = NULL;
1142
1143                         if (!dummyinfo)
1144                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1145
1146                         text = GTK_TEXT_VIEW(compose->text);
1147                         buffer = gtk_text_view_get_buffer(text);
1148                         gtk_text_buffer_get_start_iter(buffer, &start);
1149                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1150                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1151
1152                         compose_quote_fmt(compose, dummyinfo,
1153                                           body_format,
1154                                           NULL, tmp, FALSE, TRUE,
1155                                                   _("The body of the \"New message\" template has an error at line %d."));
1156                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1157                         quote_fmt_reset_vartable();
1158
1159                         g_free(tmp);
1160 #ifdef USE_ENCHANT
1161                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1162                                 gtkaspell_highlight_all(compose->gtkaspell);
1163 #endif
1164                         mfield = BODY_FIELD_PRESENT;
1165                 }
1166
1167         }
1168         procmsg_msginfo_free( dummyinfo );
1169
1170         if (attach_files) {
1171                 GList *curr;
1172                 AttachInfo *ainfo;
1173
1174                 for (curr = attach_files ; curr != NULL ; curr = curr->next) {
1175                         ainfo = (AttachInfo *) curr->data;
1176                         compose_attach_append(compose, ainfo->file, ainfo->name,
1177                                         ainfo->content_type, ainfo->charset);
1178                 }
1179         }
1180
1181         compose_show_first_last_header(compose, TRUE);
1182
1183         /* Set save folder */
1184         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1185                 gchar *folderidentifier;
1186
1187                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1188                 folderidentifier = folder_item_get_identifier(item);
1189                 compose_set_save_to(compose, folderidentifier);
1190                 g_free(folderidentifier);
1191         }
1192
1193         /* Place cursor according to provided input (mfield) */
1194         switch (mfield) { 
1195                 case NO_FIELD_PRESENT:
1196                         if (compose->header_last)
1197                                 gtk_widget_grab_focus(compose->header_last->entry);
1198                         break;
1199                 case TO_FIELD_PRESENT:
1200                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1201                         if (buf) {
1202                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1203                                 g_free(buf);
1204                         }
1205                         gtk_widget_grab_focus(compose->subject_entry);
1206                         break;
1207                 case SUBJECT_FIELD_PRESENT:
1208                         textview = GTK_TEXT_VIEW(compose->text);
1209                         if (!textview)
1210                                 break;
1211                         textbuf = gtk_text_view_get_buffer(textview);
1212                         if (!textbuf)
1213                                 break;
1214                         mark = gtk_text_buffer_get_insert(textbuf);
1215                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1216                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1217                     /* 
1218                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1219                      * only defers where it comes to the variable body
1220                      * is not null. If no body is present compose->text
1221                      * will be null in which case you cannot place the
1222                      * cursor inside the component so. An empty component
1223                      * is therefore created before placing the cursor
1224                      */
1225                 case BODY_FIELD_PRESENT:
1226                         cursor_pos = quote_fmt_get_cursor_pos();
1227                         if (cursor_pos == -1)
1228                                 gtk_widget_grab_focus(compose->header_last->entry);
1229                         else
1230                                 gtk_widget_grab_focus(compose->text);
1231                         break;
1232         }
1233
1234         undo_unblock(compose->undostruct);
1235
1236         if (prefs_common.auto_exteditor)
1237                 compose_exec_ext_editor(compose);
1238
1239         compose->draft_timeout_tag = -1;
1240         SCROLL_TO_CURSOR(compose);
1241
1242         compose->modified = FALSE;
1243         compose_set_title(compose);
1244
1245         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1246
1247         return compose;
1248 }
1249
1250 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1251                 gboolean override_pref, const gchar *system)
1252 {
1253         const gchar *privacy = NULL;
1254
1255         cm_return_if_fail(compose != NULL);
1256         cm_return_if_fail(account != NULL);
1257
1258         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1259                 return;
1260
1261         if (system)
1262                 privacy = system;
1263         else if (account->default_privacy_system
1264         &&  strlen(account->default_privacy_system)) {
1265                 privacy = account->default_privacy_system;
1266         } else {
1267                 GSList *privacy_avail = privacy_get_system_ids();
1268                 if (privacy_avail && g_slist_length(privacy_avail)) {
1269                         privacy = (gchar *)(privacy_avail->data);
1270                 }
1271         }
1272         if (privacy != NULL) {
1273                 if (system) {
1274                         g_free(compose->privacy_system);
1275                         compose->privacy_system = NULL;
1276                 }
1277                 if (compose->privacy_system == NULL)
1278                         compose->privacy_system = g_strdup(privacy);
1279                 else if (*(compose->privacy_system) == '\0') {
1280                         g_free(compose->privacy_system);
1281                         compose->privacy_system = g_strdup(privacy);
1282                 }
1283                 compose_update_privacy_system_menu_item(compose, FALSE);
1284                 compose_use_encryption(compose, TRUE);
1285         }
1286 }       
1287
1288 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1289 {
1290         const gchar *privacy = NULL;
1291
1292         if (system)
1293                 privacy = system;
1294         else if (account->default_privacy_system
1295         &&  strlen(account->default_privacy_system)) {
1296                 privacy = account->default_privacy_system;
1297         } else {
1298                 GSList *privacy_avail = privacy_get_system_ids();
1299                 if (privacy_avail && g_slist_length(privacy_avail)) {
1300                         privacy = (gchar *)(privacy_avail->data);
1301                 }
1302         }
1303
1304         if (privacy != NULL) {
1305                 if (system) {
1306                         g_free(compose->privacy_system);
1307                         compose->privacy_system = NULL;
1308                 }
1309                 if (compose->privacy_system == NULL)
1310                         compose->privacy_system = g_strdup(privacy);
1311                 compose_update_privacy_system_menu_item(compose, FALSE);
1312                 compose_use_signing(compose, TRUE);
1313         }
1314 }       
1315
1316 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1317 {
1318         MsgInfo *msginfo;
1319         guint list_len;
1320         Compose *compose = NULL;
1321         
1322         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1323
1324         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1325         cm_return_val_if_fail(msginfo != NULL, NULL);
1326
1327         list_len = g_slist_length(msginfo_list);
1328
1329         switch (mode) {
1330         case COMPOSE_REPLY:
1331         case COMPOSE_REPLY_TO_ADDRESS:
1332                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1333                               FALSE, prefs_common.default_reply_list, FALSE, body);
1334                 break;
1335         case COMPOSE_REPLY_WITH_QUOTE:
1336                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1337                         FALSE, prefs_common.default_reply_list, FALSE, body);
1338                 break;
1339         case COMPOSE_REPLY_WITHOUT_QUOTE:
1340                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1341                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1342                 break;
1343         case COMPOSE_REPLY_TO_SENDER:
1344                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1345                               FALSE, FALSE, TRUE, body);
1346                 break;
1347         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1348                 compose = compose_followup_and_reply_to(msginfo,
1349                                               COMPOSE_QUOTE_CHECK,
1350                                               FALSE, FALSE, body);
1351                 break;
1352         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1353                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1354                         FALSE, FALSE, TRUE, body);
1355                 break;
1356         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1357                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1358                         FALSE, FALSE, TRUE, NULL);
1359                 break;
1360         case COMPOSE_REPLY_TO_ALL:
1361                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1362                         TRUE, FALSE, FALSE, body);
1363                 break;
1364         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1365                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1366                         TRUE, FALSE, FALSE, body);
1367                 break;
1368         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1369                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1370                         TRUE, FALSE, FALSE, NULL);
1371                 break;
1372         case COMPOSE_REPLY_TO_LIST:
1373                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1374                         FALSE, TRUE, FALSE, body);
1375                 break;
1376         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1377                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1378                         FALSE, TRUE, FALSE, body);
1379                 break;
1380         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1381                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1382                         FALSE, TRUE, FALSE, NULL);
1383                 break;
1384         case COMPOSE_FORWARD:
1385                 if (prefs_common.forward_as_attachment) {
1386                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1387                         return compose;
1388                 } else {
1389                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1390                         return compose;
1391                 }
1392                 break;
1393         case COMPOSE_FORWARD_INLINE:
1394                 /* check if we reply to more than one Message */
1395                 if (list_len == 1) {
1396                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1397                         break;
1398                 } 
1399                 /* more messages FALL THROUGH */
1400         case COMPOSE_FORWARD_AS_ATTACH:
1401                 compose = compose_forward_multiple(NULL, msginfo_list);
1402                 break;
1403         case COMPOSE_REDIRECT:
1404                 compose = compose_redirect(NULL, msginfo, FALSE);
1405                 break;
1406         default:
1407                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1408         }
1409         
1410         if (compose == NULL) {
1411                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1412                 return NULL;
1413         }
1414
1415         compose->rmode = mode;
1416         switch (compose->rmode) {
1417         case COMPOSE_REPLY:
1418         case COMPOSE_REPLY_WITH_QUOTE:
1419         case COMPOSE_REPLY_WITHOUT_QUOTE:
1420         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1421                 debug_print("reply mode Normal\n");
1422                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1423                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1424                 break;
1425         case COMPOSE_REPLY_TO_SENDER:
1426         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1427         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1428                 debug_print("reply mode Sender\n");
1429                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1430                 break;
1431         case COMPOSE_REPLY_TO_ALL:
1432         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1433         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1434                 debug_print("reply mode All\n");
1435                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1436                 break;
1437         case COMPOSE_REPLY_TO_LIST:
1438         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1439         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1440                 debug_print("reply mode List\n");
1441                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1442                 break;
1443         case COMPOSE_REPLY_TO_ADDRESS:
1444                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/ReplyMode", FALSE);
1445                 break;
1446         default:
1447                 break;
1448         }
1449         return compose;
1450 }
1451
1452 static Compose *compose_reply(MsgInfo *msginfo,
1453                                    ComposeQuoteMode quote_mode,
1454                                    gboolean to_all,
1455                                    gboolean to_ml,
1456                                    gboolean to_sender, 
1457                                    const gchar *body)
1458 {
1459         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1460                               to_sender, FALSE, body);
1461 }
1462
1463 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1464                                    ComposeQuoteMode quote_mode,
1465                                    gboolean to_all,
1466                                    gboolean to_sender,
1467                                    const gchar *body)
1468 {
1469         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1470                               to_sender, TRUE, body);
1471 }
1472
1473 static void compose_extract_original_charset(Compose *compose)
1474 {
1475         MsgInfo *info = NULL;
1476         if (compose->replyinfo) {
1477                 info = compose->replyinfo;
1478         } else if (compose->fwdinfo) {
1479                 info = compose->fwdinfo;
1480         } else if (compose->targetinfo) {
1481                 info = compose->targetinfo;
1482         }
1483         if (info) {
1484                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1485                 MimeInfo *partinfo = mimeinfo;
1486                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1487                         partinfo = procmime_mimeinfo_next(partinfo);
1488                 if (partinfo) {
1489                         compose->orig_charset = 
1490                                 g_strdup(procmime_mimeinfo_get_parameter(
1491                                                 partinfo, "charset"));
1492                 }
1493                 procmime_mimeinfo_free_all(mimeinfo);
1494         }
1495 }
1496
1497 #define SIGNAL_BLOCK(buffer) {                                  \
1498         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1499                                 G_CALLBACK(compose_changed_cb), \
1500                                 compose);                       \
1501         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1502                                 G_CALLBACK(text_inserted),      \
1503                                 compose);                       \
1504 }
1505
1506 #define SIGNAL_UNBLOCK(buffer) {                                \
1507         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1508                                 G_CALLBACK(compose_changed_cb), \
1509                                 compose);                       \
1510         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1511                                 G_CALLBACK(text_inserted),      \
1512                                 compose);                       \
1513 }
1514
1515 static Compose *compose_generic_reply(MsgInfo *msginfo,
1516                                   ComposeQuoteMode quote_mode,
1517                                   gboolean to_all, gboolean to_ml,
1518                                   gboolean to_sender,
1519                                   gboolean followup_and_reply_to,
1520                                   const gchar *body)
1521 {
1522         Compose *compose;
1523         PrefsAccount *account = NULL;
1524         GtkTextView *textview;
1525         GtkTextBuffer *textbuf;
1526         gboolean quote = FALSE;
1527         const gchar *qmark = NULL;
1528         const gchar *body_fmt = NULL;
1529         gchar *s_system = NULL;
1530         START_TIMING("");
1531         cm_return_val_if_fail(msginfo != NULL, NULL);
1532         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1533
1534         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1535
1536         cm_return_val_if_fail(account != NULL, NULL);
1537
1538         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1539
1540         compose->updating = TRUE;
1541
1542         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1543         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1544
1545         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1546         if (!compose->replyinfo)
1547                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1548
1549         compose_extract_original_charset(compose);
1550         
1551         if (msginfo->folder && msginfo->folder->ret_rcpt)
1552                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1553
1554         /* Set save folder */
1555         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1556                 gchar *folderidentifier;
1557
1558                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1559                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1560                 compose_set_save_to(compose, folderidentifier);
1561                 g_free(folderidentifier);
1562         }
1563
1564         if (compose_parse_header(compose, msginfo) < 0) {
1565                 compose->updating = FALSE;
1566                 compose_destroy(compose);
1567                 return NULL;
1568         }
1569
1570         /* override from name according to folder properties */
1571         if (msginfo->folder && msginfo->folder->prefs &&
1572                 msginfo->folder->prefs->reply_with_format &&
1573                 msginfo->folder->prefs->reply_override_from_format &&
1574                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1575
1576                 gchar *tmp = NULL;
1577                 gchar *buf = NULL;
1578
1579                 /* decode \-escape sequences in the internal representation of the quote format */
1580                 tmp = g_malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1581                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1582
1583 #ifdef USE_ENCHANT
1584                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1585                                 compose->gtkaspell);
1586 #else
1587                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1588 #endif
1589                 quote_fmt_scan_string(tmp);
1590                 quote_fmt_parse();
1591
1592                 buf = quote_fmt_get_buffer();
1593                 if (buf == NULL)
1594                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1595                 else
1596                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1597                 quote_fmt_reset_vartable();
1598
1599                 g_free(tmp);
1600         }
1601
1602         textview = (GTK_TEXT_VIEW(compose->text));
1603         textbuf = gtk_text_view_get_buffer(textview);
1604         compose_create_tags(textview, compose);
1605
1606         undo_block(compose->undostruct);
1607 #ifdef USE_ENCHANT
1608         compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1609         gtkaspell_block_check(compose->gtkaspell);
1610 #endif
1611
1612         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1613                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1614                 /* use the reply format of folder (if enabled), or the account's one
1615                    (if enabled) or fallback to the global reply format, which is always
1616                    enabled (even if empty), and use the relevant quotemark */
1617                 quote = TRUE;
1618                 if (msginfo->folder && msginfo->folder->prefs &&
1619                                 msginfo->folder->prefs->reply_with_format) {
1620                         qmark = msginfo->folder->prefs->reply_quotemark;
1621                         body_fmt = msginfo->folder->prefs->reply_body_format;
1622
1623                 } else if (account->reply_with_format) {
1624                         qmark = account->reply_quotemark;
1625                         body_fmt = account->reply_body_format;
1626
1627                 } else {
1628                         qmark = prefs_common.quotemark;
1629                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1630                                 body_fmt = gettext(prefs_common.quotefmt);
1631                         else
1632                                 body_fmt = "";
1633                 }
1634         }
1635
1636         if (quote) {
1637                 /* empty quotemark is not allowed */
1638                 if (qmark == NULL || *qmark == '\0')
1639                         qmark = "> ";
1640                 compose_quote_fmt(compose, compose->replyinfo,
1641                                   body_fmt, qmark, body, FALSE, TRUE,
1642                                           _("The body of the \"Reply\" template has an error at line %d."));
1643                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1644                 quote_fmt_reset_vartable();
1645         }
1646
1647         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1648                 compose_force_encryption(compose, account, FALSE, s_system);
1649         }
1650
1651         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1652         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1653                 compose_force_signing(compose, account, s_system);
1654         }
1655         g_free(s_system);
1656
1657         SIGNAL_BLOCK(textbuf);
1658         
1659         if (account->auto_sig)
1660                 compose_insert_sig(compose, FALSE);
1661
1662         compose_wrap_all(compose);
1663
1664 #ifdef USE_ENCHANT
1665         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1666                 gtkaspell_highlight_all(compose->gtkaspell);
1667         gtkaspell_unblock_check(compose->gtkaspell);
1668 #endif
1669         SIGNAL_UNBLOCK(textbuf);
1670         
1671         gtk_widget_grab_focus(compose->text);
1672
1673         undo_unblock(compose->undostruct);
1674
1675         if (prefs_common.auto_exteditor)
1676                 compose_exec_ext_editor(compose);
1677                 
1678         compose->modified = FALSE;
1679         compose_set_title(compose);
1680
1681         compose->updating = FALSE;
1682         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1683         SCROLL_TO_CURSOR(compose);
1684         
1685         if (compose->deferred_destroy) {
1686                 compose_destroy(compose);
1687                 return NULL;
1688         }
1689         END_TIMING();
1690
1691         return compose;
1692 }
1693
1694 #define INSERT_FW_HEADER(var, hdr) \
1695 if (msginfo->var && *msginfo->var) { \
1696         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1697         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1698         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1699 }
1700
1701 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1702                          gboolean as_attach, const gchar *body,
1703                          gboolean no_extedit,
1704                          gboolean batch)
1705 {
1706         Compose *compose;
1707         GtkTextView *textview;
1708         GtkTextBuffer *textbuf;
1709         gint cursor_pos = -1;
1710         ComposeMode mode;
1711
1712         cm_return_val_if_fail(msginfo != NULL, NULL);
1713         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1714
1715         if (!account && 
1716             !(account = compose_guess_forward_account_from_msginfo
1717                                 (msginfo)))
1718                 account = cur_account;
1719
1720         if (!prefs_common.forward_as_attachment)
1721                 mode = COMPOSE_FORWARD_INLINE;
1722         else
1723                 mode = COMPOSE_FORWARD;
1724         compose = compose_create(account, msginfo->folder, mode, batch);
1725
1726         compose->updating = TRUE;
1727         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1728         if (!compose->fwdinfo)
1729                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1730
1731         compose_extract_original_charset(compose);
1732
1733         if (msginfo->subject && *msginfo->subject) {
1734                 gchar *buf, *buf2, *p;
1735
1736                 buf = p = g_strdup(msginfo->subject);
1737                 p += subject_get_prefix_length(p);
1738                 memmove(buf, p, strlen(p) + 1);
1739
1740                 buf2 = g_strdup_printf("Fw: %s", buf);
1741                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1742                 
1743                 g_free(buf);
1744                 g_free(buf2);
1745         }
1746
1747         /* override from name according to folder properties */
1748         if (msginfo->folder && msginfo->folder->prefs &&
1749                 msginfo->folder->prefs->forward_with_format &&
1750                 msginfo->folder->prefs->forward_override_from_format &&
1751                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1752
1753                 gchar *tmp = NULL;
1754                 gchar *buf = NULL;
1755                 MsgInfo *full_msginfo = NULL;
1756
1757                 if (!as_attach)
1758                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1759                 if (!full_msginfo)
1760                         full_msginfo = procmsg_msginfo_copy(msginfo);
1761
1762                 /* decode \-escape sequences in the internal representation of the quote format */
1763                 tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1764                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1765
1766 #ifdef USE_ENCHANT
1767                 gtkaspell_block_check(compose->gtkaspell);
1768                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1769                                 compose->gtkaspell);
1770 #else
1771                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1772 #endif
1773                 quote_fmt_scan_string(tmp);
1774                 quote_fmt_parse();
1775
1776                 buf = quote_fmt_get_buffer();
1777                 if (buf == NULL)
1778                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1779                 else
1780                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1781                 quote_fmt_reset_vartable();
1782
1783                 g_free(tmp);
1784                 procmsg_msginfo_free(full_msginfo);
1785         }
1786
1787         textview = GTK_TEXT_VIEW(compose->text);
1788         textbuf = gtk_text_view_get_buffer(textview);
1789         compose_create_tags(textview, compose);
1790         
1791         undo_block(compose->undostruct);
1792         if (as_attach) {
1793                 gchar *msgfile;
1794
1795                 msgfile = procmsg_get_message_file(msginfo);
1796                 if (!is_file_exist(msgfile))
1797                         g_warning("%s: file not exist\n", msgfile);
1798                 else
1799                         compose_attach_append(compose, msgfile, msgfile,
1800                                               "message/rfc822", NULL);
1801
1802                 g_free(msgfile);
1803         } else {
1804                 const gchar *qmark = NULL;
1805                 const gchar *body_fmt = NULL;
1806                 MsgInfo *full_msginfo;
1807
1808                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1809                 if (!full_msginfo)
1810                         full_msginfo = procmsg_msginfo_copy(msginfo);
1811
1812                 /* use the forward format of folder (if enabled), or the account's one
1813                    (if enabled) or fallback to the global forward format, which is always
1814                    enabled (even if empty), and use the relevant quotemark */
1815                 if (msginfo->folder && msginfo->folder->prefs &&
1816                                 msginfo->folder->prefs->forward_with_format) {
1817                         qmark = msginfo->folder->prefs->forward_quotemark;
1818                         body_fmt = msginfo->folder->prefs->forward_body_format;
1819
1820                 } else if (account->forward_with_format) {
1821                         qmark = account->forward_quotemark;
1822                         body_fmt = account->forward_body_format;
1823
1824                 } else {
1825                         qmark = prefs_common.fw_quotemark;
1826                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1827                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1828                         else
1829                                 body_fmt = "";
1830                 }
1831
1832                 /* empty quotemark is not allowed */
1833                 if (qmark == NULL || *qmark == '\0')
1834                         qmark = "> ";
1835
1836                 compose_quote_fmt(compose, full_msginfo,
1837                                   body_fmt, qmark, body, FALSE, TRUE,
1838                                           _("The body of the \"Forward\" template has an error at line %d."));
1839                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1840                 quote_fmt_reset_vartable();
1841                 compose_attach_parts(compose, msginfo);
1842
1843                 procmsg_msginfo_free(full_msginfo);
1844         }
1845
1846         SIGNAL_BLOCK(textbuf);
1847
1848         if (account->auto_sig)
1849                 compose_insert_sig(compose, FALSE);
1850
1851         compose_wrap_all(compose);
1852
1853 #ifdef USE_ENCHANT
1854         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1855                 gtkaspell_highlight_all(compose->gtkaspell);
1856         gtkaspell_unblock_check(compose->gtkaspell);
1857 #endif
1858         SIGNAL_UNBLOCK(textbuf);
1859         
1860         cursor_pos = quote_fmt_get_cursor_pos();
1861         if (cursor_pos == -1)
1862                 gtk_widget_grab_focus(compose->header_last->entry);
1863         else
1864                 gtk_widget_grab_focus(compose->text);
1865
1866         if (!no_extedit && prefs_common.auto_exteditor)
1867                 compose_exec_ext_editor(compose);
1868         
1869         /*save folder*/
1870         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1871                 gchar *folderidentifier;
1872
1873                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1874                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1875                 compose_set_save_to(compose, folderidentifier);
1876                 g_free(folderidentifier);
1877         }
1878
1879         undo_unblock(compose->undostruct);
1880         
1881         compose->modified = FALSE;
1882         compose_set_title(compose);
1883
1884         compose->updating = FALSE;
1885         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1886         SCROLL_TO_CURSOR(compose);
1887
1888         if (compose->deferred_destroy) {
1889                 compose_destroy(compose);
1890                 return NULL;
1891         }
1892
1893         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1894
1895         return compose;
1896 }
1897
1898 #undef INSERT_FW_HEADER
1899
1900 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1901 {
1902         Compose *compose;
1903         GtkTextView *textview;
1904         GtkTextBuffer *textbuf;
1905         GtkTextIter iter;
1906         GSList *msginfo;
1907         gchar *msgfile;
1908         gboolean single_mail = TRUE;
1909         
1910         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1911
1912         if (g_slist_length(msginfo_list) > 1)
1913                 single_mail = FALSE;
1914
1915         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1916                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1917                         return NULL;
1918
1919         /* guess account from first selected message */
1920         if (!account && 
1921             !(account = compose_guess_forward_account_from_msginfo
1922                                 (msginfo_list->data)))
1923                 account = cur_account;
1924
1925         cm_return_val_if_fail(account != NULL, NULL);
1926
1927         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1928                 if (msginfo->data) {
1929                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1930                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1931                 }
1932         }
1933
1934         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1935                 g_warning("no msginfo_list");
1936                 return NULL;
1937         }
1938
1939         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1940
1941         compose->updating = TRUE;
1942
1943         /* override from name according to folder properties */
1944         if (msginfo_list->data) {
1945                 MsgInfo *msginfo = msginfo_list->data;
1946
1947                 if (msginfo->folder && msginfo->folder->prefs &&
1948                         msginfo->folder->prefs->forward_with_format &&
1949                         msginfo->folder->prefs->forward_override_from_format &&
1950                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1951
1952                         gchar *tmp = NULL;
1953                         gchar *buf = NULL;
1954
1955                         /* decode \-escape sequences in the internal representation of the quote format */
1956                         tmp = g_malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1957                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1958
1959 #ifdef USE_ENCHANT
1960                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1961                                         compose->gtkaspell);
1962 #else
1963                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1964 #endif
1965                         quote_fmt_scan_string(tmp);
1966                         quote_fmt_parse();
1967
1968                         buf = quote_fmt_get_buffer();
1969                         if (buf == NULL)
1970                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1971                         else
1972                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1973                         quote_fmt_reset_vartable();
1974
1975                         g_free(tmp);
1976                 }
1977         }
1978
1979         textview = GTK_TEXT_VIEW(compose->text);
1980         textbuf = gtk_text_view_get_buffer(textview);
1981         compose_create_tags(textview, compose);
1982         
1983         undo_block(compose->undostruct);
1984         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1985                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1986
1987                 if (!is_file_exist(msgfile))
1988                         g_warning("%s: file not exist\n", msgfile);
1989                 else
1990                         compose_attach_append(compose, msgfile, msgfile,
1991                                 "message/rfc822", NULL);
1992                 g_free(msgfile);
1993         }
1994         
1995         if (single_mail) {
1996                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1997                 if (info->subject && *info->subject) {
1998                         gchar *buf, *buf2, *p;
1999
2000                         buf = p = g_strdup(info->subject);
2001                         p += subject_get_prefix_length(p);
2002                         memmove(buf, p, strlen(p) + 1);
2003
2004                         buf2 = g_strdup_printf("Fw: %s", buf);
2005                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2006
2007                         g_free(buf);
2008                         g_free(buf2);
2009                 }
2010         } else {
2011                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2012                         _("Fw: multiple emails"));
2013         }
2014
2015         SIGNAL_BLOCK(textbuf);
2016         
2017         if (account->auto_sig)
2018                 compose_insert_sig(compose, FALSE);
2019
2020         compose_wrap_all(compose);
2021
2022         SIGNAL_UNBLOCK(textbuf);
2023         
2024         gtk_text_buffer_get_start_iter(textbuf, &iter);
2025         gtk_text_buffer_place_cursor(textbuf, &iter);
2026
2027         gtk_widget_grab_focus(compose->header_last->entry);
2028         undo_unblock(compose->undostruct);
2029         compose->modified = FALSE;
2030         compose_set_title(compose);
2031
2032         compose->updating = FALSE;
2033         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2034         SCROLL_TO_CURSOR(compose);
2035
2036         if (compose->deferred_destroy) {
2037                 compose_destroy(compose);
2038                 return NULL;
2039         }
2040
2041         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2042
2043         return compose;
2044 }
2045
2046 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2047 {
2048         GtkTextIter start = *iter;
2049         GtkTextIter end_iter;
2050         int start_pos = gtk_text_iter_get_offset(&start);
2051         gchar *str = NULL;
2052         if (!compose->account->sig_sep)
2053                 return FALSE;
2054         
2055         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2056                 start_pos+strlen(compose->account->sig_sep));
2057
2058         /* check sig separator */
2059         str = gtk_text_iter_get_text(&start, &end_iter);
2060         if (!strcmp(str, compose->account->sig_sep)) {
2061                 gchar *tmp = NULL;
2062                 /* check end of line (\n) */
2063                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2064                         start_pos+strlen(compose->account->sig_sep));
2065                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2066                         start_pos+strlen(compose->account->sig_sep)+1);
2067                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2068                 if (!strcmp(tmp,"\n")) {
2069                         g_free(str);
2070                         g_free(tmp);
2071                         return TRUE;
2072                 }
2073                 g_free(tmp);    
2074         }
2075         g_free(str);
2076
2077         return FALSE;
2078 }
2079
2080 static void compose_colorize_signature(Compose *compose)
2081 {
2082         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2083         GtkTextIter iter;
2084         GtkTextIter end_iter;
2085         gtk_text_buffer_get_start_iter(buffer, &iter);
2086         while (gtk_text_iter_forward_line(&iter))
2087                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2088                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2089                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2090                 }
2091 }
2092
2093 #define BLOCK_WRAP() {                                                  \
2094         prev_autowrap = compose->autowrap;                              \
2095         buffer = gtk_text_view_get_buffer(                              \
2096                                         GTK_TEXT_VIEW(compose->text));  \
2097         compose->autowrap = FALSE;                                      \
2098                                                                         \
2099         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2100                                 G_CALLBACK(compose_changed_cb),         \
2101                                 compose);                               \
2102         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2103                                 G_CALLBACK(text_inserted),              \
2104                                 compose);                               \
2105 }
2106 #define UNBLOCK_WRAP() {                                                \
2107         compose->autowrap = prev_autowrap;                              \
2108         if (compose->autowrap) {                                        \
2109                 gint old = compose->draft_timeout_tag;                  \
2110                 compose->draft_timeout_tag = -2;                        \
2111                 compose_wrap_all(compose);                              \
2112                 compose->draft_timeout_tag = old;                       \
2113         }                                                               \
2114                                                                         \
2115         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2116                                 G_CALLBACK(compose_changed_cb),         \
2117                                 compose);                               \
2118         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2119                                 G_CALLBACK(text_inserted),              \
2120                                 compose);                               \
2121 }
2122
2123 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2124 {
2125         Compose *compose = NULL;
2126         PrefsAccount *account = NULL;
2127         GtkTextView *textview;
2128         GtkTextBuffer *textbuf;
2129         GtkTextMark *mark;
2130         GtkTextIter iter;
2131         FILE *fp;
2132         gchar buf[BUFFSIZE];
2133         gboolean use_signing = FALSE;
2134         gboolean use_encryption = FALSE;
2135         gchar *privacy_system = NULL;
2136         int priority = PRIORITY_NORMAL;
2137         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2138         gboolean autowrap = prefs_common.autowrap;
2139         gboolean autoindent = prefs_common.auto_indent;
2140         HeaderEntry *manual_headers = NULL;
2141
2142         cm_return_val_if_fail(msginfo != NULL, NULL);
2143         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2144
2145         if (compose_put_existing_to_front(msginfo)) {
2146                 return NULL;
2147         }
2148
2149         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2150             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2151             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2152                 gchar queueheader_buf[BUFFSIZE];
2153                 gint id, param;
2154
2155                 /* Select Account from queue headers */
2156                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2157                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2158                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2159                         account = account_find_from_id(id);
2160                 }
2161                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2162                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2163                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2164                         account = account_find_from_id(id);
2165                 }
2166                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2167                                              sizeof(queueheader_buf), "NAID:")) {
2168                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2169                         account = account_find_from_id(id);
2170                 }
2171                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2172                                                     sizeof(queueheader_buf), "MAID:")) {
2173                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2174                         account = account_find_from_id(id);
2175                 }
2176                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2177                                                                 sizeof(queueheader_buf), "S:")) {
2178                         account = account_find_from_address(queueheader_buf, FALSE);
2179                 }
2180                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2181                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2182                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2183                         use_signing = param;
2184                         
2185                 }
2186                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2187                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2188                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2189                         use_signing = param;
2190                         
2191                 }
2192                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2193                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2194                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2195                         use_encryption = param;
2196                 }
2197                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2198                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2199                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2200                         use_encryption = param;
2201                 }
2202                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2203                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2204                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2205                         autowrap = param;
2206                 }
2207                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2208                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2209                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2210                         autoindent = param;
2211                 }
2212                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2213                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2214                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2215                 }
2216                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2217                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2218                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2219                 }
2220                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2221                                              sizeof(queueheader_buf), "X-Priority: ")) {
2222                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2223                         priority = param;
2224                 }
2225                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2226                                              sizeof(queueheader_buf), "RMID:")) {
2227                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2228                         if (tokens[0] && tokens[1] && tokens[2]) {
2229                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2230                                 if (orig_item != NULL) {
2231                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2232                                 }
2233                         }
2234                         g_strfreev(tokens);
2235                 }
2236                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2237                                              sizeof(queueheader_buf), "FMID:")) {
2238                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2239                         if (tokens[0] && tokens[1] && tokens[2]) {
2240                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2241                                 if (orig_item != NULL) {
2242                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2243                                 }
2244                         }
2245                         g_strfreev(tokens);
2246                 }
2247                 /* Get manual headers */
2248                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "X-Claws-Manual-Headers:")) {
2249                         gchar *listmh = g_strdup(&queueheader_buf[strlen("X-Claws-Manual-Headers:")]);
2250                         if (*listmh != '\0') {
2251                                 debug_print("Got manual headers: %s\n", listmh);
2252                                 manual_headers = procheader_entries_from_str(listmh);
2253                         }
2254                         g_free(listmh);
2255                 }
2256         } else {
2257                 account = msginfo->folder->folder->account;
2258         }
2259
2260         if (!account && prefs_common.reedit_account_autosel) {
2261                 gchar from[BUFFSIZE];
2262                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2263                         extract_address(from);
2264                         account = account_find_from_address(from, FALSE);
2265                 }
2266         }
2267         if (!account) {
2268                 account = cur_account;
2269         }
2270         cm_return_val_if_fail(account != NULL, NULL);
2271
2272         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2273
2274         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2275         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2276         compose->autowrap = autowrap;
2277         compose->replyinfo = replyinfo;
2278         compose->fwdinfo = fwdinfo;
2279
2280         compose->updating = TRUE;
2281         compose->priority = priority;
2282
2283         if (privacy_system != NULL) {
2284                 compose->privacy_system = privacy_system;
2285                 compose_use_signing(compose, use_signing);
2286                 compose_use_encryption(compose, use_encryption);
2287                 compose_update_privacy_system_menu_item(compose, FALSE);
2288         } else {
2289                 activate_privacy_system(compose, account, FALSE);
2290         }
2291
2292         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2293
2294         compose_extract_original_charset(compose);
2295
2296         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2297             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2298             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2299                 gchar queueheader_buf[BUFFSIZE];
2300
2301                 /* Set message save folder */
2302                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2303                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2304                         compose_set_save_to(compose, &queueheader_buf[4]);
2305                 }
2306                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2307                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2308                         if (active) {
2309                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2310                         }
2311                 }
2312         }
2313         
2314         if (compose_parse_header(compose, msginfo) < 0) {
2315                 compose->updating = FALSE;
2316                 compose_destroy(compose);
2317                 return NULL;
2318         }
2319         compose_reedit_set_entry(compose, msginfo);
2320
2321         textview = GTK_TEXT_VIEW(compose->text);
2322         textbuf = gtk_text_view_get_buffer(textview);
2323         compose_create_tags(textview, compose);
2324
2325         mark = gtk_text_buffer_get_insert(textbuf);
2326         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2327
2328         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2329                                         G_CALLBACK(compose_changed_cb),
2330                                         compose);
2331         
2332         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2333                 fp = procmime_get_first_encrypted_text_content(msginfo);
2334                 if (fp) {
2335                         compose_force_encryption(compose, account, TRUE, NULL);
2336                 }
2337         } else {
2338                 fp = procmime_get_first_text_content(msginfo);
2339         }
2340         if (fp == NULL) {
2341                 g_warning("Can't get text part\n");
2342         }
2343
2344         if (fp != NULL) {
2345                 gboolean prev_autowrap;
2346                 GtkTextBuffer *buffer;
2347                 BLOCK_WRAP();
2348                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2349                         strcrchomp(buf);
2350                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2351                 }
2352                 UNBLOCK_WRAP();
2353                 fclose(fp);
2354         }
2355         
2356         compose_attach_parts(compose, msginfo);
2357
2358         compose_colorize_signature(compose);
2359
2360         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2361                                         G_CALLBACK(compose_changed_cb),
2362                                         compose);
2363
2364         if (manual_headers != NULL) {
2365                 if (compose_parse_manual_headers(compose, msginfo, manual_headers) < 0) {
2366                         procheader_entries_free(manual_headers);
2367                         compose->updating = FALSE;
2368                         compose_destroy(compose);
2369                         return NULL;
2370                 }
2371                 procheader_entries_free(manual_headers);
2372         }
2373
2374         gtk_widget_grab_focus(compose->text);
2375
2376         if (prefs_common.auto_exteditor) {
2377                 compose_exec_ext_editor(compose);
2378         }
2379         compose->modified = FALSE;
2380         compose_set_title(compose);
2381
2382         compose->updating = FALSE;
2383         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2384         SCROLL_TO_CURSOR(compose);
2385
2386         if (compose->deferred_destroy) {
2387                 compose_destroy(compose);
2388                 return NULL;
2389         }
2390         
2391         compose->sig_str = account_get_signature_str(compose->account);
2392         
2393         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2394
2395         return compose;
2396 }
2397
2398 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2399                                                  gboolean batch)
2400 {
2401         Compose *compose;
2402         gchar *filename;
2403         FolderItem *item;
2404
2405         cm_return_val_if_fail(msginfo != NULL, NULL);
2406
2407         if (!account)
2408                 account = account_get_reply_account(msginfo,
2409                                         prefs_common.reply_account_autosel);
2410         cm_return_val_if_fail(account != NULL, NULL);
2411
2412         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2413
2414         compose->updating = TRUE;
2415
2416         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2417         compose->replyinfo = NULL;
2418         compose->fwdinfo = NULL;
2419
2420         compose_show_first_last_header(compose, TRUE);
2421
2422         gtk_widget_grab_focus(compose->header_last->entry);
2423
2424         filename = procmsg_get_message_file(msginfo);
2425
2426         if (filename == NULL) {
2427                 compose->updating = FALSE;
2428                 compose_destroy(compose);
2429
2430                 return NULL;
2431         }
2432
2433         compose->redirect_filename = filename;
2434         
2435         /* Set save folder */
2436         item = msginfo->folder;
2437         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2438                 gchar *folderidentifier;
2439
2440                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2441                 folderidentifier = folder_item_get_identifier(item);
2442                 compose_set_save_to(compose, folderidentifier);
2443                 g_free(folderidentifier);
2444         }
2445
2446         compose_attach_parts(compose, msginfo);
2447
2448         if (msginfo->subject)
2449                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2450                                    msginfo->subject);
2451         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2452
2453         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2454                                           _("The body of the \"Redirect\" template has an error at line %d."));
2455         quote_fmt_reset_vartable();
2456         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2457
2458         compose_colorize_signature(compose);
2459
2460         
2461         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2462         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2463         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2464
2465         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2466         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2467         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2468         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2469         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2470         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2471         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2472         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2473         
2474         if (compose->toolbar->draft_btn)
2475                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2476         if (compose->toolbar->insert_btn)
2477                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2478         if (compose->toolbar->attach_btn)
2479                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2480         if (compose->toolbar->sig_btn)
2481                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2482         if (compose->toolbar->exteditor_btn)
2483                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2484         if (compose->toolbar->linewrap_current_btn)
2485                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2486         if (compose->toolbar->linewrap_all_btn)
2487                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2488
2489         compose->modified = FALSE;
2490         compose_set_title(compose);
2491         compose->updating = FALSE;
2492         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2493         SCROLL_TO_CURSOR(compose);
2494
2495         if (compose->deferred_destroy) {
2496                 compose_destroy(compose);
2497                 return NULL;
2498         }
2499         
2500         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2501
2502         return compose;
2503 }
2504
2505 GList *compose_get_compose_list(void)
2506 {
2507         return compose_list;
2508 }
2509
2510 void compose_entry_append(Compose *compose, const gchar *address,
2511                           ComposeEntryType type, ComposePrefType pref_type)
2512 {
2513         const gchar *header;
2514         gchar *cur, *begin;
2515         gboolean in_quote = FALSE;
2516         if (!address || *address == '\0') return;
2517
2518         switch (type) {
2519         case COMPOSE_CC:
2520                 header = N_("Cc:");
2521                 break;
2522         case COMPOSE_BCC:
2523                 header = N_("Bcc:");
2524                 break;
2525         case COMPOSE_REPLYTO:
2526                 header = N_("Reply-To:");
2527                 break;
2528         case COMPOSE_NEWSGROUPS:
2529                 header = N_("Newsgroups:");
2530                 break;
2531         case COMPOSE_FOLLOWUPTO:
2532                 header = N_( "Followup-To:");
2533                 break;
2534         case COMPOSE_INREPLYTO:
2535                 header = N_( "In-Reply-To:");
2536                 break;
2537         case COMPOSE_TO:
2538         default:
2539                 header = N_("To:");
2540                 break;
2541         }
2542         header = prefs_common_translated_header_name(header);
2543         
2544         cur = begin = (gchar *)address;
2545         
2546         /* we separate the line by commas, but not if we're inside a quoted
2547          * string */
2548         while (*cur != '\0') {
2549                 if (*cur == '"') 
2550                         in_quote = !in_quote;
2551                 if (*cur == ',' && !in_quote) {
2552                         gchar *tmp = g_strdup(begin);
2553                         gchar *o_tmp = tmp;
2554                         tmp[cur-begin]='\0';
2555                         cur++;
2556                         begin = cur;
2557                         while (*tmp == ' ' || *tmp == '\t')
2558                                 tmp++;
2559                         compose_add_header_entry(compose, header, tmp, pref_type);
2560                         g_free(o_tmp);
2561                         continue;
2562                 }
2563                 cur++;
2564         }
2565         if (begin < cur) {
2566                 gchar *tmp = g_strdup(begin);
2567                 gchar *o_tmp = tmp;
2568                 tmp[cur-begin]='\0';
2569                 while (*tmp == ' ' || *tmp == '\t')
2570                         tmp++;
2571                 compose_add_header_entry(compose, header, tmp, pref_type);
2572                 g_free(o_tmp);          
2573         }
2574 }
2575
2576 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2577 {
2578 #if !GTK_CHECK_VERSION(3, 0, 0)
2579         static GdkColor yellow;
2580         static GdkColor black;
2581         static gboolean yellow_initialised = FALSE;
2582 #else
2583         static GdkColor yellow = { (guint32)0, (guint16)0xf5, (guint16)0xf6, (guint16)0xbe };
2584         static GdkColor black = { (guint32)0, (guint16)0x0, (guint16)0x0, (guint16)0x0 };
2585 #endif
2586         GSList *h_list;
2587         GtkEntry *entry;
2588                 
2589 #if !GTK_CHECK_VERSION(3, 0, 0)
2590         if (!yellow_initialised) {
2591                 gdk_color_parse("#f5f6be", &yellow);
2592                 gdk_color_parse("#000000", &black);
2593                 yellow_initialised = gdk_colormap_alloc_color(
2594                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2595                 yellow_initialised &= gdk_colormap_alloc_color(
2596                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2597         }
2598 #endif
2599
2600         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2601                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2602                 if (gtk_entry_get_text(entry) && 
2603                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2604 #if !GTK_CHECK_VERSION(3, 0, 0)
2605                         if (yellow_initialised) {
2606 #endif
2607                                 gtk_widget_modify_base(
2608                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2609                                         GTK_STATE_NORMAL, &yellow);
2610                                 gtk_widget_modify_text(
2611                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2612                                         GTK_STATE_NORMAL, &black);
2613 #if !GTK_CHECK_VERSION(3, 0, 0)
2614                         }
2615 #endif
2616                 }
2617         }
2618 }
2619
2620 void compose_toolbar_cb(gint action, gpointer data)
2621 {
2622         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2623         Compose *compose = (Compose*)toolbar_item->parent;
2624         
2625         cm_return_if_fail(compose != NULL);
2626
2627         switch(action) {
2628         case A_SEND:
2629                 compose_send_cb(NULL, compose);
2630                 break;
2631         case A_SENDL:
2632                 compose_send_later_cb(NULL, compose);
2633                 break;
2634         case A_DRAFT:
2635                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2636                 break;
2637         case A_INSERT:
2638                 compose_insert_file_cb(NULL, compose);
2639                 break;
2640         case A_ATTACH:
2641                 compose_attach_cb(NULL, compose);
2642                 break;
2643         case A_SIG:
2644                 compose_insert_sig(compose, FALSE);
2645                 break;
2646         case A_EXTEDITOR:
2647                 compose_ext_editor_cb(NULL, compose);
2648                 break;
2649         case A_LINEWRAP_CURRENT:
2650                 compose_beautify_paragraph(compose, NULL, TRUE);
2651                 break;
2652         case A_LINEWRAP_ALL:
2653                 compose_wrap_all_full(compose, TRUE);
2654                 break;
2655         case A_ADDRBOOK:
2656                 compose_address_cb(NULL, compose);
2657                 break;
2658 #ifdef USE_ENCHANT
2659         case A_CHECK_SPELLING:
2660                 compose_check_all(NULL, compose);
2661                 break;
2662 #endif
2663         default:
2664                 break;
2665         }
2666 }
2667
2668 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2669 {
2670         gchar *to = NULL;
2671         gchar *cc = NULL;
2672         gchar *bcc = NULL;
2673         gchar *subject = NULL;
2674         gchar *body = NULL;
2675         gchar *temp = NULL;
2676         gsize  len = 0;
2677         gchar **attach = NULL;
2678         gchar *inreplyto = NULL;
2679         MailField mfield = NO_FIELD_PRESENT;
2680
2681         /* get mailto parts but skip from */
2682         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2683
2684         if (to) {
2685                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2686                 mfield = TO_FIELD_PRESENT;
2687         }
2688         if (cc)
2689                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2690         if (bcc)
2691                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2692         if (subject) {
2693                 if (!g_utf8_validate (subject, -1, NULL)) {
2694                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2695                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2696                         g_free(temp);
2697                 } else {
2698                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2699                 }
2700                 mfield = SUBJECT_FIELD_PRESENT;
2701         }
2702         if (body) {
2703                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2704                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2705                 GtkTextMark *mark;
2706                 GtkTextIter iter;
2707                 gboolean prev_autowrap = compose->autowrap;
2708
2709                 compose->autowrap = FALSE;
2710
2711                 mark = gtk_text_buffer_get_insert(buffer);
2712                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2713
2714                 if (!g_utf8_validate (body, -1, NULL)) {
2715                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2716                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2717                         g_free(temp);
2718                 } else {
2719                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2720                 }
2721                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2722
2723                 compose->autowrap = prev_autowrap;
2724                 if (compose->autowrap)
2725                         compose_wrap_all(compose);
2726                 mfield = BODY_FIELD_PRESENT;
2727         }
2728
2729         if (attach) {
2730                 gint i = 0, att = 0;
2731                 gchar *warn_files = NULL;
2732                 while (attach[i] != NULL) {
2733                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2734                         if (utf8_filename) {
2735                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL, NULL)) {
2736                                         gchar *tmp = g_strdup_printf("%s%s\n",
2737                                                         warn_files?warn_files:"",
2738                                                         utf8_filename);
2739                                         g_free(warn_files);
2740                                         warn_files = tmp;
2741                                         att++;
2742                                 }
2743                                 g_free(utf8_filename);
2744                         } else {
2745                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2746                         }
2747                         i++;
2748                 }
2749                 if (warn_files) {
2750                         alertpanel_notice(ngettext(
2751                         "The following file has been attached: \n%s",
2752                         "The following files have been attached: \n%s", att), warn_files);
2753                         g_free(warn_files);
2754                 }
2755         }
2756         if (inreplyto)
2757                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2758
2759         g_free(to);
2760         g_free(cc);
2761         g_free(bcc);
2762         g_free(subject);
2763         g_free(body);
2764         g_strfreev(attach);
2765         g_free(inreplyto);
2766         
2767         return mfield;
2768 }
2769
2770 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2771 {
2772         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2773                                        {"Cc:",          NULL, TRUE},
2774                                        {"References:",  NULL, FALSE},
2775                                        {"Bcc:",         NULL, TRUE},
2776                                        {"Newsgroups:",  NULL, TRUE},
2777                                        {"Followup-To:", NULL, TRUE},
2778                                        {"List-Post:",   NULL, FALSE},
2779                                        {"X-Priority:",  NULL, FALSE},
2780                                        {NULL,           NULL, FALSE}};
2781
2782         enum
2783         {
2784                 H_REPLY_TO      = 0,
2785                 H_CC            = 1,
2786                 H_REFERENCES    = 2,
2787                 H_BCC           = 3,
2788                 H_NEWSGROUPS    = 4,
2789                 H_FOLLOWUP_TO   = 5,
2790                 H_LIST_POST     = 6,
2791                 H_X_PRIORITY    = 7
2792         };
2793
2794         FILE *fp;
2795
2796         cm_return_val_if_fail(msginfo != NULL, -1);
2797
2798         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2799         procheader_get_header_fields(fp, hentry);
2800         fclose(fp);
2801
2802         if (hentry[H_REPLY_TO].body != NULL) {
2803                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2804                         compose->replyto =
2805                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2806                                                    NULL, TRUE);
2807                 }
2808                 g_free(hentry[H_REPLY_TO].body);
2809                 hentry[H_REPLY_TO].body = NULL;
2810         }
2811         if (hentry[H_CC].body != NULL) {
2812                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL, TRUE);
2813                 g_free(hentry[H_CC].body);
2814                 hentry[H_CC].body = NULL;
2815         }
2816         if (hentry[H_REFERENCES].body != NULL) {
2817                 if (compose->mode == COMPOSE_REEDIT)
2818                         compose->references = hentry[H_REFERENCES].body;
2819                 else {
2820                         compose->references = compose_parse_references
2821                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2822                         g_free(hentry[H_REFERENCES].body);
2823                 }
2824                 hentry[H_REFERENCES].body = NULL;
2825         }
2826         if (hentry[H_BCC].body != NULL) {
2827                 if (compose->mode == COMPOSE_REEDIT)
2828                         compose->bcc =
2829                                 conv_unmime_header(hentry[H_BCC].body, NULL, TRUE);
2830                 g_free(hentry[H_BCC].body);
2831                 hentry[H_BCC].body = NULL;
2832         }
2833         if (hentry[H_NEWSGROUPS].body != NULL) {
2834                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2835                 hentry[H_NEWSGROUPS].body = NULL;
2836         }
2837         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2838                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2839                         compose->followup_to =
2840                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2841                                                    NULL, TRUE);
2842                 }
2843                 g_free(hentry[H_FOLLOWUP_TO].body);
2844                 hentry[H_FOLLOWUP_TO].body = NULL;
2845         }
2846         if (hentry[H_LIST_POST].body != NULL) {
2847                 gchar *to = NULL, *start = NULL;
2848
2849                 extract_address(hentry[H_LIST_POST].body);
2850                 if (hentry[H_LIST_POST].body[0] != '\0') {
2851                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2852                         
2853                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2854                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2855
2856                         if (to) {
2857                                 g_free(compose->ml_post);
2858                                 compose->ml_post = to;
2859                         }
2860                 }
2861                 g_free(hentry[H_LIST_POST].body);
2862                 hentry[H_LIST_POST].body = NULL;
2863         }
2864
2865         /* CLAWS - X-Priority */
2866         if (compose->mode == COMPOSE_REEDIT)
2867                 if (hentry[H_X_PRIORITY].body != NULL) {
2868                         gint priority;
2869                         
2870                         priority = atoi(hentry[H_X_PRIORITY].body);
2871                         g_free(hentry[H_X_PRIORITY].body);
2872                         
2873                         hentry[H_X_PRIORITY].body = NULL;
2874                         
2875                         if (priority < PRIORITY_HIGHEST || 
2876                             priority > PRIORITY_LOWEST)
2877                                 priority = PRIORITY_NORMAL;
2878                         
2879                         compose->priority =  priority;
2880                 }
2881  
2882         if (compose->mode == COMPOSE_REEDIT) {
2883                 if (msginfo->inreplyto && *msginfo->inreplyto)
2884                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2885                 return 0;
2886         }
2887
2888         if (msginfo->msgid && *msginfo->msgid)
2889                 compose->inreplyto = g_strdup(msginfo->msgid);
2890
2891         if (!compose->references) {
2892                 if (msginfo->msgid && *msginfo->msgid) {
2893                         if (msginfo->inreplyto && *msginfo->inreplyto)
2894                                 compose->references =
2895                                         g_strdup_printf("<%s>\n\t<%s>",
2896                                                         msginfo->inreplyto,
2897                                                         msginfo->msgid);
2898                         else
2899                                 compose->references =
2900                                         g_strconcat("<", msginfo->msgid, ">",
2901                                                     NULL);
2902                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2903                         compose->references =
2904                                 g_strconcat("<", msginfo->inreplyto, ">",
2905                                             NULL);
2906                 }
2907         }
2908
2909         return 0;
2910 }
2911
2912 static gint compose_parse_manual_headers(Compose *compose, MsgInfo *msginfo, HeaderEntry *entries)
2913 {
2914         FILE *fp;
2915         HeaderEntry *he;
2916
2917         cm_return_val_if_fail(msginfo != NULL, -1);
2918
2919         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2920         procheader_get_header_fields(fp, entries);
2921         fclose(fp);
2922
2923         he = entries;
2924         while (he != NULL && he->name != NULL) {
2925                 GtkTreeIter iter;
2926                 GtkListStore *model = NULL;
2927
2928                 debug_print("Adding manual header: %s with value %s\n", he->name, he->body);
2929                 model = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(compose->header_last->combo)));
2930                 COMBOBOX_ADD(model, he->name, COMPOSE_TO);
2931                 gtk_combo_box_set_active_iter(GTK_COMBO_BOX(compose->header_last->combo), &iter);
2932                 gtk_entry_set_text(GTK_ENTRY(compose->header_last->entry), he->body);
2933                 ++he;
2934         }
2935
2936         return 0;
2937 }
2938
2939 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2940 {
2941         GSList *ref_id_list, *cur;
2942         GString *new_ref;
2943         gchar *new_ref_str;
2944
2945         ref_id_list = references_list_append(NULL, ref);
2946         if (!ref_id_list) return NULL;
2947         if (msgid && *msgid)
2948                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2949
2950         for (;;) {
2951                 gint len = 0;
2952
2953                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2954                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2955                         len += strlen((gchar *)cur->data) + 5;
2956
2957                 if (len > MAX_REFERENCES_LEN) {
2958                         /* remove second message-ID */
2959                         if (ref_id_list && ref_id_list->next &&
2960                             ref_id_list->next->next) {
2961                                 g_free(ref_id_list->next->data);
2962                                 ref_id_list = g_slist_remove
2963                                         (ref_id_list, ref_id_list->next->data);
2964                         } else {
2965                                 slist_free_strings(ref_id_list);
2966                                 g_slist_free(ref_id_list);
2967                                 return NULL;
2968                         }
2969                 } else
2970                         break;
2971         }
2972
2973         new_ref = g_string_new("");
2974         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2975                 if (new_ref->len > 0)
2976                         g_string_append(new_ref, "\n\t");
2977                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2978         }
2979
2980         slist_free_strings(ref_id_list);
2981         g_slist_free(ref_id_list);
2982
2983         new_ref_str = new_ref->str;
2984         g_string_free(new_ref, FALSE);
2985
2986         return new_ref_str;
2987 }
2988
2989 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2990                                 const gchar *fmt, const gchar *qmark,
2991                                 const gchar *body, gboolean rewrap,
2992                                 gboolean need_unescape,
2993                                 const gchar *err_msg)
2994 {
2995         MsgInfo* dummyinfo = NULL;
2996         gchar *quote_str = NULL;
2997         gchar *buf;
2998         gboolean prev_autowrap;
2999         const gchar *trimmed_body = body;
3000         gint cursor_pos = -1;
3001         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3002         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3003         GtkTextIter iter;
3004         GtkTextMark *mark;
3005         
3006
3007         SIGNAL_BLOCK(buffer);
3008
3009         if (!msginfo) {
3010                 dummyinfo = compose_msginfo_new_from_compose(compose);
3011                 msginfo = dummyinfo;
3012         }
3013
3014         if (qmark != NULL) {
3015 #ifdef USE_ENCHANT
3016                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
3017                                 compose->gtkaspell);
3018 #else
3019                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
3020 #endif
3021                 quote_fmt_scan_string(qmark);
3022                 quote_fmt_parse();
3023
3024                 buf = quote_fmt_get_buffer();
3025                 if (buf == NULL)
3026                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
3027                 else
3028                         Xstrdup_a(quote_str, buf, goto error)
3029         }
3030
3031         if (fmt && *fmt != '\0') {
3032
3033                 if (trimmed_body)
3034                         while (*trimmed_body == '\n')
3035                                 trimmed_body++;
3036
3037 #ifdef USE_ENCHANT
3038                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
3039                                 compose->gtkaspell);
3040 #else
3041                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
3042 #endif
3043                 if (need_unescape) {
3044                         gchar *tmp = NULL;
3045
3046                         /* decode \-escape sequences in the internal representation of the quote format */
3047                         tmp = g_malloc(strlen(fmt)+1);
3048                         pref_get_unescaped_pref(tmp, fmt);
3049                         quote_fmt_scan_string(tmp);
3050                         quote_fmt_parse();
3051                         g_free(tmp);
3052                 } else {
3053                         quote_fmt_scan_string(fmt);
3054                         quote_fmt_parse();
3055                 }
3056
3057                 buf = quote_fmt_get_buffer();
3058                 if (buf == NULL) {
3059                         gint line = quote_fmt_get_line();
3060                         alertpanel_error(err_msg, line);
3061                         goto error;
3062                 }
3063         } else
3064                 buf = "";
3065
3066         prev_autowrap = compose->autowrap;
3067         compose->autowrap = FALSE;
3068
3069         mark = gtk_text_buffer_get_insert(buffer);
3070         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3071         if (g_utf8_validate(buf, -1, NULL)) { 
3072                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
3073         } else {
3074                 gchar *tmpout = NULL;
3075                 tmpout = conv_codeset_strdup
3076                         (buf, conv_get_locale_charset_str_no_utf8(),
3077                          CS_INTERNAL);
3078                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
3079                         g_free(tmpout);
3080                         tmpout = g_malloc(strlen(buf)*2+1);
3081                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3082                 }
3083                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3084                 g_free(tmpout);
3085         }
3086
3087         cursor_pos = quote_fmt_get_cursor_pos();
3088         if (cursor_pos == -1)
3089                 cursor_pos = gtk_text_iter_get_offset(&iter);
3090         compose->set_cursor_pos = cursor_pos;
3091
3092         gtk_text_buffer_get_start_iter(buffer, &iter);
3093         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3094         gtk_text_buffer_place_cursor(buffer, &iter);
3095
3096         compose->autowrap = prev_autowrap;
3097         if (compose->autowrap && rewrap)
3098                 compose_wrap_all(compose);
3099
3100         goto ok;
3101
3102 error:
3103         buf = NULL;
3104 ok:
3105         SIGNAL_UNBLOCK(buffer);
3106
3107         procmsg_msginfo_free( dummyinfo );
3108
3109         return buf;
3110 }
3111
3112 /* if ml_post is of type addr@host and from is of type
3113  * addr-anything@host, return TRUE
3114  */
3115 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3116 {
3117         gchar *left_ml = NULL;
3118         gchar *right_ml = NULL;
3119         gchar *left_from = NULL;
3120         gchar *right_from = NULL;
3121         gboolean result = FALSE;
3122         
3123         if (!ml_post || !from)
3124                 return FALSE;
3125         
3126         left_ml = g_strdup(ml_post);
3127         if (strstr(left_ml, "@")) {
3128                 right_ml = strstr(left_ml, "@")+1;
3129                 *(strstr(left_ml, "@")) = '\0';
3130         }
3131         
3132         left_from = g_strdup(from);
3133         if (strstr(left_from, "@")) {
3134                 right_from = strstr(left_from, "@")+1;
3135                 *(strstr(left_from, "@")) = '\0';
3136         }
3137         
3138         if (left_ml && left_from && right_ml && right_from
3139         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3140         &&  !strcmp(right_from, right_ml)) {
3141                 result = TRUE;
3142         }
3143         g_free(left_ml);
3144         g_free(left_from);
3145         
3146         return result;
3147 }
3148
3149 static void compose_set_folder_prefs(Compose *compose, FolderItem *folder,
3150                                      gboolean respect_default_to)
3151 {
3152         if (!compose)
3153                 return;
3154         if (!folder || !folder->prefs)
3155                 return;
3156
3157         if (respect_default_to && folder->prefs->enable_default_to) {
3158                 compose_entry_append(compose, folder->prefs->default_to,
3159                                         COMPOSE_TO, PREF_FOLDER);
3160                 compose_entry_mark_default_to(compose, folder->prefs->default_to);
3161         }
3162         if (folder->prefs->enable_default_cc)
3163                 compose_entry_append(compose, folder->prefs->default_cc,
3164                                         COMPOSE_CC, PREF_FOLDER);
3165         if (folder->prefs->enable_default_bcc)
3166                 compose_entry_append(compose, folder->prefs->default_bcc,
3167                                         COMPOSE_BCC, PREF_FOLDER);
3168         if (folder->prefs->enable_default_replyto)
3169                 compose_entry_append(compose, folder->prefs->default_replyto,
3170                                         COMPOSE_REPLYTO, PREF_FOLDER);
3171 }
3172
3173 static void compose_reply_set_subject(Compose *compose, MsgInfo *msginfo)
3174 {
3175         gchar *buf, *buf2;
3176         gchar *p;
3177         
3178         if (!compose || !msginfo)
3179                 return;
3180
3181         if (msginfo->subject && *msginfo->subject) {
3182                 buf = p = g_strdup(msginfo->subject);
3183                 p += subject_get_prefix_length(p);
3184                 memmove(buf, p, strlen(p) + 1);
3185
3186                 buf2 = g_strdup_printf("Re: %s", buf);