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