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