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