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