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