dca67d9249fe59a4e9688b5e5fd26f38e3f92133
[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_GB18030, C_GB18030, "_GB18030"), /* RADIO compose_set_encoding_cb */
714         ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
715         ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
716         ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
717         ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
718         ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
719         ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
720         ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
721         ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
722 };
723
724 static GtkTargetEntry compose_mime_types[] =
725 {
726         {"text/uri-list", 0, 0},
727         {"UTF8_STRING", 0, 0},
728         {"text/plain", 0, 0}
729 };
730
731 static gboolean compose_put_existing_to_front(MsgInfo *info)
732 {
733         GList *compose_list = compose_get_compose_list();
734         GList *elem = NULL;
735         
736         if (compose_list) {
737                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
738                      elem = elem->next) {
739                         Compose *c = (Compose*)elem->data;
740
741                         if (!c->targetinfo || !c->targetinfo->msgid ||
742                             !info->msgid)
743                                 continue;
744
745                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
746                                 gtkut_window_popup(c->window);
747                                 return TRUE;
748                         }
749                 }
750         }
751         return FALSE;
752 }
753
754 static GdkColor quote_color1 = 
755         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_color2 = 
757         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_color3 = 
759         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
760
761 static GdkColor quote_bgcolor1 = 
762         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
763 static GdkColor quote_bgcolor2 = 
764         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
765 static GdkColor quote_bgcolor3 = 
766         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
767
768 static GdkColor signature_color = {
769         (gulong)0,
770         (gushort)0x7fff,
771         (gushort)0x7fff,
772         (gushort)0x7fff
773 };
774
775 static GdkColor uri_color = {
776         (gulong)0,
777         (gushort)0,
778         (gushort)0,
779         (gushort)0
780 };
781
782 static void compose_create_tags(GtkTextView *text, Compose *compose)
783 {
784         GtkTextBuffer *buffer;
785         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
786         GdkColormap *cmap;
787         GdkColor color[8];
788         gboolean success[8];
789         int i;
790
791         buffer = gtk_text_view_get_buffer(text);
792
793         if (prefs_common.enable_color) {
794                 /* grab the quote colors, converting from an int to a GdkColor */
795                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
796                                                &quote_color1);
797                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
798                                                &quote_color2);
799                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
800                                                &quote_color3);
801                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
802                                                &quote_bgcolor1);
803                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
804                                                &quote_bgcolor2);
805                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
806                                                &quote_bgcolor3);
807                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
808                                                &signature_color);
809                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
810                                                &uri_color);
811         } else {
812                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
813                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
814         }
815
816         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
817                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
818                                            "foreground-gdk", &quote_color1,
819                                            "paragraph-background-gdk", &quote_bgcolor1,
820                                            NULL);
821                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
822                                            "foreground-gdk", &quote_color2,
823                                            "paragraph-background-gdk", &quote_bgcolor2,
824                                            NULL);
825                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826                                            "foreground-gdk", &quote_color3,
827                                            "paragraph-background-gdk", &quote_bgcolor3,
828                                            NULL);
829         } else {
830                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
831                                            "foreground-gdk", &quote_color1,
832                                            NULL);
833                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
834                                            "foreground-gdk", &quote_color2,
835                                            NULL);
836                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
837                                            "foreground-gdk", &quote_color3,
838                                            NULL);
839         }
840         
841         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
842                                    "foreground-gdk", &signature_color,
843                                    NULL);
844         
845         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
846                                         "foreground-gdk", &uri_color,
847                                          NULL);
848         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
849         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
850
851         color[0] = quote_color1;
852         color[1] = quote_color2;
853         color[2] = quote_color3;
854         color[3] = quote_bgcolor1;
855         color[4] = quote_bgcolor2;
856         color[5] = quote_bgcolor3;
857         color[6] = signature_color;
858         color[7] = uri_color;
859         cmap = gdk_drawable_get_colormap(compose->window->window);
860         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
861
862         for (i = 0; i < 8; i++) {
863                 if (success[i] == FALSE) {
864                         GtkStyle *style;
865
866                         g_warning("Compose: color allocation failed.\n");
867                         style = gtk_widget_get_style(GTK_WIDGET(text));
868                         quote_color1 = quote_color2 = quote_color3 = 
869                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
870                                 signature_color = uri_color = black;
871                 }
872         }
873 }
874
875 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
876                      GPtrArray *attach_files)
877 {
878         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
879 }
880
881 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
882 {
883         return compose_generic_new(account, mailto, item, NULL, NULL);
884 }
885
886 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
887 {
888         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
889 }
890
891 #define SCROLL_TO_CURSOR(compose) {                             \
892         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
893                 gtk_text_view_get_buffer(                       \
894                         GTK_TEXT_VIEW(compose->text)));         \
895         gtk_text_view_scroll_mark_onscreen(                     \
896                 GTK_TEXT_VIEW(compose->text),                   \
897                 cmark);                                         \
898 }
899
900 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
901 {
902         GtkEditable *entry;
903         if (folderidentifier) {
904                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
905                 prefs_common.compose_save_to_history = add_history(
906                                 prefs_common.compose_save_to_history, folderidentifier);
907                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
908                                 prefs_common.compose_save_to_history);
909         }
910
911         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
912         if (folderidentifier)
913                 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
914         else
915                 gtk_entry_set_text(GTK_ENTRY(entry), "");
916 }
917
918 static gchar *compose_get_save_to(Compose *compose)
919 {
920         GtkEditable *entry;
921         gchar *result = NULL;
922         entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
923         result = gtk_editable_get_chars(entry, 0, -1);
924         
925         if (result) {
926                 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
927                 prefs_common.compose_save_to_history = add_history(
928                                 prefs_common.compose_save_to_history, result);
929                 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
930                                 prefs_common.compose_save_to_history);
931         }
932         return result;
933 }
934
935 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
936                              GPtrArray *attach_files, GList *listAddress )
937 {
938         Compose *compose;
939         GtkTextView *textview;
940         GtkTextBuffer *textbuf;
941         GtkTextIter iter;
942         const gchar *subject_format = NULL;
943         const gchar *body_format = NULL;
944         gchar *mailto_from = NULL;
945         PrefsAccount *mailto_account = NULL;
946         MsgInfo* dummyinfo = NULL;
947         MailField mfield = NO_FIELD_PRESENT;
948         gchar* buf;
949         GtkTextMark *mark;
950
951         /* check if mailto defines a from */
952         if (mailto && *mailto != '\0') {
953                 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
954                 /* mailto defines a from, check if we can get account prefs from it,
955                    if not, the account prefs will be guessed using other ways, but we'll keep
956                    the from anyway */
957                 if (mailto_from)
958                         mailto_account = account_find_from_address(mailto_from, TRUE);
959                 if (mailto_account)
960                         account = mailto_account;
961         }
962
963         /* if no account prefs set from mailto, set if from folder prefs (if any) */
964         if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
965                 account = account_find_from_id(item->prefs->default_account);
966
967         /* if no account prefs set, fallback to the current one */
968         if (!account) account = cur_account;
969         cm_return_val_if_fail(account != NULL, NULL);
970
971         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
972
973         /* override from name if mailto asked for it */
974         if (mailto_from) {
975                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
976                 g_free(mailto_from);
977         } else
978                 /* override from name according to folder properties */
979                 if (item && item->prefs &&
980                         item->prefs->compose_with_format &&
981                         item->prefs->compose_override_from_format &&
982                         *item->prefs->compose_override_from_format != '\0') {
983
984                         gchar *tmp = NULL;
985                         gchar *buf = NULL;
986
987                         dummyinfo = compose_msginfo_new_from_compose(compose);
988
989                         /* decode \-escape sequences in the internal representation of the quote format */
990                         tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
991                         pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
992
993 #ifdef USE_ENCHANT
994                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
995                                         compose->gtkaspell);
996 #else
997                         quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
998 #endif
999                         quote_fmt_scan_string(tmp);
1000                         quote_fmt_parse();
1001
1002                         buf = quote_fmt_get_buffer();
1003                         if (buf == NULL)
1004                                 alertpanel_error(_("New message From format error."));
1005                         else
1006                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1007                         quote_fmt_reset_vartable();
1008
1009                         g_free(tmp);
1010                 }
1011
1012         compose->replyinfo = NULL;
1013         compose->fwdinfo   = NULL;
1014
1015         textview = GTK_TEXT_VIEW(compose->text);
1016         textbuf = gtk_text_view_get_buffer(textview);
1017         compose_create_tags(textview, compose);
1018
1019         undo_block(compose->undostruct);
1020 #ifdef USE_ENCHANT
1021         compose_set_dictionaries_from_folder_prefs(compose, item);
1022 #endif
1023
1024         if (account->auto_sig)
1025                 compose_insert_sig(compose, FALSE);
1026         gtk_text_buffer_get_start_iter(textbuf, &iter);
1027         gtk_text_buffer_place_cursor(textbuf, &iter);
1028
1029         if (account->protocol != A_NNTP) {
1030                 if (mailto && *mailto != '\0') {
1031                         mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1032
1033                 } else if (item && item->prefs) {
1034                         if (item->prefs->enable_default_bcc) {
1035                                 compose_entry_append(compose, item->prefs->default_bcc,
1036                                                 COMPOSE_BCC, PREF_FOLDER);
1037                         }
1038                         if (item->prefs->enable_default_cc) {
1039                                 compose_entry_append(compose, item->prefs->default_cc,
1040                                                 COMPOSE_CC, PREF_FOLDER);
1041                         }
1042                         if (item->prefs->enable_default_replyto) {
1043                                 compose_entry_append(compose, item->prefs->default_replyto,
1044                                                 COMPOSE_REPLYTO, PREF_FOLDER);
1045                         }
1046                         if (item->prefs->enable_default_to) {
1047                                 compose_entry_append(compose, item->prefs->default_to,
1048                                                 COMPOSE_TO, PREF_FOLDER);
1049                                 compose_entry_mark_default_to(compose, item->prefs->default_to);
1050                         }
1051                 }
1052                 if (item && item->ret_rcpt) {
1053                         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1054                 }
1055         } else {
1056                 if (mailto && *mailto != '\0') {
1057                         if (!strchr(mailto, '@'))
1058                                 mfield = compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1059                         else
1060                                 mfield = compose_entries_set(compose, mailto, COMPOSE_TO);
1061                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1062                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS, PREF_FOLDER);
1063                         mfield = TO_FIELD_PRESENT;
1064                 }
1065                 /*
1066                  * CLAWS: just don't allow return receipt request, even if the user
1067                  * may want to send an email. simple but foolproof.
1068                  */
1069                 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE); 
1070         }
1071         compose_add_field_list( compose, listAddress );
1072
1073         if (item && item->prefs && item->prefs->compose_with_format) {
1074                 subject_format = item->prefs->compose_subject_format;
1075                 body_format = item->prefs->compose_body_format;
1076         } else if (account->compose_with_format) {
1077                 subject_format = account->compose_subject_format;
1078                 body_format = account->compose_body_format;
1079         } else if (prefs_common.compose_with_format) {
1080                 subject_format = prefs_common.compose_subject_format;
1081                 body_format = prefs_common.compose_body_format;
1082         }
1083
1084         if (subject_format || body_format) {
1085
1086                 if ( subject_format
1087                          && *subject_format != '\0' )
1088                 {
1089                         gchar *subject = NULL;
1090                         gchar *tmp = NULL;
1091                         gchar *buf = NULL;
1092
1093                         if (!dummyinfo)
1094                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1095
1096                         /* decode \-escape sequences in the internal representation of the quote format */
1097                         tmp = malloc(strlen(subject_format)+1);
1098                         pref_get_unescaped_pref(tmp, subject_format);
1099
1100                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1101 #ifdef USE_ENCHANT
1102                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1103                                         compose->gtkaspell);
1104 #else
1105                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1106 #endif
1107                         quote_fmt_scan_string(tmp);
1108                         quote_fmt_parse();
1109
1110                         buf = quote_fmt_get_buffer();
1111                         if (buf == NULL)
1112                                 alertpanel_error(_("New message subject format error."));
1113                         else
1114                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1115                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1116                         quote_fmt_reset_vartable();
1117
1118                         g_free(subject);
1119                         g_free(tmp);
1120                         mfield = SUBJECT_FIELD_PRESENT;
1121                 }
1122
1123                 if ( body_format
1124                          && *body_format != '\0' )
1125                 {
1126                         GtkTextView *text;
1127                         GtkTextBuffer *buffer;
1128                         GtkTextIter start, end;
1129                         gchar *tmp = NULL;
1130
1131                         if (!dummyinfo)
1132                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1133
1134                         text = GTK_TEXT_VIEW(compose->text);
1135                         buffer = gtk_text_view_get_buffer(text);
1136                         gtk_text_buffer_get_start_iter(buffer, &start);
1137                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1138                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1139
1140                         compose_quote_fmt(compose, dummyinfo,
1141                                           body_format,
1142                                           NULL, tmp, FALSE, TRUE,
1143                                                   _("The body of the \"New message\" template has an error at line %d."));
1144                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1145                         quote_fmt_reset_vartable();
1146
1147                         g_free(tmp);
1148 #ifdef USE_ENCHANT
1149                         if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1150                                 gtkaspell_highlight_all(compose->gtkaspell);
1151 #endif
1152                         mfield = BODY_FIELD_PRESENT;
1153                 }
1154
1155         }
1156         procmsg_msginfo_free( dummyinfo );
1157
1158         if (attach_files) {
1159                 gint i;
1160                 gchar *file;
1161
1162                 for (i = 0; i < attach_files->len; i++) {
1163                         file = g_ptr_array_index(attach_files, i);
1164                         compose_attach_append(compose, file, file, NULL);
1165                 }
1166         }
1167
1168         compose_show_first_last_header(compose, TRUE);
1169
1170         /* Set save folder */
1171         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1172                 gchar *folderidentifier;
1173
1174                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1175                 folderidentifier = folder_item_get_identifier(item);
1176                 compose_set_save_to(compose, folderidentifier);
1177                 g_free(folderidentifier);
1178         }
1179
1180         /* Place cursor according to provided input (mfield) */
1181         switch (mfield) { 
1182                 case NO_FIELD_PRESENT:
1183                         if (compose->header_last)
1184                                 gtk_widget_grab_focus(compose->header_last->entry);
1185                         break;
1186                 case TO_FIELD_PRESENT:
1187                         buf = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1188                         if (buf) {
1189                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1190                                 g_free(buf);
1191                         }
1192                         gtk_widget_grab_focus(compose->subject_entry);
1193                         break;
1194                 case SUBJECT_FIELD_PRESENT:
1195                         textview = GTK_TEXT_VIEW(compose->text);
1196                         if (!textview)
1197                                 break;
1198                         textbuf = gtk_text_view_get_buffer(textview);
1199                         if (!textbuf)
1200                                 break;
1201                         mark = gtk_text_buffer_get_insert(textbuf);
1202                         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
1203                         gtk_text_buffer_insert(textbuf, &iter, "", -1);
1204                     /* 
1205                      * SUBJECT_FIELD_PRESENT and BODY_FIELD_PRESENT
1206                      * only defers where it comes to the variable body
1207                      * is not null. If no body is present compose->text
1208                      * will be null in which case you cannot place the
1209                      * cursor inside the component so. An empty component
1210                      * is therefore created before placing the cursor
1211                      */
1212                 case BODY_FIELD_PRESENT:
1213                         gtk_widget_grab_focus(compose->text);
1214                         break;
1215         }
1216
1217         undo_unblock(compose->undostruct);
1218
1219         if (prefs_common.auto_exteditor)
1220                 compose_exec_ext_editor(compose);
1221
1222         compose->draft_timeout_tag = -1;
1223         SCROLL_TO_CURSOR(compose);
1224
1225         compose->modified = FALSE;
1226         compose_set_title(compose);
1227
1228   hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1229
1230         return compose;
1231 }
1232
1233 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1234                 gboolean override_pref, const gchar *system)
1235 {
1236         const gchar *privacy = NULL;
1237
1238         cm_return_if_fail(compose != NULL);
1239         cm_return_if_fail(account != NULL);
1240
1241         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1242                 return;
1243
1244         if (system)
1245                 privacy = system;
1246         else if (account->default_privacy_system
1247         &&  strlen(account->default_privacy_system)) {
1248                 privacy = account->default_privacy_system;
1249         } else {
1250                 GSList *privacy_avail = privacy_get_system_ids();
1251                 if (privacy_avail && g_slist_length(privacy_avail)) {
1252                         privacy = (gchar *)(privacy_avail->data);
1253                 }
1254         }
1255         if (privacy != NULL) {
1256                 if (system) {
1257                         g_free(compose->privacy_system);
1258                         compose->privacy_system = NULL;
1259                 }
1260                 if (compose->privacy_system == NULL)
1261                         compose->privacy_system = g_strdup(privacy);
1262                 else if (*(compose->privacy_system) == '\0') {
1263                         g_free(compose->privacy_system);
1264                         compose->privacy_system = g_strdup(privacy);
1265                 }
1266                 compose_update_privacy_system_menu_item(compose, FALSE);
1267                 compose_use_encryption(compose, TRUE);
1268         }
1269 }       
1270
1271 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1272 {
1273         const gchar *privacy = NULL;
1274
1275         if (system)
1276                 privacy = system;
1277         else if (account->default_privacy_system
1278         &&  strlen(account->default_privacy_system)) {
1279                 privacy = account->default_privacy_system;
1280         } else {
1281                 GSList *privacy_avail = privacy_get_system_ids();
1282                 if (privacy_avail && g_slist_length(privacy_avail)) {
1283                         privacy = (gchar *)(privacy_avail->data);
1284                 }
1285         }
1286
1287         if (privacy != NULL) {
1288                 if (system) {
1289                         g_free(compose->privacy_system);
1290                         compose->privacy_system = NULL;
1291                 }
1292                 if (compose->privacy_system == NULL)
1293                         compose->privacy_system = g_strdup(privacy);
1294                 compose_update_privacy_system_menu_item(compose, FALSE);
1295                 compose_use_signing(compose, TRUE);
1296         }
1297 }       
1298
1299 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1300 {
1301         MsgInfo *msginfo;
1302         guint list_len;
1303         Compose *compose = NULL;
1304         
1305         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1306
1307         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1308         cm_return_val_if_fail(msginfo != NULL, NULL);
1309
1310         list_len = g_slist_length(msginfo_list);
1311
1312         switch (mode) {
1313         case COMPOSE_REPLY:
1314                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1315                               FALSE, prefs_common.default_reply_list, FALSE, body);
1316                 break;
1317         case COMPOSE_REPLY_WITH_QUOTE:
1318                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1319                         FALSE, prefs_common.default_reply_list, FALSE, body);
1320                 break;
1321         case COMPOSE_REPLY_WITHOUT_QUOTE:
1322                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1323                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1324                 break;
1325         case COMPOSE_REPLY_TO_SENDER:
1326                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1327                               FALSE, FALSE, TRUE, body);
1328                 break;
1329         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1330                 compose = compose_followup_and_reply_to(msginfo,
1331                                               COMPOSE_QUOTE_CHECK,
1332                                               FALSE, FALSE, body);
1333                 break;
1334         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1335                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1336                         FALSE, FALSE, TRUE, body);
1337                 break;
1338         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1339                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1340                         FALSE, FALSE, TRUE, NULL);
1341                 break;
1342         case COMPOSE_REPLY_TO_ALL:
1343                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1344                         TRUE, FALSE, FALSE, body);
1345                 break;
1346         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1347                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1348                         TRUE, FALSE, FALSE, body);
1349                 break;
1350         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1351                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1352                         TRUE, FALSE, FALSE, NULL);
1353                 break;
1354         case COMPOSE_REPLY_TO_LIST:
1355                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1356                         FALSE, TRUE, FALSE, body);
1357                 break;
1358         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1359                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1360                         FALSE, TRUE, FALSE, body);
1361                 break;
1362         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1363                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1364                         FALSE, TRUE, FALSE, NULL);
1365                 break;
1366         case COMPOSE_FORWARD:
1367                 if (prefs_common.forward_as_attachment) {
1368                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1369                         return compose;
1370                 } else {
1371                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1372                         return compose;
1373                 }
1374                 break;
1375         case COMPOSE_FORWARD_INLINE:
1376                 /* check if we reply to more than one Message */
1377                 if (list_len == 1) {
1378                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1379                         break;
1380                 } 
1381                 /* more messages FALL THROUGH */
1382         case COMPOSE_FORWARD_AS_ATTACH:
1383                 compose = compose_forward_multiple(NULL, msginfo_list);
1384                 break;
1385         case COMPOSE_REDIRECT:
1386                 compose = compose_redirect(NULL, msginfo, FALSE);
1387                 break;
1388         default:
1389                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1390         }
1391         
1392         if (compose == NULL) {
1393                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1394                 return NULL;
1395         }
1396
1397         compose->rmode = mode;
1398         switch (compose->rmode) {
1399         case COMPOSE_REPLY:
1400         case COMPOSE_REPLY_WITH_QUOTE:
1401         case COMPOSE_REPLY_WITHOUT_QUOTE:
1402         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1403                 debug_print("reply mode Normal\n");
1404                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1405                 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1406                 break;
1407         case COMPOSE_REPLY_TO_SENDER:
1408         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1409         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1410                 debug_print("reply mode Sender\n");
1411                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1412                 break;
1413         case COMPOSE_REPLY_TO_ALL:
1414         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1415         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1416                 debug_print("reply mode All\n");
1417                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1418                 break;
1419         case COMPOSE_REPLY_TO_LIST:
1420         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1421         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1422                 debug_print("reply mode List\n");
1423                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1424                 break;
1425         default:
1426                 break;
1427         }
1428         return compose;
1429 }
1430
1431 static Compose *compose_reply(MsgInfo *msginfo,
1432                                    ComposeQuoteMode quote_mode,
1433                                    gboolean to_all,
1434                                    gboolean to_ml,
1435                                    gboolean to_sender, 
1436                    const gchar *body)
1437 {
1438         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1439                               to_sender, FALSE, body);
1440 }
1441
1442 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1443                                    ComposeQuoteMode quote_mode,
1444                                    gboolean to_all,
1445                                    gboolean to_sender,
1446                                    const gchar *body)
1447 {
1448         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1449                               to_sender, TRUE, body);
1450 }
1451
1452 static void compose_extract_original_charset(Compose *compose)
1453 {
1454         MsgInfo *info = NULL;
1455         if (compose->replyinfo) {
1456                 info = compose->replyinfo;
1457         } else if (compose->fwdinfo) {
1458                 info = compose->fwdinfo;
1459         } else if (compose->targetinfo) {
1460                 info = compose->targetinfo;
1461         }
1462         if (info) {
1463                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1464                 MimeInfo *partinfo = mimeinfo;
1465                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1466                         partinfo = procmime_mimeinfo_next(partinfo);
1467                 if (partinfo) {
1468                         compose->orig_charset = 
1469                                 g_strdup(procmime_mimeinfo_get_parameter(
1470                                                 partinfo, "charset"));
1471                 }
1472                 procmime_mimeinfo_free_all(mimeinfo);
1473         }
1474 }
1475
1476 #define SIGNAL_BLOCK(buffer) {                                  \
1477         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1478                                 G_CALLBACK(compose_changed_cb), \
1479                                 compose);                       \
1480         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1481                                 G_CALLBACK(text_inserted),      \
1482                                 compose);                       \
1483 }
1484
1485 #define SIGNAL_UNBLOCK(buffer) {                                \
1486         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1487                                 G_CALLBACK(compose_changed_cb), \
1488                                 compose);                       \
1489         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1490                                 G_CALLBACK(text_inserted),      \
1491                                 compose);                       \
1492 }
1493
1494 static Compose *compose_generic_reply(MsgInfo *msginfo,
1495                                   ComposeQuoteMode quote_mode,
1496                                   gboolean to_all, gboolean to_ml,
1497                                   gboolean to_sender,
1498                                   gboolean followup_and_reply_to,
1499                                   const gchar *body)
1500 {
1501         Compose *compose;
1502         PrefsAccount *account = NULL;
1503         GtkTextView *textview;
1504         GtkTextBuffer *textbuf;
1505         gboolean quote = FALSE;
1506         const gchar *qmark = NULL;
1507         const gchar *body_fmt = NULL;
1508         gchar *s_system = NULL;
1509         START_TIMING("");
1510         cm_return_val_if_fail(msginfo != NULL, NULL);
1511         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1512
1513         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1514
1515         cm_return_val_if_fail(account != NULL, NULL);
1516
1517         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1518
1519         compose->updating = TRUE;
1520
1521         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1522         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1523
1524         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1525         if (!compose->replyinfo)
1526                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1527
1528         compose_extract_original_charset(compose);
1529         
1530         if (msginfo->folder && msginfo->folder->ret_rcpt)
1531                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1532
1533         /* Set save folder */
1534         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1535                 gchar *folderidentifier;
1536
1537                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1538                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1539                 compose_set_save_to(compose, folderidentifier);
1540                 g_free(folderidentifier);
1541         }
1542
1543         if (compose_parse_header(compose, msginfo) < 0) {
1544                 compose->updating = FALSE;
1545                 compose_destroy(compose);
1546                 return NULL;
1547         }
1548
1549         /* override from name according to folder properties */
1550         if (msginfo->folder && msginfo->folder->prefs &&
1551                 msginfo->folder->prefs->reply_with_format &&
1552                 msginfo->folder->prefs->reply_override_from_format &&
1553                 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1554
1555                 gchar *tmp = NULL;
1556                 gchar *buf = NULL;
1557
1558                 /* decode \-escape sequences in the internal representation of the quote format */
1559                 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1560                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1561
1562 #ifdef USE_ENCHANT
1563                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1564                                 compose->gtkaspell);
1565 #else
1566                 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1567 #endif
1568                 quote_fmt_scan_string(tmp);
1569                 quote_fmt_parse();
1570
1571                 buf = quote_fmt_get_buffer();
1572                 if (buf == NULL)
1573                         alertpanel_error(_("The \"From\" field of the \"Reply\" template contains an invalid email address."));
1574                 else
1575                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1576                 quote_fmt_reset_vartable();
1577
1578                 g_free(tmp);
1579         }
1580
1581         textview = (GTK_TEXT_VIEW(compose->text));
1582         textbuf = gtk_text_view_get_buffer(textview);
1583         compose_create_tags(textview, compose);
1584
1585         undo_block(compose->undostruct);
1586 #ifdef USE_ENCHANT
1587                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1588 #endif
1589
1590         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1591                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1592                 /* use the reply format of folder (if enabled), or the account's one
1593                    (if enabled) or fallback to the global reply format, which is always
1594                    enabled (even if empty), and use the relevant quotemark */
1595                 quote = TRUE;
1596                 if (msginfo->folder && msginfo->folder->prefs &&
1597                                 msginfo->folder->prefs->reply_with_format) {
1598                         qmark = msginfo->folder->prefs->reply_quotemark;
1599                         body_fmt = msginfo->folder->prefs->reply_body_format;
1600
1601                 } else if (account->reply_with_format) {
1602                         qmark = account->reply_quotemark;
1603                         body_fmt = account->reply_body_format;
1604
1605                 } else {
1606                         qmark = prefs_common.quotemark;
1607                         if (prefs_common.quotefmt && *prefs_common.quotefmt)
1608                                 body_fmt = gettext(prefs_common.quotefmt);
1609                         else
1610                                 body_fmt = "";
1611                 }
1612         }
1613
1614         if (quote) {
1615                 /* empty quotemark is not allowed */
1616                 if (qmark == NULL || *qmark == '\0')
1617                         qmark = "> ";
1618                 compose_quote_fmt(compose, compose->replyinfo,
1619                                   body_fmt, qmark, body, FALSE, TRUE,
1620                                           _("The body of the \"Reply\" template has an error at line %d."));
1621                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1622                 quote_fmt_reset_vartable();
1623 #ifdef USE_ENCHANT
1624                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1625                         gtkaspell_highlight_all(compose->gtkaspell);
1626 #endif
1627         }
1628
1629         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1630                 compose_force_encryption(compose, account, FALSE, s_system);
1631         }
1632
1633         privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1634         if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1635                 compose_force_signing(compose, account, s_system);
1636         }
1637         g_free(s_system);
1638
1639         SIGNAL_BLOCK(textbuf);
1640         
1641         if (account->auto_sig)
1642                 compose_insert_sig(compose, FALSE);
1643
1644         compose_wrap_all(compose);
1645
1646         SIGNAL_UNBLOCK(textbuf);
1647         
1648         gtk_widget_grab_focus(compose->text);
1649
1650         undo_unblock(compose->undostruct);
1651
1652         if (prefs_common.auto_exteditor)
1653                 compose_exec_ext_editor(compose);
1654                 
1655         compose->modified = FALSE;
1656         compose_set_title(compose);
1657
1658         compose->updating = FALSE;
1659         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1660         SCROLL_TO_CURSOR(compose);
1661         
1662         if (compose->deferred_destroy) {
1663                 compose_destroy(compose);
1664                 return NULL;
1665         }
1666         END_TIMING();
1667
1668         return compose;
1669 }
1670
1671 #define INSERT_FW_HEADER(var, hdr) \
1672 if (msginfo->var && *msginfo->var) { \
1673         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1674         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1675         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1676 }
1677
1678 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1679                          gboolean as_attach, const gchar *body,
1680                          gboolean no_extedit,
1681                          gboolean batch)
1682 {
1683         Compose *compose;
1684         GtkTextView *textview;
1685         GtkTextBuffer *textbuf;
1686         GtkTextIter iter;
1687
1688         cm_return_val_if_fail(msginfo != NULL, NULL);
1689         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1690
1691         if (!account && 
1692             !(account = compose_guess_forward_account_from_msginfo
1693                                 (msginfo)))
1694                 account = cur_account;
1695
1696         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1697
1698         compose->updating = TRUE;
1699         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1700         if (!compose->fwdinfo)
1701                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1702
1703         compose_extract_original_charset(compose);
1704
1705         if (msginfo->subject && *msginfo->subject) {
1706                 gchar *buf, *buf2, *p;
1707
1708                 buf = p = g_strdup(msginfo->subject);
1709                 p += subject_get_prefix_length(p);
1710                 memmove(buf, p, strlen(p) + 1);
1711
1712                 buf2 = g_strdup_printf("Fw: %s", buf);
1713                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1714                 
1715                 g_free(buf);
1716                 g_free(buf2);
1717         }
1718
1719         /* override from name according to folder properties */
1720         if (msginfo->folder && msginfo->folder->prefs &&
1721                 msginfo->folder->prefs->forward_with_format &&
1722                 msginfo->folder->prefs->forward_override_from_format &&
1723                 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1724
1725                 gchar *tmp = NULL;
1726                 gchar *buf = NULL;
1727                 MsgInfo *full_msginfo = NULL;
1728
1729                 if (!as_attach)
1730                         full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1731                 if (!full_msginfo)
1732                         full_msginfo = procmsg_msginfo_copy(msginfo);
1733
1734                 /* decode \-escape sequences in the internal representation of the quote format */
1735                 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1736                 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1737
1738 #ifdef USE_ENCHANT
1739                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1740                                 compose->gtkaspell);
1741 #else
1742                 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1743 #endif
1744                 quote_fmt_scan_string(tmp);
1745                 quote_fmt_parse();
1746
1747                 buf = quote_fmt_get_buffer();
1748                 if (buf == NULL)
1749                         alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1750                 else
1751                         gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1752                 quote_fmt_reset_vartable();
1753
1754                 g_free(tmp);
1755                 procmsg_msginfo_free(full_msginfo);
1756         }
1757
1758         textview = GTK_TEXT_VIEW(compose->text);
1759         textbuf = gtk_text_view_get_buffer(textview);
1760         compose_create_tags(textview, compose);
1761         
1762         undo_block(compose->undostruct);
1763         if (as_attach) {
1764                 gchar *msgfile;
1765
1766                 msgfile = procmsg_get_message_file(msginfo);
1767                 if (!is_file_exist(msgfile))
1768                         g_warning("%s: file not exist\n", msgfile);
1769                 else
1770                         compose_attach_append(compose, msgfile, msgfile,
1771                                               "message/rfc822");
1772
1773                 g_free(msgfile);
1774         } else {
1775                 const gchar *qmark = NULL;
1776                 const gchar *body_fmt = NULL;
1777                 MsgInfo *full_msginfo;
1778
1779                 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1780                         body_fmt = gettext(prefs_common.fw_quotefmt);
1781                 else
1782                         body_fmt = "";
1783         
1784                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1785                 if (!full_msginfo)
1786                         full_msginfo = procmsg_msginfo_copy(msginfo);
1787
1788                 /* use the forward format of folder (if enabled), or the account's one
1789                    (if enabled) or fallback to the global forward format, which is always
1790                    enabled (even if empty), and use the relevant quotemark */
1791                 if (msginfo->folder && msginfo->folder->prefs &&
1792                                 msginfo->folder->prefs->forward_with_format) {
1793                         qmark = msginfo->folder->prefs->forward_quotemark;
1794                         body_fmt = msginfo->folder->prefs->forward_body_format;
1795
1796                 } else if (account->forward_with_format) {
1797                         qmark = account->forward_quotemark;
1798                         body_fmt = account->forward_body_format;
1799
1800                 } else {
1801                         qmark = prefs_common.fw_quotemark;
1802                         if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1803                                 body_fmt = gettext(prefs_common.fw_quotefmt);
1804                         else
1805                                 body_fmt = "";
1806                 }
1807
1808                 /* empty quotemark is not allowed */
1809                 if (qmark == NULL || *qmark == '\0')
1810                         qmark = "> ";
1811
1812                 compose_quote_fmt(compose, full_msginfo,
1813                                   body_fmt, qmark, body, FALSE, TRUE,
1814                                           _("The body of the \"Forward\" template has an error at line %d."));
1815                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1816                 quote_fmt_reset_vartable();
1817                 compose_attach_parts(compose, msginfo);
1818
1819                 procmsg_msginfo_free(full_msginfo);
1820 #ifdef USE_ENCHANT
1821                 if (compose->gtkaspell && compose->gtkaspell->check_while_typing)
1822                         gtkaspell_highlight_all(compose->gtkaspell);
1823 #endif
1824         }
1825
1826         SIGNAL_BLOCK(textbuf);
1827
1828         if (account->auto_sig)
1829                 compose_insert_sig(compose, FALSE);
1830
1831         compose_wrap_all(compose);
1832
1833         SIGNAL_UNBLOCK(textbuf);
1834         
1835         gtk_text_buffer_get_start_iter(textbuf, &iter);
1836         gtk_text_buffer_place_cursor(textbuf, &iter);
1837
1838         gtk_widget_grab_focus(compose->header_last->entry);
1839
1840         if (!no_extedit && prefs_common.auto_exteditor)
1841                 compose_exec_ext_editor(compose);
1842         
1843         /*save folder*/
1844         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1845                 gchar *folderidentifier;
1846
1847                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1848                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1849                 compose_set_save_to(compose, folderidentifier);
1850                 g_free(folderidentifier);
1851         }
1852
1853         undo_unblock(compose->undostruct);
1854         
1855         compose->modified = FALSE;
1856         compose_set_title(compose);
1857
1858         compose->updating = FALSE;
1859         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1860         SCROLL_TO_CURSOR(compose);
1861
1862         if (compose->deferred_destroy) {
1863                 compose_destroy(compose);
1864                 return NULL;
1865         }
1866
1867         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
1868
1869         return compose;
1870 }
1871
1872 #undef INSERT_FW_HEADER
1873
1874 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1875 {
1876         Compose *compose;
1877         GtkTextView *textview;
1878         GtkTextBuffer *textbuf;
1879         GtkTextIter iter;
1880         GSList *msginfo;
1881         gchar *msgfile;
1882         gboolean single_mail = TRUE;
1883         
1884         cm_return_val_if_fail(msginfo_list != NULL, NULL);
1885
1886         if (g_slist_length(msginfo_list) > 1)
1887                 single_mail = FALSE;
1888
1889         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1890                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1891                         return NULL;
1892
1893         /* guess account from first selected message */
1894         if (!account && 
1895             !(account = compose_guess_forward_account_from_msginfo
1896                                 (msginfo_list->data)))
1897                 account = cur_account;
1898
1899         cm_return_val_if_fail(account != NULL, NULL);
1900
1901         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1902                 if (msginfo->data) {
1903                         MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1904                         MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1905                 }
1906         }
1907
1908         if (msginfo_list == NULL || msginfo_list->data == NULL) {
1909                 g_warning("no msginfo_list");
1910                 return NULL;
1911         }
1912
1913         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1914
1915         compose->updating = TRUE;
1916
1917         /* override from name according to folder properties */
1918         if (msginfo_list->data) {
1919                 MsgInfo *msginfo = msginfo_list->data;
1920
1921                 if (msginfo->folder && msginfo->folder->prefs &&
1922                         msginfo->folder->prefs->forward_with_format &&
1923                         msginfo->folder->prefs->forward_override_from_format &&
1924                         *msginfo->folder->prefs->forward_override_from_format != '\0') {
1925
1926                         gchar *tmp = NULL;
1927                         gchar *buf = NULL;
1928
1929                         /* decode \-escape sequences in the internal representation of the quote format */
1930                         tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1931                         pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1932
1933 #ifdef USE_ENCHANT
1934                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1935                                         compose->gtkaspell);
1936 #else
1937                         quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1938 #endif
1939                         quote_fmt_scan_string(tmp);
1940                         quote_fmt_parse();
1941
1942                         buf = quote_fmt_get_buffer();
1943                         if (buf == NULL)
1944                                 alertpanel_error(_("The \"From\" field of the \"Forward\" template contains an invalid email address."));
1945                         else
1946                                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1947                         quote_fmt_reset_vartable();
1948
1949                         g_free(tmp);
1950                 }
1951         }
1952
1953         textview = GTK_TEXT_VIEW(compose->text);
1954         textbuf = gtk_text_view_get_buffer(textview);
1955         compose_create_tags(textview, compose);
1956         
1957         undo_block(compose->undostruct);
1958         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1959                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1960
1961                 if (!is_file_exist(msgfile))
1962                         g_warning("%s: file not exist\n", msgfile);
1963                 else
1964                         compose_attach_append(compose, msgfile, msgfile,
1965                                 "message/rfc822");
1966                 g_free(msgfile);
1967         }
1968         
1969         if (single_mail) {
1970                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1971                 if (info->subject && *info->subject) {
1972                         gchar *buf, *buf2, *p;
1973
1974                         buf = p = g_strdup(info->subject);
1975                         p += subject_get_prefix_length(p);
1976                         memmove(buf, p, strlen(p) + 1);
1977
1978                         buf2 = g_strdup_printf("Fw: %s", buf);
1979                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1980
1981                         g_free(buf);
1982                         g_free(buf2);
1983                 }
1984         } else {
1985                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1986                         _("Fw: multiple emails"));
1987         }
1988
1989         SIGNAL_BLOCK(textbuf);
1990         
1991         if (account->auto_sig)
1992                 compose_insert_sig(compose, FALSE);
1993
1994         compose_wrap_all(compose);
1995
1996         SIGNAL_UNBLOCK(textbuf);
1997         
1998         gtk_text_buffer_get_start_iter(textbuf, &iter);
1999         gtk_text_buffer_place_cursor(textbuf, &iter);
2000
2001         gtk_widget_grab_focus(compose->header_last->entry);
2002         undo_unblock(compose->undostruct);
2003         compose->modified = FALSE;
2004         compose_set_title(compose);
2005
2006         compose->updating = FALSE;
2007         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2008         SCROLL_TO_CURSOR(compose);
2009
2010         if (compose->deferred_destroy) {
2011                 compose_destroy(compose);
2012                 return NULL;
2013         }
2014
2015         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2016
2017         return compose;
2018 }
2019
2020 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
2021 {
2022         GtkTextIter start = *iter;
2023         GtkTextIter end_iter;
2024         int start_pos = gtk_text_iter_get_offset(&start);
2025         gchar *str = NULL;
2026         if (!compose->account->sig_sep)
2027                 return FALSE;
2028         
2029         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2030                 start_pos+strlen(compose->account->sig_sep));
2031
2032         /* check sig separator */
2033         str = gtk_text_iter_get_text(&start, &end_iter);
2034         if (!strcmp(str, compose->account->sig_sep)) {
2035                 gchar *tmp = NULL;
2036                 /* check end of line (\n) */
2037                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
2038                         start_pos+strlen(compose->account->sig_sep));
2039                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
2040                         start_pos+strlen(compose->account->sig_sep)+1);
2041                 tmp = gtk_text_iter_get_text(&start, &end_iter);
2042                 if (!strcmp(tmp,"\n")) {
2043                         g_free(str);
2044                         g_free(tmp);
2045                         return TRUE;
2046                 }
2047                 g_free(tmp);    
2048         }
2049         g_free(str);
2050
2051         return FALSE;
2052 }
2053
2054 static void compose_colorize_signature(Compose *compose)
2055 {
2056         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
2057         GtkTextIter iter;
2058         GtkTextIter end_iter;
2059         gtk_text_buffer_get_start_iter(buffer, &iter);
2060         while (gtk_text_iter_forward_line(&iter))
2061                 if (compose_is_sig_separator(compose, buffer, &iter)) {
2062                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
2063                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
2064                 }
2065 }
2066
2067 #define BLOCK_WRAP() {                                                  \
2068         prev_autowrap = compose->autowrap;                              \
2069         buffer = gtk_text_view_get_buffer(                              \
2070                                         GTK_TEXT_VIEW(compose->text));  \
2071         compose->autowrap = FALSE;                                      \
2072                                                                         \
2073         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2074                                 G_CALLBACK(compose_changed_cb),         \
2075                                 compose);                               \
2076         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
2077                                 G_CALLBACK(text_inserted),              \
2078                                 compose);                               \
2079 }
2080 #define UNBLOCK_WRAP() {                                                \
2081         compose->autowrap = prev_autowrap;                              \
2082         if (compose->autowrap) {                                        \
2083                 gint old = compose->draft_timeout_tag;                  \
2084                 compose->draft_timeout_tag = -2;                        \
2085                 compose_wrap_all(compose);                              \
2086                 compose->draft_timeout_tag = old;                       \
2087         }                                                               \
2088                                                                         \
2089         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2090                                 G_CALLBACK(compose_changed_cb),         \
2091                                 compose);                               \
2092         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
2093                                 G_CALLBACK(text_inserted),              \
2094                                 compose);                               \
2095 }
2096
2097 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2098 {
2099         Compose *compose = NULL;
2100         PrefsAccount *account = NULL;
2101         GtkTextView *textview;
2102         GtkTextBuffer *textbuf;
2103         GtkTextMark *mark;
2104         GtkTextIter iter;
2105         FILE *fp;
2106         gchar buf[BUFFSIZE];
2107         gboolean use_signing = FALSE;
2108         gboolean use_encryption = FALSE;
2109         gchar *privacy_system = NULL;
2110         int priority = PRIORITY_NORMAL;
2111         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2112         gboolean autowrap = prefs_common.autowrap;
2113         gboolean autoindent = prefs_common.auto_indent;
2114
2115         cm_return_val_if_fail(msginfo != NULL, NULL);
2116         cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2117
2118         if (compose_put_existing_to_front(msginfo)) {
2119                 return NULL;
2120         }
2121
2122         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2123             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2124             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2125                 gchar queueheader_buf[BUFFSIZE];
2126                 gint id, param;
2127
2128                 /* Select Account from queue headers */
2129                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2130                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2131                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2132                         account = account_find_from_id(id);
2133                 }
2134                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2135                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2136                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2137                         account = account_find_from_id(id);
2138                 }
2139                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2140                                              sizeof(queueheader_buf), "NAID:")) {
2141                         id = atoi(&queueheader_buf[strlen("NAID:")]);
2142                         account = account_find_from_id(id);
2143                 }
2144                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2145                                                     sizeof(queueheader_buf), "MAID:")) {
2146                         id = atoi(&queueheader_buf[strlen("MAID:")]);
2147                         account = account_find_from_id(id);
2148                 }
2149                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2150                                                                 sizeof(queueheader_buf), "S:")) {
2151                         account = account_find_from_address(queueheader_buf, FALSE);
2152                 }
2153                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2154                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
2155                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2156                         use_signing = param;
2157                         
2158                 }
2159                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2160                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2161                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2162                         use_signing = param;
2163                         
2164                 }
2165                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2166                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2167                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2168                         use_encryption = param;
2169                 }
2170                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2171                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2172                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2173                         use_encryption = param;
2174                 }
2175                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2176                                              sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2177                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2178                         autowrap = param;
2179                 }
2180                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2181                                              sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2182                         param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2183                         autoindent = param;
2184                 }
2185                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2186                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2187                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2188                 }
2189                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2190                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2191                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2192                 }
2193                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2194                                              sizeof(queueheader_buf), "X-Priority: ")) {
2195                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2196                         priority = param;
2197                 }
2198                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2199                                              sizeof(queueheader_buf), "RMID:")) {
2200                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2201                         if (tokens[0] && tokens[1] && tokens[2]) {
2202                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2203                                 if (orig_item != NULL) {
2204                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2205                                 }
2206                         }
2207                         g_strfreev(tokens);
2208                 }
2209                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
2210                                              sizeof(queueheader_buf), "FMID:")) {
2211                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2212                         if (tokens[0] && tokens[1] && tokens[2]) {
2213                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2214                                 if (orig_item != NULL) {
2215                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2216                                 }
2217                         }
2218                         g_strfreev(tokens);
2219                 }
2220         } else {
2221                 account = msginfo->folder->folder->account;
2222         }
2223
2224         if (!account && prefs_common.reedit_account_autosel) {
2225                 gchar from[BUFFSIZE];
2226                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2227                         extract_address(from);
2228                         account = account_find_from_address(from, FALSE);
2229                 }
2230         }
2231         if (!account) {
2232                 account = cur_account;
2233         }
2234         cm_return_val_if_fail(account != NULL, NULL);
2235
2236         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2237
2238         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2239         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2240         compose->autowrap = autowrap;
2241         compose->replyinfo = replyinfo;
2242         compose->fwdinfo = fwdinfo;
2243
2244         compose->updating = TRUE;
2245         compose->priority = priority;
2246
2247         if (privacy_system != NULL) {
2248                 compose->privacy_system = privacy_system;
2249                 compose_use_signing(compose, use_signing);
2250                 compose_use_encryption(compose, use_encryption);
2251                 compose_update_privacy_system_menu_item(compose, FALSE);
2252         } else {
2253                 activate_privacy_system(compose, account, FALSE);
2254         }
2255
2256         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2257
2258         compose_extract_original_charset(compose);
2259
2260         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2261             folder_has_parent_of_type(msginfo->folder, F_DRAFT) ||
2262             folder_has_parent_of_type(msginfo->folder, F_OUTBOX)) {
2263                 gchar queueheader_buf[BUFFSIZE];
2264
2265                 /* Set message save folder */
2266                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2267                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2268                         compose_set_save_to(compose, &queueheader_buf[4]);
2269                 }
2270                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2271                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2272                         if (active) {
2273                                 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2274                         }
2275                 }
2276         }
2277         
2278         if (compose_parse_header(compose, msginfo) < 0) {
2279                 compose->updating = FALSE;
2280                 compose_destroy(compose);
2281                 return NULL;
2282         }
2283         compose_reedit_set_entry(compose, msginfo);
2284
2285         textview = GTK_TEXT_VIEW(compose->text);
2286         textbuf = gtk_text_view_get_buffer(textview);
2287         compose_create_tags(textview, compose);
2288
2289         mark = gtk_text_buffer_get_insert(textbuf);
2290         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2291
2292         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2293                                         G_CALLBACK(compose_changed_cb),
2294                                         compose);
2295         
2296         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2297                 fp = procmime_get_first_encrypted_text_content(msginfo);
2298                 if (fp) {
2299                         compose_force_encryption(compose, account, TRUE, NULL);
2300                 }
2301         } else {
2302                 fp = procmime_get_first_text_content(msginfo);
2303         }
2304         if (fp == NULL) {
2305                 g_warning("Can't get text part\n");
2306         }
2307
2308         if (fp != NULL) {
2309                 gboolean prev_autowrap = compose->autowrap;
2310                 GtkTextBuffer *buffer = textbuf;
2311                 BLOCK_WRAP();
2312                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2313                         strcrchomp(buf);
2314                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2315                 }
2316                 UNBLOCK_WRAP();
2317                 fclose(fp);
2318         }
2319         
2320         compose_attach_parts(compose, msginfo);
2321
2322         compose_colorize_signature(compose);
2323
2324         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2325                                         G_CALLBACK(compose_changed_cb),
2326                                         compose);
2327
2328         gtk_widget_grab_focus(compose->text);
2329
2330         if (prefs_common.auto_exteditor) {
2331                 compose_exec_ext_editor(compose);
2332         }
2333         compose->modified = FALSE;
2334         compose_set_title(compose);
2335
2336         compose->updating = FALSE;
2337         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2338         SCROLL_TO_CURSOR(compose);
2339
2340         if (compose->deferred_destroy) {
2341                 compose_destroy(compose);
2342                 return NULL;
2343         }
2344         
2345         compose->sig_str = account_get_signature_str(compose->account);
2346         
2347         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2348
2349         return compose;
2350 }
2351
2352 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2353                                                  gboolean batch)
2354 {
2355         Compose *compose;
2356         gchar *filename;
2357         FolderItem *item;
2358
2359         cm_return_val_if_fail(msginfo != NULL, NULL);
2360
2361         if (!account)
2362                 account = account_get_reply_account(msginfo,
2363                                         prefs_common.reply_account_autosel);
2364         cm_return_val_if_fail(account != NULL, NULL);
2365
2366         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2367
2368         compose->updating = TRUE;
2369
2370         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2371         compose->replyinfo = NULL;
2372         compose->fwdinfo = NULL;
2373
2374         compose_show_first_last_header(compose, TRUE);
2375
2376         gtk_widget_grab_focus(compose->header_last->entry);
2377
2378         filename = procmsg_get_message_file(msginfo);
2379
2380         if (filename == NULL) {
2381                 compose->updating = FALSE;
2382                 compose_destroy(compose);
2383
2384                 return NULL;
2385         }
2386
2387         compose->redirect_filename = filename;
2388         
2389         /* Set save folder */
2390         item = msginfo->folder;
2391         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2392                 gchar *folderidentifier;
2393
2394                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2395                 folderidentifier = folder_item_get_identifier(item);
2396                 compose_set_save_to(compose, folderidentifier);
2397                 g_free(folderidentifier);
2398         }
2399
2400         compose_attach_parts(compose, msginfo);
2401
2402         if (msginfo->subject)
2403                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2404                                    msginfo->subject);
2405         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2406
2407         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2408                                           _("The body of the \"Redirect\" template has an error at line %d."));
2409         quote_fmt_reset_vartable();
2410         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2411
2412         compose_colorize_signature(compose);
2413
2414         
2415         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2416         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2417         cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2418
2419         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2420         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2421         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2422         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2423         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2424         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2425         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2426         cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2427         
2428         if (compose->toolbar->draft_btn)
2429                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2430         if (compose->toolbar->insert_btn)
2431                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2432         if (compose->toolbar->attach_btn)
2433                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2434         if (compose->toolbar->sig_btn)
2435                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2436         if (compose->toolbar->exteditor_btn)
2437                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2438         if (compose->toolbar->linewrap_current_btn)
2439                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2440         if (compose->toolbar->linewrap_all_btn)
2441                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2442
2443         compose->modified = FALSE;
2444         compose_set_title(compose);
2445         compose->updating = FALSE;
2446         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2447         SCROLL_TO_CURSOR(compose);
2448
2449         if (compose->deferred_destroy) {
2450                 compose_destroy(compose);
2451                 return NULL;
2452         }
2453         
2454         hooks_invoke(COMPOSE_CREATED_HOOKLIST, compose);
2455
2456         return compose;
2457 }
2458
2459 GList *compose_get_compose_list(void)
2460 {
2461         return compose_list;
2462 }
2463
2464 void compose_entry_append(Compose *compose, const gchar *address,
2465                           ComposeEntryType type, ComposePrefType pref_type)
2466 {
2467         const gchar *header;
2468         gchar *cur, *begin;
2469         gboolean in_quote = FALSE;
2470         if (!address || *address == '\0') return;
2471
2472         switch (type) {
2473         case COMPOSE_CC:
2474                 header = N_("Cc:");
2475                 break;
2476         case COMPOSE_BCC:
2477                 header = N_("Bcc:");
2478                 break;
2479         case COMPOSE_REPLYTO:
2480                 header = N_("Reply-To:");
2481                 break;
2482         case COMPOSE_NEWSGROUPS:
2483                 header = N_("Newsgroups:");
2484                 break;
2485         case COMPOSE_FOLLOWUPTO:
2486                 header = N_( "Followup-To:");
2487                 break;
2488         case COMPOSE_INREPLYTO:
2489                 header = N_( "In-Reply-To:");
2490                 break;
2491         case COMPOSE_TO:
2492         default:
2493                 header = N_("To:");
2494                 break;
2495         }
2496         header = prefs_common_translated_header_name(header);
2497         
2498         cur = begin = (gchar *)address;
2499         
2500         /* we separate the line by commas, but not if we're inside a quoted
2501          * string */
2502         while (*cur != '\0') {
2503                 if (*cur == '"') 
2504                         in_quote = !in_quote;
2505                 if (*cur == ',' && !in_quote) {
2506                         gchar *tmp = g_strdup(begin);
2507                         gchar *o_tmp = tmp;
2508                         tmp[cur-begin]='\0';
2509                         cur++;
2510                         begin = cur;
2511                         while (*tmp == ' ' || *tmp == '\t')
2512                                 tmp++;
2513                         compose_add_header_entry(compose, header, tmp, pref_type);
2514                         g_free(o_tmp);
2515                         continue;
2516                 }
2517                 cur++;
2518         }
2519         if (begin < cur) {
2520                 gchar *tmp = g_strdup(begin);
2521                 gchar *o_tmp = tmp;
2522                 tmp[cur-begin]='\0';
2523                 cur++;
2524                 begin = cur;
2525                 while (*tmp == ' ' || *tmp == '\t')
2526                         tmp++;
2527                 compose_add_header_entry(compose, header, tmp, pref_type);
2528                 g_free(o_tmp);          
2529         }
2530 }
2531
2532 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2533 {
2534         static GdkColor yellow;
2535         static GdkColor black;
2536         static gboolean yellow_initialised = FALSE;
2537         GSList *h_list;
2538         GtkEntry *entry;
2539                 
2540         if (!yellow_initialised) {
2541                 gdk_color_parse("#f5f6be", &yellow);
2542                 gdk_color_parse("#000000", &black);
2543                 yellow_initialised = gdk_colormap_alloc_color(
2544                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2545                 yellow_initialised &= gdk_colormap_alloc_color(
2546                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2547         }
2548
2549         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2550                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2551                 if (gtk_entry_get_text(entry) && 
2552                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2553                         if (yellow_initialised) {
2554                                 gtk_widget_modify_base(
2555                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2556                                         GTK_STATE_NORMAL, &yellow);
2557                                 gtk_widget_modify_text(
2558                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2559                                         GTK_STATE_NORMAL, &black);
2560                         }
2561                 }
2562         }
2563 }
2564
2565 void compose_toolbar_cb(gint action, gpointer data)
2566 {
2567         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2568         Compose *compose = (Compose*)toolbar_item->parent;
2569         
2570         cm_return_if_fail(compose != NULL);
2571
2572         switch(action) {
2573         case A_SEND:
2574                 compose_send_cb(NULL, compose);
2575                 break;
2576         case A_SENDL:
2577                 compose_send_later_cb(NULL, compose);
2578                 break;
2579         case A_DRAFT:
2580                 compose_draft(compose, COMPOSE_QUIT_EDITING);
2581                 break;
2582         case A_INSERT:
2583                 compose_insert_file_cb(NULL, compose);
2584                 break;
2585         case A_ATTACH:
2586                 compose_attach_cb(NULL, compose);
2587                 break;
2588         case A_SIG:
2589                 compose_insert_sig(compose, FALSE);
2590                 break;
2591         case A_EXTEDITOR:
2592                 compose_ext_editor_cb(NULL, compose);
2593                 break;
2594         case A_LINEWRAP_CURRENT:
2595                 compose_beautify_paragraph(compose, NULL, TRUE);
2596                 break;
2597         case A_LINEWRAP_ALL:
2598                 compose_wrap_all_full(compose, TRUE);
2599                 break;
2600         case A_ADDRBOOK:
2601                 compose_address_cb(NULL, compose);
2602                 break;
2603 #ifdef USE_ENCHANT
2604         case A_CHECK_SPELLING:
2605                 compose_check_all(NULL, compose);
2606                 break;
2607 #endif
2608         default:
2609                 break;
2610         }
2611 }
2612
2613 static MailField compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2614 {
2615         gchar *to = NULL;
2616         gchar *cc = NULL;
2617         gchar *bcc = NULL;
2618         gchar *subject = NULL;
2619         gchar *body = NULL;
2620         gchar *temp = NULL;
2621         gsize  len = 0;
2622         gchar **attach = NULL;
2623         gchar *inreplyto = NULL;
2624         MailField mfield = NO_FIELD_PRESENT;
2625
2626         /* get mailto parts but skip from */
2627         scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach, &inreplyto);
2628
2629         if (to) {
2630                 compose_entry_append(compose, to, to_type, PREF_MAILTO);
2631                 mfield = TO_FIELD_PRESENT;
2632         }
2633         if (cc)
2634                 compose_entry_append(compose, cc, COMPOSE_CC, PREF_MAILTO);
2635         if (bcc)
2636                 compose_entry_append(compose, bcc, COMPOSE_BCC, PREF_MAILTO);
2637         if (subject) {
2638                 if (!g_utf8_validate (subject, -1, NULL)) {
2639                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2640                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2641                         g_free(temp);
2642                 } else {
2643                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2644                 }
2645                 mfield = SUBJECT_FIELD_PRESENT;
2646         }
2647         if (body) {
2648                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2649                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2650                 GtkTextMark *mark;
2651                 GtkTextIter iter;
2652                 gboolean prev_autowrap = compose->autowrap;
2653
2654                 compose->autowrap = FALSE;
2655
2656                 mark = gtk_text_buffer_get_insert(buffer);
2657                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2658
2659                 if (!g_utf8_validate (body, -1, NULL)) {
2660                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2661                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2662                         g_free(temp);
2663                 } else {
2664                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2665                 }
2666                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2667
2668                 compose->autowrap = prev_autowrap;
2669                 if (compose->autowrap)
2670                         compose_wrap_all(compose);
2671                 mfield = BODY_FIELD_PRESENT;
2672         }
2673
2674         if (attach) {
2675                 gint i = 0, att = 0;
2676                 gchar *warn_files = NULL;
2677                 while (attach[i] != NULL) {
2678                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2679                         if (utf8_filename) {
2680                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2681                                         gchar *tmp = g_strdup_printf("%s%s\n",
2682                                                         warn_files?warn_files:"",
2683                                                         utf8_filename);
2684                                         g_free(warn_files);
2685                                         warn_files = tmp;
2686                                         att++;
2687                                 }
2688                                 g_free(utf8_filename);
2689                         } else {
2690                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2691                         }
2692                         i++;
2693                 }
2694                 if (warn_files) {
2695                         alertpanel_notice(ngettext(
2696                         "The following file has been attached: \n%s",
2697                         "The following files have been attached: \n%s", att), warn_files);
2698                         g_free(warn_files);
2699                 }
2700         }
2701         if (inreplyto)
2702                 compose_entry_append(compose, inreplyto, COMPOSE_INREPLYTO, PREF_MAILTO);
2703
2704         g_free(to);
2705         g_free(cc);
2706         g_free(bcc);
2707         g_free(subject);
2708         g_free(body);
2709         g_strfreev(attach);
2710         g_free(inreplyto);
2711         
2712         return mfield;
2713 }
2714
2715 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2716 {
2717         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2718                                        {"Cc:",          NULL, TRUE},
2719                                        {"References:",  NULL, FALSE},
2720                                        {"Bcc:",         NULL, TRUE},
2721                                        {"Newsgroups:",  NULL, TRUE},
2722                                        {"Followup-To:", NULL, TRUE},
2723                                        {"List-Post:",   NULL, FALSE},
2724                                        {"X-Priority:",  NULL, FALSE},
2725                                        {NULL,           NULL, FALSE}};
2726
2727         enum
2728         {
2729                 H_REPLY_TO      = 0,
2730                 H_CC            = 1,
2731                 H_REFERENCES    = 2,
2732                 H_BCC           = 3,
2733                 H_NEWSGROUPS    = 4,
2734                 H_FOLLOWUP_TO   = 5,
2735                 H_LIST_POST     = 6,
2736                 H_X_PRIORITY    = 7
2737         };
2738
2739         FILE *fp;
2740
2741         cm_return_val_if_fail(msginfo != NULL, -1);
2742
2743         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2744         procheader_get_header_fields(fp, hentry);
2745         fclose(fp);
2746
2747         if (hentry[H_REPLY_TO].body != NULL) {
2748                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2749                         compose->replyto =
2750                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2751                                                    NULL);
2752                 }
2753                 g_free(hentry[H_REPLY_TO].body);
2754                 hentry[H_REPLY_TO].body = NULL;
2755         }
2756         if (hentry[H_CC].body != NULL) {
2757                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2758                 g_free(hentry[H_CC].body);
2759                 hentry[H_CC].body = NULL;
2760         }
2761         if (hentry[H_REFERENCES].body != NULL) {
2762                 if (compose->mode == COMPOSE_REEDIT)
2763                         compose->references = hentry[H_REFERENCES].body;
2764                 else {
2765                         compose->references = compose_parse_references
2766                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2767                         g_free(hentry[H_REFERENCES].body);
2768                 }
2769                 hentry[H_REFERENCES].body = NULL;
2770         }
2771         if (hentry[H_BCC].body != NULL) {
2772                 if (compose->mode == COMPOSE_REEDIT)
2773                         compose->bcc =
2774                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2775                 g_free(hentry[H_BCC].body);
2776                 hentry[H_BCC].body = NULL;
2777         }
2778         if (hentry[H_NEWSGROUPS].body != NULL) {
2779                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2780                 hentry[H_NEWSGROUPS].body = NULL;
2781         }
2782         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2783                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2784                         compose->followup_to =
2785                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2786                                                    NULL);
2787                 }
2788                 g_free(hentry[H_FOLLOWUP_TO].body);
2789                 hentry[H_FOLLOWUP_TO].body = NULL;
2790         }
2791         if (hentry[H_LIST_POST].body != NULL) {
2792                 gchar *to = NULL, *start = NULL;
2793
2794                 extract_address(hentry[H_LIST_POST].body);
2795                 if (hentry[H_LIST_POST].body[0] != '\0') {
2796                         start = strstr(hentry[H_LIST_POST].body, "mailto:");
2797                         
2798                         scan_mailto_url(start ? start : hentry[H_LIST_POST].body,
2799                                         NULL, &to, NULL, NULL, NULL, NULL, NULL, NULL);
2800
2801                         if (to) {
2802                                 g_free(compose->ml_post);
2803                                 compose->ml_post = to;
2804                         }
2805                 }
2806                 g_free(hentry[H_LIST_POST].body);
2807                 hentry[H_LIST_POST].body = NULL;
2808         }
2809
2810         /* CLAWS - X-Priority */
2811         if (compose->mode == COMPOSE_REEDIT)
2812                 if (hentry[H_X_PRIORITY].body != NULL) {
2813                         gint priority;
2814                         
2815                         priority = atoi(hentry[H_X_PRIORITY].body);
2816                         g_free(hentry[H_X_PRIORITY].body);
2817                         
2818                         hentry[H_X_PRIORITY].body = NULL;
2819                         
2820                         if (priority < PRIORITY_HIGHEST || 
2821                             priority > PRIORITY_LOWEST)
2822                                 priority = PRIORITY_NORMAL;
2823                         
2824                         compose->priority =  priority;
2825                 }
2826  
2827         if (compose->mode == COMPOSE_REEDIT) {
2828                 if (msginfo->inreplyto && *msginfo->inreplyto)
2829                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2830                 return 0;
2831         }
2832
2833         if (msginfo->msgid && *msginfo->msgid)
2834                 compose->inreplyto = g_strdup(msginfo->msgid);
2835
2836         if (!compose->references) {
2837                 if (msginfo->msgid && *msginfo->msgid) {
2838                         if (msginfo->inreplyto && *msginfo->inreplyto)
2839                                 compose->references =
2840                                         g_strdup_printf("<%s>\n\t<%s>",
2841                                                         msginfo->inreplyto,
2842                                                         msginfo->msgid);
2843                         else
2844                                 compose->references =
2845                                         g_strconcat("<", msginfo->msgid, ">",
2846                                                     NULL);
2847                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2848                         compose->references =
2849                                 g_strconcat("<", msginfo->inreplyto, ">",
2850                                             NULL);
2851                 }
2852         }
2853
2854         return 0;
2855 }
2856
2857 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2858 {
2859         GSList *ref_id_list, *cur;
2860         GString *new_ref;
2861         gchar *new_ref_str;
2862
2863         ref_id_list = references_list_append(NULL, ref);
2864         if (!ref_id_list) return NULL;
2865         if (msgid && *msgid)
2866                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2867
2868         for (;;) {
2869                 gint len = 0;
2870
2871                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2872                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2873                         len += strlen((gchar *)cur->data) + 5;
2874
2875                 if (len > MAX_REFERENCES_LEN) {
2876                         /* remove second message-ID */
2877                         if (ref_id_list && ref_id_list->next &&
2878                             ref_id_list->next->next) {
2879                                 g_free(ref_id_list->next->data);
2880                                 ref_id_list = g_slist_remove
2881                                         (ref_id_list, ref_id_list->next->data);
2882                         } else {
2883                                 slist_free_strings(ref_id_list);
2884                                 g_slist_free(ref_id_list);
2885                                 return NULL;
2886                         }
2887                 } else
2888                         break;
2889         }
2890
2891         new_ref = g_string_new("");
2892         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2893                 if (new_ref->len > 0)
2894                         g_string_append(new_ref, "\n\t");
2895                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2896         }
2897
2898         slist_free_strings(ref_id_list);
2899         g_slist_free(ref_id_list);
2900
2901         new_ref_str = new_ref->str;
2902         g_string_free(new_ref, FALSE);
2903
2904         return new_ref_str;
2905 }
2906
2907 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2908                                 const gchar *fmt, const gchar *qmark,
2909                                 const gchar *body, gboolean rewrap,
2910                                 gboolean need_unescape,
2911                                 const gchar *err_msg)
2912 {
2913         MsgInfo* dummyinfo = NULL;
2914         gchar *quote_str = NULL;
2915         gchar *buf;
2916         gboolean prev_autowrap;
2917         const gchar *trimmed_body = body;
2918         gint cursor_pos = -1;
2919         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2920         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2921         GtkTextIter iter;
2922         GtkTextMark *mark;
2923         
2924
2925         SIGNAL_BLOCK(buffer);
2926
2927         if (!msginfo) {
2928                 dummyinfo = compose_msginfo_new_from_compose(compose);
2929                 msginfo = dummyinfo;
2930         }
2931
2932         if (qmark != NULL) {
2933 #ifdef USE_ENCHANT
2934                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2935                                 compose->gtkaspell);
2936 #else
2937                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2938 #endif
2939                 quote_fmt_scan_string(qmark);
2940                 quote_fmt_parse();
2941
2942                 buf = quote_fmt_get_buffer();
2943                 if (buf == NULL)
2944                         alertpanel_error(_("The \"Quotation mark\" of the template is invalid."));
2945                 else
2946                         Xstrdup_a(quote_str, buf, goto error)
2947         }
2948
2949         if (fmt && *fmt != '\0') {
2950
2951                 if (trimmed_body)
2952                         while (*trimmed_body == '\n')
2953                                 trimmed_body++;
2954
2955 #ifdef USE_ENCHANT
2956                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2957                                 compose->gtkaspell);
2958 #else
2959                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2960 #endif
2961                 if (need_unescape) {
2962                         gchar *tmp = NULL;
2963
2964                         /* decode \-escape sequences in the internal representation of the quote format */
2965                         tmp = malloc(strlen(fmt)+1);
2966                         pref_get_unescaped_pref(tmp, fmt);
2967                         quote_fmt_scan_string(tmp);
2968                         quote_fmt_parse();
2969                         g_free(tmp);
2970                 } else {
2971                         quote_fmt_scan_string(fmt);
2972                         quote_fmt_parse();
2973                 }
2974
2975                 buf = quote_fmt_get_buffer();
2976                 if (buf == NULL) {
2977                         gint line = quote_fmt_get_line();
2978                         alertpanel_error(err_msg, line);
2979                         goto error;
2980                 }
2981         } else
2982                 buf = "";
2983
2984         prev_autowrap = compose->autowrap;
2985         compose->autowrap = FALSE;
2986
2987         mark = gtk_text_buffer_get_insert(buffer);
2988         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2989         if (g_utf8_validate(buf, -1, NULL)) { 
2990                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2991         } else {
2992                 gchar *tmpout = NULL;
2993                 tmpout = conv_codeset_strdup
2994                         (buf, conv_get_locale_charset_str_no_utf8(),
2995                          CS_INTERNAL);
2996                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2997                         g_free(tmpout);
2998                         tmpout = g_malloc(strlen(buf)*2+1);
2999                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
3000                 }
3001                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
3002                 g_free(tmpout);
3003         }
3004
3005         cursor_pos = quote_fmt_get_cursor_pos();
3006         if (cursor_pos == -1)
3007                 cursor_pos = gtk_text_iter_get_offset(&iter);
3008         compose->set_cursor_pos = cursor_pos;
3009
3010         gtk_text_buffer_get_start_iter(buffer, &iter);
3011         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
3012         gtk_text_buffer_place_cursor(buffer, &iter);
3013
3014         compose->autowrap = prev_autowrap;
3015         if (compose->autowrap && rewrap)
3016                 compose_wrap_all(compose);
3017
3018         goto ok;
3019
3020 error:
3021         buf = NULL;
3022 ok:
3023         SIGNAL_UNBLOCK(buffer);
3024
3025         procmsg_msginfo_free( dummyinfo );
3026
3027         return buf;
3028 }
3029
3030 /* if ml_post is of type addr@host and from is of type
3031  * addr-anything@host, return TRUE
3032  */
3033 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
3034 {
3035         gchar *left_ml = NULL;
3036         gchar *right_ml = NULL;
3037         gchar *left_from = NULL;
3038         gchar *right_from = NULL;
3039         gboolean result = FALSE;
3040         
3041         if (!ml_post || !from)
3042                 return FALSE;
3043         
3044         left_ml = g_strdup(ml_post);
3045         if (strstr(left_ml, "@")) {
3046                 right_ml = strstr(left_ml, "@")+1;
3047                 *(strstr(left_ml, "@")) = '\0';
3048         }
3049         
3050         left_from = g_strdup(from);
3051         if (strstr(left_from, "@")) {
3052                 right_from = strstr(left_from, "@")+1;
3053                 *(strstr(left_from, "@")) = '\0';
3054         }
3055         
3056         if (left_ml && left_from && right_ml && right_from
3057         &&  !strncmp(left_from, left_ml, strlen(left_ml))
3058         &&  !strcmp(right_from, right_ml)) {
3059                 result = TRUE;
3060         }
3061         g_free(left_ml);
3062         g_free(left_from);
3063         
3064         return result;
3065 }
3066
3067 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
3068                                     gboolean to_all, gboolean to_ml,
3069                                     gboolean to_sender,
3070                                     gboolean followup_and_reply_to)
3071 {
3072         GSList *cc_list = NULL;
3073         GSList *cur;
3074         gchar *from = NULL;
3075         gchar *replyto = NULL;
3076         gchar *ac_email = NULL;
3077
3078         gboolean reply_to_ml = FALSE;
3079         gboolean default_reply_to = FALSE;
3080
3081         cm_return_if_fail(compose->account != NULL);
3082         cm_return_if_fail(msginfo != NULL);
3083
3084         reply_to_ml = to_ml && compose->ml_post;
3085
3086         default_reply_to = msginfo->folder && 
3087                 msginfo->folder->prefs->enable_default_reply_to;
3088
3089         if (compose->account->protocol != A_NNTP) {
3090                 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3091                         if (msginfo->folder->prefs->enable_default_replyto) {
3092                                 compose_entry_append(compose, msginfo->folder->prefs->default_replyto,
3093                                                         COMPOSE_REPLYTO, PREF_FOLDER);
3094                         }
3095                         if (msginfo->folder->prefs->enable_default_bcc) {
3096                                 compose_entry_append(compose, msginfo->folder->prefs->default_bcc,
3097                                                         COMPOSE_BCC, PREF_FOLDER);
3098                         }
3099                         if (msginfo->folder->prefs->enable_default_cc) {
3100                                 compose_entry_append(compose, msginfo->folder->prefs->default_cc,
3101                                                         COMPOSE_CC, PREF_FOLDER);
3102                         }
3103                 }
3104                 if (reply_to_ml && !default_reply_to) {
3105                         
3106                         gboolean is_subscr = is_subscription(compose->ml_post,
3107                                                              msginfo->from);
3108                         if (!is_subscr) {
3109                                 /* normal answer to ml post with a reply-to */
3110                                 compose_entry_append(compose,
3111                                            compose->ml_post,
3112                                            COMPOSE_TO, PREF_ML);
3113                                 if (compose->replyto)
3114                                         compose_entry_append(compose,
3115                                                 compose->replyto,
3116                                                 COMPOSE_CC, PREF_ML);
3117                         } else {
3118                                 /* answer to subscription confirmation */
3119                                 if (compose->replyto)
3120                                         compose_entry_append(compose,
3121                                                 compose->replyto,
3122                                                 COMPOSE_TO, PREF_ML);
3123                                 else if (msginfo->from)
3124                                         compose_entry_append(compose,
3125                                                 msginfo->from,
3126                                                 COMPOSE_TO, PREF_ML);
3127                         }
3128                 }
3129                 else if (!(to_all || to_sender) && default_reply_to) {
3130                         compose_entry_append(compose,
3131                             msginfo->folder->prefs->default_reply_to,
3132                             COMPOSE_TO, PREF_FOLDER);
3133                         compose_entry_mark_default_to(compose,
3134                                 msginfo->folder->prefs->default_reply_to);
3135                 } else {
3136                         gchar *tmp1 = NULL;
3137                         if (!msginfo->from)
3138                                 return;
3139                         Xstrdup_a(tmp1, msginfo->from, return);
3140                         extract_address(tmp1);
3141                         if (to_all || to_sender ||
3142                             !account_find_from_address(tmp1, FALSE))
3143                                 compose_entry_append(compose,
3144                                  (compose->replyto && !to_sender)
3145                                           ? compose->replyto :
3146                                           msginfo->from ? msginfo->from : "",
3147                                           COMPOSE_TO, PREF_NONE);
3148                         else if (!to_all && !to_sender) {
3149                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3150                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3151                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3152                                         if (compose->replyto) {
3153                                                 compose_entry_append(compose,
3154                                                         compose->replyto,
3155                                                         COMPOSE_TO, PREF_NONE);
3156                                         } else {
3157                                                 compose_entry_append(compose,
3158                                                           msginfo->from ? msginfo->from : "",
3159                                                           COMPOSE_TO, PREF_NONE);
3160                                         }
3161                                 } else {
3162                                         /* replying to own mail, use original recp */
3163                                         compose_entry_append(compose,
3164                                                   msginfo->to ? msginfo->to : "",
3165                                                   COMPOSE_TO, PREF_NONE);
3166                                         compose_entry_append(compose,
3167                                                   msginfo->cc ? msginfo->cc : "",
3168                                                   COMPOSE_CC, PREF_NONE);
3169                                 }
3170                         }
3171                 }
3172         } else {
3173                 if (to_sender || (compose->followup_to && 
3174                         !strncmp(compose->followup_to, "poster", 6)))
3175                         compose_entry_append
3176                                 (compose, 
3177                                  (compose->replyto ? compose->replyto :
3178                                         msginfo->from ? msginfo->from : ""),
3179                                  COMPOSE_TO, PREF_NONE);
3180                                  
3181                 else if (followup_and_reply_to || to_all) {
3182                         compose_entry_append
3183                                 (compose,
3184                                  (compose->replyto ? compose->replyto :
3185                                  msginfo->from ? msginfo->from : ""),
3186                                  COMPOSE_TO, PREF_NONE);                                
3187                 
3188                         compose_entry_append
3189                                 (compose,
3190                                  compose->followup_to ? compose->followup_to :
3191                                  compose->newsgroups ? compose->newsgroups : "",
3192                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3193                 } 
3194                 else 
3195                         compose_entry_append
3196                                 (compose,
3197                                  compose->followup_to ? compose->followup_to :
3198                                  compose->newsgroups ? compose->newsgroups : "",
3199                                  COMPOSE_NEWSGROUPS, PREF_NONE);
3200         }
3201
3202         if (msginfo->subject && *msginfo->subject) {
3203                 gchar *buf, *buf2;
3204                 gchar *p;
3205
3206                 buf = p = g_strdup(msginfo->subject);
3207                 p += subject_get_prefix_length(p);
3208                 memmove(buf, p, strlen(p) + 1);
3209
3210                 buf2 = g_strdup_printf("Re: %s", buf);
3211                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3212
3213                 g_free(buf2);
3214                 g_free(buf);
3215         } else
3216                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3217
3218         if (to_ml && compose->ml_post) return;
3219         if (!to_all || compose->account->protocol == A_NNTP) return;
3220
3221         if (compose->replyto) {
3222                 Xstrdup_a(replyto, compose->replyto, return);
3223                 extract_address(replyto);
3224         }
3225         if (msginfo->from) {
3226                 Xstrdup_a(from, msginfo->from, return);
3227                 extract_address(from);
3228         }
3229
3230         if (replyto && from)
3231                 cc_list = address_list_append_with_comments(cc_list, from);
3232         if (to_all && msginfo->folder && 
3233             msginfo->folder->prefs->enable_default_reply_to)
3234                 cc_list = address_list_append_with_comments(cc_list,
3235                                 msginfo->folder->prefs->default_reply_to);
3236         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3237         cc_list = address_list_append_with_comments(cc_list, compose->cc);
3238
3239         ac_email = g_utf8_strdown(compose->account->address, -1);
3240
3241         if (cc_list) {
3242                 for (cur = cc_list; cur != NULL; cur = cur->next) {
3243                         gchar *addr = g_utf8_strdown(cur->data, -1);
3244                         extract_address(addr);
3245                 
3246                         if (strcmp(ac_email, addr))
3247                                 compose_entry_append(compose, (gchar *)cur->data,
3248                                                      COMPOSE_CC, PREF_NONE);
3249                         else
3250                                 debug_print("Cc address same as compose account's, ignoring\n");
3251
3252                         g_free(addr);
3253                 }
3254                 
3255                 slist_free_strings(cc_list);
3256                 g_slist_free(cc_list);
3257         }
3258         
3259         g_free(ac_email);
3260 }
3261
3262 #define SET_ENTRY(entry, str) \
3263 { \
3264         if (str && *str) \
3265                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3266 }
3267
3268 #define SET_ADDRESS(type, str) \
3269 { \
3270         if (str && *str) \
3271                 compose_entry_append(compose, str, type, PREF_NONE); \
3272 }
3273
3274 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3275 {
3276         cm_return_if_fail(msginfo != NULL);
3277
3278         SET_ENTRY(subject_entry, msginfo->subject);
3279         SET_ENTRY(from_name, msginfo->from);
3280         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3281         SET_ADDRESS(COMPOSE_CC, compose->cc);
3282         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3283         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3284         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3285         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3286
3287         compose_update_priority_menu_item(compose);
3288         compose_update_privacy_system_menu_item(compose, FALSE);
3289         compose_show_first_last_header(compose, TRUE);
3290 }
3291
3292 #undef SET_ENTRY
3293 #undef SET_ADDRESS
3294
3295 static void compose_insert_sig(Compose *compose, gboolean replace)
3296 {
3297         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3298         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3299         GtkTextMark *mark;
3300         GtkTextIter iter, iter_end;
3301         gint cur_pos, ins_pos;
3302         gboolean prev_autowrap;
3303         gboolean found = FALSE;
3304         gboolean exists = FALSE;
3305         
3306         cm_return_if_fail(compose->account != NULL);
3307
3308         BLOCK_WRAP();
3309
3310         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3311                                         G_CALLBACK(compose_changed_cb),
3312                                         compose);
3313         
3314         mark = gtk_text_buffer_get_insert(buffer);
3315         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3316         cur_pos = gtk_text_iter_get_offset (&iter);
3317         ins_pos = cur_pos;
3318
3319         gtk_text_buffer_get_end_iter(buffer, &iter);
3320
3321         exists = (compose->sig_str != NULL);
3322
3323         if (replace) {
3324                 GtkTextIter first_iter, start_iter, end_iter;
3325
3326                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3327
3328                 if (!exists || compose->sig_str[0] == '\0')
3329                         found = FALSE;
3330                 else
3331                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3332                                         compose->signature_tag);
3333
3334                 if (found) {
3335                         /* include previous \n\n */
3336                         gtk_text_iter_backward_chars(&first_iter, 1);
3337                         start_iter = first_iter;
3338                         end_iter = first_iter;
3339                         /* skip re-start */
3340                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3341                                         compose->signature_tag);
3342                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3343                                         compose->signature_tag);
3344                         if (found) {
3345                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3346                                 iter = start_iter;
3347                         }
3348                 } 
3349         } 
3350
3351         g_free(compose->sig_str);
3352         compose->sig_str = account_get_signature_str(compose->account);
3353
3354         cur_pos = gtk_text_iter_get_offset(&iter);
3355
3356         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3357                 g_free(compose->sig_str);
3358                 compose->sig_str = NULL;
3359         } else {
3360                 if (compose->sig_inserted == FALSE)
3361                         gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3362                 compose->sig_inserted = TRUE;
3363
3364                 cur_pos = gtk_text_iter_get_offset(&iter);
3365                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3366                 /* remove \n\n */
3367                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3368                 gtk_text_iter_forward_chars(&iter, 1);
3369                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3370                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3371
3372                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3373                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3374         }
3375
3376         /* put the cursor where it should be 
3377          * either where the quote_fmt says, either where it was */
3378         if (compose->set_cursor_pos < 0)
3379                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3380         else
3381                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3382                         compose->set_cursor_pos);
3383         
3384         compose->set_cursor_pos = -1;
3385         gtk_text_buffer_place_cursor(buffer, &iter);
3386         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3387                                         G_CALLBACK(compose_changed_cb),
3388                                         compose);
3389                 
3390         UNBLOCK_WRAP();
3391 }
3392
3393 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3394 {
3395         GtkTextView *text;
3396         GtkTextBuffer *buffer;
3397         GtkTextMark *mark;
3398         GtkTextIter iter;
3399         const gchar *cur_encoding;
3400         gchar buf[BUFFSIZE];
3401         gint len;
3402         FILE *fp;
3403         gboolean prev_autowrap;
3404         gboolean badtxt = FALSE;
3405         struct stat file_stat;
3406         int ret;
3407
3408         cm_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3409
3410         /* get the size of the file we are about to insert */
3411         ret = g_stat(file, &file_stat);
3412         if (ret != 0) {
3413                 gchar *shortfile = g_path_get_basename(file);
3414                 alertpanel_error(_("Could not get size of file '%s'."), shortfile);
3415                 g_free(shortfile);
3416                 return COMPOSE_INSERT_NO_FILE;
3417         } else if (prefs_common.warn_large_insert == TRUE) {
3418
3419                 /* ask user for confirmation if the file is large */
3420                 if (prefs_common.warn_large_insert_size < 0 ||
3421                     file_stat.st_size > (prefs_common.warn_large_insert_size * 1024)) {
3422                         AlertValue aval;
3423                         gchar *msg;
3424
3425                         msg = g_strdup_printf(_("You are about to insert a file of %s "
3426                                                 "in the message body. Are you sure you want to do that?"),
3427                                                 to_human_readable(file_stat.st_size));
3428                         aval = alertpanel_full(_("Are you sure?"), msg, GTK_STOCK_CANCEL,
3429                                         _("+_Insert"), NULL, TRUE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
3430                         g_free(msg);
3431
3432                         /* do we ask for confirmation next time? */
3433                         if (aval & G_ALERTDISABLE) {
3434                                 /* no confirmation next time, disable feature in preferences */
3435                                 aval &= ~G_ALERTDISABLE;
3436                                 prefs_common.warn_large_insert = FALSE;
3437                         }
3438
3439                         /* abort file insertion if user canceled action */
3440                         if (aval != G_ALERTALTERNATE) {
3441                                 return COMPOSE_INSERT_NO_FILE;
3442                         }
3443                 }
3444         }
3445
3446
3447         if ((fp = g_fopen(file, "rb")) == NULL) {
3448                 FILE_OP_ERROR(file, "fopen");
3449                 return COMPOSE_INSERT_READ_ERROR;
3450         }
3451
3452         prev_autowrap = compose->autowrap;
3453         compose->autowrap = FALSE;
3454
3455         text = GTK_TEXT_VIEW(compose->text);
3456         buffer = gtk_text_view_get_buffer(text);
3457         mark = gtk_text_buffer_get_insert(buffer);
3458         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3459
3460         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3461                                         G_CALLBACK(text_inserted),
3462                                         compose);
3463
3464         cur_encoding = conv_get_locale_charset_str_no_utf8();
3465
3466         while (fgets(buf, sizeof(buf), fp) != NULL) {
3467                 gchar *str;
3468
3469                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3470                         str = g_strdup(buf);
3471                 else
3472                         str = conv_codeset_strdup
3473                                 (buf, cur_encoding, CS_INTERNAL);
3474                 if (!str) continue;
3475
3476                 /* strip <CR> if DOS/Windows file,
3477                    replace <CR> with <LF> if Macintosh file. */
3478                 strcrchomp(str);
3479                 len = strlen(str);
3480                 if (len > 0 && str[len - 1] != '\n') {
3481                         while (--len >= 0)
3482                                 if (str[len] == '\r') str[len] = '\n';
3483                 }
3484
3485                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3486                 g_free(str);
3487         }
3488
3489         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3490                                           G_CALLBACK(text_inserted),
3491                                           compose);
3492         compose->autowrap = prev_autowrap;
3493         if (compose->autowrap)
3494                 compose_wrap_all(compose);
3495
3496         fclose(fp);
3497
3498         if (badtxt)
3499                 return COMPOSE_INSERT_INVALID_CHARACTER;
3500         else 
3501                 return COMPOSE_INSERT_SUCCESS;
3502 }
3503
3504 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3505                                   const gchar *filename,
3506                                   const gchar *content_type)
3507 {
3508         AttachInfo *ainfo;
3509         GtkTreeIter iter;
3510         FILE *fp;
3511         off_t size;
3512         GAuto *auto_ainfo;
3513         gchar *size_text;
3514         GtkListStore *store;
3515         gchar *name;
3516         gboolean has_binary = FALSE;
3517
3518         if (!is_file_exist(file)) {
3519                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3520                 gboolean result = FALSE;
3521                 if (file_from_uri && is_file_exist(file_from_uri)) {
3522                         result = compose_attach_append(
3523                                                 compose, file_from_uri,
3524                                                 filename,
3525                                                 content_type);
3526                 }
3527                 g_free(file_from_uri);
3528                 if (result)
3529                         return TRUE;
3530                 alertpanel_error("File %s doesn't exist\n", filename);
3531                 return FALSE;
3532         }
3533         if ((size = get_file_size(file)) < 0) {
3534                 alertpanel_error("Can't get file size of %s\n", filename);
3535                 return FALSE;
3536         }
3537         if (size == 0) {
3538                 alertpanel_error(_("File %s is empty."), filename);
3539                 return FALSE;
3540         }
3541         if ((fp = g_fopen(file, "rb")) == NULL) {
3542                 alertpanel_error(_("Can't read %s."), filename);
3543                 return FALSE;
3544         }
3545         fclose(fp);
3546
3547         ainfo = g_new0(AttachInfo, 1);
3548         auto_ainfo = g_auto_pointer_new_with_free
3549                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3550         ainfo->file = g_strdup(file);
3551
3552         if (content_type) {
3553                 ainfo->content_type = g_strdup(content_type);
3554                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3555                         MsgInfo *msginfo;
3556                         MsgFlags flags = {0, 0};
3557
3558                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3559                                 ainfo->encoding = ENC_7BIT;
3560                         else
3561                                 ainfo->encoding = ENC_8BIT;
3562
3563                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3564                         if (msginfo && msginfo->subject)
3565                                 name = g_strdup(msginfo->subject);
3566                         else
3567                                 name = g_path_get_basename(filename ? filename : file);
3568
3569                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3570
3571                         procmsg_msginfo_free(msginfo);
3572                 } else {
3573                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3574                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3575                         else
3576                                 ainfo->encoding = ENC_BASE64;
3577                         name = g_path_get_basename(filename ? filename : file);
3578                         ainfo->name = g_strdup(name);
3579                 }
3580                 g_free(name);
3581         } else {
3582                 ainfo->content_type = procmime_get_mime_type(file);
3583                 if (!ainfo->content_type) {
3584                         ainfo->content_type =
3585                                 g_strdup("application/octet-stream");
3586                         ainfo->encoding = ENC_BASE64;
3587                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3588                         ainfo->encoding =
3589                                 procmime_get_encoding_for_text_file(file, &has_binary);
3590                 else
3591                         ainfo->encoding = ENC_BASE64;
3592                 name = g_path_get_basename(filename ? filename : file);
3593                 ainfo->name = g_strdup(name);   
3594                 g_free(name);
3595         }
3596
3597         if (ainfo->name != NULL
3598         &&  !strcmp(ainfo->name, ".")) {
3599                 g_free(ainfo->name);
3600                 ainfo->name = NULL;
3601         }
3602
3603         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3604                 g_free(ainfo->content_type);
3605                 ainfo->content_type = g_strdup("application/octet-stream");
3606         }
3607
3608         ainfo->size = (goffset)size;
3609         size_text = to_human_readable((goffset)size);
3610
3611         store = GTK_LIST_STORE(gtk_tree_view_get_model
3612                         (GTK_TREE_VIEW(compose->attach_clist)));
3613                 
3614         gtk_list_store_append(store, &iter);
3615         gtk_list_store_set(store, &iter, 
3616                            COL_MIMETYPE, ainfo->content_type,
3617                            COL_SIZE, size_text,
3618                            COL_NAME, ainfo->name,
3619                            COL_DATA, ainfo,
3620                            COL_AUTODATA, auto_ainfo,
3621                            -1);
3622         
3623         g_auto_pointer_free(auto_ainfo);
3624         compose_attach_update_label(compose);
3625         return TRUE;
3626 }
3627
3628 static void compose_use_signing(Compose *compose, gboolean use_signing)
3629 {
3630         compose->use_signing = use_signing;
3631         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3632 }
3633
3634 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3635 {
3636         compose->use_encryption = use_encryption;
3637         cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3638 }
3639
3640 #define NEXT_PART_NOT_CHILD(info)  \
3641 {  \
3642         node = info->node;  \
3643         while (node->children)  \
3644                 node = g_node_last_child(node);  \
3645         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3646 }
3647
3648 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3649 {
3650         MimeInfo *mimeinfo;
3651         MimeInfo *child;
3652         MimeInfo *firsttext = NULL;
3653         MimeInfo *encrypted = NULL;
3654         GNode    *node;
3655         gchar *outfile;
3656         const gchar *partname = NULL;
3657
3658         mimeinfo = procmime_scan_message(msginfo);
3659         if (!mimeinfo) return;
3660
3661         if (mimeinfo->node->children == NULL) {
3662                 procmime_mimeinfo_free_all(mimeinfo);
3663                 return;