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