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