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