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