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