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