2008-01-20 [colin] 3.2.0cvs52
[claws.git] / src / compose.c
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23
24 #include "defs.h"
25
26 #ifndef PANGO_ENABLE_ENGINE
27 #  define PANGO_ENABLE_ENGINE
28 #endif
29
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkvpaned.h>
41 #include <gtk/gtkentry.h>
42 #include <gtk/gtkeditable.h>
43 #include <gtk/gtkwindow.h>
44 #include <gtk/gtksignal.h>
45 #include <gtk/gtkvbox.h>
46 #include <gtk/gtkcontainer.h>
47 #include <gtk/gtkhandlebox.h>
48 #include <gtk/gtktoolbar.h>
49 #include <gtk/gtktable.h>
50 #include <gtk/gtkhbox.h>
51 #include <gtk/gtklabel.h>
52 #include <gtk/gtkscrolledwindow.h>
53 #include <gtk/gtktreeview.h>
54 #include <gtk/gtkliststore.h>
55 #include <gtk/gtktreeselection.h>
56 #include <gtk/gtktreemodel.h>
57
58 #include <gtk/gtkdnd.h>
59 #include <gtk/gtkclipboard.h>
60 #include <pango/pango-break.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 #include <ctype.h>
65 #include <sys/types.h>
66 #include <sys/stat.h>
67 #include <unistd.h>
68 #include <time.h>
69 #include <stdlib.h>
70 #if HAVE_SYS_WAIT_H
71 #  include <sys/wait.h>
72 #endif
73 #include <signal.h>
74 #include <errno.h>
75 #ifndef G_OS_WIN32  /* fixme we should have a configure test. */
76 #include <libgen.h>
77 #endif
78
79 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
80 #  include <wchar.h>
81 #  include <wctype.h>
82 #endif
83
84 #include "claws.h"
85 #include "main.h"
86 #include "mainwindow.h"
87 #include "compose.h"
88 #include "addressbook.h"
89 #include "folderview.h"
90 #include "procmsg.h"
91 #include "menu.h"
92 #include "stock_pixmap.h"
93 #include "send_message.h"
94 #include "imap.h"
95 #include "news.h"
96 #include "customheader.h"
97 #include "prefs_common.h"
98 #include "prefs_account.h"
99 #include "action.h"
100 #include "account.h"
101 #include "filesel.h"
102 #include "procheader.h"
103 #include "procmime.h"
104 #include "statusbar.h"
105 #include "about.h"
106 #include "base64.h"
107 #include "quoted-printable.h"
108 #include "codeconv.h"
109 #include "utils.h"
110 #include "gtkutils.h"
111 #include "socket.h"
112 #include "alertpanel.h"
113 #include "manage_window.h"
114 #include "gtkshruler.h"
115 #include "folder.h"
116 #include "addr_compl.h"
117 #include "quote_fmt.h"
118 #include "undo.h"
119 #include "foldersel.h"
120 #include "toolbar.h"
121 #include "inc.h"
122 #include "message_search.h"
123 #include "combobox.h"
124 #include "hooks.h"
125 #include "privacy.h"
126 #include "timing.h"
127
128 enum
129 {
130         COL_MIMETYPE = 0,
131         COL_SIZE     = 1,
132         COL_NAME     = 2,
133         COL_DATA     = 3,
134         COL_AUTODATA = 4,
135         N_COL_COLUMNS
136 };
137
138 #define N_ATTACH_COLS   (N_COL_COLUMNS)
139
140 typedef enum
141 {
142         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
143         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
144         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
145         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
146         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
147         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
148         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
149         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
150         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
151         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
152         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
153         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
154         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
155         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
156         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
157 } ComposeCallAdvancedAction;
158
159 typedef enum
160 {
161         PRIORITY_HIGHEST = 1,
162         PRIORITY_HIGH,
163         PRIORITY_NORMAL,
164         PRIORITY_LOW,
165         PRIORITY_LOWEST
166 } PriorityLevel;
167
168 typedef enum
169 {
170         COMPOSE_INSERT_SUCCESS,
171         COMPOSE_INSERT_READ_ERROR,
172         COMPOSE_INSERT_INVALID_CHARACTER,
173         COMPOSE_INSERT_NO_FILE
174 } ComposeInsertResult;
175
176 typedef enum
177 {
178         COMPOSE_WRITE_FOR_SEND,
179         COMPOSE_WRITE_FOR_STORE
180 } ComposeWriteType;
181
182 typedef enum
183 {
184         COMPOSE_QUOTE_FORCED,
185         COMPOSE_QUOTE_CHECK,
186         COMPOSE_QUOTE_SKIP
187 } ComposeQuoteMode;
188
189 #define B64_LINE_SIZE           57
190 #define B64_BUFFSIZE            77
191
192 #define MAX_REFERENCES_LEN      999
193
194 static GList *compose_list = NULL;
195
196 static Compose *compose_generic_new                     (PrefsAccount   *account,
197                                                  const gchar    *to,
198                                                  FolderItem     *item,
199                                                  GPtrArray      *attach_files,
200                                                  GList          *listAddress );
201
202 static Compose *compose_create                  (PrefsAccount   *account,
203                                                  FolderItem              *item,
204                                                  ComposeMode     mode,
205                                                  gboolean batch);
206
207 static void compose_entry_mark_default_to       (Compose          *compose,
208                                          const gchar      *address);
209 static Compose *compose_followup_and_reply_to   (MsgInfo        *msginfo,
210                                          ComposeQuoteMode        quote_mode,
211                                          gboolean        to_all,
212                                          gboolean        to_sender,
213                                          const gchar    *body);
214 static Compose *compose_forward_multiple        (PrefsAccount   *account, 
215                                          GSList         *msginfo_list);
216 static Compose *compose_reply                   (MsgInfo        *msginfo,
217                                          ComposeQuoteMode        quote_mode,
218                                          gboolean        to_all,
219                                          gboolean        to_ml,
220                                          gboolean        to_sender,
221                                          const gchar    *body);
222 static Compose *compose_reply_mode              (ComposeMode     mode, 
223                                          GSList         *msginfo_list, 
224                                          gchar          *body);
225 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
226 static void compose_update_privacy_systems_menu(Compose *compose);
227
228 static GtkWidget *compose_account_option_menu_create
229                                                 (Compose        *compose);
230 static void compose_set_out_encoding            (Compose        *compose);
231 static void compose_set_template_menu           (Compose        *compose);
232 static void compose_template_apply              (Compose        *compose,
233                                                  Template       *tmpl,
234                                                  gboolean        replace);
235 static void compose_destroy                     (Compose        *compose);
236
237 static void compose_entries_set                 (Compose        *compose,
238                                                  const gchar    *mailto);
239 static gint compose_parse_header                (Compose        *compose,
240                                                  MsgInfo        *msginfo);
241 static gchar *compose_parse_references          (const gchar    *ref,
242                                                  const gchar    *msgid);
243
244 static gchar *compose_quote_fmt                 (Compose        *compose,
245                                                  MsgInfo        *msginfo,
246                                                  const gchar    *fmt,
247                                                  const gchar    *qmark,
248                                                  const gchar    *body,
249                                                  gboolean        rewrap,
250                                                  gboolean        need_unescape,
251                                                  const gchar *err_msg);
252
253 static void compose_reply_set_entry             (Compose        *compose,
254                                                  MsgInfo        *msginfo,
255                                                  gboolean        to_all,
256                                                  gboolean        to_ml,
257                                                  gboolean        to_sender,
258                                                  gboolean
259                                                  followup_and_reply_to);
260 static void compose_reedit_set_entry            (Compose        *compose,
261                                                  MsgInfo        *msginfo);
262
263 static void compose_insert_sig                  (Compose        *compose,
264                                                  gboolean        replace);
265 static gchar *compose_get_signature_str         (Compose        *compose);
266 static ComposeInsertResult compose_insert_file  (Compose        *compose,
267                                                  const gchar    *file);
268
269 static gboolean compose_attach_append           (Compose        *compose,
270                                                  const gchar    *file,
271                                                  const gchar    *type,
272                                                  const gchar    *content_type);
273 static void compose_attach_parts                (Compose        *compose,
274                                                  MsgInfo        *msginfo);
275
276 static gboolean compose_beautify_paragraph      (Compose        *compose,
277                                                  GtkTextIter    *par_iter,
278                                                  gboolean        force);
279 static void compose_wrap_all                    (Compose        *compose);
280 static void compose_wrap_all_full               (Compose        *compose,
281                                                  gboolean        autowrap);
282
283 static void compose_set_title                   (Compose        *compose);
284 static void compose_select_account              (Compose        *compose,
285                                                  PrefsAccount   *account,
286                                                  gboolean        init);
287
288 static PrefsAccount *compose_current_mail_account(void);
289 /* static gint compose_send                     (Compose        *compose); */
290 static gboolean compose_check_for_valid_recipient
291                                                 (Compose        *compose);
292 static gboolean compose_check_entries           (Compose        *compose,
293                                                  gboolean       check_everything);
294 static gint compose_write_to_file               (Compose        *compose,
295                                                  FILE           *fp,
296                                                  gint            action,
297                                                  gboolean        attach_parts);
298 static gint compose_write_body_to_file          (Compose        *compose,
299                                                  const gchar    *file);
300 static gint compose_remove_reedit_target        (Compose        *compose,
301                                                  gboolean        force);
302 static void compose_remove_draft                        (Compose        *compose);
303 static gint compose_queue_sub                   (Compose        *compose,
304                                                  gint           *msgnum,
305                                                  FolderItem     **item,
306                                                  gchar          **msgpath,
307                                                  gboolean       check_subject,
308                                                  gboolean       remove_reedit_target);
309 static void compose_add_attachments             (Compose        *compose,
310                                                  MimeInfo       *parent);
311 static gchar *compose_get_header                (Compose        *compose);
312
313 static void compose_convert_header              (Compose        *compose,
314                                                  gchar          *dest,
315                                                  gint            len,
316                                                  gchar          *src,
317                                                  gint            header_len,
318                                                  gboolean        addr_field);
319
320 static void compose_attach_info_free            (AttachInfo     *ainfo);
321 static void compose_attach_remove_selected      (Compose        *compose);
322
323 static void compose_attach_property             (Compose        *compose);
324 static void compose_attach_property_create      (gboolean       *cancelled);
325 static void attach_property_ok                  (GtkWidget      *widget,
326                                                  gboolean       *cancelled);
327 static void attach_property_cancel              (GtkWidget      *widget,
328                                                  gboolean       *cancelled);
329 static gint attach_property_delete_event        (GtkWidget      *widget,
330                                                  GdkEventAny    *event,
331                                                  gboolean       *cancelled);
332 static gboolean attach_property_key_pressed     (GtkWidget      *widget,
333                                                  GdkEventKey    *event,
334                                                  gboolean       *cancelled);
335
336 static void compose_exec_ext_editor             (Compose        *compose);
337 #ifdef G_OS_UNIX
338 static gint compose_exec_ext_editor_real        (const gchar    *file);
339 static gboolean compose_ext_editor_kill         (Compose        *compose);
340 static gboolean compose_input_cb                (GIOChannel     *source,
341                                                  GIOCondition    condition,
342                                                  gpointer        data);
343 static void compose_set_ext_editor_sensitive    (Compose        *compose,
344                                                  gboolean        sensitive);
345 #endif /* G_OS_UNIX */
346
347 static void compose_undo_state_changed          (UndoMain       *undostruct,
348                                                  gint            undo_state,
349                                                  gint            redo_state,
350                                                  gpointer        data);
351
352 static void compose_create_header_entry (Compose *compose);
353 static void compose_add_header_entry    (Compose *compose, const gchar *header, gchar *text);
354 static void compose_remove_header_entries(Compose *compose);
355
356 static void compose_update_priority_menu_item(Compose * compose);
357 #if USE_ASPELL
358 static void compose_spell_menu_changed  (void *data);
359 #endif
360 static void compose_add_field_list      ( Compose *compose,
361                                           GList *listAddress );
362
363 /* callback functions */
364
365 static gboolean compose_edit_size_alloc (GtkEditable    *widget,
366                                          GtkAllocation  *allocation,
367                                          GtkSHRuler     *shruler);
368 static void account_activated           (GtkComboBox *optmenu,
369                                          gpointer        data);
370 static void attach_selected             (GtkTreeView    *tree_view, 
371                                          GtkTreePath    *tree_path,
372                                          GtkTreeViewColumn *column, 
373                                          Compose *compose);
374 static gboolean attach_button_pressed   (GtkWidget      *widget,
375                                          GdkEventButton *event,
376                                          gpointer        data);
377 static gboolean attach_key_pressed      (GtkWidget      *widget,
378                                          GdkEventKey    *event,
379                                          gpointer        data);
380 static void compose_send_cb             (gpointer        data,
381                                          guint           action,
382                                          GtkWidget      *widget);
383 static void compose_send_later_cb       (gpointer        data,
384                                          guint           action,
385                                          GtkWidget      *widget);
386
387 static void compose_draft_cb            (gpointer        data,
388                                          guint           action,
389                                          GtkWidget      *widget);
390
391 static void compose_attach_cb           (gpointer        data,
392                                          guint           action,
393                                          GtkWidget      *widget);
394 static void compose_insert_file_cb      (gpointer        data,
395                                          guint           action,
396                                          GtkWidget      *widget);
397 static void compose_insert_sig_cb       (gpointer        data,
398                                          guint           action,
399                                          GtkWidget      *widget);
400
401 static void compose_close_cb            (gpointer        data,
402                                          guint           action,
403                                          GtkWidget      *widget);
404
405 static void compose_set_encoding_cb     (gpointer        data,
406                                          guint           action,
407                                          GtkWidget      *widget);
408
409 static void compose_address_cb          (gpointer        data,
410                                          guint           action,
411                                          GtkWidget      *widget);
412 static void compose_template_activate_cb(GtkWidget      *widget,
413                                          gpointer        data);
414
415 static void compose_ext_editor_cb       (gpointer        data,
416                                          guint           action,
417                                          GtkWidget      *widget);
418
419 static gint compose_delete_cb           (GtkWidget      *widget,
420                                          GdkEventAny    *event,
421                                          gpointer        data);
422
423 static void compose_undo_cb             (Compose        *compose);
424 static void compose_redo_cb             (Compose        *compose);
425 static void compose_cut_cb              (Compose        *compose);
426 static void compose_copy_cb             (Compose        *compose);
427 static void compose_paste_cb            (Compose        *compose);
428 static void compose_paste_as_quote_cb   (Compose        *compose);
429 static void compose_paste_no_wrap_cb    (Compose        *compose);
430 static void compose_paste_wrap_cb       (Compose        *compose);
431 static void compose_allsel_cb           (Compose        *compose);
432
433 static void compose_advanced_action_cb  (Compose                   *compose,
434                                          ComposeCallAdvancedAction  action);
435
436 static void compose_grab_focus_cb       (GtkWidget      *widget,
437                                          Compose        *compose);
438
439 static void compose_changed_cb          (GtkTextBuffer  *textbuf,
440                                          Compose        *compose);
441
442 static void compose_wrap_cb             (gpointer        data,
443                                          guint           action,
444                                          GtkWidget      *widget);
445 static void compose_find_cb             (gpointer        data,
446                                          guint           action,
447                                          GtkWidget      *widget);
448 static void compose_toggle_autowrap_cb  (gpointer        data,
449                                          guint           action,
450                                          GtkWidget      *widget);
451
452 static void compose_toggle_ruler_cb     (gpointer        data,
453                                          guint           action,
454                                          GtkWidget      *widget);
455 static void compose_toggle_sign_cb      (gpointer        data,
456                                          guint           action,
457                                          GtkWidget      *widget);
458 static void compose_toggle_encrypt_cb   (gpointer        data,
459                                          guint           action,
460                                          GtkWidget      *widget);
461 static void compose_set_privacy_system_cb(GtkWidget      *widget,
462                                           gpointer        data);
463 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
464 static void activate_privacy_system     (Compose *compose, 
465                                          PrefsAccount *account,
466                                          gboolean warn);
467 static void compose_use_signing(Compose *compose, gboolean use_signing);
468 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
469 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
470                                              GtkWidget *widget);
471 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
472                                              GtkWidget *widget);
473 static void compose_set_priority_cb     (gpointer        data,
474                                          guint           action,
475                                          GtkWidget      *widget);
476 static void compose_reply_change_mode   (gpointer        data,
477                                          ComposeMode    action,
478                                          GtkWidget      *widget);
479
480 static void compose_attach_drag_received_cb (GtkWidget          *widget,
481                                              GdkDragContext     *drag_context,
482                                              gint                x,
483                                              gint                y,
484                                              GtkSelectionData   *data,
485                                              guint               info,
486                                              guint               time,
487                                              gpointer            user_data);
488 static void compose_insert_drag_received_cb (GtkWidget          *widget,
489                                              GdkDragContext     *drag_context,
490                                              gint                x,
491                                              gint                y,
492                                              GtkSelectionData   *data,
493                                              guint               info,
494                                              guint               time,
495                                              gpointer            user_data);
496 static void compose_header_drag_received_cb (GtkWidget          *widget,
497                                              GdkDragContext     *drag_context,
498                                              gint                x,
499                                              gint                y,
500                                              GtkSelectionData   *data,
501                                              guint               info,
502                                              guint               time,
503                                              gpointer            user_data);
504
505 static gboolean compose_drag_drop           (GtkWidget *widget,
506                                              GdkDragContext *drag_context,
507                                              gint x, gint y,
508                                              guint time, gpointer user_data);
509
510 static void text_inserted               (GtkTextBuffer  *buffer,
511                                          GtkTextIter    *iter,
512                                          const gchar    *text,
513                                          gint            len,
514                                          Compose        *compose);
515 static Compose *compose_generic_reply(MsgInfo *msginfo,
516                                   ComposeQuoteMode quote_mode,
517                                   gboolean to_all,
518                                   gboolean to_ml,
519                                   gboolean to_sender,
520                                   gboolean followup_and_reply_to,
521                                   const gchar *body);
522
523 static gboolean compose_headerentry_changed_cb     (GtkWidget          *entry,
524                                             ComposeHeaderEntry *headerentry);
525 static gboolean compose_headerentry_key_press_event_cb(GtkWidget               *entry,
526                                             GdkEventKey        *event,
527                                             ComposeHeaderEntry *headerentry);
528
529 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
530
531 static void compose_allow_user_actions (Compose *compose, gboolean allow);
532
533 #if USE_ASPELL
534 static void compose_check_all              (Compose *compose);
535 static void compose_highlight_all          (Compose *compose);
536 static void compose_check_backwards        (Compose *compose);
537 static void compose_check_forwards_go      (Compose *compose);
538 #endif
539
540 static gint compose_defer_auto_save_draft       (Compose        *compose);
541 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
542
543 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
544
545 #ifdef USE_ASPELL
546 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
547                                                 FolderItem *folder_item);
548 #endif
549 static void compose_attach_update_label(Compose *compose);
550
551 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
552
553 static GtkItemFactoryEntry compose_popup_entries[] =
554 {
555         {N_("/_Add..."),        NULL, compose_attach_cb, 0, NULL},
556         {N_("/_Remove"),        NULL, compose_attach_remove_selected, 0, NULL},
557         {"/---",                NULL, NULL, 0, "<Separator>"},
558         {N_("/_Properties..."), NULL, compose_attach_property, 0, NULL}
559 };
560
561 static GtkItemFactoryEntry compose_entries[] =
562 {
563         {N_("/_Message"),                               NULL, NULL, 0, "<Branch>"},
564         {N_("/_Message/S_end"),         "<control>Return",
565                                         compose_send_cb, 0, NULL},
566         {N_("/_Message/Send _later"),   "<shift><control>S",
567                                         compose_send_later_cb,  0, NULL},
568         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
569         {N_("/_Message/_Attach file"),          "<control>M", compose_attach_cb,      0, NULL},
570         {N_("/_Message/_Insert file"),          "<control>I", compose_insert_file_cb, 0, NULL},
571         {N_("/_Message/Insert si_gnature"),     "<control>G", compose_insert_sig_cb,  0, NULL},
572         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
573         {N_("/_Message/_Save"),
574                                                 "<control>S", compose_draft_cb, COMPOSE_KEEP_EDITING, NULL},
575         {N_("/_Message/---"),                   NULL, NULL, 0, "<Separator>"},
576         {N_("/_Message/_Close"),                        "<control>W", compose_close_cb, 0, NULL},
577
578         {N_("/_Edit"),                  NULL, NULL, 0, "<Branch>"},
579         {N_("/_Edit/_Undo"),            "<control>Z", compose_undo_cb, 0, NULL},
580         {N_("/_Edit/_Redo"),            "<control>Y", compose_redo_cb, 0, NULL},
581         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
582         {N_("/_Edit/Cu_t"),             "<control>X", compose_cut_cb,    0, NULL},
583         {N_("/_Edit/_Copy"),            "<control>C", compose_copy_cb,   0, NULL},
584         {N_("/_Edit/_Paste"),           "<control>V", compose_paste_cb,  0, NULL},
585         {N_("/_Edit/Special paste"),    NULL, NULL, 0, "<Branch>"},
586         {N_("/_Edit/Special paste/as _quotation"),
587                                         NULL, compose_paste_as_quote_cb, 0, NULL},
588         {N_("/_Edit/Special paste/_wrapped"),
589                                         NULL, compose_paste_wrap_cb, 0, NULL},
590         {N_("/_Edit/Special paste/_unwrapped"),
591                                         NULL, compose_paste_no_wrap_cb, 0, NULL},
592         {N_("/_Edit/Select _all"),      "<control>A", compose_allsel_cb, 0, NULL},
593         {N_("/_Edit/A_dvanced"),        NULL, NULL, 0, "<Branch>"},
594         {N_("/_Edit/A_dvanced/Move a character backward"),
595                                         "<shift><control>B",
596                                         compose_advanced_action_cb,
597                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
598                                         NULL},
599         {N_("/_Edit/A_dvanced/Move a character forward"),
600                                         "<shift><control>F",
601                                         compose_advanced_action_cb,
602                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
603                                         NULL},
604         {N_("/_Edit/A_dvanced/Move a word backward"),
605                                         NULL, /* "<alt>B" */
606                                         compose_advanced_action_cb,
607                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
608                                         NULL},
609         {N_("/_Edit/A_dvanced/Move a word forward"),
610                                         NULL, /* "<alt>F" */
611                                         compose_advanced_action_cb,
612                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
613                                         NULL},
614         {N_("/_Edit/A_dvanced/Move to beginning of line"),
615                                         NULL, /* "<control>A" */
616                                         compose_advanced_action_cb,
617                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
618                                         NULL},
619         {N_("/_Edit/A_dvanced/Move to end of line"),
620                                         "<control>E",
621                                         compose_advanced_action_cb,
622                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
623                                         NULL},
624         {N_("/_Edit/A_dvanced/Move to previous line"),
625                                         "<control>P",
626                                         compose_advanced_action_cb,
627                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
628                                         NULL},
629         {N_("/_Edit/A_dvanced/Move to next line"),
630                                         "<control>N",
631                                         compose_advanced_action_cb,
632                                         COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
633                                         NULL},
634         {N_("/_Edit/A_dvanced/Delete a character backward"),
635                                         "<control>H",
636                                         compose_advanced_action_cb,
637                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
638                                         NULL},
639         {N_("/_Edit/A_dvanced/Delete a character forward"),
640                                         "<control>D",
641                                         compose_advanced_action_cb,
642                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
643                                         NULL},
644         {N_("/_Edit/A_dvanced/Delete a word backward"),
645                                         NULL, /* "<control>W" */
646                                         compose_advanced_action_cb,
647                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
648                                         NULL},
649         {N_("/_Edit/A_dvanced/Delete a word forward"),
650                                         NULL, /* "<alt>D", */
651                                         compose_advanced_action_cb,
652                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
653                                         NULL},
654         {N_("/_Edit/A_dvanced/Delete line"),
655                                         "<control>U",
656                                         compose_advanced_action_cb,
657                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
658                                         NULL},
659         {N_("/_Edit/A_dvanced/Delete entire line"),
660                                         NULL,
661                                         compose_advanced_action_cb,
662                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE_N,
663                                         NULL},
664         {N_("/_Edit/A_dvanced/Delete to end of line"),
665                                         "<control>K",
666                                         compose_advanced_action_cb,
667                                         COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END,
668                                         NULL},
669         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
670         {N_("/_Edit/_Find"),
671                                         "<control>F", compose_find_cb, 0, NULL},
672         {N_("/_Edit/---"),                      NULL, NULL, 0, "<Separator>"},
673         {N_("/_Edit/_Wrap current paragraph"),
674                                         "<control>L", compose_wrap_cb, 0, NULL},
675         {N_("/_Edit/Wrap all long _lines"),
676                                         "<control><alt>L", compose_wrap_cb, 1, NULL},
677         {N_("/_Edit/Aut_o wrapping"),   "<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
678         {N_("/_Edit/---"),              NULL, NULL, 0, "<Separator>"},
679         {N_("/_Edit/Edit with e_xternal editor"),
680                                         "<shift><control>X", compose_ext_editor_cb, 0, NULL},
681 #if USE_ASPELL
682         {N_("/_Spelling"),              NULL, NULL, 0, "<Branch>"},
683         {N_("/_Spelling/_Check all or check selection"),
684                                         NULL, compose_check_all, 0, NULL},
685         {N_("/_Spelling/_Highlight all misspelled words"),
686                                         NULL, compose_highlight_all, 0, NULL},
687         {N_("/_Spelling/Check _backwards misspelled word"),
688                                         NULL, compose_check_backwards , 0, NULL},
689         {N_("/_Spelling/_Forward to next misspelled word"),
690                                         NULL, compose_check_forwards_go, 0, NULL},
691         {N_("/_Spelling/---"),          NULL, NULL, 0, "<Separator>"},
692         {N_("/_Spelling/Options"),
693                                         NULL, NULL, 0, "<Branch>"},
694 #endif
695         {N_("/_Options"),               NULL, NULL, 0, "<Branch>"},
696         {N_("/_Options/Reply _mode"),           NULL, NULL,   0, "<Branch>"},
697         {N_("/_Options/Reply _mode/_Normal"),   NULL, compose_reply_change_mode,   COMPOSE_REPLY, "<RadioItem>"},
698         {N_("/_Options/Reply _mode/_All"),              NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_ALL, "/Options/Reply mode/Normal"},
699         {N_("/_Options/Reply _mode/_Sender"),           NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_SENDER, "/Options/Reply mode/Normal"},
700         {N_("/_Options/Reply _mode/_Mailing-list"),     NULL, compose_reply_change_mode,   COMPOSE_REPLY_TO_LIST, "/Options/Reply mode/Normal"},
701         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
702         {N_("/_Options/Privacy _System"),               NULL, NULL,   0, "<Branch>"},
703         {N_("/_Options/Privacy _System/None"),  NULL, NULL,   0, "<RadioItem>"},
704         {N_("/_Options/Si_gn"),         NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
705         {N_("/_Options/_Encrypt"),      NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
706         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
707         {N_("/_Options/_Priority"),     NULL,           NULL,   0, "<Branch>"},
708         {N_("/_Options/Priority/_Highest"), NULL, compose_set_priority_cb, PRIORITY_HIGHEST, "<RadioItem>"},
709         {N_("/_Options/Priority/Hi_gh"),    NULL, compose_set_priority_cb, PRIORITY_HIGH, "/Options/Priority/Highest"},
710         {N_("/_Options/Priority/_Normal"),  NULL, compose_set_priority_cb, PRIORITY_NORMAL, "/Options/Priority/Highest"},
711         {N_("/_Options/Priority/Lo_w"),    NULL, compose_set_priority_cb, PRIORITY_LOW, "/Options/Priority/Highest"},
712         {N_("/_Options/Priority/_Lowest"),  NULL, compose_set_priority_cb, PRIORITY_LOWEST, "/Options/Priority/Highest"},
713         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
714         {N_("/_Options/_Request Return Receipt"),       NULL, compose_toggle_return_receipt_cb, 0, "<ToggleItem>"},
715         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
716         {N_("/_Options/Remo_ve references"),    NULL, compose_toggle_remove_refs_cb, 0, "<ToggleItem>"},
717         {N_("/_Options/---"),           NULL,           NULL,   0, "<Separator>"},
718
719 #define ENC_ACTION(action) \
720         NULL, compose_set_encoding_cb, action, \
721         "/Options/Character encoding/Automatic"
722
723         {N_("/_Options/Character _encoding"), NULL, NULL, 0, "<Branch>"},
724         {N_("/_Options/Character _encoding/_Automatic"),
725                         NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
726         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
727
728         {N_("/_Options/Character _encoding/7bit ASCII (US-ASC_II)"),
729          ENC_ACTION(C_US_ASCII)},
730         {N_("/_Options/Character _encoding/Unicode (_UTF-8)"),
731          ENC_ACTION(C_UTF_8)},
732         {N_("/_Options/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
733
734         {N_("/_Options/Character _encoding/Western European"), NULL, NULL, 0, "<Branch>"},
735         {N_("/_Options/Character _encoding/Western European/ISO-8859-_1"),
736          ENC_ACTION(C_ISO_8859_1)},
737         {N_("/_Options/Character _encoding/Western European/ISO-8859-15"),
738          ENC_ACTION(C_ISO_8859_15)},
739         {N_("/_Options/Character _encoding/Western European/Windows-1252"),
740          ENC_ACTION(C_WINDOWS_1252)},
741
742         {N_("/_Options/Character _encoding/Central European (ISO-8859-_2)"),
743          ENC_ACTION(C_ISO_8859_2)},
744
745         {N_("/_Options/Character _encoding/Baltic"), NULL, NULL, 0, "<Branch>"},
746         {N_("/_Options/Character _encoding/Baltic/ISO-8859-13"),
747          ENC_ACTION(C_ISO_8859_13)},
748         {N_("/_Options/Character _encoding/Baltic/ISO-8859-_4"),
749          ENC_ACTION(C_ISO_8859_4)},
750
751         {N_("/_Options/Character _encoding/Greek (ISO-8859-_7)"),
752          ENC_ACTION(C_ISO_8859_7)},
753
754         {N_("/_Options/Character _encoding/Hebrew"), NULL, NULL, 0, "<Branch>"},
755         {N_("/_Options/Character _encoding/Hebrew/ISO-8859-_8"),
756          ENC_ACTION(C_ISO_8859_8)},
757         {N_("/_Options/Character _encoding/Hebrew/Windows-1255"),
758          ENC_ACTION(C_WINDOWS_1255)},
759
760         {N_("/_Options/Character _encoding/Arabic"), NULL, NULL, 0, "<Branch>"},
761         {N_("/_Options/Character _encoding/Arabic/ISO-8859-_6"),
762          ENC_ACTION(C_ISO_8859_6)},
763         {N_("/_Options/Character _encoding/Arabic/Windows-1256"),
764          ENC_ACTION(C_CP1256)},
765
766         {N_("/_Options/Character _encoding/Turkish (ISO-8859-_9)"),
767          ENC_ACTION(C_ISO_8859_9)},
768
769         {N_("/_Options/Character _encoding/Cyrillic"), NULL, NULL, 0, "<Branch>"},
770         {N_("/_Options/Character _encoding/Cyrillic/ISO-8859-_5"),
771          ENC_ACTION(C_ISO_8859_5)},
772         {N_("/_Options/Character _encoding/Cyrillic/KOI8-_R"),
773          ENC_ACTION(C_KOI8_R)},
774         {N_("/_Options/Character _encoding/Cyrillic/KOI8-U"),
775          ENC_ACTION(C_KOI8_U)},
776         {N_("/_Options/Character _encoding/Cyrillic/Windows-1251"),
777          ENC_ACTION(C_WINDOWS_1251)},
778
779         {N_("/_Options/Character _encoding/Japanese"), NULL, NULL, 0, "<Branch>"},
780         {N_("/_Options/Character _encoding/Japanese/ISO-2022-_JP"),
781          ENC_ACTION(C_ISO_2022_JP)},
782         {N_("/_Options/Character _encoding/Japanese/ISO-2022-JP-2"),
783          ENC_ACTION(C_ISO_2022_JP_2)},
784         {N_("/_Options/Character _encoding/Japanese/_EUC-JP"),
785          ENC_ACTION(C_EUC_JP)},
786         {N_("/_Options/Character _encoding/Japanese/_Shift__JIS"),
787          ENC_ACTION(C_SHIFT_JIS)},
788
789         {N_("/_Options/Character _encoding/Chinese"), NULL, NULL, 0, "<Branch>"},
790         {N_("/_Options/Character _encoding/Chinese/Simplified (_GB2312)"),
791          ENC_ACTION(C_GB2312)},
792         {N_("/_Options/Character _encoding/Chinese/Simplified (GBK)"),
793          ENC_ACTION(C_GBK)},
794         {N_("/_Options/Character _encoding/Chinese/Traditional (_Big5)"),
795          ENC_ACTION(C_BIG5)},
796         {N_("/_Options/Character _encoding/Chinese/Traditional (EUC-_TW)"),
797          ENC_ACTION(C_EUC_TW)},
798
799         {N_("/_Options/Character _encoding/Korean"), NULL, NULL, 0, "<Branch>"},
800         {N_("/_Options/Character _encoding/Korean/EUC-_KR"),
801          ENC_ACTION(C_EUC_KR)},
802         {N_("/_Options/Character _encoding/Korean/ISO-2022-KR"),
803          ENC_ACTION(C_ISO_2022_KR)},
804
805         {N_("/_Options/Character _encoding/Thai"), NULL, NULL, 0, "<Branch>"},
806         {N_("/_Options/Character _encoding/Thai/TIS-620"),
807          ENC_ACTION(C_TIS_620)},
808         {N_("/_Options/Character _encoding/Thai/Windows-874"),
809          ENC_ACTION(C_WINDOWS_874)},
810
811         {N_("/_Tools"),                 NULL, NULL, 0, "<Branch>"},
812         {N_("/_Tools/Show _ruler"),     NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
813         {N_("/_Tools/_Address book"),   "<shift><control>A", compose_address_cb , 0, NULL},
814         {N_("/_Tools/_Template"),       NULL, NULL, 0, "<Branch>"},
815         {N_("/_Tools/Actio_ns"),        NULL, NULL, 0, "<Branch>"},
816         {N_("/_Help"),                  NULL, NULL, 0, "<Branch>"},
817         {N_("/_Help/_About"),           NULL, about_show, 0, NULL}
818 };
819
820 static GtkTargetEntry compose_mime_types[] =
821 {
822         {"text/uri-list", 0, 0},
823         {"UTF8_STRING", 0, 0},
824         {"text/plain", 0, 0}
825 };
826
827 static gboolean compose_put_existing_to_front(MsgInfo *info)
828 {
829         GList *compose_list = compose_get_compose_list();
830         GList *elem = NULL;
831         
832         if (compose_list) {
833                 for (elem = compose_list; elem != NULL && elem->data != NULL; 
834                      elem = elem->next) {
835                         Compose *c = (Compose*)elem->data;
836
837                         if (!c->targetinfo || !c->targetinfo->msgid ||
838                             !info->msgid)
839                                 continue;
840
841                         if (!strcmp(c->targetinfo->msgid, info->msgid)) {
842                                 gtkut_window_popup(c->window);
843                                 return TRUE;
844                         }
845                 }
846         }
847         return FALSE;
848 }
849
850 static GdkColor quote_color1 = 
851         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
852 static GdkColor quote_color2 = 
853         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
854 static GdkColor quote_color3 = 
855         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
856
857 static GdkColor quote_bgcolor1 = 
858         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
859 static GdkColor quote_bgcolor2 = 
860         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
861 static GdkColor quote_bgcolor3 = 
862         {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
863
864 static GdkColor signature_color = {
865         (gulong)0,
866         (gushort)0x7fff,
867         (gushort)0x7fff,
868         (gushort)0x7fff
869 };
870
871 static GdkColor uri_color = {
872         (gulong)0,
873         (gushort)0,
874         (gushort)0,
875         (gushort)0
876 };
877
878 static void compose_create_tags(GtkTextView *text, Compose *compose)
879 {
880         GtkTextBuffer *buffer;
881         GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
882         GdkColormap *cmap;
883         GdkColor color[8];
884         gboolean success[8];
885         int i;
886
887         buffer = gtk_text_view_get_buffer(text);
888
889         if (prefs_common.enable_color) {
890                 /* grab the quote colors, converting from an int to a GdkColor */
891                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
892                                                &quote_color1);
893                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
894                                                &quote_color2);
895                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
896                                                &quote_color3);
897                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
898                                                &quote_bgcolor1);
899                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
900                                                &quote_bgcolor2);
901                 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
902                                                &quote_bgcolor3);
903                 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
904                                                &signature_color);
905                 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
906                                                &uri_color);
907         } else {
908                 signature_color = quote_color1 = quote_color2 = quote_color3 = 
909                         quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
910         }
911
912         if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
913                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
914                                            "foreground-gdk", &quote_color1,
915                                            "paragraph-background-gdk", &quote_bgcolor1,
916                                            NULL);
917                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
918                                            "foreground-gdk", &quote_color2,
919                                            "paragraph-background-gdk", &quote_bgcolor2,
920                                            NULL);
921                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
922                                            "foreground-gdk", &quote_color3,
923                                            "paragraph-background-gdk", &quote_bgcolor3,
924                                            NULL);
925         } else {
926                 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
927                                            "foreground-gdk", &quote_color1,
928                                            NULL);
929                 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
930                                            "foreground-gdk", &quote_color2,
931                                            NULL);
932                 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
933                                            "foreground-gdk", &quote_color3,
934                                            NULL);
935         }
936         
937         compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
938                                    "foreground-gdk", &signature_color,
939                                    NULL);
940         
941         compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
942                                         "foreground-gdk", &uri_color,
943                                          NULL);
944         compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
945         compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
946
947         color[0] = quote_color1;
948         color[1] = quote_color2;
949         color[2] = quote_color3;
950         color[3] = quote_bgcolor1;
951         color[4] = quote_bgcolor2;
952         color[5] = quote_bgcolor3;
953         color[6] = signature_color;
954         color[7] = uri_color;
955         cmap = gdk_drawable_get_colormap(compose->window->window);
956         gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
957
958         for (i = 0; i < 8; i++) {
959                 if (success[i] == FALSE) {
960                         GtkStyle *style;
961
962                         g_warning("Compose: color allocation failed.\n");
963                         style = gtk_widget_get_style(GTK_WIDGET(text));
964                         quote_color1 = quote_color2 = quote_color3 = 
965                                 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = 
966                                 signature_color = uri_color = black;
967                 }
968         }
969 }
970
971 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
972                      GPtrArray *attach_files)
973 {
974         return compose_generic_new(account, mailto, NULL, attach_files, NULL);
975 }
976
977 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
978 {
979         return compose_generic_new(account, mailto, item, NULL, NULL);
980 }
981
982 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
983 {
984         return compose_generic_new( account, NULL, NULL, NULL, listAddress );
985 }
986
987 #define SCROLL_TO_CURSOR(compose) {                             \
988         GtkTextMark *cmark = gtk_text_buffer_get_insert(        \
989                 gtk_text_view_get_buffer(                       \
990                         GTK_TEXT_VIEW(compose->text)));         \
991         gtk_text_view_scroll_mark_onscreen(                     \
992                 GTK_TEXT_VIEW(compose->text),                   \
993                 cmark);                                         \
994 }
995
996 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
997                              GPtrArray *attach_files, GList *listAddress )
998 {
999         Compose *compose;
1000         GtkTextView *textview;
1001         GtkTextBuffer *textbuf;
1002         GtkTextIter iter;
1003         GtkItemFactory *ifactory;
1004         const gchar *subject_format = NULL;
1005         const gchar *body_format = NULL;
1006
1007         if (item && item->prefs && item->prefs->enable_default_account)
1008                 account = account_find_from_id(item->prefs->default_account);
1009
1010         if (!account) account = cur_account;
1011         g_return_val_if_fail(account != NULL, NULL);
1012
1013         compose = compose_create(account, item, COMPOSE_NEW, FALSE);
1014
1015         ifactory = gtk_item_factory_from_widget(compose->menubar);
1016
1017         compose->replyinfo = NULL;
1018         compose->fwdinfo   = NULL;
1019
1020         textview = GTK_TEXT_VIEW(compose->text);
1021         textbuf = gtk_text_view_get_buffer(textview);
1022         compose_create_tags(textview, compose);
1023
1024         undo_block(compose->undostruct);
1025 #ifdef USE_ASPELL
1026         compose_set_dictionaries_from_folder_prefs(compose, item);
1027 #endif
1028
1029         if (account->auto_sig)
1030                 compose_insert_sig(compose, FALSE);
1031         gtk_text_buffer_get_start_iter(textbuf, &iter);
1032         gtk_text_buffer_place_cursor(textbuf, &iter);
1033
1034         if (account->protocol != A_NNTP) {
1035                 if (mailto && *mailto != '\0') {
1036                         compose_entries_set(compose, mailto);
1037
1038                 } else if (item && item->prefs->enable_default_to) {
1039                         compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1040                         compose_entry_mark_default_to(compose, item->prefs->default_to);
1041                 }
1042                 if (item && item->ret_rcpt) {
1043                         menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1044                 }
1045         } else {
1046                 if (mailto) {
1047                         compose_entry_append(compose, mailto, COMPOSE_NEWSGROUPS);
1048                 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1049                         compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1050                 }
1051                 /*
1052                  * CLAWS: just don't allow return receipt request, even if the user
1053                  * may want to send an email. simple but foolproof.
1054                  */
1055                 menu_set_sensitive(ifactory, "/Options/Request Return Receipt", FALSE); 
1056         }
1057         compose_add_field_list( compose, listAddress );
1058
1059         if (item && item->prefs && item->prefs->compose_with_format) {
1060                 subject_format = item->prefs->compose_subject_format;
1061                 body_format = item->prefs->compose_body_format;
1062         } else if (account->compose_with_format) {
1063                 subject_format = account->compose_subject_format;
1064                 body_format = account->compose_body_format;
1065         } else if (prefs_common.compose_with_format) {
1066                 subject_format = prefs_common.compose_subject_format;
1067                 body_format = prefs_common.compose_body_format;
1068         }
1069
1070         if (subject_format || body_format) {
1071                 MsgInfo* dummyinfo = NULL;
1072
1073                 if ( subject_format
1074                          && *subject_format != '\0' )
1075                 {
1076                         gchar *subject = NULL;
1077                         gchar *tmp = NULL;
1078                         gchar *buf = NULL;
1079
1080                         dummyinfo = compose_msginfo_new_from_compose(compose);
1081
1082                         /* decode \-escape sequences in the internal representation of the quote format */
1083                         tmp = malloc(strlen(subject_format)+1);
1084                         pref_get_unescaped_pref(tmp, subject_format);
1085
1086                         subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1087 #ifdef USE_ASPELL
1088                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account,
1089                                         compose->gtkaspell);
1090 #else
1091                         quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account);
1092 #endif
1093                         quote_fmt_scan_string(tmp);
1094                         quote_fmt_parse();
1095
1096                         buf = quote_fmt_get_buffer();
1097                         if (buf == NULL)
1098                                 alertpanel_error(_("New message subject format error."));
1099                         else
1100                                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1101                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1102                         quote_fmt_reset_vartable();
1103
1104                         g_free(subject);
1105                         g_free(tmp);
1106                 }
1107
1108                 if ( body_format
1109                          && *body_format != '\0' )
1110                 {
1111                         GtkTextView *text;
1112                         GtkTextBuffer *buffer;
1113                         GtkTextIter start, end;
1114                         gchar *tmp = NULL;
1115
1116                         if ( dummyinfo == NULL )
1117                                 dummyinfo = compose_msginfo_new_from_compose(compose);
1118
1119                         text = GTK_TEXT_VIEW(compose->text);
1120                         buffer = gtk_text_view_get_buffer(text);
1121                         gtk_text_buffer_get_start_iter(buffer, &start);
1122                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1123                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1124
1125                         compose_quote_fmt(compose, dummyinfo,
1126                                           body_format,
1127                                           NULL, tmp, FALSE, TRUE,
1128                                                   _("New message body format error at line %d."));
1129                         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1130                         quote_fmt_reset_vartable();
1131
1132                         g_free(tmp);
1133                 }
1134
1135                 procmsg_msginfo_free( dummyinfo );
1136         }
1137
1138         if (attach_files) {
1139                 gint i;
1140                 gchar *file;
1141
1142                 for (i = 0; i < attach_files->len; i++) {
1143                         file = g_ptr_array_index(attach_files, i);
1144                         compose_attach_append(compose, file, file, NULL);
1145                 }
1146         }
1147
1148         compose_show_first_last_header(compose, TRUE);
1149
1150         /* Set save folder */
1151         if (item && item->prefs && item->prefs->save_copy_to_folder) {
1152                 gchar *folderidentifier;
1153
1154                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1155                 folderidentifier = folder_item_get_identifier(item);
1156                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1157                 g_free(folderidentifier);
1158         }
1159         
1160         gtk_widget_grab_focus(compose->header_last->entry);
1161
1162         undo_unblock(compose->undostruct);
1163
1164         if (prefs_common.auto_exteditor)
1165                 compose_exec_ext_editor(compose);
1166
1167         compose->draft_timeout_tag = -1;
1168         SCROLL_TO_CURSOR(compose);
1169
1170         compose->modified = FALSE;
1171         compose_set_title(compose);
1172         return compose;
1173 }
1174
1175 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1176                 gboolean override_pref)
1177 {
1178         gchar *privacy = NULL;
1179
1180         g_return_if_fail(compose != NULL);
1181         g_return_if_fail(account != NULL);
1182
1183         if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1184                 return;
1185
1186         if (account->default_privacy_system
1187         &&  strlen(account->default_privacy_system)) {
1188                 privacy = account->default_privacy_system;
1189         } else {
1190                 GSList *privacy_avail = privacy_get_system_ids();
1191                 if (privacy_avail && g_slist_length(privacy_avail)) {
1192                         privacy = (gchar *)(privacy_avail->data);
1193                 }
1194         }
1195         if (privacy != NULL) {
1196                 if (compose->privacy_system == NULL)
1197                         compose->privacy_system = g_strdup(privacy);
1198                 else if (*(compose->privacy_system) == '\0') {
1199                         g_free(compose->privacy_system);
1200                         compose->privacy_system = g_strdup(privacy);
1201                 }
1202                 compose_update_privacy_system_menu_item(compose, FALSE);
1203                 compose_use_encryption(compose, TRUE);
1204         }
1205 }       
1206
1207 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1208 {
1209         gchar *privacy = NULL;
1210
1211         if (account->default_privacy_system
1212         &&  strlen(account->default_privacy_system)) {
1213                 privacy = account->default_privacy_system;
1214         } else {
1215                 GSList *privacy_avail = privacy_get_system_ids();
1216                 if (privacy_avail && g_slist_length(privacy_avail)) {
1217                         privacy = (gchar *)(privacy_avail->data);
1218                 }
1219         }
1220         if (privacy != NULL) {
1221                 if (compose->privacy_system == NULL)
1222                         compose->privacy_system = g_strdup(privacy);
1223                 compose_update_privacy_system_menu_item(compose, FALSE);
1224                 compose_use_signing(compose, TRUE);
1225         }
1226 }       
1227
1228 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1229 {
1230         MsgInfo *msginfo;
1231         guint list_len;
1232         Compose *compose = NULL;
1233         GtkItemFactory *ifactory = NULL;
1234         
1235         g_return_val_if_fail(msginfo_list != NULL, NULL);
1236
1237         msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1238         g_return_val_if_fail(msginfo != NULL, NULL);
1239
1240         list_len = g_slist_length(msginfo_list);
1241
1242         switch (mode) {
1243         case COMPOSE_REPLY:
1244                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1245                               FALSE, prefs_common.default_reply_list, FALSE, body);
1246                 break;
1247         case COMPOSE_REPLY_WITH_QUOTE:
1248                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1249                         FALSE, prefs_common.default_reply_list, FALSE, body);
1250                 break;
1251         case COMPOSE_REPLY_WITHOUT_QUOTE:
1252                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1253                         FALSE, prefs_common.default_reply_list, FALSE, NULL);
1254                 break;
1255         case COMPOSE_REPLY_TO_SENDER:
1256                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1257                               FALSE, FALSE, TRUE, body);
1258                 break;
1259         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1260                 compose = compose_followup_and_reply_to(msginfo,
1261                                               COMPOSE_QUOTE_CHECK,
1262                                               FALSE, FALSE, body);
1263                 break;
1264         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1265                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1266                         FALSE, FALSE, TRUE, body);
1267                 break;
1268         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1269                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1270                         FALSE, FALSE, TRUE, NULL);
1271                 break;
1272         case COMPOSE_REPLY_TO_ALL:
1273                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1274                         TRUE, FALSE, FALSE, body);
1275                 break;
1276         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1277                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1278                         TRUE, FALSE, FALSE, body);
1279                 break;
1280         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1281                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1282                         TRUE, FALSE, FALSE, NULL);
1283                 break;
1284         case COMPOSE_REPLY_TO_LIST:
1285                 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1286                         FALSE, TRUE, FALSE, body);
1287                 break;
1288         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1289                 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED, 
1290                         FALSE, TRUE, FALSE, body);
1291                 break;
1292         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1293                 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP, 
1294                         FALSE, TRUE, FALSE, NULL);
1295                 break;
1296         case COMPOSE_FORWARD:
1297                 if (prefs_common.forward_as_attachment) {
1298                         compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1299                         return compose;
1300                 } else {
1301                         compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1302                         return compose;
1303                 }
1304                 break;
1305         case COMPOSE_FORWARD_INLINE:
1306                 /* check if we reply to more than one Message */
1307                 if (list_len == 1) {
1308                         compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1309                         break;
1310                 } 
1311                 /* more messages FALL THROUGH */
1312         case COMPOSE_FORWARD_AS_ATTACH:
1313                 compose = compose_forward_multiple(NULL, msginfo_list);
1314                 break;
1315         case COMPOSE_REDIRECT:
1316                 compose = compose_redirect(NULL, msginfo, FALSE);
1317                 break;
1318         default:
1319                 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1320         }
1321         
1322         if (compose == NULL) {
1323                 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1324                 return NULL;
1325         }
1326         ifactory = gtk_item_factory_from_widget(compose->menubar);
1327
1328         compose->rmode = mode;
1329         switch (compose->rmode) {
1330         case COMPOSE_REPLY:
1331         case COMPOSE_REPLY_WITH_QUOTE:
1332         case COMPOSE_REPLY_WITHOUT_QUOTE:
1333         case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1334                 debug_print("reply mode Normal\n");
1335                 menu_set_active(ifactory, "/Options/Reply mode/Normal", TRUE);
1336                 compose_reply_change_mode(compose, COMPOSE_REPLY, NULL); /* force update */
1337                 break;
1338         case COMPOSE_REPLY_TO_SENDER:
1339         case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1340         case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1341                 debug_print("reply mode Sender\n");
1342                 menu_set_active(ifactory, "/Options/Reply mode/Sender", TRUE);
1343                 break;
1344         case COMPOSE_REPLY_TO_ALL:
1345         case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1346         case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1347                 debug_print("reply mode All\n");
1348                 menu_set_active(ifactory, "/Options/Reply mode/All", TRUE);
1349                 break;
1350         case COMPOSE_REPLY_TO_LIST:
1351         case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1352         case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1353                 debug_print("reply mode List\n");
1354                 menu_set_active(ifactory, "/Options/Reply mode/Mailing-list", TRUE);
1355                 break;
1356         default:
1357                 break;
1358         }
1359         return compose;
1360 }
1361
1362 static Compose *compose_reply(MsgInfo *msginfo,
1363                                    ComposeQuoteMode quote_mode,
1364                                    gboolean to_all,
1365                                    gboolean to_ml,
1366                                    gboolean to_sender, 
1367                    const gchar *body)
1368 {
1369         return compose_generic_reply(msginfo, quote_mode, to_all, to_ml, 
1370                               to_sender, FALSE, body);
1371 }
1372
1373 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1374                                    ComposeQuoteMode quote_mode,
1375                                    gboolean to_all,
1376                                    gboolean to_sender,
1377                                    const gchar *body)
1378 {
1379         return compose_generic_reply(msginfo, quote_mode, to_all, FALSE, 
1380                               to_sender, TRUE, body);
1381 }
1382
1383 static void compose_extract_original_charset(Compose *compose)
1384 {
1385         MsgInfo *info = NULL;
1386         if (compose->replyinfo) {
1387                 info = compose->replyinfo;
1388         } else if (compose->fwdinfo) {
1389                 info = compose->fwdinfo;
1390         } else if (compose->targetinfo) {
1391                 info = compose->targetinfo;
1392         }
1393         if (info) {
1394                 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1395                 MimeInfo *partinfo = mimeinfo;
1396                 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1397                         partinfo = procmime_mimeinfo_next(partinfo);
1398                 if (partinfo) {
1399                         compose->orig_charset = 
1400                                 g_strdup(procmime_mimeinfo_get_parameter(
1401                                                 partinfo, "charset"));
1402                 }
1403                 procmime_mimeinfo_free_all(mimeinfo);
1404         }
1405 }
1406
1407 #define SIGNAL_BLOCK(buffer) {                                  \
1408         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1409                                 G_CALLBACK(compose_changed_cb), \
1410                                 compose);                       \
1411         g_signal_handlers_block_by_func(G_OBJECT(buffer),       \
1412                                 G_CALLBACK(text_inserted),      \
1413                                 compose);                       \
1414 }
1415
1416 #define SIGNAL_UNBLOCK(buffer) {                                \
1417         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1418                                 G_CALLBACK(compose_changed_cb), \
1419                                 compose);                       \
1420         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),     \
1421                                 G_CALLBACK(text_inserted),      \
1422                                 compose);                       \
1423 }
1424
1425 static Compose *compose_generic_reply(MsgInfo *msginfo,
1426                                   ComposeQuoteMode quote_mode,
1427                                   gboolean to_all, gboolean to_ml,
1428                                   gboolean to_sender,
1429                                   gboolean followup_and_reply_to,
1430                                   const gchar *body)
1431 {
1432         GtkItemFactory *ifactory;
1433         Compose *compose;
1434         PrefsAccount *account = NULL;
1435         GtkTextView *textview;
1436         GtkTextBuffer *textbuf;
1437         gboolean quote = FALSE;
1438         const gchar *qmark = NULL;
1439         const gchar *body_fmt = NULL;
1440         START_TIMING("");
1441         g_return_val_if_fail(msginfo != NULL, NULL);
1442         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1443
1444         account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1445
1446         g_return_val_if_fail(account != NULL, NULL);
1447
1448         compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1449
1450         compose->updating = TRUE;
1451
1452         ifactory = gtk_item_factory_from_widget(compose->menubar);
1453
1454         menu_set_active(ifactory, "/Options/Remove references", FALSE);
1455         menu_set_sensitive(ifactory, "/Options/Remove references", TRUE);
1456
1457         compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1458         if (!compose->replyinfo)
1459                 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1460
1461         compose_extract_original_charset(compose);
1462         
1463         if (msginfo->folder && msginfo->folder->ret_rcpt)
1464                 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
1465
1466         /* Set save folder */
1467         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1468                 gchar *folderidentifier;
1469
1470                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1471                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1472                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1473                 g_free(folderidentifier);
1474         }
1475
1476         if (compose_parse_header(compose, msginfo) < 0) return NULL;
1477
1478         textview = (GTK_TEXT_VIEW(compose->text));
1479         textbuf = gtk_text_view_get_buffer(textview);
1480         compose_create_tags(textview, compose);
1481
1482         undo_block(compose->undostruct);
1483 #ifdef USE_ASPELL
1484                 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1485 #endif
1486
1487         if (quote_mode == COMPOSE_QUOTE_FORCED ||
1488                         (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1489                 /* use the reply format of folder (if enabled), or the account's one
1490                    (if enabled) or fallback to the global reply format, which is always
1491                    enabled (even if empty), and use the relevant quotemark */
1492                 quote = TRUE;
1493                 if (msginfo->folder && msginfo->folder->prefs &&
1494                                 msginfo->folder->prefs->reply_with_format) {
1495                         qmark = msginfo->folder->prefs->reply_quotemark;
1496                         body_fmt = msginfo->folder->prefs->reply_body_format;
1497
1498                 } else if (account->reply_with_format) {
1499                         qmark = account->reply_quotemark;
1500                         body_fmt = account->reply_body_format;
1501
1502                 } else {
1503                         qmark = prefs_common.quotemark;
1504                         body_fmt = prefs_common.quotefmt;
1505                 }
1506         }
1507
1508         if (quote) {
1509                 /* empty quotemark is not allowed */
1510                 if (qmark == NULL || *qmark == '\0')
1511                         qmark = "> ";
1512                 compose_quote_fmt(compose, compose->replyinfo,
1513                                   body_fmt, qmark, body, FALSE, TRUE,
1514                                           _("Message reply format error at line %d."));
1515                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1516                 quote_fmt_reset_vartable();
1517         }
1518
1519         if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1520                 compose_force_encryption(compose, account, FALSE);
1521         }
1522
1523         SIGNAL_BLOCK(textbuf);
1524         
1525         if (account->auto_sig)
1526                 compose_insert_sig(compose, FALSE);
1527
1528         compose_wrap_all(compose);
1529
1530         SIGNAL_UNBLOCK(textbuf);
1531         
1532         gtk_widget_grab_focus(compose->text);
1533
1534         undo_unblock(compose->undostruct);
1535
1536         if (prefs_common.auto_exteditor)
1537                 compose_exec_ext_editor(compose);
1538                 
1539         compose->modified = FALSE;
1540         compose_set_title(compose);
1541
1542         compose->updating = FALSE;
1543         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1544         SCROLL_TO_CURSOR(compose);
1545         
1546         if (compose->deferred_destroy) {
1547                 compose_destroy(compose);
1548                 return NULL;
1549         }
1550         END_TIMING();
1551         return compose;
1552 }
1553
1554 #define INSERT_FW_HEADER(var, hdr) \
1555 if (msginfo->var && *msginfo->var) { \
1556         gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1557         gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1558         gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1559 }
1560
1561 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1562                          gboolean as_attach, const gchar *body,
1563                          gboolean no_extedit,
1564                          gboolean batch)
1565 {
1566         Compose *compose;
1567         GtkTextView *textview;
1568         GtkTextBuffer *textbuf;
1569         GtkTextIter iter;
1570
1571         g_return_val_if_fail(msginfo != NULL, NULL);
1572         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1573
1574         if (!account && 
1575             !(account = compose_guess_forward_account_from_msginfo
1576                                 (msginfo)))
1577                 account = cur_account;
1578
1579         compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1580
1581         compose->updating = TRUE;
1582         compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1583         if (!compose->fwdinfo)
1584                 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1585
1586         compose_extract_original_charset(compose);
1587
1588         if (msginfo->subject && *msginfo->subject) {
1589                 gchar *buf, *buf2, *p;
1590
1591                 buf = p = g_strdup(msginfo->subject);
1592                 p += subject_get_prefix_length(p);
1593                 memmove(buf, p, strlen(p) + 1);
1594
1595                 buf2 = g_strdup_printf("Fw: %s", buf);
1596                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1597                 
1598                 g_free(buf);
1599                 g_free(buf2);
1600         }
1601
1602         textview = GTK_TEXT_VIEW(compose->text);
1603         textbuf = gtk_text_view_get_buffer(textview);
1604         compose_create_tags(textview, compose);
1605         
1606         undo_block(compose->undostruct);
1607         if (as_attach) {
1608                 gchar *msgfile;
1609
1610                 msgfile = procmsg_get_message_file(msginfo);
1611                 if (!is_file_exist(msgfile))
1612                         g_warning("%s: file not exist\n", msgfile);
1613                 else
1614                         compose_attach_append(compose, msgfile, msgfile,
1615                                               "message/rfc822");
1616
1617                 g_free(msgfile);
1618         } else {
1619                 const gchar *qmark = NULL;
1620                 const gchar *body_fmt = prefs_common.fw_quotefmt;
1621                 MsgInfo *full_msginfo;
1622
1623                 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1624                 if (!full_msginfo)
1625                         full_msginfo = procmsg_msginfo_copy(msginfo);
1626
1627                 /* use the forward format of folder (if enabled), or the account's one
1628                    (if enabled) or fallback to the global forward format, which is always
1629                    enabled (even if empty), and use the relevant quotemark */
1630                 if (msginfo->folder && msginfo->folder->prefs &&
1631                                 msginfo->folder->prefs->forward_with_format) {
1632                         qmark = msginfo->folder->prefs->forward_quotemark;
1633                         body_fmt = msginfo->folder->prefs->forward_body_format;
1634
1635                 } else if (account->forward_with_format) {
1636                         qmark = account->forward_quotemark;
1637                         body_fmt = account->forward_body_format;
1638
1639                 } else {
1640                         qmark = prefs_common.fw_quotemark;
1641                         body_fmt = prefs_common.fw_quotefmt;
1642                 }
1643
1644                 /* empty quotemark is not allowed */
1645                 if (qmark == NULL || *qmark == '\0')
1646                         qmark = "> ";
1647
1648                 compose_quote_fmt(compose, full_msginfo,
1649                                   body_fmt, qmark, body, FALSE, TRUE,
1650                                           _("Message forward format error at line %d."));
1651                 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1652                 quote_fmt_reset_vartable();
1653                 compose_attach_parts(compose, msginfo);
1654
1655                 procmsg_msginfo_free(full_msginfo);
1656         }
1657
1658         SIGNAL_BLOCK(textbuf);
1659
1660         if (account->auto_sig)
1661                 compose_insert_sig(compose, FALSE);
1662
1663         compose_wrap_all(compose);
1664
1665         SIGNAL_UNBLOCK(textbuf);
1666         
1667         gtk_text_buffer_get_start_iter(textbuf, &iter);
1668         gtk_text_buffer_place_cursor(textbuf, &iter);
1669
1670         gtk_widget_grab_focus(compose->header_last->entry);
1671
1672         if (!no_extedit && prefs_common.auto_exteditor)
1673                 compose_exec_ext_editor(compose);
1674         
1675         /*save folder*/
1676         if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1677                 gchar *folderidentifier;
1678
1679                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1680                 folderidentifier = folder_item_get_identifier(msginfo->folder);
1681                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1682                 g_free(folderidentifier);
1683         }
1684
1685         undo_unblock(compose->undostruct);
1686         
1687         compose->modified = FALSE;
1688         compose_set_title(compose);
1689
1690         compose->updating = FALSE;
1691         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1692         SCROLL_TO_CURSOR(compose);
1693
1694         if (compose->deferred_destroy) {
1695                 compose_destroy(compose);
1696                 return NULL;
1697         }
1698
1699         return compose;
1700 }
1701
1702 #undef INSERT_FW_HEADER
1703
1704 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1705 {
1706         Compose *compose;
1707         GtkTextView *textview;
1708         GtkTextBuffer *textbuf;
1709         GtkTextIter iter;
1710         GSList *msginfo;
1711         gchar *msgfile;
1712         gboolean single_mail = TRUE;
1713         
1714         g_return_val_if_fail(msginfo_list != NULL, NULL);
1715
1716         if (g_slist_length(msginfo_list) > 1)
1717                 single_mail = FALSE;
1718
1719         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1720                 if (((MsgInfo *)msginfo->data)->folder == NULL)
1721                         return NULL;
1722
1723         /* guess account from first selected message */
1724         if (!account && 
1725             !(account = compose_guess_forward_account_from_msginfo
1726                                 (msginfo_list->data)))
1727                 account = cur_account;
1728
1729         g_return_val_if_fail(account != NULL, NULL);
1730
1731         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1732                 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1733                 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1734         }
1735
1736         compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1737
1738         compose->updating = TRUE;
1739
1740         textview = GTK_TEXT_VIEW(compose->text);
1741         textbuf = gtk_text_view_get_buffer(textview);
1742         compose_create_tags(textview, compose);
1743         
1744         undo_block(compose->undostruct);
1745         for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1746                 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1747
1748                 if (!is_file_exist(msgfile))
1749                         g_warning("%s: file not exist\n", msgfile);
1750                 else
1751                         compose_attach_append(compose, msgfile, msgfile,
1752                                 "message/rfc822");
1753                 g_free(msgfile);
1754         }
1755         
1756         if (single_mail) {
1757                 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1758                 if (info->subject && *info->subject) {
1759                         gchar *buf, *buf2, *p;
1760
1761                         buf = p = g_strdup(info->subject);
1762                         p += subject_get_prefix_length(p);
1763                         memmove(buf, p, strlen(p) + 1);
1764
1765                         buf2 = g_strdup_printf("Fw: %s", buf);
1766                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1767
1768                         g_free(buf);
1769                         g_free(buf2);
1770                 }
1771         } else {
1772                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1773                         _("Fw: multiple emails"));
1774         }
1775
1776         SIGNAL_BLOCK(textbuf);
1777         
1778         if (account->auto_sig)
1779                 compose_insert_sig(compose, FALSE);
1780
1781         compose_wrap_all(compose);
1782
1783         SIGNAL_UNBLOCK(textbuf);
1784         
1785         gtk_text_buffer_get_start_iter(textbuf, &iter);
1786         gtk_text_buffer_place_cursor(textbuf, &iter);
1787
1788         gtk_widget_grab_focus(compose->header_last->entry);
1789         undo_unblock(compose->undostruct);
1790         compose->modified = FALSE;
1791         compose_set_title(compose);
1792
1793         compose->updating = FALSE;
1794         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1795         SCROLL_TO_CURSOR(compose);
1796
1797         if (compose->deferred_destroy) {
1798                 compose_destroy(compose);
1799                 return NULL;
1800         }
1801
1802         return compose;
1803 }
1804
1805 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter) 
1806 {
1807         GtkTextIter start = *iter;
1808         GtkTextIter end_iter;
1809         int start_pos = gtk_text_iter_get_offset(&start);
1810         gchar *str = NULL;
1811         if (!compose->account->sig_sep)
1812                 return FALSE;
1813         
1814         gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1815                 start_pos+strlen(compose->account->sig_sep));
1816
1817         /* check sig separator */
1818         str = gtk_text_iter_get_text(&start, &end_iter);
1819         if (!strcmp(str, compose->account->sig_sep)) {
1820                 gchar *tmp = NULL;
1821                 /* check end of line (\n) */
1822                 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1823                         start_pos+strlen(compose->account->sig_sep));
1824                 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1825                         start_pos+strlen(compose->account->sig_sep)+1);
1826                 tmp = gtk_text_iter_get_text(&start, &end_iter);
1827                 if (!strcmp(tmp,"\n")) {
1828                         g_free(str);
1829                         g_free(tmp);
1830                         return TRUE;
1831                 }
1832                 g_free(tmp);    
1833         }
1834         g_free(str);
1835
1836         return FALSE;
1837 }
1838
1839 static void compose_colorize_signature(Compose *compose)
1840 {
1841         GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1842         GtkTextIter iter;
1843         GtkTextIter end_iter;
1844         gtk_text_buffer_get_start_iter(buffer, &iter);
1845         while (gtk_text_iter_forward_line(&iter))
1846                 if (compose_is_sig_separator(compose, buffer, &iter)) {
1847                         gtk_text_buffer_get_end_iter(buffer, &end_iter);
1848                         gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1849                 }
1850 }
1851
1852 #define BLOCK_WRAP() {                                                  \
1853         prev_autowrap = compose->autowrap;                              \
1854         buffer = gtk_text_view_get_buffer(                              \
1855                                         GTK_TEXT_VIEW(compose->text));  \
1856         compose->autowrap = FALSE;                                      \
1857                                                                         \
1858         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
1859                                 G_CALLBACK(compose_changed_cb),         \
1860                                 compose);                               \
1861         g_signal_handlers_block_by_func(G_OBJECT(buffer),               \
1862                                 G_CALLBACK(text_inserted),              \
1863                                 compose);                               \
1864 }
1865 #define UNBLOCK_WRAP() {                                                \
1866         compose->autowrap = prev_autowrap;                              \
1867         if (compose->autowrap) {                                        \
1868                 gint old = compose->draft_timeout_tag;                  \
1869                 compose->draft_timeout_tag = -2;                        \
1870                 compose_wrap_all(compose);                              \
1871                 compose->draft_timeout_tag = old;                       \
1872         }                                                               \
1873                                                                         \
1874         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
1875                                 G_CALLBACK(compose_changed_cb),         \
1876                                 compose);                               \
1877         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),             \
1878                                 G_CALLBACK(text_inserted),              \
1879                                 compose);                               \
1880 }
1881
1882 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1883 {
1884         Compose *compose = NULL;
1885         PrefsAccount *account = NULL;
1886         GtkTextView *textview;
1887         GtkTextBuffer *textbuf;
1888         GtkTextMark *mark;
1889         GtkTextIter iter;
1890         FILE *fp;
1891         gchar buf[BUFFSIZE];
1892         gboolean use_signing = FALSE;
1893         gboolean use_encryption = FALSE;
1894         gchar *privacy_system = NULL;
1895         int priority = PRIORITY_NORMAL;
1896         MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1897
1898         g_return_val_if_fail(msginfo != NULL, NULL);
1899         g_return_val_if_fail(msginfo->folder != NULL, NULL);
1900
1901         if (compose_put_existing_to_front(msginfo)) {
1902                 return NULL;
1903         }
1904
1905         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1906             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1907                 gchar queueheader_buf[BUFFSIZE];
1908                 gint id, param;
1909
1910                 /* Select Account from queue headers */
1911                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1912                                              sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1913                         id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1914                         account = account_find_from_id(id);
1915                 }
1916                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1917                                              sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1918                         id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1919                         account = account_find_from_id(id);
1920                 }
1921                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1922                                              sizeof(queueheader_buf), "NAID:")) {
1923                         id = atoi(&queueheader_buf[strlen("NAID:")]);
1924                         account = account_find_from_id(id);
1925                 }
1926                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1927                                                     sizeof(queueheader_buf), "MAID:")) {
1928                         id = atoi(&queueheader_buf[strlen("MAID:")]);
1929                         account = account_find_from_id(id);
1930                 }
1931                 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1932                                                                 sizeof(queueheader_buf), "S:")) {
1933                         account = account_find_from_address(queueheader_buf);
1934                 }
1935                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1936                                              sizeof(queueheader_buf), "X-Claws-Sign:")) {
1937                         param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
1938                         use_signing = param;
1939                         
1940                 }
1941                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1942                                              sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
1943                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
1944                         use_signing = param;
1945                         
1946                 }
1947                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1948                                              sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
1949                         param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
1950                         use_encryption = param;
1951                 }
1952                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1953                                              sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
1954                         param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
1955                         use_encryption = param;
1956                 }
1957                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1958                                             sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
1959                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
1960                 }
1961                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1962                                             sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
1963                         privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
1964                 }
1965                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1966                                              sizeof(queueheader_buf), "X-Priority: ")) {
1967                         param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
1968                         priority = param;
1969                 }
1970                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1971                                              sizeof(queueheader_buf), "RMID:")) {
1972                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
1973                         if (tokens[0] && tokens[1] && tokens[2]) {
1974                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1975                                 if (orig_item != NULL) {
1976                                         replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1977                                 }
1978                         }
1979                         g_strfreev(tokens);
1980                 }
1981                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, 
1982                                              sizeof(queueheader_buf), "FMID:")) {
1983                         gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
1984                         if (tokens[0] && tokens[1] && tokens[2]) {
1985                                 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
1986                                 if (orig_item != NULL) {
1987                                         fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
1988                                 }
1989                         }
1990                         g_strfreev(tokens);
1991                 }
1992         } else {
1993                 account = msginfo->folder->folder->account;
1994         }
1995
1996         if (!account && prefs_common.reedit_account_autosel) {
1997                 gchar from[BUFFSIZE];
1998                 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
1999                         extract_address(from);
2000                         account = account_find_from_address(from);
2001                 }
2002         }
2003         if (!account) {
2004                 account = cur_account;
2005         }
2006         g_return_val_if_fail(account != NULL, NULL);
2007
2008         compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2009         
2010         compose->replyinfo = replyinfo;
2011         compose->fwdinfo = fwdinfo;
2012
2013         compose->updating = TRUE;
2014         compose->priority = priority;
2015
2016         if (privacy_system != NULL) {
2017                 compose->privacy_system = privacy_system;
2018                 compose_use_signing(compose, use_signing);
2019                 compose_use_encryption(compose, use_encryption);
2020                 compose_update_privacy_system_menu_item(compose, FALSE);
2021         } else {
2022                 activate_privacy_system(compose, account, FALSE);
2023         }
2024
2025         compose->targetinfo = procmsg_msginfo_copy(msginfo);
2026
2027         compose_extract_original_charset(compose);
2028
2029         if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2030             folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2031                 gchar queueheader_buf[BUFFSIZE];
2032
2033                 /* Set message save folder */
2034                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2035                         gint startpos = 0;
2036
2037                         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2038                         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2039                         gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2040                 }
2041                 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2042                         gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2043                         if (active) {
2044                                 GtkItemFactory *ifactory;
2045                                 ifactory = gtk_item_factory_from_widget(compose->menubar);
2046                                 menu_set_active(ifactory, "/Options/Request Return Receipt", TRUE);
2047                         }
2048                 }
2049         }
2050         
2051         if (compose_parse_header(compose, msginfo) < 0) {
2052                 compose->updating = FALSE;
2053                 compose_destroy(compose);
2054                 return NULL;
2055         }
2056         compose_reedit_set_entry(compose, msginfo);
2057
2058         textview = GTK_TEXT_VIEW(compose->text);
2059         textbuf = gtk_text_view_get_buffer(textview);
2060         compose_create_tags(textview, compose);
2061
2062         mark = gtk_text_buffer_get_insert(textbuf);
2063         gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2064
2065         g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2066                                         G_CALLBACK(compose_changed_cb),
2067                                         compose);
2068         
2069         if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2070                 fp = procmime_get_first_encrypted_text_content(msginfo);
2071                 if (fp) {
2072                         compose_force_encryption(compose, account, TRUE);
2073                 }
2074         } else {
2075                 fp = procmime_get_first_text_content(msginfo);
2076         }
2077         if (fp == NULL) {
2078                 g_warning("Can't get text part\n");
2079         }
2080
2081         if (fp != NULL) {
2082                 gboolean prev_autowrap = compose->autowrap;
2083                 GtkTextBuffer *buffer = textbuf;
2084                 BLOCK_WRAP();
2085                 while (fgets(buf, sizeof(buf), fp) != NULL) {
2086                         strcrchomp(buf);
2087                         gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2088                 }
2089                 UNBLOCK_WRAP();
2090                 fclose(fp);
2091         }
2092         
2093         compose_attach_parts(compose, msginfo);
2094
2095         compose_colorize_signature(compose);
2096
2097         g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2098                                         G_CALLBACK(compose_changed_cb),
2099                                         compose);
2100
2101         gtk_widget_grab_focus(compose->text);
2102
2103         if (prefs_common.auto_exteditor) {
2104                 compose_exec_ext_editor(compose);
2105         }
2106         compose->modified = FALSE;
2107         compose_set_title(compose);
2108
2109         compose->updating = FALSE;
2110         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2111         SCROLL_TO_CURSOR(compose);
2112
2113         if (compose->deferred_destroy) {
2114                 compose_destroy(compose);
2115                 return NULL;
2116         }
2117         
2118         compose->sig_str = compose_get_signature_str(compose);
2119         
2120         return compose;
2121 }
2122
2123 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2124                                                  gboolean batch)
2125 {
2126         Compose *compose;
2127         gchar *filename;
2128         GtkItemFactory *ifactory;
2129         FolderItem *item;
2130
2131         g_return_val_if_fail(msginfo != NULL, NULL);
2132
2133         if (!account)
2134                 account = account_get_reply_account(msginfo,
2135                                         prefs_common.reply_account_autosel);
2136         g_return_val_if_fail(account != NULL, NULL);
2137
2138         compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2139
2140         compose->updating = TRUE;
2141
2142         ifactory = gtk_item_factory_from_widget(compose->menubar);
2143         compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2144         compose->replyinfo = NULL;
2145         compose->fwdinfo = NULL;
2146
2147         compose_show_first_last_header(compose, TRUE);
2148
2149         gtk_widget_grab_focus(compose->header_last->entry);
2150
2151         filename = procmsg_get_message_file(msginfo);
2152
2153         if (filename == NULL) {
2154                 compose->updating = FALSE;
2155                 compose_destroy(compose);
2156
2157                 return NULL;
2158         }
2159
2160         compose->redirect_filename = filename;
2161         
2162         /* Set save folder */
2163         item = msginfo->folder;
2164         if (item && item->prefs && item->prefs->save_copy_to_folder) {
2165                 gchar *folderidentifier;
2166
2167                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2168                 folderidentifier = folder_item_get_identifier(item);
2169                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2170                 g_free(folderidentifier);
2171         }
2172
2173         compose_attach_parts(compose, msginfo);
2174
2175         if (msginfo->subject)
2176                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2177                                    msginfo->subject);
2178         gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2179
2180         compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2181                                           _("Message redirect format error at line %d."));
2182         quote_fmt_reset_vartable();
2183         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2184
2185         compose_colorize_signature(compose);
2186
2187         ifactory = gtk_item_factory_from_widget(compose->popupmenu);
2188         menu_set_sensitive(ifactory, "/Add...", FALSE);
2189         menu_set_sensitive(ifactory, "/Remove", FALSE);
2190         menu_set_sensitive(ifactory, "/Properties...", FALSE);
2191
2192         ifactory = gtk_item_factory_from_widget(compose->menubar);
2193         menu_set_sensitive(ifactory, "/Message/Save", FALSE);
2194         menu_set_sensitive(ifactory, "/Message/Insert file", FALSE);
2195         menu_set_sensitive(ifactory, "/Message/Attach file", FALSE);
2196         menu_set_sensitive(ifactory, "/Message/Insert signature", FALSE);
2197         menu_set_sensitive(ifactory, "/Edit", FALSE);
2198         menu_set_sensitive(ifactory, "/Options", FALSE);
2199         menu_set_sensitive(ifactory, "/Tools/Show ruler", FALSE);
2200         menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
2201         
2202         if (compose->toolbar->draft_btn)
2203                 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2204         if (compose->toolbar->insert_btn)
2205                 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2206         if (compose->toolbar->attach_btn)
2207                 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2208         if (compose->toolbar->sig_btn)
2209                 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2210         if (compose->toolbar->exteditor_btn)
2211                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2212         if (compose->toolbar->linewrap_current_btn)
2213                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2214         if (compose->toolbar->linewrap_all_btn)
2215                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2216
2217         compose->modified = FALSE;
2218         compose_set_title(compose);
2219         compose->updating = FALSE;
2220         compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2221         SCROLL_TO_CURSOR(compose);
2222
2223         if (compose->deferred_destroy) {
2224                 compose_destroy(compose);
2225                 return NULL;
2226         }
2227         
2228         return compose;
2229 }
2230
2231 GList *compose_get_compose_list(void)
2232 {
2233         return compose_list;
2234 }
2235
2236 void compose_entry_append(Compose *compose, const gchar *address,
2237                           ComposeEntryType type)
2238 {
2239         const gchar *header;
2240         gchar *cur, *begin;
2241         gboolean in_quote = FALSE;
2242         if (!address || *address == '\0') return;
2243
2244         switch (type) {
2245         case COMPOSE_CC:
2246                 header = N_("Cc:");
2247                 break;
2248         case COMPOSE_BCC:
2249                 header = N_("Bcc:");
2250                 break;
2251         case COMPOSE_REPLYTO:
2252                 header = N_("Reply-To:");
2253                 break;
2254         case COMPOSE_NEWSGROUPS:
2255                 header = N_("Newsgroups:");
2256                 break;
2257         case COMPOSE_FOLLOWUPTO:
2258                 header = N_( "Followup-To:");
2259                 break;
2260         case COMPOSE_TO:
2261         default:
2262                 header = N_("To:");
2263                 break;
2264         }
2265         header = prefs_common_translated_header_name(header);
2266         
2267         cur = begin = (gchar *)address;
2268         
2269         /* we separate the line by commas, but not if we're inside a quoted
2270          * string */
2271         while (*cur != '\0') {
2272                 if (*cur == '"') 
2273                         in_quote = !in_quote;
2274                 if (*cur == ',' && !in_quote) {
2275                         gchar *tmp = g_strdup(begin);
2276                         gchar *o_tmp = tmp;
2277                         tmp[cur-begin]='\0';
2278                         cur++;
2279                         begin = cur;
2280                         while (*tmp == ' ' || *tmp == '\t')
2281                                 tmp++;
2282                         compose_add_header_entry(compose, header, tmp);
2283                         g_free(o_tmp);
2284                         continue;
2285                 }
2286                 cur++;
2287         }
2288         if (begin < cur) {
2289                 gchar *tmp = g_strdup(begin);
2290                 gchar *o_tmp = tmp;
2291                 tmp[cur-begin]='\0';
2292                 cur++;
2293                 begin = cur;
2294                 while (*tmp == ' ' || *tmp == '\t')
2295                         tmp++;
2296                 compose_add_header_entry(compose, header, tmp);
2297                 g_free(o_tmp);          
2298         }
2299 }
2300
2301 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2302 {
2303         static GdkColor yellow;
2304         static GdkColor black;
2305         static gboolean yellow_initialised = FALSE;
2306         GSList *h_list;
2307         GtkEntry *entry;
2308                 
2309         if (!yellow_initialised) {
2310                 gdk_color_parse("#f5f6be", &yellow);
2311                 gdk_color_parse("#000000", &black);
2312                 yellow_initialised = gdk_colormap_alloc_color(
2313                         gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2314                 yellow_initialised &= gdk_colormap_alloc_color(
2315                         gdk_colormap_get_system(), &black, FALSE, TRUE);
2316         }
2317
2318         for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2319                 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2320                 if (gtk_entry_get_text(entry) && 
2321                     !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2322                         if (yellow_initialised) {
2323                                 gtk_widget_modify_base(
2324                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2325                                         GTK_STATE_NORMAL, &yellow);
2326                                 gtk_widget_modify_text(
2327                                         GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2328                                         GTK_STATE_NORMAL, &black);
2329                         }
2330                 }
2331         }
2332 }
2333
2334 void compose_toolbar_cb(gint action, gpointer data)
2335 {
2336         ToolbarItem *toolbar_item = (ToolbarItem*)data;
2337         Compose *compose = (Compose*)toolbar_item->parent;
2338         
2339         g_return_if_fail(compose != NULL);
2340
2341         switch(action) {
2342         case A_SEND:
2343                 compose_send_cb(compose, 0, NULL);
2344                 break;
2345         case A_SENDL:
2346                 compose_send_later_cb(compose, 0, NULL);
2347                 break;
2348         case A_DRAFT:
2349                 compose_draft_cb(compose, COMPOSE_QUIT_EDITING, NULL);
2350                 break;
2351         case A_INSERT:
2352                 compose_insert_file_cb(compose, 0, NULL);
2353                 break;
2354         case A_ATTACH:
2355                 compose_attach_cb(compose, 0, NULL);
2356                 break;
2357         case A_SIG:
2358                 compose_insert_sig(compose, FALSE);
2359                 break;
2360         case A_EXTEDITOR:
2361                 compose_ext_editor_cb(compose, 0, NULL);
2362                 break;
2363         case A_LINEWRAP_CURRENT:
2364                 compose_beautify_paragraph(compose, NULL, TRUE);
2365                 break;
2366         case A_LINEWRAP_ALL:
2367                 compose_wrap_all_full(compose, TRUE);
2368                 break;
2369         case A_ADDRBOOK:
2370                 compose_address_cb(compose, 0, NULL);
2371                 break;
2372 #ifdef USE_ASPELL
2373         case A_CHECK_SPELLING:
2374                 compose_check_all(compose);
2375                 break;
2376 #endif
2377         default:
2378                 break;
2379         }
2380 }
2381
2382 static void compose_entries_set(Compose *compose, const gchar *mailto)
2383 {
2384         gchar *to = NULL;
2385         gchar *cc = NULL;
2386         gchar *bcc = NULL;
2387         gchar *subject = NULL;
2388         gchar *body = NULL;
2389         gchar *temp = NULL;
2390         gsize  len = 0;
2391         gchar **attach = NULL;
2392         
2393         scan_mailto_url(mailto, &to, &cc, &bcc, &subject, &body, &attach);
2394
2395         if (to)
2396                 compose_entry_append(compose, to, COMPOSE_TO);
2397         if (cc)
2398                 compose_entry_append(compose, cc, COMPOSE_CC);
2399         if (bcc)
2400                 compose_entry_append(compose, bcc, COMPOSE_BCC);
2401         if (subject) {
2402                 if (!g_utf8_validate (subject, -1, NULL)) {
2403                         temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2404                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2405                         g_free(temp);
2406                 } else {
2407                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2408                 }
2409         }
2410         if (body) {
2411                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2412                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2413                 GtkTextMark *mark;
2414                 GtkTextIter iter;
2415                 gboolean prev_autowrap = compose->autowrap;
2416
2417                 compose->autowrap = FALSE;
2418
2419                 mark = gtk_text_buffer_get_insert(buffer);
2420                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2421
2422                 if (!g_utf8_validate (body, -1, NULL)) {
2423                         temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2424                         gtk_text_buffer_insert(buffer, &iter, temp, -1);
2425                         g_free(temp);
2426                 } else {
2427                         gtk_text_buffer_insert(buffer, &iter, body, -1);
2428                 }
2429                 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2430
2431                 compose->autowrap = prev_autowrap;
2432                 if (compose->autowrap)
2433                         compose_wrap_all(compose);
2434         }
2435
2436         if (attach) {
2437                 gint i = 0, att = 0;
2438                 gchar *warn_files = NULL;
2439                 while (attach[i] != NULL) {
2440                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2441                         if (utf8_filename) {
2442                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2443                                         gchar *tmp = g_strdup_printf("%s%s\n",
2444                                                         warn_files?warn_files:"",
2445                                                         utf8_filename);
2446                                         g_free(warn_files);
2447                                         warn_files = tmp;
2448                                         att++;
2449                                 }
2450                                 g_free(utf8_filename);
2451                         } else {
2452                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2453                         }
2454                         i++;
2455                 }
2456                 if (warn_files) {
2457                         alertpanel_notice(ngettext(
2458                         "The following file has been attached: \n%s",
2459                         "The following files have been attached: \n%s", att), warn_files);
2460                         g_free(warn_files);
2461                 }
2462         }
2463         g_free(to);
2464         g_free(cc);
2465         g_free(bcc);
2466         g_free(subject);
2467         g_free(body);
2468         g_strfreev(attach);
2469 }
2470
2471 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2472 {
2473         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2474                                        {"Cc:",          NULL, TRUE},
2475                                        {"References:",  NULL, FALSE},
2476                                        {"Bcc:",         NULL, TRUE},
2477                                        {"Newsgroups:",  NULL, TRUE},
2478                                        {"Followup-To:", NULL, TRUE},
2479                                        {"List-Post:",   NULL, FALSE},
2480                                        {"X-Priority:",  NULL, FALSE},
2481                                        {NULL,           NULL, FALSE}};
2482
2483         enum
2484         {
2485                 H_REPLY_TO      = 0,
2486                 H_CC            = 1,
2487                 H_REFERENCES    = 2,
2488                 H_BCC           = 3,
2489                 H_NEWSGROUPS    = 4,
2490                 H_FOLLOWUP_TO   = 5,
2491                 H_LIST_POST     = 6,
2492                 H_X_PRIORITY    = 7
2493         };
2494
2495         FILE *fp;
2496
2497         g_return_val_if_fail(msginfo != NULL, -1);
2498
2499         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2500         procheader_get_header_fields(fp, hentry);
2501         fclose(fp);
2502
2503         if (hentry[H_REPLY_TO].body != NULL) {
2504                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2505                         compose->replyto =
2506                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2507                                                    NULL);
2508                 }
2509                 g_free(hentry[H_REPLY_TO].body);
2510                 hentry[H_REPLY_TO].body = NULL;
2511         }
2512         if (hentry[H_CC].body != NULL) {
2513                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2514                 g_free(hentry[H_CC].body);
2515                 hentry[H_CC].body = NULL;
2516         }
2517         if (hentry[H_REFERENCES].body != NULL) {
2518                 if (compose->mode == COMPOSE_REEDIT)
2519                         compose->references = hentry[H_REFERENCES].body;
2520                 else {
2521                         compose->references = compose_parse_references
2522                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2523                         g_free(hentry[H_REFERENCES].body);
2524                 }
2525                 hentry[H_REFERENCES].body = NULL;
2526         }
2527         if (hentry[H_BCC].body != NULL) {
2528                 if (compose->mode == COMPOSE_REEDIT)
2529                         compose->bcc =
2530                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2531                 g_free(hentry[H_BCC].body);
2532                 hentry[H_BCC].body = NULL;
2533         }
2534         if (hentry[H_NEWSGROUPS].body != NULL) {
2535                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2536                 hentry[H_NEWSGROUPS].body = NULL;
2537         }
2538         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2539                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2540                         compose->followup_to =
2541                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2542                                                    NULL);
2543                 }
2544                 g_free(hentry[H_FOLLOWUP_TO].body);
2545                 hentry[H_FOLLOWUP_TO].body = NULL;
2546         }
2547         if (hentry[H_LIST_POST].body != NULL) {
2548                 gchar *to = NULL;
2549
2550                 extract_address(hentry[H_LIST_POST].body);
2551                 if (hentry[H_LIST_POST].body[0] != '\0') {
2552                         scan_mailto_url(hentry[H_LIST_POST].body,
2553                                         &to, NULL, NULL, NULL, NULL, NULL);
2554                         if (to) {
2555                                 g_free(compose->ml_post);
2556                                 compose->ml_post = to;
2557                         }
2558                 }
2559                 g_free(hentry[H_LIST_POST].body);
2560                 hentry[H_LIST_POST].body = NULL;
2561         }
2562
2563         /* CLAWS - X-Priority */
2564         if (compose->mode == COMPOSE_REEDIT)
2565                 if (hentry[H_X_PRIORITY].body != NULL) {
2566                         gint priority;
2567                         
2568                         priority = atoi(hentry[H_X_PRIORITY].body);
2569                         g_free(hentry[H_X_PRIORITY].body);
2570                         
2571                         hentry[H_X_PRIORITY].body = NULL;
2572                         
2573                         if (priority < PRIORITY_HIGHEST || 
2574                             priority > PRIORITY_LOWEST)
2575                                 priority = PRIORITY_NORMAL;
2576                         
2577                         compose->priority =  priority;
2578                 }
2579  
2580         if (compose->mode == COMPOSE_REEDIT) {
2581                 if (msginfo->inreplyto && *msginfo->inreplyto)
2582                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2583                 return 0;
2584         }
2585
2586         if (msginfo->msgid && *msginfo->msgid)
2587                 compose->inreplyto = g_strdup(msginfo->msgid);
2588
2589         if (!compose->references) {
2590                 if (msginfo->msgid && *msginfo->msgid) {
2591                         if (msginfo->inreplyto && *msginfo->inreplyto)
2592                                 compose->references =
2593                                         g_strdup_printf("<%s>\n\t<%s>",
2594                                                         msginfo->inreplyto,
2595                                                         msginfo->msgid);
2596                         else
2597                                 compose->references =
2598                                         g_strconcat("<", msginfo->msgid, ">",
2599                                                     NULL);
2600                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2601                         compose->references =
2602                                 g_strconcat("<", msginfo->inreplyto, ">",
2603                                             NULL);
2604                 }
2605         }
2606
2607         return 0;
2608 }
2609
2610 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2611 {
2612         GSList *ref_id_list, *cur;
2613         GString *new_ref;
2614         gchar *new_ref_str;
2615
2616         ref_id_list = references_list_append(NULL, ref);
2617         if (!ref_id_list) return NULL;
2618         if (msgid && *msgid)
2619                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2620
2621         for (;;) {
2622                 gint len = 0;
2623
2624                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2625                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2626                         len += strlen((gchar *)cur->data) + 5;
2627
2628                 if (len > MAX_REFERENCES_LEN) {
2629                         /* remove second message-ID */
2630                         if (ref_id_list && ref_id_list->next &&
2631                             ref_id_list->next->next) {
2632                                 g_free(ref_id_list->next->data);
2633                                 ref_id_list = g_slist_remove
2634                                         (ref_id_list, ref_id_list->next->data);
2635                         } else {
2636                                 slist_free_strings(ref_id_list);
2637                                 g_slist_free(ref_id_list);
2638                                 return NULL;
2639                         }
2640                 } else
2641                         break;
2642         }
2643
2644         new_ref = g_string_new("");
2645         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2646                 if (new_ref->len > 0)
2647                         g_string_append(new_ref, "\n\t");
2648                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2649         }
2650
2651         slist_free_strings(ref_id_list);
2652         g_slist_free(ref_id_list);
2653
2654         new_ref_str = new_ref->str;
2655         g_string_free(new_ref, FALSE);
2656
2657         return new_ref_str;
2658 }
2659
2660 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2661                                 const gchar *fmt, const gchar *qmark,
2662                                 const gchar *body, gboolean rewrap,
2663                                 gboolean need_unescape,
2664                                 const gchar *err_msg)
2665 {
2666         MsgInfo* dummyinfo = NULL;
2667         gchar *quote_str = NULL;
2668         gchar *buf;
2669         gboolean prev_autowrap;
2670         const gchar *trimmed_body = body;
2671         gint cursor_pos = -1;
2672         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2673         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2674         GtkTextIter iter;
2675         GtkTextMark *mark;
2676         
2677
2678         SIGNAL_BLOCK(buffer);
2679
2680         if (!msginfo) {
2681                 dummyinfo = compose_msginfo_new_from_compose(compose);
2682                 msginfo = dummyinfo;
2683         }
2684
2685         if (qmark != NULL) {
2686 #ifdef USE_ASPELL
2687                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2688                                 compose->gtkaspell);
2689 #else
2690                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2691 #endif
2692                 quote_fmt_scan_string(qmark);
2693                 quote_fmt_parse();
2694
2695                 buf = quote_fmt_get_buffer();
2696                 if (buf == NULL)
2697                         alertpanel_error(_("Quote mark format error."));
2698                 else
2699                         Xstrdup_a(quote_str, buf, goto error)
2700         }
2701
2702         if (fmt && *fmt != '\0') {
2703
2704                 if (trimmed_body)
2705                         while (*trimmed_body == '\n')
2706                                 trimmed_body++;
2707
2708 #ifdef USE_ASPELL
2709                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2710                                 compose->gtkaspell);
2711 #else
2712                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2713 #endif
2714                 if (need_unescape) {
2715                         gchar *tmp = NULL;
2716
2717                         /* decode \-escape sequences in the internal representation of the quote format */
2718                         tmp = malloc(strlen(fmt)+1);
2719                         pref_get_unescaped_pref(tmp, fmt);
2720                         quote_fmt_scan_string(tmp);
2721                         quote_fmt_parse();
2722                         g_free(tmp);
2723                 } else {
2724                         quote_fmt_scan_string(fmt);
2725                         quote_fmt_parse();
2726                 }
2727
2728                 buf = quote_fmt_get_buffer();
2729                 if (buf == NULL) {
2730                         gint line = quote_fmt_get_line();
2731                         alertpanel_error(err_msg, line);
2732                         goto error;
2733                 }
2734         } else
2735                 buf = "";
2736
2737         prev_autowrap = compose->autowrap;
2738         compose->autowrap = FALSE;
2739
2740         mark = gtk_text_buffer_get_insert(buffer);
2741         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2742         if (g_utf8_validate(buf, -1, NULL)) { 
2743                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2744         } else {
2745                 gchar *tmpout = NULL;
2746                 tmpout = conv_codeset_strdup
2747                         (buf, conv_get_locale_charset_str_no_utf8(),
2748                          CS_INTERNAL);
2749                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2750                         g_free(tmpout);
2751                         tmpout = g_malloc(strlen(buf)*2+1);
2752                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2753                 }
2754                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2755                 g_free(tmpout);
2756         }
2757
2758         cursor_pos = quote_fmt_get_cursor_pos();
2759         compose->set_cursor_pos = cursor_pos;
2760         if (cursor_pos == -1) {
2761                 cursor_pos = 0;
2762         }
2763         gtk_text_buffer_get_start_iter(buffer, &iter);
2764         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2765         gtk_text_buffer_place_cursor(buffer, &iter);
2766
2767         compose->autowrap = prev_autowrap;
2768         if (compose->autowrap && rewrap)
2769                 compose_wrap_all(compose);
2770
2771         goto ok;
2772
2773 error:
2774         buf = NULL;
2775 ok:
2776         SIGNAL_UNBLOCK(buffer);
2777
2778         procmsg_msginfo_free( dummyinfo );
2779
2780         return buf;
2781 }
2782
2783 /* if ml_post is of type addr@host and from is of type
2784  * addr-anything@host, return TRUE
2785  */
2786 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2787 {
2788         gchar *left_ml = NULL;
2789         gchar *right_ml = NULL;
2790         gchar *left_from = NULL;
2791         gchar *right_from = NULL;
2792         gboolean result = FALSE;
2793         
2794         if (!ml_post || !from)
2795                 return FALSE;
2796         
2797         left_ml = g_strdup(ml_post);
2798         if (strstr(left_ml, "@")) {
2799                 right_ml = strstr(left_ml, "@")+1;
2800                 *(strstr(left_ml, "@")) = '\0';
2801         }
2802         
2803         left_from = g_strdup(from);
2804         if (strstr(left_from, "@")) {
2805                 right_from = strstr(left_from, "@")+1;
2806                 *(strstr(left_from, "@")) = '\0';
2807         }
2808         
2809         if (left_ml && left_from && right_ml && right_from
2810         &&  !strncmp(left_from, left_ml, strlen(left_ml))
2811         &&  !strcmp(right_from, right_ml)) {
2812                 result = TRUE;
2813         }
2814         g_free(left_ml);
2815         g_free(left_from);
2816         
2817         return result;
2818 }
2819
2820 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2821 {
2822         gchar *my_addr1, *my_addr2;
2823         
2824         if (!addr1 || !addr2)
2825                 return FALSE;
2826
2827         Xstrdup_a(my_addr1, addr1, return FALSE);
2828         Xstrdup_a(my_addr2, addr2, return FALSE);
2829         
2830         extract_address(my_addr1);
2831         extract_address(my_addr2);
2832         
2833         return !strcasecmp(my_addr1, my_addr2);
2834 }
2835
2836 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2837                                     gboolean to_all, gboolean to_ml,
2838                                     gboolean to_sender,
2839                                     gboolean followup_and_reply_to)
2840 {
2841         GSList *cc_list = NULL;
2842         GSList *cur;
2843         gchar *from = NULL;
2844         gchar *replyto = NULL;
2845         GHashTable *to_table;
2846
2847         gboolean reply_to_ml = FALSE;
2848         gboolean default_reply_to = FALSE;
2849
2850         g_return_if_fail(compose->account != NULL);
2851         g_return_if_fail(msginfo != NULL);
2852
2853         reply_to_ml = to_ml && compose->ml_post;
2854
2855         default_reply_to = msginfo->folder && 
2856                 msginfo->folder->prefs->enable_default_reply_to;
2857
2858         if (compose->account->protocol != A_NNTP) {
2859                 if (reply_to_ml && !default_reply_to) {
2860                         
2861                         gboolean is_subscr = is_subscription(compose->ml_post,
2862                                                              msginfo->from);
2863                         if (!is_subscr) {
2864                                 /* normal answer to ml post with a reply-to */
2865                                 compose_entry_append(compose,
2866                                            compose->ml_post,
2867                                            COMPOSE_TO);
2868                                 if (compose->replyto
2869                                 &&  !same_address(compose->ml_post, compose->replyto))
2870                                         compose_entry_append(compose,
2871                                                 compose->replyto,
2872                                                 COMPOSE_CC);
2873                         } else {
2874                                 /* answer to subscription confirmation */
2875                                 if (compose->replyto)
2876                                         compose_entry_append(compose,
2877                                                 compose->replyto,
2878                                                 COMPOSE_TO);
2879                                 else if (msginfo->from)
2880                                         compose_entry_append(compose,
2881                                                 msginfo->from,
2882                                                 COMPOSE_TO);
2883                         }
2884                 }
2885                 else if (!(to_all || to_sender) && default_reply_to) {
2886                         compose_entry_append(compose,
2887                             msginfo->folder->prefs->default_reply_to,
2888                             COMPOSE_TO);
2889                         compose_entry_mark_default_to(compose,
2890                                 msginfo->folder->prefs->default_reply_to);
2891                 } else {
2892                         gchar *tmp1 = NULL;
2893                         if (!msginfo->from)
2894                                 return;
2895                         Xstrdup_a(tmp1, msginfo->from, return);
2896                         extract_address(tmp1);
2897                         if (to_all || to_sender ||
2898                             !account_find_from_address(tmp1))
2899                                 compose_entry_append(compose,
2900                                  (compose->replyto && !to_sender)
2901                                           ? compose->replyto :
2902                                           msginfo->from ? msginfo->from : "",
2903                                           COMPOSE_TO);
2904                         else if (!to_all && !to_sender) {
2905                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2906                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2907                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2908                                         if (compose->replyto) {
2909                                                 compose_entry_append(compose,
2910                                                         compose->replyto,
2911                                                         COMPOSE_TO);
2912                                         } else {
2913                                                 compose_entry_append(compose,
2914                                                           msginfo->from ? msginfo->from : "",
2915                                                           COMPOSE_TO);
2916                                         }
2917                                 } else {
2918                                         /* replying to own mail, use original recp */
2919                                         compose_entry_append(compose,
2920                                                   msginfo->to ? msginfo->to : "",
2921                                                   COMPOSE_TO);
2922                                         compose_entry_append(compose,
2923                                                   msginfo->cc ? msginfo->cc : "",
2924                                                   COMPOSE_CC);
2925                                 }
2926                         }
2927                 }
2928         } else {
2929                 if (to_sender || (compose->followup_to && 
2930                         !strncmp(compose->followup_to, "poster", 6)))
2931                         compose_entry_append
2932                                 (compose, 
2933                                  (compose->replyto ? compose->replyto :
2934                                         msginfo->from ? msginfo->from : ""),
2935                                  COMPOSE_TO);
2936                                  
2937                 else if (followup_and_reply_to || to_all) {
2938                         compose_entry_append
2939                                 (compose,
2940                                  (compose->replyto ? compose->replyto :
2941                                  msginfo->from ? msginfo->from : ""),
2942                                  COMPOSE_TO);                           
2943                 
2944                         compose_entry_append
2945                                 (compose,
2946                                  compose->followup_to ? compose->followup_to :
2947                                  compose->newsgroups ? compose->newsgroups : "",
2948                                  COMPOSE_NEWSGROUPS);
2949                 } 
2950                 else 
2951                         compose_entry_append
2952                                 (compose,
2953                                  compose->followup_to ? compose->followup_to :
2954                                  compose->newsgroups ? compose->newsgroups : "",
2955                                  COMPOSE_NEWSGROUPS);
2956         }
2957
2958         if (msginfo->subject && *msginfo->subject) {
2959                 gchar *buf, *buf2;
2960                 gchar *p;
2961
2962                 buf = p = g_strdup(msginfo->subject);
2963                 p += subject_get_prefix_length(p);
2964                 memmove(buf, p, strlen(p) + 1);
2965
2966                 buf2 = g_strdup_printf("Re: %s", buf);
2967                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2968
2969                 g_free(buf2);
2970                 g_free(buf);
2971         } else
2972                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2973
2974         if (to_ml && compose->ml_post) return;
2975         if (!to_all || compose->account->protocol == A_NNTP) return;
2976
2977         if (compose->replyto) {
2978                 Xstrdup_a(replyto, compose->replyto, return);
2979                 extract_address(replyto);
2980         }
2981         if (msginfo->from) {
2982                 Xstrdup_a(from, msginfo->from, return);
2983                 extract_address(from);
2984         }
2985
2986         if (replyto && from)
2987                 cc_list = address_list_append_with_comments(cc_list, from);
2988         if (to_all && msginfo->folder && 
2989             msginfo->folder->prefs->enable_default_reply_to)
2990                 cc_list = address_list_append_with_comments(cc_list,
2991                                 msginfo->folder->prefs->default_reply_to);
2992         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2993         cc_list = address_list_append_with_comments(cc_list, compose->cc);
2994
2995         to_table = g_hash_table_new(g_str_hash, g_str_equal);
2996         if (replyto)
2997                 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2998         if (compose->account) {
2999                 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3000                                     GINT_TO_POINTER(1));
3001         }
3002         /* remove address on To: and that of current account */
3003         for (cur = cc_list; cur != NULL; ) {
3004                 GSList *next = cur->next;
3005                 gchar *addr;
3006
3007                 addr = g_utf8_strdown(cur->data, -1);
3008                 extract_address(addr);
3009
3010                 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3011                         cc_list = g_slist_remove(cc_list, cur->data);
3012                 else
3013                         g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3014
3015                 cur = next;
3016         }
3017         hash_free_strings(to_table);
3018         g_hash_table_destroy(to_table);
3019
3020         if (cc_list) {
3021                 for (cur = cc_list; cur != NULL; cur = cur->next)
3022                         compose_entry_append(compose, (gchar *)cur->data,
3023                                              COMPOSE_CC);
3024                 slist_free_strings(cc_list);
3025                 g_slist_free(cc_list);
3026         }
3027
3028 }
3029
3030 #define SET_ENTRY(entry, str) \
3031 { \
3032         if (str && *str) \
3033                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3034 }
3035
3036 #define SET_ADDRESS(type, str) \
3037 { \
3038         if (str && *str) \
3039                 compose_entry_append(compose, str, type); \
3040 }
3041
3042 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3043 {
3044         g_return_if_fail(msginfo != NULL);
3045
3046         SET_ENTRY(subject_entry, msginfo->subject);
3047         SET_ENTRY(from_name, msginfo->from);
3048         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3049         SET_ADDRESS(COMPOSE_CC, compose->cc);
3050         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3051         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3052         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3053         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3054
3055         compose_update_priority_menu_item(compose);
3056         compose_update_privacy_system_menu_item(compose, FALSE);
3057         compose_show_first_last_header(compose, TRUE);
3058 }
3059
3060 #undef SET_ENTRY
3061 #undef SET_ADDRESS
3062
3063 static void compose_insert_sig(Compose *compose, gboolean replace)
3064 {
3065         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3066         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3067         GtkTextMark *mark;
3068         GtkTextIter iter, iter_end;
3069         gint cur_pos;
3070         gboolean prev_autowrap;
3071         gboolean found = FALSE;
3072         gboolean exists = FALSE;
3073         
3074         g_return_if_fail(compose->account != NULL);
3075
3076         BLOCK_WRAP();
3077
3078         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3079                                         G_CALLBACK(compose_changed_cb),
3080                                         compose);
3081         
3082         mark = gtk_text_buffer_get_insert(buffer);
3083         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3084         cur_pos = gtk_text_iter_get_offset (&iter);
3085
3086         gtk_text_buffer_get_end_iter(buffer, &iter);
3087
3088         exists = (compose->sig_str != NULL);
3089
3090         if (replace) {
3091                 GtkTextIter first_iter, start_iter, end_iter;
3092
3093                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3094
3095                 if (!exists || compose->sig_str[0] == '\0')
3096                         found = FALSE;
3097                 else
3098                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3099                                         compose->signature_tag);
3100
3101                 if (found) {
3102                         /* include previous \n\n */
3103                         gtk_text_iter_backward_chars(&first_iter, 2);
3104                         start_iter = first_iter;
3105                         end_iter = first_iter;
3106                         /* skip re-start */
3107                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3108                                         compose->signature_tag);
3109                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3110                                         compose->signature_tag);
3111                         if (found) {
3112                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3113                                 iter = start_iter;
3114                         }
3115                 } 
3116         } 
3117
3118         g_free(compose->sig_str);
3119         compose->sig_str = compose_get_signature_str(compose);
3120
3121         cur_pos = gtk_text_iter_get_offset(&iter);
3122
3123         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3124                 g_free(compose->sig_str);
3125                 compose->sig_str = NULL;
3126         } else {
3127                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3128                 /* remove \n\n */
3129                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3130                 gtk_text_iter_forward_chars(&iter, 2);
3131                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3132                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3133
3134                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3135                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3136         }
3137         /* put the cursor where it should be 
3138          * either where the quote_fmt says, either before the signature */
3139         if (compose->set_cursor_pos < 0)
3140                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3141         else
3142                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3143                         compose->set_cursor_pos);
3144                 
3145         gtk_text_buffer_place_cursor(buffer, &iter);
3146         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3147                                         G_CALLBACK(compose_changed_cb),
3148                                         compose);
3149                 
3150         UNBLOCK_WRAP();
3151 }
3152
3153 static gchar *compose_get_signature_str(Compose *compose)
3154 {
3155         gchar *sig_body = NULL;
3156         gchar *sig_str = NULL;
3157         gchar *utf8_sig_str = NULL;
3158
3159         g_return_val_if_fail(compose->account != NULL, NULL);
3160
3161         if (!compose->account->sig_path)
3162                 return NULL;
3163
3164         if (compose->account->sig_type == SIG_FILE) {
3165                 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3166                         g_warning("can't open signature file: %s\n",
3167                                   compose->account->sig_path);
3168                         return NULL;
3169                 }
3170         }
3171
3172         if (compose->account->sig_type == SIG_COMMAND)
3173                 sig_body = get_command_output(compose->account->sig_path);
3174         else {
3175                 gchar *tmp;
3176
3177                 tmp = file_read_to_str(compose->account->sig_path);
3178                 if (!tmp)
3179                         return NULL;
3180                 sig_body = normalize_newlines(tmp);
3181                 g_free(tmp);
3182         }
3183
3184         if (compose->account->sig_sep) {
3185                 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3186                                       NULL);
3187                 g_free(sig_body);
3188         } else
3189                 sig_str = g_strconcat("\n\n", sig_body, NULL);
3190
3191         if (sig_str) {
3192                 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3193                         utf8_sig_str = sig_str;
3194                 else {
3195                         utf8_sig_str = conv_codeset_strdup
3196                                 (sig_str, conv_get_locale_charset_str_no_utf8(),
3197                                  CS_INTERNAL);
3198                         g_free(sig_str);
3199                 }
3200         }
3201
3202         return utf8_sig_str;
3203 }
3204
3205 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3206 {
3207         GtkTextView *text;
3208         GtkTextBuffer *buffer;
3209         GtkTextMark *mark;
3210         GtkTextIter iter;
3211         const gchar *cur_encoding;
3212         gchar buf[BUFFSIZE];
3213         gint len;
3214         FILE *fp;
3215         gboolean prev_autowrap;
3216         gboolean badtxt = FALSE;
3217
3218         g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3219
3220         if ((fp = g_fopen(file, "rb")) == NULL) {
3221                 FILE_OP_ERROR(file, "fopen");
3222                 return COMPOSE_INSERT_READ_ERROR;
3223         }
3224
3225         prev_autowrap = compose->autowrap;
3226         compose->autowrap = FALSE;
3227
3228         text = GTK_TEXT_VIEW(compose->text);
3229         buffer = gtk_text_view_get_buffer(text);
3230         mark = gtk_text_buffer_get_insert(buffer);
3231         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3232
3233         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3234                                         G_CALLBACK(text_inserted),
3235                                         compose);
3236
3237         cur_encoding = conv_get_locale_charset_str_no_utf8();
3238
3239         while (fgets(buf, sizeof(buf), fp) != NULL) {
3240                 gchar *str;
3241
3242                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3243                         str = g_strdup(buf);
3244                 else
3245                         str = conv_codeset_strdup
3246                                 (buf, cur_encoding, CS_INTERNAL);
3247                 if (!str) continue;
3248
3249                 /* strip <CR> if DOS/Windows file,
3250                    replace <CR> with <LF> if Macintosh file. */
3251                 strcrchomp(str);
3252                 len = strlen(str);
3253                 if (len > 0 && str[len - 1] != '\n') {
3254                         while (--len >= 0)
3255                                 if (str[len] == '\r') str[len] = '\n';
3256                 }
3257
3258                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3259                 g_free(str);
3260         }
3261
3262         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3263                                           G_CALLBACK(text_inserted),
3264                                           compose);
3265         compose->autowrap = prev_autowrap;
3266         if (compose->autowrap)
3267                 compose_wrap_all(compose);
3268
3269         fclose(fp);
3270
3271         if (badtxt)
3272                 return COMPOSE_INSERT_INVALID_CHARACTER;
3273         else 
3274                 return COMPOSE_INSERT_SUCCESS;
3275 }
3276
3277 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3278                                   const gchar *filename,
3279                                   const gchar *content_type)
3280 {
3281         AttachInfo *ainfo;
3282         GtkTreeIter iter;
3283         FILE *fp;
3284         off_t size;
3285         GAuto *auto_ainfo;
3286         gchar *size_text;
3287         GtkListStore *store;
3288         gchar *name;
3289         gboolean has_binary = FALSE;
3290
3291         if (!is_file_exist(file)) {
3292                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3293                 gboolean result = FALSE;
3294                 if (file_from_uri && is_file_exist(file_from_uri)) {
3295                         result = compose_attach_append(
3296                                                 compose, file_from_uri,
3297                                                 filename,
3298                                                 content_type);
3299                 }
3300                 g_free(file_from_uri);
3301                 if (result)
3302                         return TRUE;
3303                 alertpanel_error("File %s doesn't exist\n", filename);
3304                 return FALSE;
3305         }
3306         if ((size = get_file_size(file)) < 0) {
3307                 alertpanel_error("Can't get file size of %s\n", filename);
3308                 return FALSE;
3309         }
3310         if (size == 0) {
3311                 alertpanel_error(_("File %s is empty."), filename);
3312                 return FALSE;
3313         }
3314         if ((fp = g_fopen(file, "rb")) == NULL) {
3315                 alertpanel_error(_("Can't read %s."), filename);
3316                 return FALSE;
3317         }
3318         fclose(fp);
3319
3320         ainfo = g_new0(AttachInfo, 1);
3321         auto_ainfo = g_auto_pointer_new_with_free
3322                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3323         ainfo->file = g_strdup(file);
3324
3325         if (content_type) {
3326                 ainfo->content_type = g_strdup(content_type);
3327                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3328                         MsgInfo *msginfo;
3329                         MsgFlags flags = {0, 0};
3330
3331                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3332                                 ainfo->encoding = ENC_7BIT;
3333                         else
3334                                 ainfo->encoding = ENC_8BIT;
3335
3336                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3337                         if (msginfo && msginfo->subject)
3338                                 name = g_strdup(msginfo->subject);
3339                         else
3340                                 name = g_path_get_basename(filename ? filename : file);
3341
3342                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3343
3344                         procmsg_msginfo_free(msginfo);
3345                 } else {
3346                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3347                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3348                         else
3349                                 ainfo->encoding = ENC_BASE64;
3350                         name = g_path_get_basename(filename ? filename : file);
3351                         ainfo->name = g_strdup(name);
3352                 }
3353                 g_free(name);
3354         } else {
3355                 ainfo->content_type = procmime_get_mime_type(file);
3356                 if (!ainfo->content_type) {
3357                         ainfo->content_type =
3358                                 g_strdup("application/octet-stream");
3359                         ainfo->encoding = ENC_BASE64;
3360                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3361                         ainfo->encoding =
3362                                 procmime_get_encoding_for_text_file(file, &has_binary);
3363                 else
3364                         ainfo->encoding = ENC_BASE64;
3365                 name = g_path_get_basename(filename ? filename : file);
3366                 ainfo->name = g_strdup(name);   
3367                 g_free(name);
3368         }
3369
3370         if (ainfo->name != NULL
3371         &&  !strcmp(ainfo->name, ".")) {
3372                 g_free(ainfo->name);
3373                 ainfo->name = NULL;
3374         }
3375
3376         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3377                 g_free(ainfo->content_type);
3378                 ainfo->content_type = g_strdup("application/octet-stream");
3379         }
3380
3381         ainfo->size = size;
3382         size_text = to_human_readable(size);
3383
3384         store = GTK_LIST_STORE(gtk_tree_view_get_model
3385                         (GTK_TREE_VIEW(compose->attach_clist)));
3386                 
3387         gtk_list_store_append(store, &iter);
3388         gtk_list_store_set(store, &iter, 
3389                            COL_MIMETYPE, ainfo->content_type,
3390                            COL_SIZE, size_text,
3391                            COL_NAME, ainfo->name,
3392                            COL_DATA, ainfo,
3393                            COL_AUTODATA, auto_ainfo,
3394                            -1);
3395         
3396         g_auto_pointer_free(auto_ainfo);
3397         compose_attach_update_label(compose);
3398         return TRUE;
3399 }
3400
3401 static void compose_use_signing(Compose *compose, gboolean use_signing)
3402 {
3403         GtkItemFactory *ifactory;
3404         GtkWidget *menuitem = NULL;
3405
3406         compose->use_signing = use_signing;
3407         ifactory = gtk_item_factory_from_widget(compose->menubar);
3408         menuitem = gtk_item_factory_get_item
3409                 (ifactory, "/Options/Sign");
3410         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3411                                        use_signing);
3412 }
3413
3414 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3415 {
3416         GtkItemFactory *ifactory;
3417         GtkWidget *menuitem = NULL;
3418
3419         compose->use_encryption = use_encryption;
3420         ifactory = gtk_item_factory_from_widget(compose->menubar);
3421         menuitem = gtk_item_factory_get_item
3422                 (ifactory, "/Options/Encrypt");
3423
3424         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3425                                        use_encryption);
3426 }
3427
3428 #define NEXT_PART_NOT_CHILD(info)  \
3429 {  \
3430         node = info->node;  \
3431         while (node->children)  \
3432                 node = g_node_last_child(node);  \
3433         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3434 }
3435
3436 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3437 {
3438         MimeInfo *mimeinfo;
3439         MimeInfo *child;
3440         MimeInfo *firsttext = NULL;
3441         MimeInfo *encrypted = NULL;
3442         GNode    *node;
3443         gchar *outfile;
3444         const gchar *partname = NULL;
3445
3446         mimeinfo = procmime_scan_message(msginfo);
3447         if (!mimeinfo) return;
3448
3449         if (mimeinfo->node->children == NULL) {
3450                 procmime_mimeinfo_free_all(mimeinfo);
3451                 return;
3452         }
3453
3454         /* find first content part */
3455         child = (MimeInfo *) mimeinfo->node->children->data;
3456         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3457                 child = (MimeInfo *)child->node->children->data;
3458
3459         if (child->type == MIMETYPE_TEXT) {
3460                 firsttext = child;
3461                 debug_print("First text part found\n");
3462         } else if (compose->mode == COMPOSE_REEDIT &&
3463                  child->type == MIMETYPE_APPLICATION &&
3464                  !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3465                 encrypted = (MimeInfo *)child->node->parent->data;
3466         }
3467      
3468         child = (MimeInfo *) mimeinfo->node->children->data;
3469         while (child != NULL) {
3470                 gint err;
3471
3472                 if (child == encrypted) {
3473                         /* skip this part of tree */
3474                         NEXT_PART_NOT_CHILD(child);
3475                         continue;
3476                 }
3477
3478                 if (child->type == MIMETYPE_MULTIPART) {
3479                         /* get the actual content */
3480                         child = procmime_mimeinfo_next(child);
3481                         continue;
3482                 }
3483                     
3484                 if (child == firsttext) {
3485                         child = procmime_mimeinfo_next(child);
3486                         continue;
3487                 }
3488
3489                 outfile = procmime_get_tmp_file_name(child);
3490                 if ((err = procmime_get_part(outfile, child)) < 0)
3491                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3492                 else {
3493                         gchar *content_type;
3494
3495                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3496
3497                         /* if we meet a pgp signature, we don't attach it, but
3498                          * we force signing. */
3499                         if ((strcmp(content_type, "application/pgp-signature") &&
3500                             strcmp(content_type, "application/pkcs7-signature") &&
3501                             strcmp(content_type, "application/x-pkcs7-signature"))
3502                             || compose->mode == COMPOSE_REDIRECT) {
3503                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3504                                 if (partname == NULL)
3505                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3506                                 if (partname == NULL)
3507                                         partname = "";
3508                                 compose_attach_append(compose, outfile, 
3509                                                       partname, content_type);
3510                         } else {
3511                                 compose_force_signing(compose, compose->account);
3512                         }
3513                         g_free(content_type);
3514                 }
3515                 g_free(outfile);
3516                 NEXT_PART_NOT_CHILD(child);
3517         }
3518         procmime_mimeinfo_free_all(mimeinfo);
3519 }
3520
3521 #undef NEXT_PART_NOT_CHILD
3522
3523
3524
3525 typedef enum {
3526         WAIT_FOR_INDENT_CHAR,
3527         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3528 } IndentState;
3529
3530 /* return indent length, we allow:
3531    indent characters followed by indent characters or spaces/tabs,
3532    alphabets and numbers immediately followed by indent characters,
3533    and the repeating sequences of the above
3534    If quote ends with multiple spaces, only the first one is included. */
3535 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3536                                     const GtkTextIter *start, gint *len)
3537 {
3538         GtkTextIter iter = *start;
3539         gunichar wc;
3540         gchar ch[6];
3541         gint clen;
3542         IndentState state = WAIT_FOR_INDENT_CHAR;
3543         gboolean is_space;
3544         gboolean is_indent;
3545         gint alnum_count = 0;
3546         gint space_count = 0;
3547         gint quote_len = 0;
3548
3549         if (prefs_common.quote_chars == NULL) {
3550                 return 0 ;
3551         }
3552
3553         while (!gtk_text_iter_ends_line(&iter)) {
3554                 wc = gtk_text_iter_get_char(&iter);
3555                 if (g_unichar_iswide(wc))
3556                         break;
3557                 clen = g_unichar_to_utf8(wc, ch);
3558                 if (clen != 1)
3559                         break;
3560
3561                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3562                 is_space = g_unichar_isspace(wc);
3563
3564                 if (state == WAIT_FOR_INDENT_CHAR) {
3565                         if (!is_indent && !g_unichar_isalnum(wc))
3566                                 break;
3567                         if (is_indent) {
3568                                 quote_len += alnum_count + space_count + 1;
3569                                 alnum_count = space_count = 0;
3570                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3571                         } else
3572                                 alnum_count++;
3573                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3574                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3575                                 break;
3576                         if (is_space)
3577                                 space_count++;
3578                         else if (is_indent) {
3579                                 quote_len += alnum_count + space_count + 1;
3580                                 alnum_count = space_count = 0;
3581                         } else {
3582                                 alnum_count++;
3583                                 state = WAIT_FOR_INDENT_CHAR;
3584                         }
3585                 }
3586
3587                 gtk_text_iter_forward_char(&iter);
3588         }
3589
3590         if (quote_len > 0 && space_count > 0)
3591                 quote_len++;
3592
3593         if (len)
3594                 *len = quote_len;
3595
3596         if (quote_len > 0) {
3597                 iter = *start;
3598                 gtk_text_iter_forward_chars(&iter, quote_len);
3599                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3600         }
3601
3602         return NULL;
3603 }
3604
3605 /* return TRUE if the line is itemized */
3606 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3607                                     const GtkTextIter *start)
3608 {
3609         GtkTextIter iter = *start;
3610         gunichar wc;
3611         gchar ch[6];
3612         gint clen;
3613
3614         if (gtk_text_iter_ends_line(&iter))
3615                 return FALSE;
3616
3617         while (1) {
3618                 wc = gtk_text_iter_get_char(&iter);
3619                 if (!g_unichar_isspace(wc))
3620                         break;
3621                 gtk_text_iter_forward_char(&iter);
3622                 if (gtk_text_iter_ends_line(&iter))
3623                         return FALSE;
3624         }
3625
3626         clen = g_unichar_to_utf8(wc, ch);
3627         if (clen != 1)
3628                 return FALSE;
3629
3630         if (!strchr("*-+", ch[0]))
3631                 return FALSE;
3632
3633         gtk_text_iter_forward_char(&iter);
3634         if (gtk_text_iter_ends_line(&iter))
3635                 return FALSE;
3636         wc = gtk_text_iter_get_char(&iter);
3637         if (g_unichar_isspace(wc))
3638                 return TRUE;
3639
3640         return FALSE;
3641 }
3642
3643 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3644                                            const GtkTextIter *start,
3645                                            GtkTextIter *break_pos,
3646                                            gint max_col,
3647                                            gint quote_len)
3648 {
3649         GtkTextIter iter = *start, line_end = *start;
3650         PangoLogAttr *attrs;
3651         gchar *str;
3652         gchar *p;
3653         gint len;
3654         gint i;
3655         gint col = 0;
3656         gint pos = 0;
3657         gboolean can_break = FALSE;
3658         gboolean do_break = FALSE;
3659         gboolean was_white = FALSE;
3660         gboolean prev_dont_break = FALSE;
3661
3662         gtk_text_iter_forward_to_line_end(&line_end);
3663         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3664         len = g_utf8_strlen(str, -1);
3665         
3666         if (len == 0) {
3667                 g_free(str);
3668                 g_warning("compose_get_line_break_pos: len = 0!\n");
3669                 return FALSE;
3670         }
3671
3672         /* g_print("breaking line: %d: %s (len = %d)\n",
3673                 gtk_text_iter_get_line(&iter), str, len); */
3674
3675         attrs = g_new(PangoLogAttr, len + 1);
3676
3677         pango_default_break(str, -1, NULL, attrs, len + 1);
3678
3679         p = str;
3680
3681         /* skip quote and leading spaces */
3682         for (i = 0; *p != '\0' && i < len; i++) {
3683                 gunichar wc;
3684
3685                 wc = g_utf8_get_char(p);
3686                 if (i >= quote_len && !g_unichar_isspace(wc))
3687                         break;
3688                 if (g_unichar_iswide(wc))
3689                         col += 2;
3690                 else if (*p == '\t')
3691                         col += 8;
3692                 else
3693                         col++;
3694                 p = g_utf8_next_char(p);
3695         }
3696
3697         for (; *p != '\0' && i < len; i++) {
3698                 PangoLogAttr *attr = attrs + i;
3699                 gunichar wc;
3700                 gint uri_len;
3701
3702                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3703                         pos = i;
3704                 
3705                 was_white = attr->is_white;
3706
3707                 /* don't wrap URI */
3708                 if ((uri_len = get_uri_len(p)) > 0) {
3709                         col += uri_len;
3710                         if (pos > 0 && col > max_col) {
3711                                 do_break = TRUE;
3712                                 break;
3713                         }
3714                         i += uri_len - 1;
3715                         p += uri_len;
3716                         can_break = TRUE;
3717                         continue;
3718                 }
3719
3720                 wc = g_utf8_get_char(p);
3721                 if (g_unichar_iswide(wc)) {
3722                         col += 2;
3723                         if (prev_dont_break && can_break && attr->is_line_break)
3724                                 pos = i;
3725                 } else if (*p == '\t')
3726                         col += 8;
3727                 else
3728                         col++;
3729                 if (pos > 0 && col > max_col) {
3730                         do_break = TRUE;
3731                         break;
3732                 }
3733
3734                 if (*p == '-' || *p == '/')
3735                         prev_dont_break = TRUE;
3736                 else
3737                         prev_dont_break = FALSE;
3738
3739                 p = g_utf8_next_char(p);
3740                 can_break = TRUE;
3741         }
3742
3743         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3744
3745         g_free(attrs);
3746         g_free(str);
3747
3748         *break_pos = *start;
3749         gtk_text_iter_set_line_offset(break_pos, pos);
3750
3751         return do_break;
3752 }
3753
3754 static gboolean compose_join_next_line(Compose *compose,
3755                                        GtkTextBuffer *buffer,
3756                                        GtkTextIter *iter,
3757                                        const gchar *quote_str)
3758 {
3759         GtkTextIter iter_ = *iter, cur, prev, next, end;
3760         PangoLogAttr attrs[3];
3761         gchar *str;
3762         gchar *next_quote_str;
3763         gunichar wc1, wc2;
3764         gint quote_len;
3765         gboolean keep_cursor = FALSE;
3766
3767         if (!gtk_text_iter_forward_line(&iter_) ||
3768             gtk_text_iter_ends_line(&iter_))
3769                 return FALSE;
3770
3771         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3772
3773         if ((quote_str || next_quote_str) &&
3774             strcmp2(quote_str, next_quote_str) != 0) {
3775                 g_free(next_quote_str);
3776                 return FALSE;
3777         }
3778         g_free(next_quote_str);
3779
3780         end = iter_;
3781         if (quote_len > 0) {
3782                 gtk_text_iter_forward_chars(&end, quote_len);
3783                 if (gtk_text_iter_ends_line(&end))
3784                         return FALSE;
3785         }
3786
3787         /* don't join itemized lines */
3788         if (compose_is_itemized(buffer, &end))
3789                 return FALSE;
3790
3791         /* don't join signature separator */
3792         if (compose_is_sig_separator(compose, buffer, &iter_))
3793                 return FALSE;
3794
3795         /* delete quote str */
3796         if (quote_len > 0)
3797                 gtk_text_buffer_delete(buffer, &iter_, &end);
3798
3799         /* don't join line breaks put by the user */
3800         prev = cur = iter_;
3801         gtk_text_iter_backward_char(&cur);
3802         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3803                 gtk_text_iter_forward_char(&cur);
3804                 *iter = cur;
3805                 return FALSE;
3806         }
3807         gtk_text_iter_forward_char(&cur);
3808         /* delete linebreak and extra spaces */
3809         while (gtk_text_iter_backward_char(&cur)) {
3810                 wc1 = gtk_text_iter_get_char(&cur);
3811                 if (!g_unichar_isspace(wc1))
3812                         break;
3813                 prev = cur;
3814         }
3815         next = cur = iter_;
3816         while (!gtk_text_iter_ends_line(&cur)) {
3817                 wc1 = gtk_text_iter_get_char(&cur);
3818                 if (!g_unichar_isspace(wc1))
3819                         break;
3820                 gtk_text_iter_forward_char(&cur);
3821                 next = cur;
3822         }
3823         if (!gtk_text_iter_equal(&prev, &next)) {
3824                 GtkTextMark *mark;
3825
3826                 mark = gtk_text_buffer_get_insert(buffer);
3827                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3828                 if (gtk_text_iter_equal(&prev, &cur))
3829                         keep_cursor = TRUE;
3830                 gtk_text_buffer_delete(buffer, &prev, &next);
3831         }
3832         iter_ = prev;
3833
3834         /* insert space if required */
3835         gtk_text_iter_backward_char(&prev);
3836         wc1 = gtk_text_iter_get_char(&prev);
3837         wc2 = gtk_text_iter_get_char(&next);
3838         gtk_text_iter_forward_char(&next);
3839         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3840         pango_default_break(str, -1, NULL, attrs, 3);
3841         if (!attrs[1].is_line_break ||
3842             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3843                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3844                 if (keep_cursor) {
3845                         gtk_text_iter_backward_char(&iter_);
3846                         gtk_text_buffer_place_cursor(buffer, &iter_);
3847                 }
3848         }
3849         g_free(str);
3850
3851         *iter = iter_;
3852         return TRUE;
3853 }
3854
3855 #define ADD_TXT_POS(bp_, ep_, pti_) \
3856         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3857                 last = last->next; \
3858                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3859                 last->next = NULL; \
3860         } else { \
3861                 g_warning("alloc error scanning URIs\n"); \
3862         }
3863
3864 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3865 {
3866         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3867         GtkTextBuffer *buffer;
3868         GtkTextIter iter, break_pos, end_of_line;
3869         gchar *quote_str = NULL;
3870         gint quote_len;
3871         gboolean wrap_quote = prefs_common.linewrap_quote;
3872         gboolean prev_autowrap = compose->autowrap;
3873         gint startq_offset = -1, noq_offset = -1;
3874         gint uri_start = -1, uri_stop = -1;
3875         gint nouri_start = -1, nouri_stop = -1;
3876         gint num_blocks = 0;
3877         gint quotelevel = -1;
3878         gboolean modified = force;
3879         gboolean removed = FALSE;
3880         gboolean modified_before_remove = FALSE;
3881         gint lines = 0;
3882         gboolean start = TRUE;
3883
3884         if (force) {
3885                 modified = TRUE;
3886         }
3887         if (compose->draft_timeout_tag == -2) {
3888                 modified = TRUE;
3889         }
3890
3891         compose->autowrap = FALSE;
3892
3893         buffer = gtk_text_view_get_buffer(text);
3894         undo_wrapping(compose->undostruct, TRUE);
3895         if (par_iter) {
3896                 iter = *par_iter;
3897         } else {
3898                 GtkTextMark *mark;
3899                 mark = gtk_text_buffer_get_insert(buffer);
3900                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3901         }
3902
3903
3904         if (compose->draft_timeout_tag == -2) {
3905                 if (gtk_text_iter_ends_line(&iter)) {
3906                         while (gtk_text_iter_ends_line(&iter) &&
3907                                gtk_text_iter_forward_line(&iter))
3908                                 ;
3909                 } else {
3910                         while (gtk_text_iter_backward_line(&iter)) {
3911                                 if (gtk_text_iter_ends_line(&iter)) {
3912                                         gtk_text_iter_forward_line(&iter);
3913                                         break;
3914                                 }
3915                         }
3916                 }
3917         } else {
3918                 /* move to line start */
3919                 gtk_text_iter_set_line_offset(&iter, 0);
3920         }
3921         /* go until paragraph end (empty line) */
3922         while (start || !gtk_text_iter_ends_line(&iter)) {
3923                 gchar *scanpos = NULL;
3924                 /* parse table - in order of priority */
3925                 struct table {
3926                         const gchar *needle; /* token */
3927
3928                         /* token search function */
3929                         gchar    *(*search)     (const gchar *haystack,
3930                                                  const gchar *needle);
3931                         /* part parsing function */
3932                         gboolean  (*parse)      (const gchar *start,
3933                                                  const gchar *scanpos,
3934                                                  const gchar **bp_,
3935                                                  const gchar **ep_,
3936                                                  gboolean hdr);
3937                         /* part to URI function */
3938                         gchar    *(*build_uri)  (const gchar *bp,
3939                                                  const gchar *ep);
3940                 };
3941
3942                 static struct table parser[] = {
3943                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
3944                         {"https://", strcasestr, get_uri_part,   make_uri_string},
3945                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
3946                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
3947                         {"www.",     strcasestr, get_uri_part,   make_http_string},
3948                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
3949                         {"@",        strcasestr, get_email_part, make_email_string}
3950                 };
3951                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
3952                 gint last_index = PARSE_ELEMS;
3953                 gint  n;
3954                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
3955                 gint walk_pos;
3956                 
3957                 start = FALSE;
3958                 if (!prev_autowrap && num_blocks == 0) {
3959                         num_blocks++;
3960                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3961                                         G_CALLBACK(text_inserted),
3962                                         compose);
3963                 }
3964                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
3965                         goto colorize;
3966
3967                 uri_start = uri_stop = -1;
3968                 quote_len = 0;
3969                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
3970
3971                 if (quote_str) {
3972                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
3973                         if (startq_offset == -1) 
3974                                 startq_offset = gtk_text_iter_get_offset(&iter);
3975                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
3976                         if (quotelevel > 2) {
3977                                 /* recycle colors */
3978                                 if (prefs_common.recycle_quote_colors)
3979                                         quotelevel %= 3;
3980                                 else
3981                                         quotelevel = 2;
3982                         }
3983                         if (!wrap_quote) {
3984                                 goto colorize;
3985                         }
3986                 } else {
3987                         if (startq_offset == -1)
3988                                 noq_offset = gtk_text_iter_get_offset(&iter);
3989                         quotelevel = -1;
3990                 }
3991
3992                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
3993                         goto colorize;
3994                 }
3995                 if (gtk_text_iter_ends_line(&iter)) {
3996                         goto colorize;
3997                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
3998                                                prefs_common.linewrap_len,
3999                                                quote_len)) {
4000                         GtkTextIter prev, next, cur;
4001
4002                         if (prev_autowrap != FALSE || force) {
4003                                 compose->automatic_break = TRUE;
4004                                 modified = TRUE;
4005                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4006                                 compose->automatic_break = FALSE;
4007                         } else if (quote_str && wrap_quote) {
4008                                 compose->automatic_break = TRUE;
4009                                 modified = TRUE;
4010                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4011                                 compose->automatic_break = FALSE;
4012                         } else 
4013                                 goto colorize;
4014                         /* remove trailing spaces */
4015                         cur = break_pos;
4016                         gtk_text_iter_backward_char(&cur);
4017                         prev = next = cur;
4018                         while (!gtk_text_iter_starts_line(&cur)) {
4019                                 gunichar wc;
4020
4021                                 gtk_text_iter_backward_char(&cur);
4022                                 wc = gtk_text_iter_get_char(&cur);
4023                                 if (!g_unichar_isspace(wc))
4024                                         break;
4025                                 prev = cur;
4026                         }
4027                         if (!gtk_text_iter_equal(&prev, &next)) {
4028                                 gtk_text_buffer_delete(buffer, &prev, &next);
4029                                 break_pos = next;
4030                                 gtk_text_iter_forward_char(&break_pos);
4031                         }
4032
4033                         if (quote_str)
4034                                 gtk_text_buffer_insert(buffer, &break_pos,
4035                                                        quote_str, -1);
4036
4037                         iter = break_pos;
4038                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4039
4040                         /* move iter to current line start */
4041                         gtk_text_iter_set_line_offset(&iter, 0);
4042                         if (quote_str) {
4043                                 g_free(quote_str);
4044                                 quote_str = NULL;
4045                         }
4046                         continue;       
4047                 } else {
4048                         /* move iter to next line start */
4049                         iter = break_pos;
4050                         lines++;
4051                 }
4052
4053 colorize:
4054                 if (!prev_autowrap && num_blocks > 0) {
4055                         num_blocks--;
4056                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4057                                         G_CALLBACK(text_inserted),
4058                                         compose);
4059                 }
4060                 end_of_line = iter;
4061                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4062                         gtk_text_iter_forward_char(&end_of_line);
4063                 }
4064                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4065
4066                 nouri_start = gtk_text_iter_get_offset(&iter);
4067                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4068
4069                 walk_pos = gtk_text_iter_get_offset(&iter);
4070                 /* FIXME: this looks phony. scanning for anything in the parse table */
4071                 for (n = 0; n < PARSE_ELEMS; n++) {
4072                         gchar *tmp;
4073
4074                         tmp = parser[n].search(walk, parser[n].needle);
4075                         if (tmp) {
4076                                 if (scanpos == NULL || tmp < scanpos) {
4077                                         scanpos = tmp;
4078                                         last_index = n;
4079                                 }
4080                         }                                       
4081                 }
4082
4083                 bp = ep = 0;
4084                 if (scanpos) {
4085                         /* check if URI can be parsed */
4086                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4087                                         (const gchar **)&ep, FALSE)
4088                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4089                                         walk = ep;
4090                         } else
4091                                 walk = scanpos +
4092                                         strlen(parser[last_index].needle);
4093                 } 
4094                 if (bp && ep) {
4095                         uri_start = walk_pos + (bp - o_walk);
4096                         uri_stop  = walk_pos + (ep - o_walk);
4097                 }
4098                 g_free(o_walk);
4099                 o_walk = NULL;
4100                 gtk_text_iter_forward_line(&iter);
4101                 g_free(quote_str);
4102                 quote_str = NULL;
4103                 if (startq_offset != -1) {
4104                         GtkTextIter startquote, endquote;
4105                         gtk_text_buffer_get_iter_at_offset(
4106                                 buffer, &startquote, startq_offset);
4107                         endquote = iter;
4108
4109                         switch (quotelevel) {
4110                         case 0: 
4111                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4112                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4113                                         gtk_text_buffer_apply_tag_by_name(
4114                                                 buffer, "quote0", &startquote, &endquote);
4115                                         gtk_text_buffer_remove_tag_by_name(
4116                                                 buffer, "quote1", &startquote, &endquote);
4117                                         gtk_text_buffer_remove_tag_by_name(
4118                                                 buffer, "quote2", &startquote, &endquote);
4119                                         modified = TRUE;
4120                                 }
4121                                 break;
4122                         case 1: 
4123                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4124                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4125                                         gtk_text_buffer_apply_tag_by_name(
4126                                                 buffer, "quote1", &startquote, &endquote);
4127                                         gtk_text_buffer_remove_tag_by_name(
4128                                                 buffer, "quote0", &startquote, &endquote);
4129                                         gtk_text_buffer_remove_tag_by_name(
4130                                                 buffer, "quote2", &startquote, &endquote);
4131                                         modified = TRUE;
4132                                 }
4133                                 break;
4134                         case 2: 
4135                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4136                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4137                                         gtk_text_buffer_apply_tag_by_name(
4138                                                 buffer, "quote2", &startquote, &endquote);
4139                                         gtk_text_buffer_remove_tag_by_name(
4140                                                 buffer, "quote0", &startquote, &endquote);
4141                                         gtk_text_buffer_remove_tag_by_name(
4142                                                 buffer, "quote1", &startquote, &endquote);
4143                                         modified = TRUE;
4144                                 }
4145                                 break;
4146                         }
4147                         startq_offset = -1;
4148                 } else if (noq_offset != -1) {
4149                         GtkTextIter startnoquote, endnoquote;
4150                         gtk_text_buffer_get_iter_at_offset(
4151                                 buffer, &startnoquote, noq_offset);
4152                         endnoquote = iter;
4153
4154                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4155                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4156                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4157                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4158                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4159                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4160                                 gtk_text_buffer_remove_tag_by_name(
4161                                         buffer, "quote0", &startnoquote, &endnoquote);
4162                                 gtk_text_buffer_remove_tag_by_name(
4163                                         buffer, "quote1", &startnoquote, &endnoquote);
4164                                 gtk_text_buffer_remove_tag_by_name(
4165                                         buffer, "quote2", &startnoquote, &endnoquote);
4166                                 modified = TRUE;
4167                         }
4168                         noq_offset = -1;
4169                 }
4170                 
4171                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4172                         GtkTextIter nouri_start_iter, nouri_end_iter;
4173                         gtk_text_buffer_get_iter_at_offset(
4174                                 buffer, &nouri_start_iter, nouri_start);
4175                         gtk_text_buffer_get_iter_at_offset(
4176                                 buffer, &nouri_end_iter, nouri_stop);
4177                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4178                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4179                                 gtk_text_buffer_remove_tag_by_name(
4180                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4181                                 modified_before_remove = modified;
4182                                 modified = TRUE;
4183                                 removed = TRUE;
4184                         }
4185                 }
4186                 if (uri_start >= 0 && uri_stop > 0) {
4187                         GtkTextIter uri_start_iter, uri_end_iter, back;
4188                         gtk_text_buffer_get_iter_at_offset(
4189                                 buffer, &uri_start_iter, uri_start);
4190                         gtk_text_buffer_get_iter_at_offset(
4191                                 buffer, &uri_end_iter, uri_stop);
4192                         back = uri_end_iter;
4193                         gtk_text_iter_backward_char(&back);
4194                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4195                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4196                                 gtk_text_buffer_apply_tag_by_name(
4197                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4198                                 modified = TRUE;
4199                                 if (removed && !modified_before_remove) {
4200                                         modified = FALSE;
4201                                 } 
4202                         }
4203                 }
4204                 if (!modified) {
4205                         debug_print("not modified, out after %d lines\n", lines);
4206                         goto end;
4207                 }
4208         }
4209
4210         debug_print("modified, out after %d lines\n", lines);
4211 end:
4212         if (par_iter)
4213                 *par_iter = iter;
4214         undo_wrapping(compose->undostruct, FALSE);
4215         compose->autowrap = prev_autowrap;
4216         
4217         return modified;
4218 }
4219
4220 void compose_action_cb(void *data)
4221 {
4222         Compose *compose = (Compose *)data;
4223         compose_wrap_all(compose);
4224 }
4225
4226 static void compose_wrap_all(Compose *compose)
4227 {
4228         compose_wrap_all_full(compose, FALSE);
4229 }
4230
4231 static void compose_wrap_all_full(Compose *compose, gboolean force)
4232 {
4233         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4234         GtkTextBuffer *buffer;
4235         GtkTextIter iter;
4236         gboolean modified = TRUE;
4237
4238         buffer = gtk_text_view_get_buffer(text);
4239
4240         gtk_text_buffer_get_start_iter(buffer, &iter);
4241         while (!gtk_text_iter_is_end(&iter) && modified)
4242                 modified = compose_beautify_paragraph(compose, &iter, force);
4243
4244 }
4245
4246 static void compose_set_title(Compose *compose)
4247 {
4248         gchar *str;
4249         gchar *edited;
4250         gchar *subject;
4251         
4252         edited = compose->modified ? _(" [Edited]") : "";
4253         
4254         subject = gtk_editable_get_chars(
4255                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4256
4257 #ifndef MAEMO
4258         if (subject && strlen(subject))
4259                 str = g_strdup_printf(_("%s - Compose message%s"),
4260                                       subject, edited); 
4261         else
4262                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4263 #else
4264         str = g_strdup(_("Compose message"));
4265 #endif
4266
4267         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4268         g_free(str);
4269         g_free(subject);
4270 }
4271
4272 /**
4273  * compose_current_mail_account:
4274  * 
4275  * Find a current mail account (the currently selected account, or the
4276  * default account, if a news account is currently selected).  If a
4277  * mail account cannot be found, display an error message.
4278  * 
4279  * Return value: Mail account, or NULL if not found.
4280  **/
4281 static PrefsAccount *
4282 compose_current_mail_account(void)
4283 {
4284         PrefsAccount *ac;
4285
4286         if (cur_account && cur_account->protocol != A_NNTP)
4287                 ac = cur_account;
4288         else {
4289                 ac = account_get_default();
4290                 if (!ac || ac->protocol == A_NNTP) {
4291                         alertpanel_error(_("Account for sending mail is not specified.\n"
4292                                            "Please select a mail account before sending."));
4293                         return NULL;
4294                 }
4295         }
4296         return ac;
4297 }
4298
4299 #define QUOTE_IF_REQUIRED(out, str)                                     \
4300 {                                                                       \
4301         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4302                 gchar *__tmp;                                           \
4303                 gint len;                                               \
4304                                                                         \
4305                 len = strlen(str) + 3;                                  \
4306                 if ((__tmp = alloca(len)) == NULL) {                    \
4307                         g_warning("can't allocate memory\n");           \
4308                         g_string_free(header, TRUE);                    \
4309                         return NULL;                                    \
4310                 }                                                       \
4311                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4312                 out = __tmp;                                            \
4313         } else {                                                        \
4314                 gchar *__tmp;                                           \
4315                                                                         \
4316                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4317                         g_warning("can't allocate memory\n");           \
4318                         g_string_free(header, TRUE);                    \
4319                         return NULL;                                    \
4320                 } else                                                  \
4321                         strcpy(__tmp, str);                             \
4322                                                                         \
4323                 out = __tmp;                                            \
4324         }                                                               \
4325 }
4326
4327 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4328 {                                                                       \
4329         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4330                 gchar *__tmp;                                           \
4331                 gint len;                                               \
4332                                                                         \
4333                 len = strlen(str) + 3;                                  \
4334                 if ((__tmp = alloca(len)) == NULL) {                    \
4335                         g_warning("can't allocate memory\n");           \
4336                         errret;                                         \
4337                 }                                                       \
4338                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4339                 out = __tmp;                                            \
4340         } else {                                                        \
4341                 gchar *__tmp;                                           \
4342                                                                         \
4343                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4344                         g_warning("can't allocate memory\n");           \
4345                         errret;                                         \
4346                 } else                                                  \
4347                         strcpy(__tmp, str);                             \
4348                                                                         \
4349                 out = __tmp;                                            \
4350         }                                                               \
4351 }
4352
4353 static void compose_select_account(Compose *compose, PrefsAccount *account,
4354                                    gboolean init)
4355 {
4356         GtkItemFactory *ifactory;
4357         gchar *from = NULL;
4358
4359         g_return_if_fail(account != NULL);
4360
4361         compose->account = account;
4362
4363         if (account->name && *account->name) {
4364                 gchar *buf;
4365                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4366                 from = g_strdup_printf("%s <%s>",
4367                                        buf, account->address);
4368                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4369         } else {
4370                 from = g_strdup_printf("<%s>",
4371                                        account->address);
4372                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4373         }
4374
4375         g_free(from);
4376
4377         compose_set_title(compose);
4378
4379         ifactory = gtk_item_factory_from_widget(compose->menubar);
4380
4381         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4382                 menu_set_active(ifactory, "/Options/Sign", TRUE);
4383         else
4384                 menu_set_active(ifactory, "/Options/Sign", FALSE);
4385         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4386                 menu_set_active(ifactory, "/Options/Encrypt", TRUE);
4387         else
4388                 menu_set_active(ifactory, "/Options/Encrypt", FALSE);
4389                                        
4390         activate_privacy_system(compose, account, FALSE);
4391
4392         if (!init && compose->mode != COMPOSE_REDIRECT) {
4393                 undo_block(compose->undostruct);
4394                 compose_insert_sig(compose, TRUE);
4395                 undo_unblock(compose->undostruct);
4396         }
4397
4398 #ifdef USE_ASPELL
4399         /* use account's dict info if set */
4400         if (compose->gtkaspell) {
4401                 if (account->enable_default_dictionary)
4402                         gtkaspell_change_dict(compose->gtkaspell,
4403                                         account->default_dictionary, FALSE);
4404                 if (account->enable_default_alt_dictionary)
4405                         gtkaspell_change_alt_dict(compose->gtkaspell,
4406                                         account->default_alt_dictionary);
4407                 if (account->enable_default_dictionary
4408                         || account->enable_default_alt_dictionary)
4409                         compose_spell_menu_changed(compose);
4410         }
4411 #endif
4412 }
4413
4414 gboolean compose_check_for_valid_recipient(Compose *compose) {
4415         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4416         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4417         gboolean recipient_found = FALSE;
4418         GSList *list;
4419         gchar **strptr;
4420
4421         /* free to and newsgroup list */
4422         slist_free_strings(compose->to_list);
4423         g_slist_free(compose->to_list);
4424         compose->to_list = NULL;
4425                         
4426         slist_free_strings(compose->newsgroup_list);
4427         g_slist_free(compose->newsgroup_list);
4428         compose->newsgroup_list = NULL;
4429
4430         /* search header entries for to and newsgroup entries */
4431         for (list = compose->header_list; list; list = list->next) {
4432                 gchar *header;
4433                 gchar *entry;
4434                 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4435                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4436                 g_strstrip(entry);
4437                 g_strstrip(header);
4438                 if (entry[0] != '\0') {
4439                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4440                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4441                                         compose->to_list = address_list_append(compose->to_list, entry);
4442                                         recipient_found = TRUE;
4443                                 }
4444                         }
4445                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4446                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4447                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4448                                         recipient_found = TRUE;
4449                                 }
4450                         }
4451                 }
4452                 g_free(header);
4453                 g_free(entry);
4454         }
4455         return recipient_found;
4456 }
4457
4458 static gboolean compose_check_for_set_recipients(Compose *compose)
4459 {
4460         if (compose->account->set_autocc && compose->account->auto_cc) {
4461                 gboolean found_other = FALSE;
4462                 GSList *list;
4463                 /* search header entries for to and newsgroup entries */
4464                 for (list = compose->header_list; list; list = list->next) {
4465                         gchar *entry;
4466                         gchar *header;
4467                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4468                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4469                         g_strstrip(entry);
4470                         g_strstrip(header);
4471                         if (strcmp(entry, compose->account->auto_cc)
4472                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4473                                 found_other = TRUE;
4474                                 g_free(entry);
4475                                 break;
4476                         }
4477                         g_free(entry);
4478                         g_free(header);
4479                 }
4480                 if (!found_other) {
4481                         AlertValue aval;
4482                         if (compose->batch) {
4483                                 gtk_widget_show_all(compose->window);
4484                         }
4485                         aval = alertpanel(_("Send"),
4486                                           _("The only recipient is the default CC address. Send anyway?"),
4487                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4488                         if (aval != G_ALERTALTERNATE)
4489                                 return FALSE;
4490                 }
4491         }
4492         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4493                 gboolean found_other = FALSE;
4494                 GSList *list;
4495                 /* search header entries for to and newsgroup entries */
4496                 for (list = compose->header_list; list; list = list->next) {
4497                         gchar *entry;
4498                         gchar *header;
4499                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4500                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4501                         g_strstrip(entry);
4502                         g_strstrip(header);
4503                         if (strcmp(entry, compose->account->auto_bcc)
4504                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4505                                 found_other = TRUE;
4506                                 g_free(entry);
4507                                 break;
4508                         }
4509                         g_free(entry);
4510                         g_free(header);
4511                 }
4512                 if (!found_other) {
4513                         AlertValue aval;
4514                         if (compose->batch) {
4515                                 gtk_widget_show_all(compose->window);
4516                         }
4517                         aval = alertpanel(_("Send"),
4518                                           _("The only recipient is the default BCC address. Send anyway?"),
4519                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4520                         if (aval != G_ALERTALTERNATE)
4521                                 return FALSE;
4522                 }
4523         }
4524         return TRUE;
4525 }
4526
4527 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4528 {
4529         const gchar *str;
4530
4531         if (compose_check_for_valid_recipient(compose) == FALSE) {
4532                 if (compose->batch) {
4533                         gtk_widget_show_all(compose->window);
4534                 }
4535                 alertpanel_error(_("Recipient is not specified."));
4536                 return FALSE;
4537         }
4538
4539         if (compose_check_for_set_recipients(compose) == FALSE) {
4540                 return FALSE;
4541         }
4542
4543         if (!compose->batch) {
4544                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4545                 if (*str == '\0' && check_everything == TRUE && 
4546                     compose->mode != COMPOSE_REDIRECT) {
4547                         AlertValue aval;
4548                         gchar *button_label;
4549                         gchar *message;
4550
4551                         if (compose->sending)
4552                                 button_label = _("+_Send");
4553                         else
4554                                 button_label = _("+_Queue");
4555                         message = g_strdup_printf(_("Subject is empty. %s"),
4556                                         compose->sending?_("Send it anyway?"):
4557                                         _("Queue it anyway?"));
4558
4559                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4560                                           GTK_STOCK_CANCEL, button_label, NULL);
4561                         g_free(message);
4562                         if (aval != G_ALERTALTERNATE)
4563                                 return FALSE;
4564                 }
4565         }
4566
4567         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4568                 return FALSE;
4569
4570         return TRUE;
4571 }
4572
4573 gint compose_send(Compose *compose)
4574 {
4575         gint msgnum;
4576         FolderItem *folder = NULL;
4577         gint val = -1;
4578         gchar *msgpath = NULL;
4579         gboolean discard_window = FALSE;
4580         gchar *errstr = NULL;
4581         gchar *tmsgid = NULL;
4582         MainWindow *mainwin = mainwindow_get_mainwindow();
4583         gboolean queued_removed = FALSE;
4584
4585         if (prefs_common.send_dialog_invisible
4586                         || compose->batch == TRUE)
4587                 discard_window = TRUE;
4588
4589         compose_allow_user_actions (compose, FALSE);
4590         compose->sending = TRUE;
4591
4592         if (compose_check_entries(compose, TRUE) == FALSE) {
4593                 if (compose->batch) {
4594                         gtk_widget_show_all(compose->window);
4595                 }
4596                 goto bail;
4597         }
4598
4599         inc_lock();
4600         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4601
4602         if (val) {
4603                 if (compose->batch) {
4604                         gtk_widget_show_all(compose->window);
4605                 }
4606                 if (val == -4) {
4607                         alertpanel_error(_("Could not queue message for sending:\n\n"
4608                                            "Charset conversion failed."));
4609                 } else if (val == -5) {
4610                         alertpanel_error(_("Could not queue message for sending:\n\n"
4611                                            "Couldn't get recipient encryption key."));
4612                 } else if (val == -6) {
4613                         /* silent error */
4614                 } else if (val == -3) {
4615                         if (privacy_peek_error())
4616                         alertpanel_error(_("Could not queue message for sending:\n\n"
4617                                            "Signature failed: %s"), privacy_get_error());
4618                 } else if (val == -2 && errno != 0) {
4619                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4620                 } else {
4621                         alertpanel_error(_("Could not queue message for sending."));
4622                 }
4623                 goto bail;
4624         }
4625
4626         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4627         if (discard_window) {
4628                 compose->sending = FALSE;
4629                 compose_close(compose);
4630                 /* No more compose access in the normal codepath 
4631                  * after this point! */
4632                 compose = NULL;
4633         }
4634
4635         if (msgnum == 0) {
4636                 alertpanel_error(_("The message was queued but could not be "
4637                                    "sent.\nUse \"Send queued messages\" from "
4638                                    "the main window to retry."));
4639                 if (!discard_window) {
4640                         goto bail;
4641                 }
4642                 inc_unlock();
4643                 g_free(tmsgid);
4644                 return -1;
4645         }
4646         if (msgpath == NULL) {
4647                 msgpath = folder_item_fetch_msg(folder, msgnum);
4648                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4649                 g_free(msgpath);
4650         } else {
4651                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4652                 g_unlink(msgpath);
4653                 g_free(msgpath);
4654         }
4655         if (!discard_window) {
4656                 if (val != 0) {
4657                         if (!queued_removed)
4658                                 folder_item_remove_msg(folder, msgnum);
4659                         folder_item_scan(folder);
4660                         if (tmsgid) {
4661                                 /* make sure we delete that */
4662                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4663                                 if (tmp) {
4664                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4665                                         folder_item_remove_msg(folder, tmp->msgnum);
4666                                         procmsg_msginfo_free(tmp);
4667                                 } 
4668                         }
4669                 }
4670         }
4671
4672         if (val == 0) {
4673                 if (!queued_removed)
4674                         folder_item_remove_msg(folder, msgnum);
4675                 folder_item_scan(folder);
4676                 if (tmsgid) {
4677                         /* make sure we delete that */
4678                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4679                         if (tmp) {
4680                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4681                                 folder_item_remove_msg(folder, tmp->msgnum);
4682                                 procmsg_msginfo_free(tmp);
4683                         }
4684                 }
4685                 if (!discard_window) {
4686                         compose->sending = FALSE;
4687                         compose_allow_user_actions (compose, TRUE);
4688                         compose_close(compose);
4689                 }
4690         } else {
4691                 if (errstr) {
4692                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4693                                    "the main window to retry."), errstr);
4694                         g_free(errstr);
4695                 } else {
4696                         alertpanel_error_log(_("The message was queued but could not be "
4697                                    "sent.\nUse \"Send queued messages\" from "
4698                                    "the main window to retry."));
4699                 }
4700                 if (!discard_window) {
4701                         goto bail;              
4702                 }
4703                 inc_unlock();
4704                 g_free(tmsgid);
4705                 return -1;
4706         }
4707         g_free(tmsgid);
4708         inc_unlock();
4709         toolbar_main_set_sensitive(mainwin);
4710         main_window_set_menu_sensitive(mainwin);
4711         return 0;
4712
4713 bail:
4714         inc_unlock();
4715         g_free(tmsgid);
4716         compose_allow_user_actions (compose, TRUE);
4717         compose->sending = FALSE;
4718         compose->modified = TRUE; 
4719         toolbar_main_set_sensitive(mainwin);
4720         main_window_set_menu_sensitive(mainwin);
4721
4722         return -1;
4723 }
4724
4725 static gboolean compose_use_attach(Compose *compose) 
4726 {
4727         GtkTreeModel *model = gtk_tree_view_get_model
4728                                 (GTK_TREE_VIEW(compose->attach_clist));
4729         return gtk_tree_model_iter_n_children(model, NULL) > 0;
4730 }
4731
4732 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
4733                                                            FILE *fp)
4734 {
4735         gchar buf[BUFFSIZE];
4736         gchar *str;
4737         gboolean first_to_address;
4738         gboolean first_cc_address;
4739         GSList *list;
4740         ComposeHeaderEntry *headerentry;
4741         const gchar *headerentryname;
4742         const gchar *cc_hdr;
4743         const gchar *to_hdr;
4744         gboolean err = FALSE;
4745
4746         debug_print("Writing redirect header\n");
4747
4748         cc_hdr = prefs_common_translated_header_name("Cc:");
4749         to_hdr = prefs_common_translated_header_name("To:");
4750
4751         first_to_address = TRUE;
4752         for (list = compose->header_list; list; list = list->next) {
4753                 headerentry = ((ComposeHeaderEntry *)list->data);
4754                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4755
4756                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4757                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4758                         Xstrdup_a(str, entstr, return -1);
4759                         g_strstrip(str);
4760                         if (str[0] != '\0') {
4761                                 compose_convert_header
4762                                         (compose, buf, sizeof(buf), str,
4763                                         strlen("Resent-To") + 2, TRUE);
4764
4765                                 if (first_to_address) {
4766                                         err |= (fprintf(fp, "Resent-To: ") < 0);
4767                                         first_to_address = FALSE;
4768                                 } else {
4769                                         err |= (fprintf(fp, ",") < 0);
4770                                 }
4771                                 err |= (fprintf(fp, "%s", buf) < 0);
4772                         }
4773                 }
4774         }
4775         if (!first_to_address) {
4776                 err |= (fprintf(fp, "\n") < 0);
4777         }
4778
4779         first_cc_address = TRUE;
4780         for (list = compose->header_list; list; list = list->next) {
4781                 headerentry = ((ComposeHeaderEntry *)list->data);
4782                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4783
4784                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4785                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4786                         Xstrdup_a(str, strg, return -1);
4787                         g_strstrip(str);
4788                         if (str[0] != '\0') {
4789                                 compose_convert_header
4790                                         (compose, buf, sizeof(buf), str,
4791                                         strlen("Resent-Cc") + 2, TRUE);
4792
4793                                 if (first_cc_address) {
4794                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
4795                                         first_cc_address = FALSE;
4796                                 } else {
4797                                         err |= (fprintf(fp, ",") < 0);
4798                                 }
4799                                 err |= (fprintf(fp, "%s", buf) < 0);
4800                         }
4801                 }
4802         }
4803         if (!first_cc_address) {
4804                 err |= (fprintf(fp, "\n") < 0);
4805         }
4806         
4807         return (err ? -1:0);
4808 }
4809
4810 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4811 {
4812         gchar buf[BUFFSIZE];
4813         gchar *str;
4814         const gchar *entstr;
4815         /* struct utsname utsbuf; */
4816         gboolean err = FALSE;
4817
4818         g_return_val_if_fail(fp != NULL, -1);
4819         g_return_val_if_fail(compose->account != NULL, -1);
4820         g_return_val_if_fail(compose->account->address != NULL, -1);
4821
4822         /* Resent-Date */
4823         get_rfc822_date(buf, sizeof(buf));
4824         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4825
4826         /* Resent-From */
4827         if (compose->account->name && *compose->account->name) {
4828                 compose_convert_header
4829                         (compose, buf, sizeof(buf), compose->account->name,
4830                          strlen("From: "), TRUE);
4831                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4832                         buf, compose->account->address) < 0);
4833         } else
4834                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4835
4836         /* Subject */
4837         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4838         if (*entstr != '\0') {
4839                 Xstrdup_a(str, entstr, return -1);
4840                 g_strstrip(str);
4841                 if (*str != '\0') {
4842                         compose_convert_header(compose, buf, sizeof(buf), str,
4843                                                strlen("Subject: "), FALSE);
4844                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4845                 }
4846         }
4847
4848         /* Resent-Message-ID */
4849         if (compose->account->set_domain && compose->account->domain) {
4850                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
4851         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4852                 g_snprintf(buf, sizeof(buf), "%s", 
4853                         strchr(compose->account->address, '@') ?
4854                                 strchr(compose->account->address, '@')+1 :
4855                                 compose->account->address);
4856         } else {
4857                 g_snprintf(buf, sizeof(buf), "%s", "");
4858         }
4859
4860         if (compose->account->gen_msgid) {
4861                 generate_msgid(buf, sizeof(buf));
4862                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4863                 compose->msgid = g_strdup(buf);
4864         } else {
4865                 compose->msgid = NULL;
4866         }
4867
4868         if (compose_redirect_write_headers_from_headerlist(compose, fp))
4869                 return -1;
4870
4871         /* separator between header and body */
4872         err |= (fputs("\n", fp) == EOF);
4873
4874         return (err ? -1:0);
4875 }
4876
4877 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4878 {
4879         FILE *fp;
4880         size_t len;
4881         gchar buf[BUFFSIZE];
4882         int i = 0;
4883         gboolean skip = FALSE;
4884         gboolean err = FALSE;
4885         gchar *not_included[]={
4886                 "Return-Path:",         "Delivered-To:",        "Received:",
4887                 "Subject:",             "X-UIDL:",              "AF:",
4888                 "NF:",                  "PS:",                  "SRH:",
4889                 "SFN:",                 "DSR:",                 "MID:",
4890                 "CFG:",                 "PT:",                  "S:",
4891                 "RQ:",                  "SSV:",                 "NSV:",
4892                 "SSH:",                 "R:",                   "MAID:",
4893                 "NAID:",                "RMID:",                "FMID:",
4894                 "SCF:",                 "RRCPT:",               "NG:",
4895                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
4896                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
4897                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
4898                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
4899                 NULL
4900                 };
4901         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4902                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4903                 return -1;
4904         }
4905
4906         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4907                 skip = FALSE;
4908                 for (i = 0; not_included[i] != NULL; i++) {
4909                         if (g_ascii_strncasecmp(buf, not_included[i],
4910                                                 strlen(not_included[i])) == 0) {
4911                                 skip = TRUE;
4912                                 break;
4913                         }
4914                 }
4915                 if (skip)
4916                         continue;
4917                 if (fputs(buf, fdest) == -1)
4918                         goto error;
4919
4920                 if (!prefs_common.redirect_keep_from) {
4921                         if (g_ascii_strncasecmp(buf, "From:",
4922                                           strlen("From:")) == 0) {
4923                                 err |= (fputs(" (by way of ", fdest) == EOF);
4924                                 if (compose->account->name
4925                                     && *compose->account->name) {
4926                                         compose_convert_header
4927                                                 (compose, buf, sizeof(buf),
4928                                                  compose->account->name,
4929                                                  strlen("From: "),
4930                                                  FALSE);
4931                                         err |= (fprintf(fdest, "%s <%s>",
4932                                                 buf,
4933                                                 compose->account->address) < 0);
4934                                 } else
4935                                         err |= (fprintf(fdest, "%s",
4936                                                 compose->account->address) < 0);
4937                                 err |= (fputs(")", fdest) == EOF);
4938                         }
4939                 }
4940
4941                 if (fputs("\n", fdest) == -1)
4942                         goto error;
4943         }
4944
4945         if (err)
4946                 goto error;
4947
4948         if (compose_redirect_write_headers(compose, fdest))
4949                 goto error;
4950
4951         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4952                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
4953                         goto error;
4954         }
4955
4956         fclose(fp);
4957
4958         return 0;
4959 error:
4960         fclose(fp);
4961
4962         return -1;
4963 }
4964
4965 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
4966 {
4967         GtkTextBuffer *buffer;
4968         GtkTextIter start, end;
4969         gchar *chars;
4970         gchar *buf;
4971         const gchar *out_codeset;
4972         EncodingType encoding;
4973         MimeInfo *mimemsg, *mimetext;
4974         gint line;
4975
4976         if (action == COMPOSE_WRITE_FOR_SEND)
4977                 attach_parts = TRUE;
4978
4979         /* create message MimeInfo */
4980         mimemsg = procmime_mimeinfo_new();
4981         mimemsg->type = MIMETYPE_MESSAGE;
4982         mimemsg->subtype = g_strdup("rfc822");
4983         mimemsg->content = MIMECONTENT_MEM;
4984         mimemsg->tmp = TRUE; /* must free content later */
4985         mimemsg->data.mem = compose_get_header(compose);
4986
4987         /* Create text part MimeInfo */
4988         /* get all composed text */
4989         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4990         gtk_text_buffer_get_start_iter(buffer, &start);
4991         gtk_text_buffer_get_end_iter(buffer, &end);
4992         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4993         if (is_ascii_str(chars)) {
4994                 buf = chars;
4995                 chars = NULL;
4996                 out_codeset = CS_US_ASCII;
4997                 encoding = ENC_7BIT;
4998         } else {
4999                 const gchar *src_codeset = CS_INTERNAL;
5000
5001                 out_codeset = conv_get_charset_str(compose->out_encoding);
5002
5003                 if (!out_codeset) {
5004                         gchar *test_conv_global_out = NULL;
5005                         gchar *test_conv_reply = NULL;
5006
5007                         /* automatic mode. be automatic. */
5008                         codeconv_set_strict(TRUE);
5009                         
5010                         out_codeset = conv_get_outgoing_charset_str();
5011                         if (out_codeset) {
5012                                 debug_print("trying to convert to %s\n", out_codeset);
5013                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5014                         }
5015                         
5016                         if (!test_conv_global_out && compose->orig_charset
5017                         &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5018                                 out_codeset = compose->orig_charset;
5019                                 debug_print("failure; trying to convert to %s\n", out_codeset);
5020                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5021                         }
5022                         
5023                         if (!test_conv_global_out && !test_conv_reply) {
5024                                 /* we're lost */
5025                                 out_codeset = CS_INTERNAL;
5026                                 debug_print("failure; finally using %s\n", out_codeset);
5027                         }
5028                         g_free(test_conv_global_out);
5029                         g_free(test_conv_reply);
5030                         codeconv_set_strict(FALSE);
5031                 }
5032
5033                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
5034                         out_codeset = CS_ISO_8859_1;
5035
5036                 if (prefs_common.encoding_method == CTE_BASE64)
5037                         encoding = ENC_BASE64;
5038                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5039                         encoding = ENC_QUOTED_PRINTABLE;
5040                 else if (prefs_common.encoding_method == CTE_8BIT)
5041                         encoding = ENC_8BIT;
5042                 else
5043                         encoding = procmime_get_encoding_for_charset(out_codeset);
5044
5045                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5046                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5047
5048                 if (action == COMPOSE_WRITE_FOR_SEND) {
5049                         codeconv_set_strict(TRUE);
5050                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5051                         codeconv_set_strict(FALSE);
5052
5053                         if (!buf) {
5054                                 AlertValue aval;
5055                                 gchar *msg;
5056
5057                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5058                                                         "to the specified %s charset.\n"
5059                                                         "Send it as %s?"), out_codeset, src_codeset);
5060                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5061                                                       NULL, ALERT_ERROR, G_ALERTDEFAULT);
5062                                 g_free(msg);
5063
5064                                 if (aval != G_ALERTALTERNATE) {
5065                                         g_free(chars);
5066                                         return -3;
5067                                 } else {
5068                                         buf = chars;
5069                                         out_codeset = src_codeset;
5070                                         chars = NULL;
5071                                 }
5072                         }
5073                 } else {
5074                         buf = chars;
5075                         out_codeset = src_codeset;
5076                         chars = NULL;
5077                 }
5078         }
5079         g_free(chars);
5080
5081         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5082                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5083                     strstr(buf, "\nFrom ") != NULL) {
5084                         encoding = ENC_QUOTED_PRINTABLE;
5085                 }
5086         }
5087
5088         mimetext = procmime_mimeinfo_new();
5089         mimetext->content = MIMECONTENT_MEM;
5090         mimetext->tmp = TRUE; /* must free content later */
5091         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5092          * and free the data, which we need later. */
5093         mimetext->data.mem = g_strdup(buf); 
5094         mimetext->type = MIMETYPE_TEXT;
5095         mimetext->subtype = g_strdup("plain");
5096         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5097                             g_strdup(out_codeset));
5098                             
5099         /* protect trailing spaces when signing message */
5100         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5101             privacy_system_can_sign(compose->privacy_system)) {
5102                 encoding = ENC_QUOTED_PRINTABLE;
5103         }
5104         
5105         debug_print("main text: %zd bytes encoded as %s in %d\n",
5106                 strlen(buf), out_codeset, encoding);
5107
5108         /* check for line length limit */
5109         if (action == COMPOSE_WRITE_FOR_SEND &&
5110             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5111             check_line_length(buf, 1000, &line) < 0) {
5112                 AlertValue aval;
5113                 gchar *msg;
5114
5115                 msg = g_strdup_printf
5116                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5117                            "The contents of the message might be broken on the way to the delivery.\n"
5118                            "\n"
5119                            "Send it anyway?"), line + 1);
5120                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5121                 g_free(msg);
5122                 if (aval != G_ALERTALTERNATE) {
5123                         g_free(buf);
5124                         return -1;
5125                 }
5126         }
5127         
5128         if (encoding != ENC_UNKNOWN)
5129                 procmime_encode_content(mimetext, encoding);
5130
5131         /* append attachment parts */
5132         if (compose_use_attach(compose) && attach_parts) {
5133                 MimeInfo *mimempart;
5134                 gchar *boundary = NULL;
5135                 mimempart = procmime_mimeinfo_new();
5136                 mimempart->content = MIMECONTENT_EMPTY;
5137                 mimempart->type = MIMETYPE_MULTIPART;
5138                 mimempart->subtype = g_strdup("mixed");
5139
5140                 do {
5141                         g_free(boundary);
5142                         boundary = generate_mime_boundary(NULL);
5143                 } while (strstr(buf, boundary) != NULL);
5144
5145                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5146                                     boundary);
5147
5148                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5149
5150                 g_node_append(mimempart->node, mimetext->node);
5151                 g_node_append(mimemsg->node, mimempart->node);
5152
5153                 compose_add_attachments(compose, mimempart);
5154         } else
5155                 g_node_append(mimemsg->node, mimetext->node);
5156
5157         g_free(buf);
5158
5159         /* sign message if sending */
5160         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5161             privacy_system_can_sign(compose->privacy_system))
5162                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5163                         return -2;
5164
5165         procmime_write_mimeinfo(mimemsg, fp);
5166         
5167         procmime_mimeinfo_free_all(mimemsg);
5168
5169         return 0;
5170 }
5171
5172 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5173 {
5174         GtkTextBuffer *buffer;
5175         GtkTextIter start, end;
5176         FILE *fp;
5177         size_t len;
5178         gchar *chars, *tmp;
5179
5180         if ((fp = g_fopen(file, "wb")) == NULL) {
5181                 FILE_OP_ERROR(file, "fopen");
5182                 return -1;
5183         }
5184
5185         /* chmod for security */
5186         if (change_file_mode_rw(fp, file) < 0) {
5187                 FILE_OP_ERROR(file, "chmod");
5188                 g_warning("can't change file mode\n");
5189         }
5190
5191         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5192         gtk_text_buffer_get_start_iter(buffer, &start);
5193         gtk_text_buffer_get_end_iter(buffer, &end);
5194         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5195
5196         chars = conv_codeset_strdup
5197                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5198
5199         g_free(tmp);
5200         if (!chars) return -1;
5201
5202         /* write body */
5203         len = strlen(chars);
5204         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5205                 FILE_OP_ERROR(file, "fwrite");
5206                 g_free(chars);
5207                 fclose(fp);
5208                 g_unlink(file);
5209                 return -1;
5210         }
5211
5212         g_free(chars);
5213
5214         if (fclose(fp) == EOF) {
5215                 FILE_OP_ERROR(file, "fclose");
5216                 g_unlink(file);
5217                 return -1;
5218         }
5219         return 0;
5220 }
5221
5222 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5223 {
5224         FolderItem *item;
5225         MsgInfo *msginfo = compose->targetinfo;
5226
5227         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5228         if (!msginfo) return -1;
5229
5230         if (!force && MSG_IS_LOCKED(msginfo->flags))
5231                 return 0;
5232
5233         item = msginfo->folder;
5234         g_return_val_if_fail(item != NULL, -1);
5235
5236         if (procmsg_msg_exist(msginfo) &&
5237             (folder_has_parent_of_type(item, F_QUEUE) ||
5238              folder_has_parent_of_type(item, F_DRAFT) 
5239              || msginfo == compose->autosaved_draft)) {
5240                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5241                         g_warning("can't remove the old message\n");
5242                         return -1;
5243                 } else {
5244                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5245                 }
5246         }
5247
5248         return 0;
5249 }
5250
5251 static void compose_remove_draft(Compose *compose)
5252 {
5253         FolderItem *drafts;
5254         MsgInfo *msginfo = compose->targetinfo;
5255         drafts = account_get_special_folder(compose->account, F_DRAFT);
5256
5257         if (procmsg_msg_exist(msginfo)) {
5258                 folder_item_remove_msg(drafts, msginfo->msgnum);
5259         }
5260
5261 }
5262
5263 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5264                    gboolean remove_reedit_target)
5265 {
5266         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5267 }
5268
5269 static gboolean compose_warn_encryption(Compose *compose)
5270 {
5271         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5272         AlertValue val = G_ALERTALTERNATE;
5273         
5274         if (warning == NULL)
5275                 return TRUE;
5276
5277         val = alertpanel_full(_("Encryption warning"), warning,
5278                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5279                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5280         if (val & G_ALERTDISABLE) {
5281                 val &= ~G_ALERTDISABLE;
5282                 if (val == G_ALERTALTERNATE)
5283                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5284                                 TRUE);
5285         }
5286
5287         if (val == G_ALERTALTERNATE) {
5288                 return TRUE;
5289         } else {
5290                 return FALSE;
5291         } 
5292 }
5293
5294 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5295                               gchar **msgpath, gboolean check_subject,
5296                               gboolean remove_reedit_target)
5297 {
5298         FolderItem *queue;
5299         gchar *tmp;
5300         FILE *fp;
5301         GSList *cur;
5302         gint num;
5303         static gboolean lock = FALSE;
5304         PrefsAccount *mailac = NULL, *newsac = NULL;
5305         gboolean err = FALSE;
5306
5307         debug_print("queueing message...\n");
5308         g_return_val_if_fail(compose->account != NULL, -1);
5309
5310         lock = TRUE;
5311         
5312         if (compose_check_entries(compose, check_subject) == FALSE) {
5313                 lock = FALSE;
5314                 if (compose->batch) {
5315                         gtk_widget_show_all(compose->window);
5316                 }
5317                 return -1;
5318         }
5319
5320         if (!compose->to_list && !compose->newsgroup_list) {
5321                 g_warning("can't get recipient list.");
5322                 lock = FALSE;
5323                 return -1;
5324         }
5325
5326         if (compose->to_list) {
5327                 if (compose->account->protocol != A_NNTP)
5328                         mailac = compose->account;
5329                 else if (cur_account && cur_account->protocol != A_NNTP)
5330                         mailac = cur_account;
5331                 else if (!(mailac = compose_current_mail_account())) {
5332                         lock = FALSE;
5333                         alertpanel_error(_("No account for sending mails available!"));
5334                         return -1;
5335                 }
5336         }
5337
5338         if (compose->newsgroup_list) {
5339                 if (compose->account->protocol == A_NNTP)
5340                         newsac = compose->account;
5341                 else if (!newsac->protocol != A_NNTP) {
5342                         lock = FALSE;
5343                         alertpanel_error(_("No account for posting news available!"));
5344                         return -1;
5345                 }                       
5346         }
5347
5348         /* write queue header */
5349         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5350                               G_DIR_SEPARATOR, compose, (guint) rand());
5351         debug_print("queuing to %s\n", tmp);
5352         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5353                 FILE_OP_ERROR(tmp, "fopen");
5354                 g_free(tmp);
5355                 lock = FALSE;
5356                 return -2;
5357         }
5358
5359         if (change_file_mode_rw(fp, tmp) < 0) {
5360                 FILE_OP_ERROR(tmp, "chmod");
5361                 g_warning("can't change file mode\n");
5362         }
5363
5364         /* queueing variables */
5365         err |= (fprintf(fp, "AF:\n") < 0);
5366         err |= (fprintf(fp, "NF:0\n") < 0);
5367         err |= (fprintf(fp, "PS:10\n") < 0);
5368         err |= (fprintf(fp, "SRH:1\n") < 0);
5369         err |= (fprintf(fp, "SFN:\n") < 0);
5370         err |= (fprintf(fp, "DSR:\n") < 0);
5371         if (compose->msgid)
5372                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5373         else
5374                 err |= (fprintf(fp, "MID:\n") < 0);
5375         err |= (fprintf(fp, "CFG:\n") < 0);
5376         err |= (fprintf(fp, "PT:0\n") < 0);
5377         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5378         err |= (fprintf(fp, "RQ:\n") < 0);
5379         if (mailac)
5380                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5381         else
5382                 err |= (fprintf(fp, "SSV:\n") < 0);
5383         if (newsac)
5384                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5385         else
5386                 err |= (fprintf(fp, "NSV:\n") < 0);
5387         err |= (fprintf(fp, "SSH:\n") < 0);
5388         /* write recepient list */
5389         if (compose->to_list) {
5390                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5391                 for (cur = compose->to_list->next; cur != NULL;
5392                      cur = cur->next)
5393                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5394                 err |= (fprintf(fp, "\n") < 0);
5395         }
5396         /* write newsgroup list */
5397         if (compose->newsgroup_list) {
5398                 err |= (fprintf(fp, "NG:") < 0);
5399                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5400                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5401                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5402                 err |= (fprintf(fp, "\n") < 0);
5403         }
5404         /* Sylpheed account IDs */
5405         if (mailac)
5406                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5407         if (newsac)
5408                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5409
5410         
5411         if (compose->privacy_system != NULL) {
5412                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5413                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5414                 if (compose->use_encryption) {
5415                         gchar *encdata;
5416                         if (!compose_warn_encryption(compose)) {
5417                                 lock = FALSE;
5418                                 fclose(fp);
5419                                 g_unlink(tmp);
5420                                 g_free(tmp);
5421                                 return -6;
5422                         }
5423                         if (mailac && mailac->encrypt_to_self) {
5424                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5425                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5426                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5427                                 g_slist_free(tmp_list);
5428                         } else {
5429                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5430                         }
5431                         if (encdata != NULL) {
5432                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5433                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5434                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5435                                                 encdata) < 0);
5436                                 } /* else we finally dont want to encrypt */
5437                         } else {
5438                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5439                                 /* and if encdata was null, it means there's been a problem in 
5440                                  * key selection */
5441                                 lock = FALSE;
5442                                 fclose(fp);
5443                                 g_unlink(tmp);
5444                                 g_free(tmp);
5445                                 return -5;
5446                         }
5447                         g_free(encdata);
5448                 }
5449         }
5450
5451         /* Save copy folder */
5452         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5453                 gchar *savefolderid;
5454                 
5455                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5456                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5457                 g_free(savefolderid);
5458         }
5459         /* Save copy folder */
5460         if (compose->return_receipt) {
5461                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5462         }
5463         /* Message-ID of message replying to */
5464         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5465                 gchar *folderid;
5466                 
5467                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5468                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5469                 g_free(folderid);
5470         }
5471         /* Message-ID of message forwarding to */
5472         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5473                 gchar *folderid;
5474                 
5475                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5476                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5477                 g_free(folderid);
5478         }
5479
5480         /* end of headers */
5481         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5482
5483         if (compose->redirect_filename != NULL) {
5484                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5485                         lock = FALSE;
5486                         fclose(fp);
5487                         g_unlink(tmp);
5488                         g_free(tmp);
5489                         return -2;
5490                 }
5491         } else {
5492                 gint result = 0;
5493                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5494                         lock = FALSE;
5495                         fclose(fp);
5496                         g_unlink(tmp);
5497                         g_free(tmp);
5498                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5499                 }
5500         }
5501         if (err == TRUE) {
5502                 g_warning("failed to write queue message\n");
5503                 fclose(fp);
5504                 g_unlink(tmp);
5505                 g_free(tmp);
5506                 lock = FALSE;
5507                 return -2;
5508         }
5509         if (fclose(fp) == EOF) {
5510                 FILE_OP_ERROR(tmp, "fclose");
5511                 g_unlink(tmp);
5512                 g_free(tmp);
5513                 lock = FALSE;
5514                 return -2;
5515         }
5516
5517         if (item && *item) {
5518                 queue = *item;
5519         } else {
5520                 queue = account_get_special_folder(compose->account, F_QUEUE);
5521         }
5522         if (!queue) {
5523                 g_warning("can't find queue folder\n");
5524                 g_unlink(tmp);
5525                 g_free(tmp);
5526                 lock = FALSE;
5527                 return -1;
5528         }
5529         folder_item_scan(queue);
5530         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5531                 g_warning("can't queue the message\n");
5532                 g_unlink(tmp);
5533                 g_free(tmp);
5534                 lock = FALSE;
5535                 return -1;
5536         }
5537         
5538         if (msgpath == NULL) {
5539                 g_unlink(tmp);
5540                 g_free(tmp);
5541         } else
5542                 *msgpath = tmp;
5543
5544         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5545                 compose_remove_reedit_target(compose, FALSE);
5546         }
5547
5548         if ((msgnum != NULL) && (item != NULL)) {
5549                 *msgnum = num;
5550                 *item = queue;
5551         }
5552
5553         return 0;
5554 }
5555
5556 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5557 {
5558         AttachInfo *ainfo;
5559         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5560         MimeInfo *mimepart;
5561         struct stat statbuf;
5562         gchar *type, *subtype;
5563         GtkTreeModel *model;
5564         GtkTreeIter iter;
5565
5566         model = gtk_tree_view_get_model(tree_view);
5567         
5568         if (!gtk_tree_model_get_iter_first(model, &iter))
5569                 return;
5570         do {
5571                 gtk_tree_model_get(model, &iter,
5572                                    COL_DATA, &ainfo,
5573                                    -1);
5574                                                            
5575                 mimepart = procmime_mimeinfo_new();
5576                 mimepart->content = MIMECONTENT_FILE;
5577                 mimepart->data.filename = g_strdup(ainfo->file);
5578                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5579                 mimepart->offset = 0;
5580
5581                 stat(ainfo->file, &statbuf);
5582                 mimepart->length = statbuf.st_size;
5583
5584                 type = g_strdup(ainfo->content_type);
5585
5586                 if (!strchr(type, '/')) {
5587                         g_free(type);
5588                         type = g_strdup("application/octet-stream");
5589                 }
5590
5591                 subtype = strchr(type, '/') + 1;
5592                 *(subtype - 1) = '\0';
5593                 mimepart->type = procmime_get_media_type(type);
5594                 mimepart->subtype = g_strdup(subtype);
5595                 g_free(type);
5596
5597                 if (mimepart->type == MIMETYPE_MESSAGE && 
5598                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5599                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5600                 } else {
5601                         if (ainfo->name) {
5602                                 g_hash_table_insert(mimepart->typeparameters,
5603                                             g_strdup("name"), g_strdup(ainfo->name));
5604                                 g_hash_table_insert(mimepart->dispositionparameters,
5605                                             g_strdup("filename"), g_strdup(ainfo->name));
5606                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5607                         }
5608                 }
5609
5610                 if (compose->use_signing) {
5611                         if (ainfo->encoding == ENC_7BIT)
5612                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5613                         else if (ainfo->encoding == ENC_8BIT)
5614                                 ainfo->encoding = ENC_BASE64;
5615                 }
5616                 
5617                 procmime_encode_content(mimepart, ainfo->encoding);
5618
5619                 g_node_append(parent->node, mimepart->node);
5620         } while (gtk_tree_model_iter_next(model, &iter));
5621 }
5622
5623 #define IS_IN_CUSTOM_HEADER(header) \
5624         (compose->account->add_customhdr && \
5625          custom_header_find(compose->account->customhdr_list, header) != NULL)
5626
5627 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5628                                                     GString *header, 
5629                                                     const gchar *fieldname,
5630                                                     const gchar *seperator)
5631 {
5632         gchar *str, *fieldname_w_colon;
5633         gboolean add_field = FALSE;
5634         GSList *list;
5635         ComposeHeaderEntry *headerentry;
5636         const gchar *headerentryname;
5637         const gchar *trans_fieldname;
5638         GString *fieldstr;
5639
5640         if (IS_IN_CUSTOM_HEADER(fieldname))
5641                 return;
5642
5643         debug_print("Adding %s-fields\n", fieldname);
5644
5645         fieldstr = g_string_sized_new(64);
5646
5647         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5648         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5649
5650         for (list = compose->header_list; list; list = list->next) {
5651                 headerentry = ((ComposeHeaderEntry *)list->data);
5652                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
5653
5654                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5655                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5656                         g_strstrip(str);
5657                         if (str[0] != '\0') {
5658                                 if (add_field)
5659                                         g_string_append(fieldstr, seperator);
5660                                 g_string_append(fieldstr, str);
5661                                 add_field = TRUE;
5662                         }
5663                         g_free(str);
5664                 }
5665         }
5666         if (add_field) {
5667                 gchar *buf;
5668
5669                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5670                 compose_convert_header
5671                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5672                         strlen(fieldname) + 2, TRUE);
5673                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5674                 g_free(buf);
5675         }
5676
5677         g_free(fieldname_w_colon);
5678         g_string_free(fieldstr, TRUE);
5679
5680         return;
5681 }
5682
5683 static gchar *compose_get_header(Compose *compose)
5684 {
5685         gchar buf[BUFFSIZE];
5686         const gchar *entry_str;
5687         gchar *str;
5688         gchar *name;
5689         GSList *list;
5690         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5691         GString *header;
5692         gchar *from_name = NULL, *from_address = NULL;
5693         gchar *tmp;
5694
5695         g_return_val_if_fail(compose->account != NULL, NULL);
5696         g_return_val_if_fail(compose->account->address != NULL, NULL);
5697
5698         header = g_string_sized_new(64);
5699
5700         /* Date */
5701         get_rfc822_date(buf, sizeof(buf));
5702         g_string_append_printf(header, "Date: %s\n", buf);
5703
5704         /* From */
5705         
5706         if (compose->account->name && *compose->account->name) {
5707                 gchar *buf;
5708                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5709                 tmp = g_strdup_printf("%s <%s>",
5710                         buf, compose->account->address);
5711         } else {
5712                 tmp = g_strdup_printf("%s",
5713                         compose->account->address);
5714         }
5715         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5716         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5717                 /* use default */
5718                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5719                 from_address = g_strdup(compose->account->address);
5720         } else {
5721                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5722                 /* extract name and address */
5723                 if (strstr(spec, " <") && strstr(spec, ">")) {
5724                         from_address = g_strdup(strrchr(spec, '<')+1);
5725                         *(strrchr(from_address, '>')) = '\0';
5726                         from_name = g_strdup(spec);
5727                         *(strrchr(from_name, '<')) = '\0';
5728                 } else {
5729                         from_name = NULL;
5730                         from_address = g_strdup(spec);
5731                 }
5732                 g_free(spec);
5733         }
5734         g_free(tmp);
5735         
5736         
5737         if (from_name && *from_name) {
5738                 compose_convert_header
5739                         (compose, buf, sizeof(buf), from_name,
5740                          strlen("From: "), TRUE);
5741                 QUOTE_IF_REQUIRED(name, buf);
5742                 
5743                 g_string_append_printf(header, "From: %s <%s>\n",
5744                         name, from_address);
5745         } else
5746                 g_string_append_printf(header, "From: %s\n", from_address);
5747         
5748         g_free(from_name);
5749         g_free(from_address);
5750
5751         /* To */
5752         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5753
5754         /* Newsgroups */
5755         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5756
5757         /* Cc */
5758         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5759
5760         /* Bcc */
5761         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5762
5763         /* Subject */
5764         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5765
5766         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5767                 g_strstrip(str);
5768                 if (*str != '\0') {
5769                         compose_convert_header(compose, buf, sizeof(buf), str,
5770                                                strlen("Subject: "), FALSE);
5771                         g_string_append_printf(header, "Subject: %s\n", buf);
5772                 }
5773         }
5774         g_free(str);
5775
5776         /* Message-ID */
5777         if (compose->account->set_domain && compose->account->domain) {
5778                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5779         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5780                 g_snprintf(buf, sizeof(buf), "%s", 
5781                         strchr(compose->account->address, '@') ?
5782                                 strchr(compose->account->address, '@')+1 :
5783                                 compose->account->address);
5784         } else {
5785                 g_snprintf(buf, sizeof(buf), "%s", "");
5786         }
5787         
5788         if (compose->account->gen_msgid) {
5789                 generate_msgid(buf, sizeof(buf));
5790                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5791                 compose->msgid = g_strdup(buf);
5792         } else {
5793                 compose->msgid = NULL;
5794         }
5795
5796         if (compose->remove_references == FALSE) {
5797                 /* In-Reply-To */
5798                 if (compose->inreplyto && compose->to_list)
5799                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5800         
5801                 /* References */
5802                 if (compose->references)
5803                         g_string_append_printf(header, "References: %s\n", compose->references);
5804         }
5805
5806         /* Followup-To */
5807         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5808
5809         /* Reply-To */
5810         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5811
5812         /* Organization */
5813         if (compose->account->organization &&
5814             strlen(compose->account->organization) &&
5815             !IS_IN_CUSTOM_HEADER("Organization")) {
5816                 compose_convert_header(compose, buf, sizeof(buf),
5817                                        compose->account->organization,
5818                                        strlen("Organization: "), FALSE);
5819                 g_string_append_printf(header, "Organization: %s\n", buf);
5820         }
5821
5822         /* Program version and system info */
5823         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5824             !compose->newsgroup_list) {
5825                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5826                         prog_version,
5827                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5828                         TARGET_ALIAS);
5829         }
5830         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5831                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5832                         prog_version,
5833                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5834                         TARGET_ALIAS);
5835         }
5836
5837         /* custom headers */
5838         if (compose->account->add_customhdr) {
5839                 GSList *cur;
5840
5841                 for (cur = compose->account->customhdr_list; cur != NULL;
5842                      cur = cur->next) {
5843                         CustomHeader *chdr = (CustomHeader *)cur->data;
5844
5845                         if (custom_header_is_allowed(chdr->name)) {
5846                                 compose_convert_header
5847                                         (compose, buf, sizeof(buf),
5848                                          chdr->value ? chdr->value : "",
5849                                          strlen(chdr->name) + 2, FALSE);
5850                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5851                         }
5852                 }
5853         }
5854
5855         /* PRIORITY */
5856         switch (compose->priority) {
5857                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5858                                                    "X-Priority: 1 (Highest)\n");
5859                         break;
5860                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5861                                                 "X-Priority: 2 (High)\n");
5862                         break;
5863                 case PRIORITY_NORMAL: break;
5864                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5865                                                "X-Priority: 4 (Low)\n");
5866                         break;
5867                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5868                                                   "X-Priority: 5 (Lowest)\n");
5869                         break;
5870                 default: debug_print("compose: priority unknown : %d\n",
5871                                      compose->priority);
5872         }
5873
5874         /* Request Return Receipt */
5875         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5876                 if (compose->return_receipt) {
5877                         if (compose->account->name
5878                             && *compose->account->name) {
5879                                 compose_convert_header(compose, buf, sizeof(buf), 
5880                                                        compose->account->name, 
5881                                                        strlen("Disposition-Notification-To: "),
5882                                                        TRUE);
5883                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5884                         } else
5885                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5886                 }
5887         }
5888
5889         /* get special headers */
5890         for (list = compose->header_list; list; list = list->next) {
5891                 ComposeHeaderEntry *headerentry;
5892                 gchar *tmp;
5893                 gchar *headername;
5894                 gchar *headername_wcolon;
5895                 const gchar *headername_trans;
5896                 gchar *headervalue;
5897                 gchar **string;
5898                 gboolean standard_header = FALSE;
5899
5900                 headerentry = ((ComposeHeaderEntry *)list->data);
5901
5902                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child)));
5903                 g_strstrip(tmp);
5904                 if (*tmp == '\0' || strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5905                         g_free(tmp);
5906                         continue;
5907                 }
5908
5909                 if (!strstr(tmp, ":")) {
5910                         headername_wcolon = g_strconcat(tmp, ":", NULL);
5911                         headername = g_strdup(tmp);
5912                 } else {
5913                         headername_wcolon = g_strdup(tmp);
5914                         headername = g_strdup(strtok(tmp, ":"));
5915                 }
5916                 g_free(tmp);
5917                 
5918                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5919                 Xstrdup_a(headervalue, entry_str, return NULL);
5920                 subst_char(headervalue, '\r', ' ');
5921                 subst_char(headervalue, '\n', ' ');
5922                 string = std_headers;
5923                 while (*string != NULL) {
5924                         headername_trans = prefs_common_translated_header_name(*string);
5925                         if (!strcmp(headername_trans, headername_wcolon))
5926                                 standard_header = TRUE;
5927                         string++;
5928                 }
5929                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
5930                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
5931                                 
5932                 g_free(headername);
5933                 g_free(headername_wcolon);              
5934         }
5935
5936         str = header->str;
5937         g_string_free(header, FALSE);
5938
5939         return str;
5940 }
5941
5942 #undef IS_IN_CUSTOM_HEADER
5943
5944 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
5945                                    gint header_len, gboolean addr_field)
5946 {
5947         gchar *tmpstr = NULL;
5948         const gchar *out_codeset = NULL;
5949
5950         g_return_if_fail(src != NULL);
5951         g_return_if_fail(dest != NULL);
5952
5953         if (len < 1) return;
5954
5955         tmpstr = g_strdup(src);
5956
5957         subst_char(tmpstr, '\n', ' ');
5958         subst_char(tmpstr, '\r', ' ');
5959         g_strchomp(tmpstr);
5960
5961         if (!g_utf8_validate(tmpstr, -1, NULL)) {
5962                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
5963                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
5964                 g_free(tmpstr);
5965                 tmpstr = mybuf;
5966         }
5967
5968         codeconv_set_strict(TRUE);
5969         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5970                 conv_get_charset_str(compose->out_encoding));
5971         codeconv_set_strict(FALSE);
5972         
5973         if (!dest || *dest == '\0') {
5974                 gchar *test_conv_global_out = NULL;
5975                 gchar *test_conv_reply = NULL;
5976
5977                 /* automatic mode. be automatic. */
5978                 codeconv_set_strict(TRUE);
5979
5980                 out_codeset = conv_get_outgoing_charset_str();
5981                 if (out_codeset) {
5982                         debug_print("trying to convert to %s\n", out_codeset);
5983                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5984                 }
5985
5986                 if (!test_conv_global_out && compose->orig_charset
5987                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5988                         out_codeset = compose->orig_charset;
5989                         debug_print("failure; trying to convert to %s\n", out_codeset);
5990                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5991                 }
5992
5993                 if (!test_conv_global_out && !test_conv_reply) {
5994                         /* we're lost */
5995                         out_codeset = CS_INTERNAL;
5996                         debug_print("finally using %s\n", out_codeset);
5997                 }
5998                 g_free(test_conv_global_out);
5999                 g_free(test_conv_reply);
6000                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
6001                                         out_codeset);
6002                 codeconv_set_strict(FALSE);
6003         }
6004         g_free(tmpstr);
6005 }
6006
6007 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
6008 {
6009         gchar *address;
6010
6011         g_return_if_fail(user_data != NULL);
6012
6013         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6014         g_strstrip(address);
6015         if (*address != '\0') {
6016                 gchar *name = procheader_get_fromname(address);
6017                 extract_address(address);
6018                 addressbook_add_contact(name, address, NULL, NULL);
6019         }
6020         g_free(address);
6021 }
6022
6023 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6024 {
6025         GtkWidget *menuitem;
6026         gchar *address;
6027
6028         g_return_if_fail(menu != NULL);
6029         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6030
6031         menuitem = gtk_separator_menu_item_new();
6032         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6033         gtk_widget_show(menuitem);
6034
6035         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6036         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6037
6038         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6039         g_strstrip(address);
6040         if (*address == '\0') {
6041                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6042         }
6043
6044         g_signal_connect(G_OBJECT(menuitem), "activate",
6045                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6046         gtk_widget_show(menuitem);
6047 }
6048
6049 static void compose_create_header_entry(Compose *compose) 
6050 {
6051         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6052
6053         GtkWidget *combo;
6054         GtkWidget *entry;
6055         gchar **string;
6056         const gchar *header = NULL;
6057         ComposeHeaderEntry *headerentry;
6058         gboolean standard_header = FALSE;
6059         
6060         headerentry = g_new0(ComposeHeaderEntry, 1);
6061
6062         /* Combo box */
6063         combo = gtk_combo_box_entry_new_text();
6064         string = headers; 
6065         while(*string != NULL) {
6066                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6067                         (gchar*)prefs_common_translated_header_name(*string));
6068                 string++;
6069         }
6070         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6071         g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6072                          G_CALLBACK(compose_grab_focus_cb), compose);
6073         gtk_widget_show(combo);
6074         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6075                         compose->header_nextrow, compose->header_nextrow+1,
6076                         GTK_SHRINK, GTK_FILL, 0, 0);
6077         if (compose->header_last) {     
6078                 const gchar *last_header_entry = gtk_entry_get_text(
6079                                 GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6080                 string = headers;
6081                 while (*string != NULL) {
6082                         if (!strcmp(*string, last_header_entry))
6083                                 standard_header = TRUE;
6084                         string++;
6085                 }
6086                 if (standard_header)
6087                         header = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6088         }
6089         if (!compose->header_last || !standard_header) {
6090                 switch(compose->account->protocol) {
6091                         case A_NNTP:
6092                                 header = prefs_common_translated_header_name("Newsgroups:");
6093                                 break;
6094                         default:
6095                                 header = prefs_common_translated_header_name("To:");
6096                                 break;
6097                 }                                                                   
6098         }
6099         if (header)
6100                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), header);
6101
6102         g_signal_connect_after(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6103                          G_CALLBACK(compose_grab_focus_cb), compose);
6104
6105         /* Entry field */
6106         entry = gtk_entry_new(); 
6107         gtk_widget_show(entry);
6108         gtk_tooltips_set_tip(compose->tooltips, entry,
6109                 _("Use <tab> to autocomplete from addressbook"), NULL);
6110         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6111                         compose->header_nextrow, compose->header_nextrow+1,
6112                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6113
6114         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6115                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6116                          headerentry);
6117         g_signal_connect(G_OBJECT(entry), "changed", 
6118                          G_CALLBACK(compose_headerentry_changed_cb), 
6119                          headerentry);
6120         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6121                          G_CALLBACK(compose_grab_focus_cb), compose);
6122                          
6123         /* email dnd */
6124         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6125                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6126                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6127         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6128                          G_CALLBACK(compose_header_drag_received_cb),
6129                          entry);
6130         g_signal_connect(G_OBJECT(entry), "drag-drop",
6131                          G_CALLBACK(compose_drag_drop),
6132                          compose);
6133         g_signal_connect(G_OBJECT(entry), "populate-popup",
6134                          G_CALLBACK(compose_entry_popup_extend),
6135                          NULL);
6136         
6137         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6138
6139         headerentry->compose = compose;
6140         headerentry->combo = combo;
6141         headerentry->entry = entry;
6142         headerentry->headernum = compose->header_nextrow;
6143
6144         compose->header_nextrow++;
6145         compose->header_last = headerentry;             
6146         compose->header_list =
6147                 g_slist_append(compose->header_list,
6148                                headerentry);
6149 }
6150
6151 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6152 {
6153         ComposeHeaderEntry *last_header;
6154         
6155         last_header = compose->header_last;
6156
6157         gtk_entry_set_text(GTK_ENTRY(GTK_BIN(last_header->combo)->child), header);
6158         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6159 }
6160
6161 static void compose_remove_header_entries(Compose *compose) 
6162 {
6163         GSList *list;
6164         for (list = compose->header_list; list; list = list->next) {
6165                 ComposeHeaderEntry *headerentry = 
6166                         (ComposeHeaderEntry *)list->data;
6167                 gtk_widget_destroy(headerentry->combo);
6168                 gtk_widget_destroy(headerentry->entry);
6169                 g_free(headerentry);
6170         }
6171         compose->header_last = NULL;
6172         g_slist_free(compose->header_list);
6173         compose->header_list = NULL;
6174         compose->header_nextrow = 1;
6175         compose_create_header_entry(compose);
6176 }
6177
6178 static GtkWidget *compose_create_header(Compose *compose) 
6179 {
6180         GtkWidget *from_optmenu_hbox;
6181         GtkWidget *header_scrolledwin;
6182         GtkWidget *header_table;
6183
6184         gint count = 0;
6185
6186         /* header labels and entries */
6187         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6188         gtk_widget_show(header_scrolledwin);
6189         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6190
6191         header_table = gtk_table_new(2, 2, FALSE);
6192         gtk_widget_show(header_table);
6193         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6194         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6195         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_NONE);
6196         count = 0;
6197
6198         /* option menu for selecting accounts */
6199         from_optmenu_hbox = compose_account_option_menu_create(compose);
6200         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6201                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6202         count++;
6203
6204         compose->header_table = header_table;
6205         compose->header_list = NULL;
6206         compose->header_nextrow = count;
6207
6208         compose_create_header_entry(compose);
6209
6210         compose->table            = NULL;
6211
6212         return header_scrolledwin ;
6213 }
6214
6215 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6216 {
6217         Compose *compose = (Compose *)data;
6218         GdkEventButton event;
6219         
6220         event.button = 3;
6221         event.time = gtk_get_current_event_time();
6222
6223         return attach_button_pressed(compose->attach_clist, &event, compose);
6224 }
6225
6226 static GtkWidget *compose_create_attach(Compose *compose)
6227 {
6228         GtkWidget *attach_scrwin;
6229         GtkWidget *attach_clist;
6230
6231         GtkListStore *store;
6232         GtkCellRenderer *renderer;
6233         GtkTreeViewColumn *column;
6234         GtkTreeSelection *selection;
6235
6236         /* attachment list */
6237         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6238         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6239                                        GTK_POLICY_AUTOMATIC,
6240                                        GTK_POLICY_AUTOMATIC);
6241         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6242
6243         store = gtk_list_store_new(N_ATTACH_COLS, 
6244                                    G_TYPE_STRING,
6245                                    G_TYPE_STRING,
6246                                    G_TYPE_STRING,
6247                                    G_TYPE_POINTER,
6248                                    G_TYPE_AUTO_POINTER,
6249                                    -1);
6250         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6251                                         (GTK_TREE_MODEL(store)));
6252         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6253         g_object_unref(store);
6254         
6255         renderer = gtk_cell_renderer_text_new();
6256         column = gtk_tree_view_column_new_with_attributes
6257                         (_("Mime type"), renderer, "text", 
6258                          COL_MIMETYPE, NULL);
6259         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6260         
6261         renderer = gtk_cell_renderer_text_new();
6262         column = gtk_tree_view_column_new_with_attributes
6263                         (_("Size"), renderer, "text", 
6264                          COL_SIZE, NULL);
6265         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6266         
6267         renderer = gtk_cell_renderer_text_new();
6268         column = gtk_tree_view_column_new_with_attributes
6269                         (_("Name"), renderer, "text", 
6270                          COL_NAME, NULL);
6271         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6272
6273         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6274                                      prefs_common.use_stripes_everywhere);
6275         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6276         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6277
6278         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6279                          G_CALLBACK(attach_selected), compose);
6280         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6281                          G_CALLBACK(attach_button_pressed), compose);
6282 #ifndef MAEMO
6283         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6284                          G_CALLBACK(popup_attach_button_pressed), compose);
6285 #else
6286         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6287                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6288         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6289                          G_CALLBACK(popup_attach_button_pressed), compose);
6290 #endif
6291         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6292                          G_CALLBACK(attach_key_pressed), compose);
6293
6294         /* drag and drop */
6295         gtk_drag_dest_set(attach_clist,
6296                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6297                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6298                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6299         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6300                          G_CALLBACK(compose_attach_drag_received_cb),
6301                          compose);
6302         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6303                          G_CALLBACK(compose_drag_drop),
6304                          compose);
6305
6306         compose->attach_scrwin = attach_scrwin;
6307         compose->attach_clist  = attach_clist;
6308
6309         return attach_scrwin;
6310 }
6311
6312 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6313 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6314
6315 static GtkWidget *compose_create_others(Compose *compose)
6316 {
6317         GtkWidget *table;
6318         GtkWidget *savemsg_checkbtn;
6319         GtkWidget *savemsg_entry;
6320         GtkWidget *savemsg_select;
6321         
6322         guint rowcount = 0;
6323         gchar *folderidentifier;
6324
6325         /* Table for settings */
6326         table = gtk_table_new(3, 1, FALSE);
6327         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6328         gtk_widget_show(table);
6329         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6330         rowcount = 0;
6331
6332         /* Save Message to folder */
6333         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6334         gtk_widget_show(savemsg_checkbtn);
6335         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6336         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6337                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6338         }
6339         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6340                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6341
6342         savemsg_entry = gtk_entry_new();
6343         gtk_widget_show(savemsg_entry);
6344         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6345         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6346         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6347                          G_CALLBACK(compose_grab_focus_cb), compose);
6348         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6349                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6350                                   (compose->account, F_OUTBOX));
6351                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6352                 g_free(folderidentifier);
6353         }
6354
6355         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6356         gtk_widget_show(savemsg_select);
6357         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6358         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6359                          G_CALLBACK(compose_savemsg_select_cb),
6360                          compose);
6361
6362         rowcount++;
6363
6364         compose->savemsg_checkbtn = savemsg_checkbtn;
6365         compose->savemsg_entry = savemsg_entry;
6366
6367         return table;   
6368 }
6369
6370 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6371 {
6372         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6373                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6374 }
6375
6376 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6377 {
6378         FolderItem *dest;
6379         gchar * path;
6380
6381         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
6382         if (!dest) return;
6383
6384         path = folder_item_get_identifier(dest);
6385
6386         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6387         g_free(path);
6388 }
6389
6390 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6391                                   GdkAtom clip, GtkTextIter *insert_place);
6392
6393
6394 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6395                                        Compose *compose)
6396 {
6397         gint prev_autowrap;
6398         GtkTextBuffer *buffer = GTK_TEXT_VIEW(text)->buffer;
6399 #if USE_ASPELL
6400         if (event->button == 3) {
6401                 GtkTextIter iter;
6402                 GtkTextIter sel_start, sel_end;
6403                 gboolean stuff_selected;
6404                 gint x, y;
6405                 /* move the cursor to allow GtkAspell to check the word
6406                  * under the mouse */
6407                 if (event->x && event->y) {
6408                         gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6409                                 GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6410                                 &x, &y);
6411                         gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6412                                 &iter, x, y);
6413                 } else {
6414                         GtkTextMark *mark = gtk_text_buffer_get_insert(buffer);
6415                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6416                 }
6417                 /* get selection */
6418                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6419                                 buffer,
6420                                 &sel_start, &sel_end);
6421
6422                 gtk_text_buffer_place_cursor (buffer, &iter);
6423                 /* reselect stuff */
6424                 if (stuff_selected 
6425                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6426                         gtk_text_buffer_select_range(buffer,
6427                                 &sel_start, &sel_end);
6428                 }
6429                 return FALSE; /* pass the event so that the right-click goes through */
6430         }
6431 #endif
6432         if (event->button == 2) {
6433                 GtkTextIter iter;
6434                 gint x, y;
6435                 BLOCK_WRAP();
6436                 
6437                 /* get the middle-click position to paste at the correct place */
6438                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6439                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6440                         &x, &y);
6441                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6442                         &iter, x, y);
6443                 
6444                 entry_paste_clipboard(compose, text, 
6445                                 prefs_common.linewrap_pastes,
6446                                 GDK_SELECTION_PRIMARY, &iter);
6447                 UNBLOCK_WRAP();
6448                 return TRUE;
6449         }
6450         return FALSE;
6451 }
6452
6453 #if USE_ASPELL
6454 static void compose_spell_menu_changed(void *data)
6455 {
6456         Compose *compose = (Compose *)data;
6457         GSList *items;
6458         GtkWidget *menuitem;
6459         GtkWidget *parent_item;
6460         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6461         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6462         GSList *spell_menu;
6463
6464         if (compose->gtkaspell == NULL)
6465                 return;
6466
6467         parent_item = gtk_item_factory_get_item(ifactory, 
6468                         "/Spelling/Options");
6469
6470         /* setting the submenu removes /Spelling/Options from the factory 
6471          * so we need to save it */
6472
6473         if (parent_item == NULL) {
6474                 parent_item = compose->aspell_options_menu;
6475                 gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
6476         } else
6477                 compose->aspell_options_menu = parent_item;
6478
6479         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6480
6481         spell_menu = g_slist_reverse(spell_menu);
6482         for (items = spell_menu;
6483              items; items = items->next) {
6484                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6485                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6486                 gtk_widget_show(GTK_WIDGET(menuitem));
6487         }
6488         g_slist_free(spell_menu);
6489
6490         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6491         
6492 }
6493 #endif
6494
6495 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6496 {
6497         Compose *compose = (Compose *)data;
6498         GdkEventButton event;
6499         
6500         event.button = 3;
6501         event.time = gtk_get_current_event_time();
6502         event.x = 0;
6503         event.y = 0;
6504
6505         return text_clicked(compose->text, &event, compose);
6506 }
6507
6508 static gboolean compose_force_window_origin = TRUE;
6509 static Compose *compose_create(PrefsAccount *account,
6510                                                  FolderItem *folder,
6511                                                  ComposeMode mode,
6512                                                  gboolean batch)
6513 {
6514         Compose   *compose;
6515         GtkWidget *window;
6516         GtkWidget *vbox;
6517         GtkWidget *menubar;
6518         GtkWidget *handlebox;
6519
6520         GtkWidget *notebook;
6521         
6522         GtkWidget *attach_hbox;
6523         GtkWidget *attach_lab1;
6524         GtkWidget *attach_lab2;
6525
6526         GtkWidget *vbox2;
6527
6528         GtkWidget *label;
6529         GtkWidget *subject_hbox;
6530         GtkWidget *subject_frame;
6531         GtkWidget *subject_entry;
6532         GtkWidget *subject;
6533         GtkWidget *paned;
6534
6535         GtkWidget *edit_vbox;
6536         GtkWidget *ruler_hbox;
6537         GtkWidget *ruler;
6538         GtkWidget *scrolledwin;
6539         GtkWidget *text;
6540         GtkTextBuffer *buffer;
6541         GtkClipboard *clipboard;
6542
6543         UndoMain *undostruct;
6544
6545         gchar *titles[N_ATTACH_COLS];
6546         guint n_menu_entries;
6547         GtkWidget *popupmenu;
6548         GtkItemFactory *popupfactory;
6549         GtkItemFactory *ifactory;
6550         GtkWidget *tmpl_menu;
6551         gint n_entries;
6552         GtkWidget *menuitem;
6553
6554 #if USE_ASPELL
6555         GtkAspell * gtkaspell = NULL;
6556 #endif
6557
6558         static GdkGeometry geometry;
6559
6560         g_return_val_if_fail(account != NULL, NULL);
6561
6562         debug_print("Creating compose window...\n");
6563         compose = g_new0(Compose, 1);
6564
6565         titles[COL_MIMETYPE] = _("MIME type");
6566         titles[COL_SIZE]     = _("Size");
6567         titles[COL_NAME]     = _("Name");
6568
6569         compose->batch = batch;
6570         compose->account = account;
6571         compose->folder = folder;
6572         
6573         compose->mutex = g_mutex_new();
6574         compose->set_cursor_pos = -1;
6575
6576         compose->tooltips = gtk_tooltips_new();
6577
6578         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6579
6580         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6581         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6582
6583         if (!geometry.max_width) {
6584                 geometry.max_width = gdk_screen_width();
6585                 geometry.max_height = gdk_screen_height();
6586         }
6587
6588         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6589                                       &geometry, GDK_HINT_MAX_SIZE);
6590         if (!geometry.min_width) {
6591                 geometry.min_width = 600;
6592                 geometry.min_height = 480;
6593         }
6594         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6595                                       &geometry, GDK_HINT_MIN_SIZE);
6596
6597 #ifndef MAEMO   
6598         if (compose_force_window_origin)
6599                 gtk_widget_set_uposition(window, prefs_common.compose_x, 
6600                                  prefs_common.compose_y);
6601 #endif
6602         g_signal_connect(G_OBJECT(window), "delete_event",
6603                          G_CALLBACK(compose_delete_cb), compose);
6604         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6605         gtk_widget_realize(window);
6606
6607         gtkut_widget_set_composer_icon(window);
6608
6609         vbox = gtk_vbox_new(FALSE, 0);
6610         gtk_container_add(GTK_CONTAINER(window), vbox);
6611
6612         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
6613         menubar = menubar_create(window, compose_entries,
6614                                  n_menu_entries, "<Compose>", compose);
6615         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6616
6617         if (prefs_common.toolbar_detachable) {
6618                 handlebox = gtk_handle_box_new();
6619         } else {
6620                 handlebox = gtk_hbox_new(FALSE, 0);
6621         }
6622         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6623
6624         gtk_widget_realize(handlebox);
6625 #ifdef MAEMO
6626         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6627                                           (gpointer)compose);
6628 #else
6629         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6630                                           (gpointer)compose);
6631 #endif
6632
6633         vbox2 = gtk_vbox_new(FALSE, 2);
6634         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6635         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6636         
6637         /* Notebook */
6638         notebook = gtk_notebook_new();
6639         gtk_widget_set_size_request(notebook, -1, 130);
6640         gtk_widget_show(notebook);
6641
6642         /* header labels and entries */
6643         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6644                         compose_create_header(compose),
6645                         gtk_label_new_with_mnemonic(_("Hea_der")));
6646         /* attachment list */
6647         attach_hbox = gtk_hbox_new(FALSE, 0);
6648         gtk_widget_show(attach_hbox);
6649         
6650         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6651         gtk_widget_show(attach_lab1);
6652         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6653         
6654         attach_lab2 = gtk_label_new("");
6655         gtk_widget_show(attach_lab2);
6656         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6657         
6658         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6659                         compose_create_attach(compose),
6660                         attach_hbox);
6661         /* Others Tab */
6662         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6663                         compose_create_others(compose),
6664                         gtk_label_new_with_mnemonic(_("Othe_rs")));
6665
6666         /* Subject */
6667         subject_hbox = gtk_hbox_new(FALSE, 0);
6668         gtk_widget_show(subject_hbox);
6669
6670         subject_frame = gtk_frame_new(NULL);
6671         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6672         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6673         gtk_widget_show(subject_frame);
6674
6675         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6676         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6677         gtk_widget_show(subject);
6678
6679         label = gtk_label_new(_("Subject:"));
6680         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6681         gtk_widget_show(label);
6682
6683         subject_entry = gtk_entry_new();
6684         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6685         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6686                          G_CALLBACK(compose_grab_focus_cb), compose);
6687         gtk_widget_show(subject_entry);
6688         compose->subject_entry = subject_entry;
6689         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6690         
6691         edit_vbox = gtk_vbox_new(FALSE, 0);
6692
6693         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6694
6695         /* ruler */
6696         ruler_hbox = gtk_hbox_new(FALSE, 0);
6697         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6698
6699         ruler = gtk_shruler_new();
6700         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6701         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6702                            BORDER_WIDTH);
6703
6704         /* text widget */
6705         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6706         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6707                                        GTK_POLICY_AUTOMATIC,
6708                                        GTK_POLICY_AUTOMATIC);
6709         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6710                                             GTK_SHADOW_IN);
6711         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6712         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6713
6714         text = gtk_text_view_new();
6715         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6716         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6717         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6718         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6719         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6720         
6721         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6722
6723         g_signal_connect_after(G_OBJECT(text), "size_allocate",
6724                                G_CALLBACK(compose_edit_size_alloc),
6725                                ruler);
6726         g_signal_connect(G_OBJECT(buffer), "changed",
6727                          G_CALLBACK(compose_changed_cb), compose);
6728         g_signal_connect(G_OBJECT(text), "grab_focus",
6729                          G_CALLBACK(compose_grab_focus_cb), compose);
6730         g_signal_connect(G_OBJECT(buffer), "insert_text",
6731                          G_CALLBACK(text_inserted), compose);
6732         g_signal_connect(G_OBJECT(text), "button_press_event",
6733                          G_CALLBACK(text_clicked), compose);
6734 #ifndef MAEMO
6735         g_signal_connect(G_OBJECT(text), "popup-menu",
6736                          G_CALLBACK(compose_popup_menu), compose);
6737 #else
6738         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6739                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6740         g_signal_connect(G_OBJECT(text), "tap-and-hold",
6741                          G_CALLBACK(compose_popup_menu), compose);
6742 #endif
6743         g_signal_connect(G_OBJECT(subject_entry), "changed",
6744                          G_CALLBACK(compose_changed_cb), compose);
6745
6746         /* drag and drop */
6747         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6748                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6749                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6750         g_signal_connect(G_OBJECT(text), "drag_data_received",
6751                          G_CALLBACK(compose_insert_drag_received_cb),
6752                          compose);
6753         g_signal_connect(G_OBJECT(text), "drag-drop",
6754                          G_CALLBACK(compose_drag_drop),
6755                          compose);
6756         gtk_widget_show_all(vbox);
6757
6758         /* pane between attach clist and text */
6759         paned = gtk_vpaned_new();
6760         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
6761         gtk_container_add(GTK_CONTAINER(vbox2), paned);
6762 #ifdef MAEMO
6763         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
6764                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
6765         else
6766                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
6767 #endif
6768         gtk_paned_add1(GTK_PANED(paned), notebook);
6769         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
6770         gtk_widget_show_all(paned);
6771
6772
6773         if (prefs_common.textfont) {
6774                 PangoFontDescription *font_desc;
6775
6776                 font_desc = pango_font_description_from_string
6777                         (prefs_common.textfont);
6778                 if (font_desc) {
6779                         gtk_widget_modify_font(text, font_desc);
6780                         pango_font_description_free(font_desc);
6781                 }
6782         }
6783
6784         n_entries = sizeof(compose_popup_entries) /
6785                 sizeof(compose_popup_entries[0]);
6786         popupmenu = menu_create_items(compose_popup_entries, n_entries,
6787                                       "<Compose>", &popupfactory,
6788                                       compose);
6789
6790         ifactory = gtk_item_factory_from_widget(menubar);
6791         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6792         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6793         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
6794
6795         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6796
6797         undostruct = undo_init(text);
6798         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
6799                                    menubar);
6800
6801         address_completion_start(window);
6802
6803         compose->window        = window;
6804         compose->vbox          = vbox;
6805         compose->menubar       = menubar;
6806         compose->handlebox     = handlebox;
6807
6808         compose->vbox2         = vbox2;
6809
6810         compose->paned = paned;
6811
6812         compose->attach_label  = attach_lab2;
6813
6814         compose->notebook      = notebook;
6815         compose->edit_vbox     = edit_vbox;
6816         compose->ruler_hbox    = ruler_hbox;
6817         compose->ruler         = ruler;
6818         compose->scrolledwin   = scrolledwin;
6819         compose->text          = text;
6820
6821         compose->focused_editable = NULL;
6822
6823         compose->popupmenu    = popupmenu;
6824         compose->popupfactory = popupfactory;
6825
6826         compose->tmpl_menu = tmpl_menu;
6827
6828         compose->mode = mode;
6829         compose->rmode = mode;
6830
6831         compose->targetinfo = NULL;
6832         compose->replyinfo  = NULL;
6833         compose->fwdinfo    = NULL;
6834
6835         compose->replyto     = NULL;
6836         compose->cc          = NULL;
6837         compose->bcc         = NULL;
6838         compose->followup_to = NULL;
6839
6840         compose->ml_post     = NULL;
6841
6842         compose->inreplyto   = NULL;
6843         compose->references  = NULL;
6844         compose->msgid       = NULL;
6845         compose->boundary    = NULL;
6846
6847         compose->autowrap       = prefs_common.autowrap;
6848
6849         compose->use_signing    = FALSE;
6850         compose->use_encryption = FALSE;
6851         compose->privacy_system = NULL;
6852
6853         compose->modified = FALSE;
6854
6855         compose->return_receipt = FALSE;
6856
6857         compose->to_list        = NULL;
6858         compose->newsgroup_list = NULL;
6859
6860         compose->undostruct = undostruct;
6861
6862         compose->sig_str = NULL;
6863
6864         compose->exteditor_file    = NULL;
6865         compose->exteditor_pid     = -1;
6866         compose->exteditor_tag     = -1;
6867         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
6868
6869 #if USE_ASPELL
6870         menu_set_sensitive(ifactory, "/Spelling", FALSE);
6871         if (mode != COMPOSE_REDIRECT) {
6872                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
6873                     strcmp(prefs_common.dictionary, "")) {
6874                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
6875                                                   prefs_common.dictionary,
6876                                                   prefs_common.alt_dictionary,
6877                                                   conv_get_locale_charset_str(),
6878                                                   prefs_common.misspelled_col,
6879                                                   prefs_common.check_while_typing,
6880                                                   prefs_common.recheck_when_changing_dict,
6881                                                   prefs_common.use_alternate,
6882                                                   prefs_common.use_both_dicts,
6883                                                   GTK_TEXT_VIEW(text),
6884                                                   GTK_WINDOW(compose->window),
6885                                                   compose_spell_menu_changed,
6886                                                   compose);
6887                         if (!gtkaspell) {
6888                                 alertpanel_error(_("Spell checker could not "
6889                                                 "be started.\n%s"),
6890                                                 gtkaspell_checkers_strerror());
6891                                 gtkaspell_checkers_reset_error();
6892                         } else {
6893                                 if (!gtkaspell_set_sug_mode(gtkaspell,
6894                                                 prefs_common.aspell_sugmode)) {
6895                                         debug_print("Aspell: could not set "
6896                                                     "suggestion mode %s\n",
6897                                                     gtkaspell_checkers_strerror());
6898                                         gtkaspell_checkers_reset_error();
6899                                 }
6900
6901                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
6902                         }
6903                 }
6904         }
6905         compose->gtkaspell = gtkaspell;
6906         compose_spell_menu_changed(compose);
6907 #endif
6908
6909         compose_select_account(compose, account, TRUE);
6910
6911         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
6912         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
6913                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
6914
6915         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
6916                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
6917         
6918         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
6919                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
6920
6921         menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
6922
6923         if (account->protocol != A_NNTP)
6924                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
6925                                 prefs_common_translated_header_name("To:"));
6926         else
6927                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
6928                                 prefs_common_translated_header_name("Newsgroups:"));
6929
6930         addressbook_set_target_compose(compose);
6931         
6932         if (mode != COMPOSE_REDIRECT)
6933                 compose_set_template_menu(compose);
6934         else {
6935                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6936                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
6937         }
6938
6939         compose_list = g_list_append(compose_list, compose);
6940
6941         if (!prefs_common.show_ruler)
6942                 gtk_widget_hide(ruler_hbox);
6943                 
6944         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
6945         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
6946                                        prefs_common.show_ruler);
6947
6948         /* Priority */
6949         compose->priority = PRIORITY_NORMAL;
6950         compose_update_priority_menu_item(compose);
6951
6952         compose_set_out_encoding(compose);
6953         
6954         /* Actions menu */
6955         compose_update_actions_menu(compose);
6956
6957         /* Privacy Systems menu */
6958         compose_update_privacy_systems_menu(compose);
6959
6960         activate_privacy_system(compose, account, TRUE);
6961         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
6962         if (batch) {
6963                 gtk_widget_realize(window);
6964         } else {
6965                 gtk_widget_show(window);
6966 #ifdef MAEMO
6967                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
6968                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
6969 #endif
6970         }
6971         
6972         return compose;
6973 }
6974
6975 static GtkWidget *compose_account_option_menu_create(Compose *compose)
6976 {
6977         GList *accounts;
6978         GtkWidget *hbox;
6979         GtkWidget *optmenu;
6980         GtkWidget *optmenubox;
6981         GtkListStore *menu;
6982         GtkTreeIter iter;
6983         GtkWidget *from_name = NULL;
6984
6985         gint num = 0, def_menu = 0;
6986         
6987         accounts = account_get_list();
6988         g_return_val_if_fail(accounts != NULL, NULL);
6989
6990         optmenubox = gtk_event_box_new();
6991         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
6992         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
6993
6994         hbox = gtk_hbox_new(FALSE, 6);
6995         from_name = gtk_entry_new();
6996         
6997         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
6998                          G_CALLBACK(compose_grab_focus_cb), compose);
6999
7000         for (; accounts != NULL; accounts = accounts->next, num++) {
7001                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
7002                 gchar *name, *from = NULL;
7003
7004                 if (ac == compose->account) def_menu = num;
7005
7006                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
7007                                        ac->account_name);
7008                 
7009                 if (ac == compose->account) {
7010                         if (ac->name && *ac->name) {
7011                                 gchar *buf;
7012                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
7013                                 from = g_strdup_printf("%s <%s>",
7014                                                        buf, ac->address);
7015                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7016                         } else {
7017                                 from = g_strdup_printf("%s",
7018                                                        ac->address);
7019                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7020                         }
7021                 }
7022                 COMBOBOX_ADD(menu, name, ac->account_id);
7023                 g_free(name);
7024                 g_free(from);
7025         }
7026
7027         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7028
7029         g_signal_connect(G_OBJECT(optmenu), "changed",
7030                         G_CALLBACK(account_activated),
7031                         compose);
7032         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7033                          G_CALLBACK(compose_entry_popup_extend),
7034                          NULL);
7035
7036         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7037         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7038         
7039         gtk_tooltips_set_tip(compose->tooltips, optmenubox,
7040                 _("Account to use for this email"), NULL);
7041         gtk_tooltips_set_tip(compose->tooltips, from_name,
7042                 _("Sender address to be used"), NULL);
7043
7044         compose->from_name = from_name;
7045         
7046         return hbox;
7047 }
7048
7049 static void compose_set_priority_cb(gpointer data,
7050                                     guint action,
7051                                     GtkWidget *widget)
7052 {
7053         Compose *compose = (Compose *) data;
7054         compose->priority = action;
7055 }
7056
7057 static void compose_reply_change_mode(gpointer data,
7058                                     ComposeMode action,
7059                                     GtkWidget *widget)
7060 {
7061         Compose *compose = (Compose *) data;
7062         gboolean was_modified = compose->modified;
7063
7064         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7065         
7066         g_return_if_fail(compose->replyinfo != NULL);
7067         
7068         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7069                 ml = TRUE;
7070         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7071                 followup = TRUE;
7072         if (action == COMPOSE_REPLY_TO_ALL)
7073                 all = TRUE;
7074         if (action == COMPOSE_REPLY_TO_SENDER)
7075                 sender = TRUE;
7076         if (action == COMPOSE_REPLY_TO_LIST)
7077                 ml = TRUE;
7078
7079         compose_remove_header_entries(compose);
7080         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7081         if (compose->account->set_autocc && compose->account->auto_cc)
7082                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7083
7084         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7085                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7086         
7087         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7088                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7089         compose_show_first_last_header(compose, TRUE);
7090         compose->modified = was_modified;
7091         compose_set_title(compose);
7092 }
7093
7094 static void compose_update_priority_menu_item(Compose * compose)
7095 {
7096         GtkItemFactory *ifactory;
7097         GtkWidget *menuitem = NULL;
7098
7099         ifactory = gtk_item_factory_from_widget(compose->menubar);
7100         
7101         switch (compose->priority) {
7102                 case PRIORITY_HIGHEST:
7103                         menuitem = gtk_item_factory_get_item
7104                                 (ifactory, "/Options/Priority/Highest");
7105                         break;
7106                 case PRIORITY_HIGH:
7107                         menuitem = gtk_item_factory_get_item
7108                                 (ifactory, "/Options/Priority/High");
7109                         break;
7110                 case PRIORITY_NORMAL:
7111                         menuitem = gtk_item_factory_get_item
7112                                 (ifactory, "/Options/Priority/Normal");
7113                         break;
7114                 case PRIORITY_LOW:
7115                         menuitem = gtk_item_factory_get_item
7116                                 (ifactory, "/Options/Priority/Low");
7117                         break;
7118                 case PRIORITY_LOWEST:
7119                         menuitem = gtk_item_factory_get_item
7120                                 (ifactory, "/Options/Priority/Lowest");
7121                         break;
7122         }
7123         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7124 }       
7125
7126 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7127 {
7128         Compose *compose = (Compose *) data;
7129         gchar *systemid;
7130         GtkItemFactory *ifactory;
7131         gboolean can_sign = FALSE, can_encrypt = FALSE;
7132
7133         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7134
7135         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7136                 return;
7137
7138         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7139         g_free(compose->privacy_system);
7140         compose->privacy_system = NULL;
7141         if (systemid != NULL) {
7142                 compose->privacy_system = g_strdup(systemid);
7143
7144                 can_sign = privacy_system_can_sign(systemid);
7145                 can_encrypt = privacy_system_can_encrypt(systemid);
7146         }
7147
7148         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7149
7150         ifactory = gtk_item_factory_from_widget(compose->menubar);
7151         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7152         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7153 }
7154
7155 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7156 {
7157         static gchar *branch_path = "/Options/Privacy System";
7158         GtkItemFactory *ifactory;
7159         GtkWidget *menuitem = NULL;
7160         GList *amenu;
7161         gboolean can_sign = FALSE, can_encrypt = FALSE;
7162         gboolean found = FALSE;
7163
7164         ifactory = gtk_item_factory_from_widget(compose->menubar);
7165
7166         if (compose->privacy_system != NULL) {
7167                 gchar *systemid;
7168                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7169                 g_return_if_fail(menuitem != NULL);
7170
7171                 amenu = GTK_MENU_SHELL(menuitem)->children;
7172                 menuitem = NULL;
7173                 while (amenu != NULL) {
7174                         GList *alist = amenu->next;
7175
7176                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7177                         if (systemid != NULL) {
7178                                 if (strcmp(systemid, compose->privacy_system) == 0) {
7179                                         menuitem = GTK_WIDGET(amenu->data);
7180
7181                                         can_sign = privacy_system_can_sign(systemid);
7182                                         can_encrypt = privacy_system_can_encrypt(systemid);
7183                                         found = TRUE;
7184                                         break;
7185                                 } 
7186                         } else if (strlen(compose->privacy_system) == 0) {
7187                                         menuitem = GTK_WIDGET(amenu->data);
7188
7189                                         can_sign = FALSE;
7190                                         can_encrypt = FALSE;
7191                                         found = TRUE;
7192                                         break;
7193                         }
7194
7195                         amenu = alist;
7196                 }
7197                 if (menuitem != NULL)
7198                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7199                 
7200                 if (warn && !found && strlen(compose->privacy_system)) {
7201                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7202                                   "will not be able to sign or encrypt this message."),
7203                                   compose->privacy_system);
7204                 }
7205         } 
7206
7207         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7208         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7209 }       
7210  
7211 static void compose_set_out_encoding(Compose *compose)
7212 {
7213         GtkItemFactoryEntry *entry;
7214         GtkItemFactory *ifactory;
7215         CharSet out_encoding;
7216         gchar *path, *p, *q;
7217         GtkWidget *item;
7218
7219         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7220         ifactory = gtk_item_factory_from_widget(compose->menubar);
7221
7222         for (entry = compose_entries; entry->callback != compose_address_cb;
7223              entry++) {
7224                 if (entry->callback == compose_set_encoding_cb &&
7225                     (CharSet)entry->callback_action == out_encoding) {
7226                         p = q = path = g_strdup(entry->path);
7227                         while (*p) {
7228                                 if (*p == '_') {
7229                                         if (p[1] == '_') {
7230                                                 p++;
7231                                                 *q++ = '_';
7232                                         }
7233                                 } else
7234                                         *q++ = *p;
7235                                 p++;
7236                         }
7237                         *q = '\0';
7238                         item = gtk_item_factory_get_item(ifactory, path);
7239                         gtk_widget_activate(item);
7240                         g_free(path);
7241                         break;
7242                 }
7243         }
7244 }
7245
7246 static void compose_set_template_menu(Compose *compose)
7247 {
7248         GSList *tmpl_list, *cur;
7249         GtkWidget *menu;
7250         GtkWidget *item;
7251
7252         tmpl_list = template_get_config();
7253
7254         menu = gtk_menu_new();
7255
7256         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7257                 Template *tmpl = (Template *)cur->data;
7258
7259                 item = gtk_menu_item_new_with_label(tmpl->name);
7260                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7261                 g_signal_connect(G_OBJECT(item), "activate",
7262                                  G_CALLBACK(compose_template_activate_cb),
7263                                  compose);
7264                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7265                 gtk_widget_show(item);
7266         }
7267
7268         gtk_widget_show(menu);
7269         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7270 }
7271
7272 void compose_update_actions_menu(Compose *compose)
7273 {
7274         GtkItemFactory *ifactory;
7275
7276         ifactory = gtk_item_factory_from_widget(compose->menubar);
7277         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
7278 }
7279
7280 static void compose_update_privacy_systems_menu(Compose *compose)
7281 {
7282         static gchar *branch_path = "/Options/Privacy System";
7283         GtkItemFactory *ifactory;
7284         GtkWidget *menuitem;
7285         GSList *systems, *cur;
7286         GList *amenu;
7287         GtkWidget *widget;
7288         GtkWidget *system_none;
7289         GSList *group;
7290
7291         ifactory = gtk_item_factory_from_widget(compose->menubar);
7292
7293         /* remove old entries */
7294         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7295         g_return_if_fail(menuitem != NULL);
7296
7297         amenu = GTK_MENU_SHELL(menuitem)->children->next;
7298         while (amenu != NULL) {
7299                 GList *alist = amenu->next;
7300                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
7301                 amenu = alist;
7302         }
7303
7304         system_none = gtk_item_factory_get_widget(ifactory,
7305                 "/Options/Privacy System/None");
7306
7307         g_signal_connect(G_OBJECT(system_none), "activate",
7308                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7309
7310         systems = privacy_get_system_ids();
7311         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7312                 gchar *systemid = cur->data;
7313
7314                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7315                 widget = gtk_radio_menu_item_new_with_label(group,
7316                         privacy_system_get_name(systemid));
7317                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7318                                        g_strdup(systemid), g_free);
7319                 g_signal_connect(G_OBJECT(widget), "activate",
7320                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7321
7322                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
7323                 gtk_widget_show(widget);
7324                 g_free(systemid);
7325         }
7326         g_slist_free(systems);
7327 }
7328
7329 void compose_reflect_prefs_all(void)
7330 {
7331         GList *cur;
7332         Compose *compose;
7333
7334         for (cur = compose_list; cur != NULL; cur = cur->next) {
7335                 compose = (Compose *)cur->data;
7336                 compose_set_template_menu(compose);
7337         }
7338 }
7339
7340 void compose_reflect_prefs_pixmap_theme(void)
7341 {
7342         GList *cur;
7343         Compose *compose;
7344
7345         for (cur = compose_list; cur != NULL; cur = cur->next) {
7346                 compose = (Compose *)cur->data;
7347                 toolbar_update(TOOLBAR_COMPOSE, compose);
7348         }
7349 }
7350
7351 static const gchar *compose_quote_char_from_context(Compose *compose)
7352 {
7353         const gchar *qmark = NULL;
7354
7355         g_return_val_if_fail(compose != NULL, NULL);
7356
7357         switch (compose->mode) {
7358                 /* use forward-specific quote char */
7359                 case COMPOSE_FORWARD:
7360                 case COMPOSE_FORWARD_AS_ATTACH:
7361                 case COMPOSE_FORWARD_INLINE:
7362                         if (compose->folder && compose->folder->prefs &&
7363                                         compose->folder->prefs->forward_with_format)
7364                                 qmark = compose->folder->prefs->forward_quotemark;
7365                         else if (compose->account->forward_with_format)
7366                                 qmark = compose->account->forward_quotemark;
7367                         else
7368                                 qmark = prefs_common.fw_quotemark;
7369                         break;
7370
7371                 /* use reply-specific quote char in all other modes */
7372                 default:
7373                         if (compose->folder && compose->folder->prefs &&
7374                                         compose->folder->prefs->reply_with_format)
7375                                 qmark = compose->folder->prefs->reply_quotemark;
7376                         else if (compose->account->reply_with_format)
7377                                 qmark = compose->account->reply_quotemark;
7378                         else
7379                                 qmark = prefs_common.quotemark;
7380                         break;
7381         }
7382
7383         if (qmark == NULL || *qmark == '\0')
7384                 qmark = "> ";
7385
7386         return qmark;
7387 }
7388
7389 static void compose_template_apply(Compose *compose, Template *tmpl,
7390                                    gboolean replace)
7391 {
7392         GtkTextView *text;
7393         GtkTextBuffer *buffer;
7394         GtkTextMark *mark;
7395         GtkTextIter iter;
7396         const gchar *qmark;
7397         gchar *parsed_str = NULL;
7398         gint cursor_pos = 0;
7399         const gchar *err_msg = _("Template body format error at line %d.");
7400         if (!tmpl) return;
7401
7402         /* process the body */
7403
7404         text = GTK_TEXT_VIEW(compose->text);
7405         buffer = gtk_text_view_get_buffer(text);
7406
7407         if (tmpl->value) {
7408                 qmark = compose_quote_char_from_context(compose);
7409
7410                 if (compose->replyinfo != NULL) {
7411
7412                         if (replace)
7413                                 gtk_text_buffer_set_text(buffer, "", -1);
7414                         mark = gtk_text_buffer_get_insert(buffer);
7415                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7416
7417                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7418                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7419
7420                 } else if (compose->fwdinfo != NULL) {
7421
7422                         if (replace)
7423                                 gtk_text_buffer_set_text(buffer, "", -1);
7424                         mark = gtk_text_buffer_get_insert(buffer);
7425                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7426
7427                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7428                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7429
7430                 } else {
7431                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7432
7433                         GtkTextIter start, end;
7434                         gchar *tmp = NULL;
7435
7436                         gtk_text_buffer_get_start_iter(buffer, &start);
7437                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7438                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7439
7440                         /* clear the buffer now */
7441                         if (replace)
7442                                 gtk_text_buffer_set_text(buffer, "", -1);
7443
7444                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7445                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7446                         procmsg_msginfo_free( dummyinfo );
7447
7448                         g_free( tmp );
7449                 } 
7450         } else {
7451                 if (replace)
7452                         gtk_text_buffer_set_text(buffer, "", -1);
7453                 mark = gtk_text_buffer_get_insert(buffer);
7454                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7455         }       
7456
7457         if (replace && parsed_str && compose->account->auto_sig)
7458                 compose_insert_sig(compose, FALSE);
7459
7460         if (replace && parsed_str) {
7461                 gtk_text_buffer_get_start_iter(buffer, &iter);
7462                 gtk_text_buffer_place_cursor(buffer, &iter);
7463         }
7464         
7465         if (parsed_str) {
7466                 cursor_pos = quote_fmt_get_cursor_pos();
7467                 compose->set_cursor_pos = cursor_pos;
7468                 if (cursor_pos == -1)
7469                         cursor_pos = 0;
7470                 gtk_text_buffer_get_start_iter(buffer, &iter);
7471                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7472                 gtk_text_buffer_place_cursor(buffer, &iter);
7473         }
7474
7475         /* process the other fields */
7476
7477         compose_template_apply_fields(compose, tmpl);
7478         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7479         quote_fmt_reset_vartable();
7480         compose_changed_cb(NULL, compose);
7481 }
7482
7483 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7484 {
7485         MsgInfo* dummyinfo = NULL;
7486         MsgInfo *msginfo = NULL;
7487         gchar *buf = NULL;
7488
7489         if (compose->replyinfo != NULL)
7490                 msginfo = compose->replyinfo;
7491         else if (compose->fwdinfo != NULL)
7492                 msginfo = compose->fwdinfo;
7493         else {
7494                 dummyinfo = compose_msginfo_new_from_compose(compose);
7495                 msginfo = dummyinfo;
7496         }
7497
7498         if (tmpl->to && *tmpl->to != '\0') {
7499 #ifdef USE_ASPELL
7500                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7501                                 compose->gtkaspell);
7502 #else
7503                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7504 #endif
7505                 quote_fmt_scan_string(tmpl->to);
7506                 quote_fmt_parse();
7507
7508                 buf = quote_fmt_get_buffer();
7509                 if (buf == NULL) {
7510                         alertpanel_error(_("Template To format error."));
7511                 } else {
7512                         compose_entry_append(compose, buf, COMPOSE_TO);
7513                 }
7514         }
7515
7516         if (tmpl->cc && *tmpl->cc != '\0') {
7517 #ifdef USE_ASPELL
7518                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7519                                 compose->gtkaspell);
7520 #else
7521                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7522 #endif
7523                 quote_fmt_scan_string(tmpl->cc);
7524                 quote_fmt_parse();
7525
7526                 buf = quote_fmt_get_buffer();
7527                 if (buf == NULL) {
7528                         alertpanel_error(_("Template Cc format error."));
7529                 } else {
7530                         compose_entry_append(compose, buf, COMPOSE_CC);
7531                 }
7532         }
7533
7534         if (tmpl->bcc && *tmpl->bcc != '\0') {
7535 #ifdef USE_ASPELL
7536                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7537                                 compose->gtkaspell);
7538 #else
7539                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7540 #endif
7541                 quote_fmt_scan_string(tmpl->bcc);
7542                 quote_fmt_parse();
7543
7544                 buf = quote_fmt_get_buffer();
7545                 if (buf == NULL) {
7546                         alertpanel_error(_("Template Bcc format error."));
7547                 } else {
7548                         compose_entry_append(compose, buf, COMPOSE_BCC);
7549                 }
7550         }
7551
7552         /* process the subject */
7553         if (tmpl->subject && *tmpl->subject != '\0') {
7554 #ifdef USE_ASPELL
7555                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7556                                 compose->gtkaspell);
7557 #else
7558                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7559 #endif
7560                 quote_fmt_scan_string(tmpl->subject);
7561                 quote_fmt_parse();
7562
7563                 buf = quote_fmt_get_buffer();
7564                 if (buf == NULL) {
7565                         alertpanel_error(_("Template subject format error."));
7566                 } else {
7567                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7568                 }
7569         }
7570
7571         procmsg_msginfo_free( dummyinfo );
7572 }
7573
7574 static void compose_destroy(Compose *compose)
7575 {
7576         GtkTextBuffer *buffer;
7577         GtkClipboard *clipboard;
7578
7579         compose_list = g_list_remove(compose_list, compose);
7580
7581         if (compose->updating) {
7582                 debug_print("danger, not destroying anything now\n");
7583                 compose->deferred_destroy = TRUE;
7584                 return;
7585         }
7586         /* NOTE: address_completion_end() does nothing with the window
7587          * however this may change. */
7588         address_completion_end(compose->window);
7589
7590         slist_free_strings(compose->to_list);
7591         g_slist_free(compose->to_list);
7592         slist_free_strings(compose->newsgroup_list);
7593         g_slist_free(compose->newsgroup_list);
7594         slist_free_strings(compose->header_list);
7595         g_slist_free(compose->header_list);
7596
7597         procmsg_msginfo_free(compose->targetinfo);
7598         procmsg_msginfo_free(compose->replyinfo);
7599         procmsg_msginfo_free(compose->fwdinfo);
7600
7601         g_free(compose->replyto);
7602         g_free(compose->cc);
7603         g_free(compose->bcc);
7604         g_free(compose->newsgroups);
7605         g_free(compose->followup_to);
7606
7607         g_free(compose->ml_post);
7608
7609         g_free(compose->inreplyto);
7610         g_free(compose->references);
7611         g_free(compose->msgid);
7612         g_free(compose->boundary);
7613
7614         g_free(compose->redirect_filename);
7615         if (compose->undostruct)
7616                 undo_destroy(compose->undostruct);
7617
7618         g_free(compose->sig_str);
7619
7620         g_free(compose->exteditor_file);
7621
7622         g_free(compose->orig_charset);
7623
7624         g_free(compose->privacy_system);
7625
7626         if (addressbook_get_target_compose() == compose)
7627                 addressbook_set_target_compose(NULL);
7628
7629 #if USE_ASPELL
7630         if (compose->gtkaspell) {
7631                 gtkaspell_delete(compose->gtkaspell);
7632                 compose->gtkaspell = NULL;
7633         }
7634 #endif
7635
7636         prefs_common.compose_width = compose->scrolledwin->allocation.width;
7637         prefs_common.compose_height = compose->window->allocation.height;
7638
7639         if (!gtk_widget_get_parent(compose->paned))
7640                 gtk_widget_destroy(compose->paned);
7641         gtk_widget_destroy(compose->popupmenu);
7642
7643         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7644         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7645         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7646
7647         gtk_widget_destroy(compose->window);
7648         toolbar_destroy(compose->toolbar);
7649         g_free(compose->toolbar);
7650         g_mutex_free(compose->mutex);
7651         g_free(compose);
7652 }
7653
7654 static void compose_attach_info_free(AttachInfo *ainfo)
7655 {
7656         g_free(ainfo->file);
7657         g_free(ainfo->content_type);
7658         g_free(ainfo->name);
7659         g_free(ainfo);
7660 }
7661
7662 static void compose_attach_update_label(Compose *compose)
7663 {
7664         GtkTreeIter iter;
7665         gint i = 1;
7666         gchar *text;
7667         GtkTreeModel *model;
7668         
7669         if(compose == NULL)
7670                 return;
7671                 
7672         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7673         if(!gtk_tree_model_get_iter_first(model, &iter)) {
7674                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
7675                 return;
7676         }
7677         
7678         while(gtk_tree_model_iter_next(model, &iter))
7679                 i++;
7680         
7681         text = g_strdup_printf("(%d)", i);
7682         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7683         g_free(text);
7684 }
7685
7686 static void compose_attach_remove_selected(Compose *compose)
7687 {
7688         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7689         GtkTreeSelection *selection;
7690         GList *sel, *cur;
7691         GtkTreeModel *model;
7692
7693         selection = gtk_tree_view_get_selection(tree_view);
7694         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7695
7696         if (!sel) 
7697                 return;
7698
7699         for (cur = sel; cur != NULL; cur = cur->next) {
7700                 GtkTreePath *path = cur->data;
7701                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7702                                                 (model, cur->data);
7703                 cur->data = ref;
7704                 gtk_tree_path_free(path);
7705         }
7706
7707         for (cur = sel; cur != NULL; cur = cur->next) {
7708                 GtkTreeRowReference *ref = cur->data;
7709                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7710                 GtkTreeIter iter;
7711
7712                 if (gtk_tree_model_get_iter(model, &iter, path))
7713                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7714                 
7715                 gtk_tree_path_free(path);
7716                 gtk_tree_row_reference_free(ref);
7717         }
7718
7719         g_list_free(sel);
7720         compose_attach_update_label(compose);
7721 }
7722
7723 static struct _AttachProperty
7724 {
7725         GtkWidget *window;
7726         GtkWidget *mimetype_entry;
7727         GtkWidget *encoding_optmenu;
7728         GtkWidget *path_entry;
7729         GtkWidget *filename_entry;
7730         GtkWidget *ok_btn;
7731         GtkWidget *cancel_btn;
7732 } attach_prop;
7733
7734 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
7735 {       
7736         gtk_tree_path_free((GtkTreePath *)ptr);
7737 }
7738
7739 static void compose_attach_property(Compose *compose)
7740 {
7741         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7742         AttachInfo *ainfo;
7743         GtkComboBox *optmenu;
7744         GtkTreeSelection *selection;
7745         GList *sel;
7746         GtkTreeModel *model;
7747         GtkTreeIter iter;
7748         GtkTreePath *path;
7749         static gboolean cancelled;
7750
7751         /* only if one selected */
7752         selection = gtk_tree_view_get_selection(tree_view);
7753         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
7754                 return;
7755
7756         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7757         if (!sel)
7758                 return;
7759
7760         path = (GtkTreePath *) sel->data;
7761         gtk_tree_model_get_iter(model, &iter, path);
7762         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
7763         
7764         if (!ainfo) {
7765                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
7766                 g_list_free(sel);
7767                 return;
7768         }               
7769         g_list_free(sel);
7770
7771         if (!attach_prop.window)
7772                 compose_attach_property_create(&cancelled);
7773         gtk_widget_grab_focus(attach_prop.ok_btn);
7774         gtk_widget_show(attach_prop.window);
7775         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
7776
7777         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
7778         if (ainfo->encoding == ENC_UNKNOWN)
7779                 combobox_select_by_data(optmenu, ENC_BASE64);
7780         else
7781                 combobox_select_by_data(optmenu, ainfo->encoding);
7782
7783         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
7784                            ainfo->content_type ? ainfo->content_type : "");
7785         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
7786                            ainfo->file ? ainfo->file : "");
7787         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
7788                            ainfo->name ? ainfo->name : "");
7789
7790         for (;;) {
7791                 const gchar *entry_text;
7792                 gchar *text;
7793                 gchar *cnttype = NULL;
7794                 gchar *file = NULL;
7795                 off_t size = 0;
7796
7797                 cancelled = FALSE;
7798                 gtk_main();
7799
7800                 gtk_widget_hide(attach_prop.window);
7801                 
7802                 if (cancelled) 
7803                         break;
7804
7805                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
7806                 if (*entry_text != '\0') {
7807                         gchar *p;
7808
7809                         text = g_strstrip(g_strdup(entry_text));
7810                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
7811                                 cnttype = g_strdup(text);
7812                                 g_free(text);
7813                         } else {
7814                                 alertpanel_error(_("Invalid MIME type."));
7815                                 g_free(text);
7816                                 continue;
7817                         }
7818                 }
7819
7820                 ainfo->encoding = combobox_get_active_data(optmenu);
7821
7822                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
7823                 if (*entry_text != '\0') {
7824                         if (is_file_exist(entry_text) &&
7825                             (size = get_file_size(entry_text)) > 0)
7826                                 file = g_strdup(entry_text);
7827                         else {
7828                                 alertpanel_error
7829                                         (_("File doesn't exist or is empty."));
7830                                 g_free(cnttype);
7831                                 continue;
7832                         }
7833                 }
7834
7835                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
7836                 if (*entry_text != '\0') {
7837                         g_free(ainfo->name);
7838                         ainfo->name = g_strdup(entry_text);
7839                 }
7840
7841                 if (cnttype) {
7842                         g_free(ainfo->content_type);
7843                         ainfo->content_type = cnttype;
7844                 }
7845                 if (file) {
7846                         g_free(ainfo->file);
7847                         ainfo->file = file;
7848                 }
7849                 if (size)
7850                         ainfo->size = size;
7851
7852                 /* update tree store */
7853                 text = to_human_readable(ainfo->size);
7854                 gtk_tree_model_get_iter(model, &iter, path);
7855                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
7856                                    COL_MIMETYPE, ainfo->content_type,
7857                                    COL_SIZE, text,
7858                                    COL_NAME, ainfo->name,
7859                                    -1);
7860                 
7861                 break;
7862         }
7863
7864         gtk_tree_path_free(path);
7865 }
7866
7867 #define SET_LABEL_AND_ENTRY(str, entry, top) \
7868 { \
7869         label = gtk_label_new(str); \
7870         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
7871                          GTK_FILL, 0, 0, 0); \
7872         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
7873  \
7874         entry = gtk_entry_new(); \
7875         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
7876                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
7877 }
7878
7879 static void compose_attach_property_create(gboolean *cancelled)
7880 {
7881         GtkWidget *window;
7882         GtkWidget *vbox;
7883         GtkWidget *table;
7884         GtkWidget *label;
7885         GtkWidget *mimetype_entry;
7886         GtkWidget *hbox;
7887         GtkWidget *optmenu;
7888         GtkListStore *optmenu_menu;
7889         GtkWidget *path_entry;
7890         GtkWidget *filename_entry;
7891         GtkWidget *hbbox;
7892         GtkWidget *ok_btn;
7893         GtkWidget *cancel_btn;
7894         GList     *mime_type_list, *strlist;
7895         GtkTreeIter iter;
7896
7897         debug_print("Creating attach_property window...\n");
7898
7899         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
7900         gtk_widget_set_size_request(window, 480, -1);
7901         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
7902         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
7903         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
7904         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
7905         g_signal_connect(G_OBJECT(window), "delete_event",
7906                          G_CALLBACK(attach_property_delete_event),
7907                          cancelled);
7908         g_signal_connect(G_OBJECT(window), "key_press_event",
7909                          G_CALLBACK(attach_property_key_pressed),
7910                          cancelled);
7911
7912         vbox = gtk_vbox_new(FALSE, 8);
7913         gtk_container_add(GTK_CONTAINER(window), vbox);
7914
7915         table = gtk_table_new(4, 2, FALSE);
7916         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
7917         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
7918         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
7919
7920         label = gtk_label_new(_("MIME type")); 
7921         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
7922                          GTK_FILL, 0, 0, 0); 
7923         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
7924         mimetype_entry = gtk_combo_box_entry_new_text(); 
7925         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
7926                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7927                          
7928         /* stuff with list */
7929         mime_type_list = procmime_get_mime_type_list();
7930         strlist = NULL;
7931         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
7932                 MimeType *type = (MimeType *) mime_type_list->data;
7933                 gchar *tmp;
7934
7935                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
7936
7937                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
7938                         g_free(tmp);
7939                 else
7940                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
7941                                         (GCompareFunc)strcmp2);
7942         }
7943
7944         for (mime_type_list = strlist; mime_type_list != NULL; 
7945                 mime_type_list = mime_type_list->next) {
7946                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
7947                 g_free(mime_type_list->data);
7948         }
7949         g_list_free(strlist);
7950         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
7951         mimetype_entry = GTK_BIN(mimetype_entry)->child;                         
7952
7953         label = gtk_label_new(_("Encoding"));
7954         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
7955                          GTK_FILL, 0, 0, 0);
7956         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
7957
7958         hbox = gtk_hbox_new(FALSE, 0);
7959         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
7960                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7961
7962         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
7963         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7964
7965         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
7966         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
7967         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
7968         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
7969         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
7970
7971         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
7972
7973         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
7974         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
7975
7976         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
7977                                       &ok_btn, GTK_STOCK_OK,
7978                                       NULL, NULL);
7979         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
7980         gtk_widget_grab_default(ok_btn);
7981
7982         g_signal_connect(G_OBJECT(ok_btn), "clicked",
7983                          G_CALLBACK(attach_property_ok),
7984                          cancelled);
7985         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
7986                          G_CALLBACK(attach_property_cancel),
7987                          cancelled);
7988
7989         gtk_widget_show_all(vbox);
7990
7991         attach_prop.window           = window;
7992         attach_prop.mimetype_entry   = mimetype_entry;
7993         attach_prop.encoding_optmenu = optmenu;
7994         attach_prop.path_entry       = path_entry;
7995         attach_prop.filename_entry   = filename_entry;
7996         attach_prop.ok_btn           = ok_btn;
7997         attach_prop.cancel_btn       = cancel_btn;
7998 }
7999
8000 #undef SET_LABEL_AND_ENTRY
8001
8002 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
8003 {
8004         *cancelled = FALSE;
8005         gtk_main_quit();
8006 }
8007
8008 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
8009 {
8010         *cancelled = TRUE;
8011         gtk_main_quit();
8012 }
8013
8014 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
8015                                          gboolean *cancelled)
8016 {
8017         *cancelled = TRUE;
8018         gtk_main_quit();
8019
8020         return TRUE;
8021 }
8022
8023 static gboolean attach_property_key_pressed(GtkWidget *widget,
8024                                             GdkEventKey *event,
8025                                             gboolean *cancelled)
8026 {
8027         if (event && event->keyval == GDK_Escape) {
8028                 *cancelled = TRUE;
8029                 gtk_main_quit();
8030         }
8031         return FALSE;
8032 }
8033
8034 static void compose_exec_ext_editor(Compose *compose)
8035 {
8036 #ifdef G_OS_UNIX
8037         gchar *tmp;
8038         pid_t pid;
8039         gint pipe_fds[2];
8040
8041         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8042                               G_DIR_SEPARATOR, compose);
8043
8044         if (pipe(pipe_fds) < 0) {
8045                 perror("pipe");
8046                 g_free(tmp);
8047                 return;
8048         }
8049
8050         if ((pid = fork()) < 0) {
8051                 perror("fork");
8052                 g_free(tmp);
8053                 return;
8054         }
8055
8056         if (pid != 0) {
8057                 /* close the write side of the pipe */
8058                 close(pipe_fds[1]);
8059
8060                 compose->exteditor_file    = g_strdup(tmp);
8061                 compose->exteditor_pid     = pid;
8062
8063                 compose_set_ext_editor_sensitive(compose, FALSE);
8064
8065                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8066                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8067                                                         G_IO_IN,
8068                                                         compose_input_cb,
8069                                                         compose);
8070         } else {        /* process-monitoring process */
8071                 pid_t pid_ed;
8072
8073                 if (setpgid(0, 0))
8074                         perror("setpgid");
8075
8076                 /* close the read side of the pipe */
8077                 close(pipe_fds[0]);
8078
8079                 if (compose_write_body_to_file(compose, tmp) < 0) {
8080                         fd_write_all(pipe_fds[1], "2\n", 2);
8081                         _exit(1);
8082                 }
8083
8084                 pid_ed = compose_exec_ext_editor_real(tmp);
8085                 if (pid_ed < 0) {
8086                         fd_write_all(pipe_fds[1], "1\n", 2);
8087                         _exit(1);
8088                 }
8089
8090                 /* wait until editor is terminated */
8091                 waitpid(pid_ed, NULL, 0);
8092
8093                 fd_write_all(pipe_fds[1], "0\n", 2);
8094
8095                 close(pipe_fds[1]);
8096                 _exit(0);
8097         }
8098
8099         g_free(tmp);
8100 #endif /* G_OS_UNIX */
8101 }
8102
8103 #ifdef G_OS_UNIX
8104 static gint compose_exec_ext_editor_real(const gchar *file)
8105 {
8106         gchar buf[1024];
8107         gchar *p;
8108         gchar **cmdline;
8109         pid_t pid;
8110
8111         g_return_val_if_fail(file != NULL, -1);
8112
8113         if ((pid = fork()) < 0) {
8114                 perror("fork");
8115                 return -1;
8116         }
8117
8118         if (pid != 0) return pid;
8119
8120         /* grandchild process */
8121
8122         if (setpgid(0, getppid()))
8123                 perror("setpgid");
8124
8125         if (prefs_common_get_ext_editor_cmd() &&
8126             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8127             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8128                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8129         } else {
8130                 if (prefs_common_get_ext_editor_cmd())
8131                         g_warning("External editor command line is invalid: '%s'\n",
8132                                   prefs_common_get_ext_editor_cmd());
8133                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8134         }
8135
8136         cmdline = strsplit_with_quote(buf, " ", 1024);
8137         execvp(cmdline[0], cmdline);
8138
8139         perror("execvp");
8140         g_strfreev(cmdline);
8141
8142         _exit(1);
8143 }
8144
8145 static gboolean compose_ext_editor_kill(Compose *compose)
8146 {
8147         pid_t pgid = compose->exteditor_pid * -1;
8148         gint ret;
8149
8150         ret = kill(pgid, 0);
8151
8152         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8153                 AlertValue val;
8154                 gchar *msg;
8155
8156                 msg = g_strdup_printf
8157                         (_("The external editor is still working.\n"
8158                            "Force terminating the process?\n"
8159                            "process group id: %d"), -pgid);
8160                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8161                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8162                         
8163                 g_free(msg);
8164
8165                 if (val == G_ALERTALTERNATE) {
8166                         g_source_remove(compose->exteditor_tag);
8167                         g_io_channel_shutdown(compose->exteditor_ch,
8168                                               FALSE, NULL);
8169                         g_io_channel_unref(compose->exteditor_ch);
8170
8171                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8172                         waitpid(compose->exteditor_pid, NULL, 0);
8173
8174                         g_warning("Terminated process group id: %d", -pgid);
8175                         g_warning("Temporary file: %s",
8176                                   compose->exteditor_file);
8177
8178                         compose_set_ext_editor_sensitive(compose, TRUE);
8179
8180                         g_free(compose->exteditor_file);
8181                         compose->exteditor_file    = NULL;
8182                         compose->exteditor_pid     = -1;
8183                         compose->exteditor_ch      = NULL;
8184                         compose->exteditor_tag     = -1;
8185                 } else
8186                         return FALSE;
8187         }
8188
8189         return TRUE;
8190 }
8191
8192 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8193                                  gpointer data)
8194 {
8195         gchar buf[3] = "3";
8196         Compose *compose = (Compose *)data;
8197         gsize bytes_read;
8198
8199         debug_print(_("Compose: input from monitoring process\n"));
8200
8201         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8202
8203         g_io_channel_shutdown(source, FALSE, NULL);
8204         g_io_channel_unref(source);
8205
8206         waitpid(compose->exteditor_pid, NULL, 0);
8207
8208         if (buf[0] == '0') {            /* success */
8209                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8210                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8211
8212                 gtk_text_buffer_set_text(buffer, "", -1);
8213                 compose_insert_file(compose, compose->exteditor_file);
8214                 compose_changed_cb(NULL, compose);
8215
8216                 if (g_unlink(compose->exteditor_file) < 0)
8217                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8218         } else if (buf[0] == '1') {     /* failed */
8219                 g_warning("Couldn't exec external editor\n");
8220                 if (g_unlink(compose->exteditor_file) < 0)
8221                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8222         } else if (buf[0] == '2') {
8223                 g_warning("Couldn't write to file\n");
8224         } else if (buf[0] == '3') {
8225                 g_warning("Pipe read failed\n");
8226         }
8227
8228         compose_set_ext_editor_sensitive(compose, TRUE);
8229
8230         g_free(compose->exteditor_file);
8231         compose->exteditor_file    = NULL;
8232         compose->exteditor_pid     = -1;
8233         compose->exteditor_ch      = NULL;
8234         compose->exteditor_tag     = -1;
8235
8236         return FALSE;
8237 }
8238
8239 static void compose_set_ext_editor_sensitive(Compose *compose,
8240                                              gboolean sensitive)
8241 {
8242         GtkItemFactory *ifactory;
8243
8244         ifactory = gtk_item_factory_from_widget(compose->menubar);
8245
8246         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
8247         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
8248         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
8249         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
8250         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
8251         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
8252         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
8253                            sensitive);
8254
8255         gtk_widget_set_sensitive(compose->text,                       sensitive);
8256         if (compose->toolbar->send_btn)
8257                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8258         if (compose->toolbar->sendl_btn)
8259                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8260         if (compose->toolbar->draft_btn)
8261                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8262         if (compose->toolbar->insert_btn)
8263                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8264         if (compose->toolbar->sig_btn)
8265                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8266         if (compose->toolbar->exteditor_btn)
8267                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8268         if (compose->toolbar->linewrap_current_btn)
8269                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8270         if (compose->toolbar->linewrap_all_btn)
8271                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8272 }
8273 #endif /* G_OS_UNIX */
8274
8275 /**
8276  * compose_undo_state_changed:
8277  *
8278  * Change the sensivity of the menuentries undo and redo
8279  **/
8280 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8281                                        gint redo_state, gpointer data)
8282 {
8283         GtkWidget *widget = GTK_WIDGET(data);
8284         GtkItemFactory *ifactory;
8285
8286         g_return_if_fail(widget != NULL);
8287
8288         ifactory = gtk_item_factory_from_widget(widget);
8289
8290         switch (undo_state) {
8291         case UNDO_STATE_TRUE:
8292                 if (!undostruct->undo_state) {
8293                         undostruct->undo_state = TRUE;
8294                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
8295                 }
8296                 break;
8297         case UNDO_STATE_FALSE:
8298                 if (undostruct->undo_state) {
8299                         undostruct->undo_state = FALSE;
8300                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
8301                 }
8302                 break;
8303         case UNDO_STATE_UNCHANGED:
8304                 break;
8305         case UNDO_STATE_REFRESH:
8306                 menu_set_sensitive(ifactory, "/Edit/Undo",
8307                                    undostruct->undo_state);
8308                 break;
8309         default:
8310                 g_warning("Undo state not recognized");
8311                 break;
8312         }
8313
8314         switch (redo_state) {
8315         case UNDO_STATE_TRUE:
8316                 if (!undostruct->redo_state) {
8317                         undostruct->redo_state = TRUE;
8318                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
8319                 }
8320                 break;
8321         case UNDO_STATE_FALSE:
8322                 if (undostruct->redo_state) {
8323                         undostruct->redo_state = FALSE;
8324                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
8325                 }
8326                 break;
8327         case UNDO_STATE_UNCHANGED:
8328                 break;
8329         case UNDO_STATE_REFRESH:
8330                 menu_set_sensitive(ifactory, "/Edit/Redo",
8331                                    undostruct->redo_state);
8332                 break;
8333         default:
8334                 g_warning("Redo state not recognized");
8335                 break;
8336         }
8337 }
8338
8339 /* callback functions */
8340
8341 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8342  * includes "non-client" (windows-izm) in calculation, so this calculation
8343  * may not be accurate.
8344  */
8345 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8346                                         GtkAllocation *allocation,
8347                                         GtkSHRuler *shruler)
8348 {
8349         if (prefs_common.show_ruler) {
8350                 gint char_width = 0, char_height = 0;
8351                 gint line_width_in_chars;
8352
8353                 gtkut_get_font_size(GTK_WIDGET(widget),
8354                                     &char_width, &char_height);
8355                 line_width_in_chars =
8356                         (allocation->width - allocation->x) / char_width;
8357
8358                 /* got the maximum */
8359                 gtk_ruler_set_range(GTK_RULER(shruler),
8360                                     0.0, line_width_in_chars, 0,
8361                                     /*line_width_in_chars*/ char_width);
8362         }
8363
8364         return TRUE;
8365 }
8366
8367 static void account_activated(GtkComboBox *optmenu, gpointer data)
8368 {
8369         Compose *compose = (Compose *)data;
8370
8371         PrefsAccount *ac;
8372         gchar *folderidentifier;
8373         gint account_id = 0;
8374         GtkTreeModel *menu;
8375         GtkTreeIter iter;
8376
8377         /* Get ID of active account in the combo box */
8378         menu = gtk_combo_box_get_model(optmenu);
8379         gtk_combo_box_get_active_iter(optmenu, &iter);
8380         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8381
8382         ac = account_find_from_id(account_id);
8383         g_return_if_fail(ac != NULL);
8384
8385         if (ac != compose->account)
8386                 compose_select_account(compose, ac, FALSE);
8387
8388         /* Set message save folder */
8389         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8390                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8391         }
8392         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8393                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8394                            
8395         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8396         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8397                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8398                                   (compose->account, F_OUTBOX));
8399                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8400                 g_free(folderidentifier);
8401         }
8402 }
8403
8404 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8405                             GtkTreeViewColumn *column, Compose *compose)
8406 {
8407         compose_attach_property(compose);
8408 }
8409
8410 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8411                                       gpointer data)
8412 {
8413         Compose *compose = (Compose *)data;
8414         GtkTreeSelection *attach_selection;
8415         gint attach_nr_selected;
8416         GtkItemFactory *ifactory;
8417         
8418         if (!event) return FALSE;
8419
8420         if (event->button == 3) {
8421                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8422                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8423                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
8424                         
8425                 if (attach_nr_selected > 0)
8426                 {
8427                         menu_set_sensitive(ifactory, "/Remove", TRUE);
8428                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
8429                 } else {
8430                         menu_set_sensitive(ifactory, "/Remove", FALSE);
8431                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
8432                 }
8433                         
8434                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8435                                NULL, NULL, event->button, event->time);
8436                 return TRUE;                           
8437         }
8438
8439         return FALSE;
8440 }
8441
8442 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8443                                    gpointer data)
8444 {
8445         Compose *compose = (Compose *)data;
8446
8447         if (!event) return FALSE;
8448
8449         switch (event->keyval) {
8450         case GDK_Delete:
8451                 compose_attach_remove_selected(compose);
8452                 break;
8453         }
8454         return FALSE;
8455 }
8456
8457 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8458 {
8459         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
8460         toolbar_comp_set_sensitive(compose, allow);
8461         menu_set_sensitive(ifactory, "/Message", allow);
8462         menu_set_sensitive(ifactory, "/Edit", allow);
8463 #if USE_ASPELL
8464         menu_set_sensitive(ifactory, "/Spelling", allow);
8465 #endif  
8466         menu_set_sensitive(ifactory, "/Options", allow);
8467         menu_set_sensitive(ifactory, "/Tools", allow);
8468         menu_set_sensitive(ifactory, "/Help", allow);
8469         
8470         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8471
8472 }
8473
8474 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
8475 {
8476         Compose *compose = (Compose *)data;
8477         
8478         if (prefs_common.work_offline && 
8479             !inc_offline_should_override(TRUE,
8480                 _("Claws Mail needs network access in order "
8481                   "to send this email.")))
8482                 return;
8483         
8484         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8485                 g_source_remove(compose->draft_timeout_tag);
8486                 compose->draft_timeout_tag = -1;
8487         }
8488
8489         compose_send(compose);
8490 }
8491
8492 static void compose_send_later_cb(gpointer data, guint action,
8493                                   GtkWidget *widget)
8494 {
8495         Compose *compose = (Compose *)data;
8496         gint val;
8497
8498         inc_lock();
8499         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8500         inc_unlock();
8501
8502         if (!val) {
8503                 compose_close(compose);
8504         } else if (val == -1) {
8505                 alertpanel_error(_("Could not queue message."));
8506         } else if (val == -2) {
8507                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8508         } else if (val == -3) {
8509                 if (privacy_peek_error())
8510                 alertpanel_error(_("Could not queue message for sending:\n\n"
8511                                    "Signature failed: %s"), privacy_get_error());
8512         } else if (val == -4) {
8513                 alertpanel_error(_("Could not queue message for sending:\n\n"
8514                                    "Charset conversion failed."));
8515         } else if (val == -5) {
8516                 alertpanel_error(_("Could not queue message for sending:\n\n"
8517                                    "Couldn't get recipient encryption key."));
8518         } else if (val == -6) {
8519                 /* silent error */
8520         }
8521         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8522 }
8523
8524 #define DRAFTED_AT_EXIT "drafted_at_exit"
8525 static void compose_register_draft(MsgInfo *info)
8526 {
8527         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8528                                       DRAFTED_AT_EXIT, NULL);
8529         FILE *fp = fopen(filepath, "ab");
8530         
8531         if (fp) {
8532                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8533                                 info->msgnum);
8534                 fclose(fp);
8535         }
8536                 
8537         g_free(filepath);       
8538 }
8539
8540 gboolean compose_draft (gpointer data, guint action) 
8541 {
8542         Compose *compose = (Compose *)data;
8543         FolderItem *draft;
8544         gchar *tmp;
8545         gint msgnum;
8546         MsgFlags flag = {0, 0};
8547         static gboolean lock = FALSE;
8548         MsgInfo *newmsginfo;
8549         FILE *fp;
8550         gboolean target_locked = FALSE;
8551         gboolean err = FALSE;
8552
8553         if (lock) return FALSE;
8554
8555         if (compose->sending)
8556                 return TRUE;
8557
8558         draft = account_get_special_folder(compose->account, F_DRAFT);
8559         g_return_val_if_fail(draft != NULL, FALSE);
8560         
8561         if (!g_mutex_trylock(compose->mutex)) {
8562                 /* we don't want to lock the mutex once it's available,
8563                  * because as the only other part of compose.c locking
8564                  * it is compose_close - which means once unlocked,
8565                  * the compose struct will be freed */
8566                 debug_print("couldn't lock mutex, probably sending\n");
8567                 return FALSE;
8568         }
8569         
8570         lock = TRUE;
8571
8572         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8573                               G_DIR_SEPARATOR, compose);
8574         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8575                 FILE_OP_ERROR(tmp, "fopen");
8576                 goto warn_err;
8577         }
8578
8579         /* chmod for security */
8580         if (change_file_mode_rw(fp, tmp) < 0) {
8581                 FILE_OP_ERROR(tmp, "chmod");
8582                 g_warning("can't change file mode\n");
8583         }
8584
8585         /* Save draft infos */
8586         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8587         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8588
8589         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8590                 gchar *savefolderid;
8591
8592                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8593                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8594                 g_free(savefolderid);
8595         }
8596         if (compose->return_receipt) {
8597                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8598         }
8599         if (compose->privacy_system) {
8600                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8601                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8602                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8603         }
8604
8605         /* Message-ID of message replying to */
8606         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8607                 gchar *folderid;
8608                 
8609                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8610                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8611                 g_free(folderid);
8612         }
8613         /* Message-ID of message forwarding to */
8614         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8615                 gchar *folderid;
8616                 
8617                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8618                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8619                 g_free(folderid);
8620         }
8621
8622         /* end of headers */
8623         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8624
8625         if (err) {
8626                 fclose(fp);
8627                 goto warn_err;
8628         }
8629
8630         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8631                 fclose(fp);
8632                 goto warn_err;
8633         }
8634         if (fclose(fp) == EOF) {
8635                 goto warn_err;
8636         }
8637         
8638         if (compose->targetinfo) {
8639                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8640                 flag.perm_flags = target_locked?MSG_LOCKED:0;
8641         }
8642         flag.tmp_flags = MSG_DRAFT;
8643
8644         folder_item_scan(draft);
8645         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8646                 MsgInfo *tmpinfo = NULL;
8647                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8648                 if (compose->msgid) {
8649                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8650                 }
8651                 if (tmpinfo) {
8652                         msgnum = tmpinfo->msgnum;
8653                         procmsg_msginfo_free(tmpinfo);
8654                         debug_print("got draft msgnum %d from scanning\n", msgnum);
8655                 } else {
8656                         debug_print("didn't get draft msgnum after scanning\n");
8657                 }
8658         } else {
8659                 debug_print("got draft msgnum %d from adding\n", msgnum);
8660         }
8661         if (msgnum < 0) {
8662 warn_err:
8663                 g_unlink(tmp);
8664                 g_free(tmp);
8665                 if (action != COMPOSE_AUTO_SAVE) {
8666                         if (action != COMPOSE_DRAFT_FOR_EXIT)
8667                                 alertpanel_error(_("Could not save draft."));
8668                         else {
8669                                 AlertValue val;
8670                                 gtkut_window_popup(compose->window);
8671                                 val = alertpanel_full(_("Could not save draft"),
8672                                         _("Could not save draft.\n"
8673                                         "Do you want to cancel exit or discard this email?"),
8674                                           _("_Cancel exit"), _("_Discard email"), NULL,
8675                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8676                                 if (val == G_ALERTALTERNATE) {
8677                                         lock = FALSE;
8678                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8679                                         compose_close(compose);
8680                                         return TRUE;
8681                                 } else {
8682                                         lock = FALSE;
8683                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8684                                         return FALSE;
8685                                 }
8686                         }
8687                 }
8688                 goto unlock;
8689         }
8690         g_free(tmp);
8691
8692         if (compose->mode == COMPOSE_REEDIT) {
8693                 compose_remove_reedit_target(compose, TRUE);
8694         }
8695
8696         newmsginfo = folder_item_get_msginfo(draft, msgnum);
8697
8698         if (newmsginfo) {
8699                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8700                 if (target_locked)
8701                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8702                 else
8703                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8704                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8705                         procmsg_msginfo_set_flags(newmsginfo, 0,
8706                                                   MSG_HAS_ATTACHMENT);
8707
8708                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8709                         compose_register_draft(newmsginfo);
8710                 }
8711                 procmsg_msginfo_free(newmsginfo);
8712         }
8713         
8714         folder_item_scan(draft);
8715         
8716         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8717                 lock = FALSE;
8718                 g_mutex_unlock(compose->mutex); /* must be done before closing */
8719                 compose_close(compose);
8720                 return TRUE;
8721         } else {
8722                 struct stat s;
8723                 gchar *path;
8724
8725                 path = folder_item_fetch_msg(draft, msgnum);
8726                 if (path == NULL) {
8727                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
8728                         goto unlock;
8729                 }
8730                 if (g_stat(path, &s) < 0) {
8731                         FILE_OP_ERROR(path, "stat");
8732                         g_free(path);
8733                         goto unlock;
8734                 }
8735                 g_free(path);
8736
8737                 procmsg_msginfo_free(compose->targetinfo);
8738                 compose->targetinfo = procmsg_msginfo_new();
8739                 compose->targetinfo->msgnum = msgnum;
8740                 compose->targetinfo->size = s.st_size;
8741                 compose->targetinfo->mtime = s.st_mtime;
8742                 compose->targetinfo->folder = draft;
8743                 if (target_locked)
8744                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
8745                 compose->mode = COMPOSE_REEDIT;
8746                 
8747                 if (action == COMPOSE_AUTO_SAVE) {
8748                         compose->autosaved_draft = compose->targetinfo;
8749                 }
8750                 compose->modified = FALSE;
8751                 compose_set_title(compose);
8752         }
8753 unlock:
8754         lock = FALSE;
8755         g_mutex_unlock(compose->mutex);
8756         return TRUE;
8757 }
8758
8759 void compose_clear_exit_drafts(void)
8760 {
8761         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8762                                       DRAFTED_AT_EXIT, NULL);
8763         if (is_file_exist(filepath))
8764                 g_unlink(filepath);
8765         
8766         g_free(filepath);
8767 }
8768
8769 void compose_reopen_exit_drafts(void)
8770 {
8771         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8772                                       DRAFTED_AT_EXIT, NULL);
8773         FILE *fp = fopen(filepath, "rb");
8774         gchar buf[1024];
8775         
8776         if (fp) {
8777                 while (fgets(buf, sizeof(buf), fp)) {
8778                         gchar **parts = g_strsplit(buf, "\t", 2);
8779                         const gchar *folder = parts[0];
8780                         int msgnum = parts[1] ? atoi(parts[1]):-1;
8781                         
8782                         if (folder && *folder && msgnum > -1) {
8783                                 FolderItem *item = folder_find_item_from_identifier(folder);
8784                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
8785                                 if (info)
8786                                         compose_reedit(info, FALSE);
8787                         }
8788                         g_strfreev(parts);
8789                 }       
8790                 fclose(fp);
8791         }       
8792         g_free(filepath);
8793         compose_clear_exit_drafts();
8794 }
8795
8796 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
8797 {
8798         compose_draft(data, action);
8799 }
8800
8801 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
8802 {
8803         if (compose && file_list) {
8804                 GList *tmp;
8805
8806                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8807                         gchar *file = (gchar *) tmp->data;
8808                         gchar *utf8_filename = conv_filename_to_utf8(file);
8809                         compose_attach_append(compose, file, utf8_filename, NULL);
8810                         compose_changed_cb(NULL, compose);
8811                         if (free_data) {
8812                         g_free(file);
8813                                 tmp->data = NULL;
8814                         }
8815                         g_free(utf8_filename);
8816                 }
8817         }
8818 }
8819
8820 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
8821 {
8822         Compose *compose = (Compose *)data;
8823         GList *file_list;
8824
8825         if (compose->redirect_filename != NULL)
8826                 return;
8827
8828         file_list = filesel_select_multiple_files_open(_("Select file"));
8829
8830         if (file_list) {
8831                 compose_attach_from_list(compose, file_list, TRUE);
8832                 g_list_free(file_list);
8833         }
8834 }
8835
8836 static void compose_insert_file_cb(gpointer data, guint action,
8837                                    GtkWidget *widget)
8838 {
8839         Compose *compose = (Compose *)data;
8840         GList *file_list;
8841
8842         file_list = filesel_select_multiple_files_open(_("Select file"));
8843
8844         if (file_list) {
8845                 GList *tmp;
8846
8847                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8848                         gchar *file = (gchar *) tmp->data;
8849                         gchar *filedup = g_strdup(file);
8850                         gchar *shortfile = g_path_get_basename(filedup);
8851                         ComposeInsertResult res;
8852
8853                         res = compose_insert_file(compose, file);
8854                         if (res == COMPOSE_INSERT_READ_ERROR) {
8855                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
8856                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
8857                                 alertpanel_error(_("File '%s' contained invalid characters\n"
8858                                                    "for the current encoding, insertion may be incorrect."), shortfile);
8859                         }
8860                         g_free(shortfile);
8861                         g_free(filedup);
8862                         g_free(file);
8863                 }
8864                 g_list_free(file_list);
8865         }
8866 }
8867
8868 static void compose_insert_sig_cb(gpointer data, guint action,
8869                                   GtkWidget *widget)
8870 {
8871         Compose *compose = (Compose *)data;
8872
8873         compose_insert_sig(compose, FALSE);
8874 }
8875
8876 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
8877                               gpointer data)
8878 {
8879         gint x, y;
8880         Compose *compose = (Compose *)data;
8881
8882         gtkut_widget_get_uposition(widget, &x, &y);
8883         prefs_common.compose_x = x;
8884         prefs_common.compose_y = y;
8885
8886         if (compose->sending || compose->updating)
8887                 return TRUE;
8888         compose_close_cb(compose, 0, NULL);
8889         return TRUE;
8890 }
8891
8892 void compose_close_toolbar(Compose *compose)
8893 {
8894         compose_close_cb(compose, 0, NULL);
8895 }
8896
8897 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
8898 {
8899         Compose *compose = (Compose *)data;
8900         AlertValue val;
8901
8902 #ifdef G_OS_UNIX
8903         if (compose->exteditor_tag != -1) {
8904                 if (!compose_ext_editor_kill(compose))
8905                         return;
8906         }
8907 #endif
8908
8909         if (compose->modified) {
8910                 val = alertpanel(_("Discard message"),
8911                                  _("This message has been modified. Discard it?"),
8912                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
8913
8914                 switch (val) {
8915                 case G_ALERTDEFAULT:
8916                         if (prefs_common.autosave)
8917                                 compose_remove_draft(compose);                  
8918                         break;
8919                 case G_ALERTALTERNATE:
8920                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
8921                         return;
8922                 default:
8923                         return;
8924                 }
8925         }
8926
8927         compose_close(compose);
8928 }
8929
8930 static void compose_set_encoding_cb(gpointer data, guint action,
8931                                     GtkWidget *widget)
8932 {
8933         Compose *compose = (Compose *)data;
8934
8935         if (GTK_CHECK_MENU_ITEM(widget)->active)
8936                 compose->out_encoding = (CharSet)action;
8937 }
8938
8939 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
8940 {
8941         Compose *compose = (Compose *)data;
8942
8943         addressbook_open(compose);
8944 }
8945
8946 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
8947 {
8948         Compose *compose = (Compose *)data;
8949         Template *tmpl;
8950         gchar *msg;
8951         AlertValue val;
8952
8953         tmpl = g_object_get_data(G_OBJECT(widget), "template");
8954         g_return_if_fail(tmpl != NULL);
8955
8956         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
8957                               tmpl->name);
8958         val = alertpanel(_("Apply template"), msg,
8959                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
8960         g_free(msg);
8961
8962         if (val == G_ALERTDEFAULT)
8963                 compose_template_apply(compose, tmpl, TRUE);
8964         else if (val == G_ALERTALTERNATE)
8965                 compose_template_apply(compose, tmpl, FALSE);
8966 }
8967
8968 static void compose_ext_editor_cb(gpointer data, guint action,
8969                                   GtkWidget *widget)
8970 {
8971         Compose *compose = (Compose *)data;
8972
8973         compose_exec_ext_editor(compose);
8974 }
8975
8976 static void compose_undo_cb(Compose *compose)
8977 {
8978         gboolean prev_autowrap = compose->autowrap;
8979
8980         compose->autowrap = FALSE;
8981         undo_undo(compose->undostruct);
8982         compose->autowrap = prev_autowrap;
8983 }
8984
8985 static void compose_redo_cb(Compose *compose)
8986 {
8987         gboolean prev_autowrap = compose->autowrap;
8988         
8989         compose->autowrap = FALSE;
8990         undo_redo(compose->undostruct);
8991         compose->autowrap = prev_autowrap;
8992 }
8993
8994 static void entry_cut_clipboard(GtkWidget *entry)
8995 {
8996         if (GTK_IS_EDITABLE(entry))
8997                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
8998         else if (GTK_IS_TEXT_VIEW(entry))
8999                 gtk_text_buffer_cut_clipboard(
9000                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9001                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
9002                         TRUE);
9003 }
9004
9005 static void entry_copy_clipboard(GtkWidget *entry)
9006 {
9007         if (GTK_IS_EDITABLE(entry))
9008                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
9009         else if (GTK_IS_TEXT_VIEW(entry))
9010                 gtk_text_buffer_copy_clipboard(
9011                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
9012                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
9013 }
9014
9015 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
9016                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
9017 {
9018         if (GTK_IS_TEXT_VIEW(entry)) {
9019                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9020                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9021                 GtkTextIter start_iter, end_iter;
9022                 gint start, end;
9023                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9024
9025                 if (contents == NULL)
9026                         return;
9027
9028                 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9029
9030                 /* we shouldn't delete the selection when middle-click-pasting, or we
9031                  * can't mid-click-paste our own selection */
9032                 if (clip != GDK_SELECTION_PRIMARY) {
9033                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9034                 }
9035                 
9036                 if (insert_place == NULL) {
9037                         /* if insert_place isn't specified, insert at the cursor.
9038                          * used for Ctrl-V pasting */
9039                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9040                         start = gtk_text_iter_get_offset(&start_iter);
9041                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9042                 } else {
9043                         /* if insert_place is specified, paste here.
9044                          * used for mid-click-pasting */
9045                         start = gtk_text_iter_get_offset(insert_place);
9046                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9047                 }
9048                 
9049                 if (!wrap) {
9050                         /* paste unwrapped: mark the paste so it's not wrapped later */
9051                         end = start + strlen(contents);
9052                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9053                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9054                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9055                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9056                         /* rewrap paragraph now (after a mid-click-paste) */
9057                         mark_start = gtk_text_buffer_get_insert(buffer);
9058                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9059                         gtk_text_iter_backward_char(&start_iter);
9060                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9061                 }
9062         } else if (GTK_IS_EDITABLE(entry))
9063                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9064         
9065 }
9066
9067 static void entry_allsel(GtkWidget *entry)
9068 {
9069         if (GTK_IS_EDITABLE(entry))
9070                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9071         else if (GTK_IS_TEXT_VIEW(entry)) {
9072                 GtkTextIter startiter, enditer;
9073                 GtkTextBuffer *textbuf;
9074
9075                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9076                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9077                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9078
9079                 gtk_text_buffer_move_mark_by_name(textbuf, 
9080                         "selection_bound", &startiter);
9081                 gtk_text_buffer_move_mark_by_name(textbuf, 
9082                         "insert", &enditer);
9083         }
9084 }
9085
9086 static void compose_cut_cb(Compose *compose)
9087 {
9088         if (compose->focused_editable 
9089 #ifndef MAEMO
9090             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9091 #endif
9092             )
9093                 entry_cut_clipboard(compose->focused_editable);
9094 }
9095
9096 static void compose_copy_cb(Compose *compose)
9097 {
9098         if (compose->focused_editable 
9099 #ifndef MAEMO
9100             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9101 #endif
9102             )
9103                 entry_copy_clipboard(compose->focused_editable);
9104 }
9105
9106 static void compose_paste_cb(Compose *compose)
9107 {
9108         gint prev_autowrap;
9109         GtkTextBuffer *buffer;
9110         BLOCK_WRAP();
9111         if (compose->focused_editable &&
9112             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9113                 entry_paste_clipboard(compose, compose->focused_editable, 
9114                                 prefs_common.linewrap_pastes,
9115                                 GDK_SELECTION_CLIPBOARD, NULL);
9116         UNBLOCK_WRAP();
9117 }
9118
9119 static void compose_paste_as_quote_cb(Compose *compose)
9120 {
9121         gint wrap_quote = prefs_common.linewrap_quote;
9122         if (compose->focused_editable 
9123 #ifndef MAEMO
9124             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9125 #endif
9126             ) {
9127                 /* let text_insert() (called directly or at a later time
9128                  * after the gtk_editable_paste_clipboard) know that 
9129                  * text is to be inserted as a quotation. implemented
9130                  * by using a simple refcount... */
9131                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9132                                                 G_OBJECT(compose->focused_editable),
9133                                                 "paste_as_quotation"));
9134                 g_object_set_data(G_OBJECT(compose->focused_editable),
9135                                     "paste_as_quotation",
9136                                     GINT_TO_POINTER(paste_as_quotation + 1));
9137                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9138                 entry_paste_clipboard(compose, compose->focused_editable, 
9139                                 prefs_common.linewrap_pastes,
9140                                 GDK_SELECTION_CLIPBOARD, NULL);
9141                 prefs_common.linewrap_quote = wrap_quote;
9142         }
9143 }
9144
9145 static void compose_paste_no_wrap_cb(Compose *compose)
9146 {
9147         gint prev_autowrap;
9148         GtkTextBuffer *buffer;
9149         BLOCK_WRAP();
9150         if (compose->focused_editable 
9151 #ifndef MAEMO
9152             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9153 #endif
9154             )
9155                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9156                         GDK_SELECTION_CLIPBOARD, NULL);
9157         UNBLOCK_WRAP();
9158 }
9159
9160 static void compose_paste_wrap_cb(Compose *compose)
9161 {
9162         gint prev_autowrap;
9163         GtkTextBuffer *buffer;
9164         BLOCK_WRAP();
9165         if (compose->focused_editable 
9166 #ifndef MAEMO
9167             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9168 #endif
9169             )
9170                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9171                         GDK_SELECTION_CLIPBOARD, NULL);
9172         UNBLOCK_WRAP();
9173 }
9174
9175 static void compose_allsel_cb(Compose *compose)
9176 {
9177         if (compose->focused_editable 
9178 #ifndef MAEMO
9179             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9180 #endif
9181             )
9182                 entry_allsel(compose->focused_editable);
9183 }
9184
9185 static void textview_move_beginning_of_line (GtkTextView *text)
9186 {
9187         GtkTextBuffer *buffer;
9188         GtkTextMark *mark;
9189         GtkTextIter ins;
9190
9191         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9192
9193         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9194         mark = gtk_text_buffer_get_insert(buffer);
9195         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9196         gtk_text_iter_set_line_offset(&ins, 0);
9197         gtk_text_buffer_place_cursor(buffer, &ins);
9198 }
9199
9200 static void textview_move_forward_character (GtkTextView *text)
9201 {
9202         GtkTextBuffer *buffer;
9203         GtkTextMark *mark;
9204         GtkTextIter ins;
9205
9206         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9207
9208         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9209         mark = gtk_text_buffer_get_insert(buffer);
9210         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9211         if (gtk_text_iter_forward_cursor_position(&ins))
9212                 gtk_text_buffer_place_cursor(buffer, &ins);
9213 }
9214
9215 static void textview_move_backward_character (GtkTextView *text)
9216 {
9217         GtkTextBuffer *buffer;
9218         GtkTextMark *mark;
9219         GtkTextIter ins;
9220
9221         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9222
9223         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9224         mark = gtk_text_buffer_get_insert(buffer);
9225         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9226         if (gtk_text_iter_backward_cursor_position(&ins))
9227                 gtk_text_buffer_place_cursor(buffer, &ins);
9228 }
9229
9230 static void textview_move_forward_word (GtkTextView *text)
9231 {
9232         GtkTextBuffer *buffer;
9233         GtkTextMark *mark;
9234         GtkTextIter ins;
9235         gint count;
9236
9237         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9238
9239         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9240         mark = gtk_text_buffer_get_insert(buffer);
9241         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9242         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9243         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9244                 gtk_text_iter_backward_word_start(&ins);
9245                 gtk_text_buffer_place_cursor(buffer, &ins);
9246         }
9247 }
9248
9249 static void textview_move_backward_word (GtkTextView *text)
9250 {
9251         GtkTextBuffer *buffer;
9252         GtkTextMark *mark;
9253         GtkTextIter ins;
9254         gint count;
9255
9256         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9257
9258         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9259         mark = gtk_text_buffer_get_insert(buffer);
9260         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9261         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9262         if (gtk_text_iter_backward_word_starts(&ins, 1))
9263                 gtk_text_buffer_place_cursor(buffer, &ins);
9264 }
9265
9266 static void textview_move_end_of_line (GtkTextView *text)
9267 {
9268         GtkTextBuffer *buffer;
9269         GtkTextMark *mark;
9270         GtkTextIter ins;
9271
9272         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9273
9274         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9275         mark = gtk_text_buffer_get_insert(buffer);
9276         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9277         if (gtk_text_iter_forward_to_line_end(&ins))
9278                 gtk_text_buffer_place_cursor(buffer, &ins);
9279 }
9280
9281 static void textview_move_next_line (GtkTextView *text)
9282 {
9283         GtkTextBuffer *buffer;
9284         GtkTextMark *mark;
9285         GtkTextIter ins;
9286         gint offset;
9287
9288         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9289
9290         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9291         mark = gtk_text_buffer_get_insert(buffer);
9292         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9293         offset = gtk_text_iter_get_line_offset(&ins);
9294         if (gtk_text_iter_forward_line(&ins)) {
9295                 gtk_text_iter_set_line_offset(&ins, offset);
9296                 gtk_text_buffer_place_cursor(buffer, &ins);
9297         }
9298 }
9299
9300 static void textview_move_previous_line (GtkTextView *text)
9301 {
9302         GtkTextBuffer *buffer;
9303         GtkTextMark *mark;
9304         GtkTextIter ins;
9305         gint offset;
9306
9307         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9308
9309         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9310         mark = gtk_text_buffer_get_insert(buffer);
9311         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9312         offset = gtk_text_iter_get_line_offset(&ins);
9313         if (gtk_text_iter_backward_line(&ins)) {
9314                 gtk_text_iter_set_line_offset(&ins, offset);
9315                 gtk_text_buffer_place_cursor(buffer, &ins);
9316         }
9317 }
9318
9319 static void textview_delete_forward_character (GtkTextView *text)
9320 {
9321         GtkTextBuffer *buffer;
9322         GtkTextMark *mark;
9323         GtkTextIter ins, end_iter;
9324
9325         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9326
9327         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9328         mark = gtk_text_buffer_get_insert(buffer);
9329         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9330         end_iter = ins;
9331         if (gtk_text_iter_forward_char(&end_iter)) {
9332                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9333         }
9334 }
9335
9336 static void textview_delete_backward_character (GtkTextView *text)
9337 {
9338         GtkTextBuffer *buffer;
9339         GtkTextMark *mark;
9340         GtkTextIter ins, end_iter;
9341
9342         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9343
9344         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9345         mark = gtk_text_buffer_get_insert(buffer);
9346         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9347         end_iter = ins;
9348         if (gtk_text_iter_backward_char(&end_iter)) {
9349                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9350         }
9351 }
9352
9353 static void textview_delete_forward_word (GtkTextView *text)
9354 {
9355         GtkTextBuffer *buffer;
9356         GtkTextMark *mark;
9357         GtkTextIter ins, end_iter;
9358
9359         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9360
9361         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9362         mark = gtk_text_buffer_get_insert(buffer);
9363         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9364         end_iter = ins;
9365         if (gtk_text_iter_forward_word_end(&end_iter)) {
9366                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9367         }
9368 }
9369
9370 static void textview_delete_backward_word (GtkTextView *text)
9371 {
9372         GtkTextBuffer *buffer;
9373         GtkTextMark *mark;
9374         GtkTextIter ins, end_iter;
9375
9376         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9377
9378         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9379         mark = gtk_text_buffer_get_insert(buffer);
9380         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9381         end_iter = ins;
9382         if (gtk_text_iter_backward_word_start(&end_iter)) {
9383                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9384         }
9385 }
9386
9387 static void textview_delete_line (GtkTextView *text)
9388 {
9389         GtkTextBuffer *buffer;
9390         GtkTextMark *mark;
9391         GtkTextIter ins, start_iter, end_iter;
9392         gboolean found;
9393
9394         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9395
9396         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9397         mark = gtk_text_buffer_get_insert(buffer);
9398         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9399
9400         start_iter = ins;
9401         gtk_text_iter_set_line_offset(&start_iter, 0);
9402
9403         end_iter = ins;
9404         if (gtk_text_iter_ends_line(&end_iter))
9405                 found = gtk_text_iter_forward_char(&end_iter);
9406         else
9407                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9408
9409         if (found)
9410                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9411 }
9412
9413 static void textview_delete_to_line_end (GtkTextView *text)
9414 {
9415         GtkTextBuffer *buffer;
9416         GtkTextMark *mark;
9417         GtkTextIter ins, end_iter;
9418         gboolean found;
9419
9420         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9421
9422         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9423         mark = gtk_text_buffer_get_insert(buffer);
9424         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9425         end_iter = ins;
9426         if (gtk_text_iter_ends_line(&end_iter))
9427                 found = gtk_text_iter_forward_char(&end_iter);
9428         else
9429                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9430         if (found)
9431                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9432 }
9433
9434 static void compose_advanced_action_cb(Compose *compose,
9435                                         ComposeCallAdvancedAction action)
9436 {
9437         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9438         static struct {
9439                 void (*do_action) (GtkTextView *text);
9440         } action_table[] = {
9441                 {textview_move_beginning_of_line},
9442                 {textview_move_forward_character},
9443                 {textview_move_backward_character},
9444                 {textview_move_forward_word},
9445                 {textview_move_backward_word},
9446                 {textview_move_end_of_line},
9447                 {textview_move_next_line},
9448                 {textview_move_previous_line},
9449                 {textview_delete_forward_character},
9450                 {textview_delete_backward_character},
9451                 {textview_delete_forward_word},
9452                 {textview_delete_backward_word},
9453                 {textview_delete_line},
9454                 {NULL}, /* gtk_stext_delete_line_n */
9455                 {textview_delete_to_line_end}
9456         };
9457
9458         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9459
9460         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9461             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9462                 if (action_table[action].do_action)
9463                         action_table[action].do_action(text);
9464                 else
9465                         g_warning("Not implemented yet.");
9466         }
9467 }
9468
9469 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9470 {
9471         gchar *str = NULL;
9472         
9473         if (GTK_IS_EDITABLE(widget)) {
9474                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9475                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9476                         strlen(str));
9477                 g_free(str);
9478                 if (widget->parent && widget->parent->parent
9479                  && widget->parent->parent->parent) {
9480                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9481                                 gint y = widget->allocation.y;
9482                                 gint height = widget->allocation.height;
9483                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9484                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9485
9486                                 if (y < (int)shown->value) {
9487                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9488                                 }
9489                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9490                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9491                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9492                                                         y + height - (int)shown->page_size - 1);
9493                                         } else {
9494                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9495                                                         (int)shown->upper - (int)shown->page_size - 1);
9496                                         }
9497                                 }
9498                         }
9499                 }
9500         }
9501
9502         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9503                 compose->focused_editable = widget;
9504         
9505 #ifdef MAEMO
9506         if (GTK_IS_TEXT_VIEW(widget) 
9507             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9508                 gtk_widget_ref(compose->notebook);
9509                 gtk_widget_ref(compose->edit_vbox);
9510                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9511                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9512                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9513                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9514                 gtk_widget_unref(compose->notebook);
9515                 gtk_widget_unref(compose->edit_vbox);
9516                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9517                                         G_CALLBACK(compose_grab_focus_cb),
9518                                         compose);
9519                 gtk_widget_grab_focus(widget);
9520                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9521                                         G_CALLBACK(compose_grab_focus_cb),
9522                                         compose);
9523         } else if (!GTK_IS_TEXT_VIEW(widget) 
9524                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9525                 gtk_widget_ref(compose->notebook);
9526                 gtk_widget_ref(compose->edit_vbox);
9527                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9528                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9529                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9530                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9531                 gtk_widget_unref(compose->notebook);
9532                 gtk_widget_unref(compose->edit_vbox);
9533                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9534                                         G_CALLBACK(compose_grab_focus_cb),
9535                                         compose);
9536                 gtk_widget_grab_focus(widget);
9537                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9538                                         G_CALLBACK(compose_grab_focus_cb),
9539                                         compose);
9540         }
9541 #endif
9542 }
9543
9544 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9545 {
9546         compose->modified = TRUE;
9547 #ifndef MAEMO
9548         compose_set_title(compose);
9549 #endif
9550 }
9551
9552 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
9553 {
9554         Compose *compose = (Compose *)data;
9555
9556         if (action == 1)
9557                 compose_wrap_all_full(compose, TRUE);
9558         else
9559                 compose_beautify_paragraph(compose, NULL, TRUE);
9560 }
9561
9562 static void compose_find_cb(gpointer data, guint action, GtkWidget *widget)
9563 {
9564         Compose *compose = (Compose *)data;
9565
9566         message_search_compose(compose);
9567 }
9568
9569 static void compose_toggle_autowrap_cb(gpointer data, guint action,
9570                                        GtkWidget *widget)
9571 {
9572         Compose *compose = (Compose *)data;
9573         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9574         if (compose->autowrap)
9575                 compose_wrap_all_full(compose, TRUE);
9576         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9577 }
9578
9579 static void compose_toggle_sign_cb(gpointer data, guint action,
9580                                    GtkWidget *widget)
9581 {
9582         Compose *compose = (Compose *)data;
9583
9584         if (GTK_CHECK_MENU_ITEM(widget)->active)
9585                 compose->use_signing = TRUE;
9586         else
9587                 compose->use_signing = FALSE;
9588 }
9589
9590 static void compose_toggle_encrypt_cb(gpointer data, guint action,
9591                                       GtkWidget *widget)
9592 {
9593         Compose *compose = (Compose *)data;
9594
9595         if (GTK_CHECK_MENU_ITEM(widget)->active)
9596                 compose->use_encryption = TRUE;
9597         else
9598                 compose->use_encryption = FALSE;
9599 }
9600
9601 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
9602 {
9603         g_free(compose->privacy_system);
9604
9605         compose->privacy_system = g_strdup(account->default_privacy_system);
9606         compose_update_privacy_system_menu_item(compose, warn);
9607 }
9608
9609 static void compose_toggle_ruler_cb(gpointer data, guint action,
9610                                     GtkWidget *widget)
9611 {
9612         Compose *compose = (Compose *)data;
9613
9614         if (GTK_CHECK_MENU_ITEM(widget)->active) {
9615                 gtk_widget_show(compose->ruler_hbox);
9616                 prefs_common.show_ruler = TRUE;
9617         } else {
9618                 gtk_widget_hide(compose->ruler_hbox);
9619                 gtk_widget_queue_resize(compose->edit_vbox);
9620                 prefs_common.show_ruler = FALSE;
9621         }
9622 }
9623
9624 static void compose_attach_drag_received_cb (GtkWidget          *widget,
9625                                              GdkDragContext     *context,
9626                                              gint                x,
9627                                              gint                y,
9628                                              GtkSelectionData   *data,
9629                                              guint               info,
9630                                              guint               time,
9631                                              gpointer            user_data)
9632 {
9633         Compose *compose = (Compose *)user_data;
9634         GList *list, *tmp;
9635
9636         if (gdk_atom_name(data->type) && 
9637             !strcmp(gdk_atom_name(data->type), "text/uri-list")
9638             && gtk_drag_get_source_widget(context) != 
9639                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9640                 list = uri_list_extract_filenames((const gchar *)data->data);
9641                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9642                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9643                         compose_attach_append
9644                                 (compose, (const gchar *)tmp->data,
9645                                  utf8_filename, NULL);
9646                         g_free(utf8_filename);
9647                 }
9648                 if (list) compose_changed_cb(NULL, compose);
9649                 list_free_strings(list);
9650                 g_list_free(list);
9651         } else if (gtk_drag_get_source_widget(context) 
9652                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9653                 /* comes from our summaryview */
9654                 SummaryView * summaryview = NULL;
9655                 GSList * list = NULL, *cur = NULL;
9656                 
9657                 if (mainwindow_get_mainwindow())
9658                         summaryview = mainwindow_get_mainwindow()->summaryview;
9659                 
9660                 if (summaryview)
9661                         list = summary_get_selected_msg_list(summaryview);
9662                 
9663                 for (cur = list; cur; cur = cur->next) {
9664                         MsgInfo *msginfo = (MsgInfo *)cur->data;
9665                         gchar *file = NULL;
9666                         if (msginfo)
9667                                 file = procmsg_get_message_file_full(msginfo, 
9668                                         TRUE, TRUE);
9669                         if (file) {
9670                                 compose_attach_append(compose, (const gchar *)file, 
9671                                         (const gchar *)file, "message/rfc822");
9672                                 g_free(file);
9673                         }
9674                 }
9675                 g_slist_free(list);
9676         }
9677 }
9678
9679 static gboolean compose_drag_drop(GtkWidget *widget,
9680                                   GdkDragContext *drag_context,
9681                                   gint x, gint y,
9682                                   guint time, gpointer user_data)
9683 {
9684         /* not handling this signal makes compose_insert_drag_received_cb
9685          * called twice */
9686         return TRUE;                                     
9687 }
9688
9689 static void compose_insert_drag_received_cb (GtkWidget          *widget,
9690                                              GdkDragContext     *drag_context,
9691                                              gint                x,
9692                                              gint                y,
9693                                              GtkSelectionData   *data,
9694                                              guint               info,
9695                                              guint               time,
9696                                              gpointer            user_data)
9697 {
9698         Compose *compose = (Compose *)user_data;
9699         GList *list, *tmp;
9700
9701         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
9702          * does not work */
9703         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
9704                 AlertValue val = G_ALERTDEFAULT;
9705
9706                 list = uri_list_extract_filenames((const gchar *)data->data);
9707
9708                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
9709                         /* Assume a list of no files, and data has ://, is a remote link */
9710                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
9711                         gchar *tmpfile = get_tmp_file();
9712                         str_write_to_file(tmpdata, tmpfile);
9713                         g_free(tmpdata);  
9714                         compose_insert_file(compose, tmpfile);
9715                         g_unlink(tmpfile);
9716                         g_free(tmpfile);
9717                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9718                         compose_beautify_paragraph(compose, NULL, TRUE);
9719                         return;
9720                 }
9721                 switch (prefs_common.compose_dnd_mode) {
9722                         case COMPOSE_DND_ASK:
9723                                 val = alertpanel_full(_("Insert or attach?"),
9724                                          _("Do you want to insert the contents of the file(s) "
9725                                            "into the message body, or attach it to the email?"),
9726                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
9727                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
9728                                 break;
9729                         case COMPOSE_DND_INSERT:
9730                                 val = G_ALERTALTERNATE;
9731                                 break;
9732                         case COMPOSE_DND_ATTACH:
9733                                 val = G_ALERTOTHER;
9734                                 break;
9735                         default:
9736                                 /* unexpected case */
9737                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
9738                 }
9739
9740                 if (val & G_ALERTDISABLE) {
9741                         val &= ~G_ALERTDISABLE;
9742                         /* remember what action to perform by default, only if we don't click Cancel */
9743                         if (val == G_ALERTALTERNATE)
9744                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
9745                         else if (val == G_ALERTOTHER)
9746                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
9747                 }
9748
9749                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
9750                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
9751                         list_free_strings(list);
9752                         g_list_free(list);
9753                         return;
9754                 } else if (val == G_ALERTOTHER) {
9755                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
9756                         list_free_strings(list);
9757                         g_list_free(list);
9758                         return;
9759                 } 
9760
9761                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9762                         compose_insert_file(compose, (const gchar *)tmp->data);
9763                 }
9764                 list_free_strings(list);
9765                 g_list_free(list);
9766                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9767                 return;
9768         } else {
9769 #if GTK_CHECK_VERSION(2, 8, 0)
9770                 /* do nothing, handled by GTK */
9771 #else
9772                 gchar *tmpfile = get_tmp_file();
9773                 str_write_to_file((const gchar *)data->data, tmpfile);
9774                 compose_insert_file(compose, tmpfile);
9775                 g_unlink(tmpfile);
9776                 g_free(tmpfile);
9777                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9778 #endif
9779                 return;
9780         }
9781         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9782 }
9783
9784 static void compose_header_drag_received_cb (GtkWidget          *widget,
9785                                              GdkDragContext     *drag_context,
9786                                              gint                x,
9787                                              gint                y,
9788                                              GtkSelectionData   *data,
9789                                              guint               info,
9790                                              guint               time,
9791                                              gpointer            user_data)
9792 {
9793         GtkEditable *entry = (GtkEditable *)user_data;
9794         gchar *email = (gchar *)data->data;
9795
9796         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
9797          * does not work */
9798
9799         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
9800                 gchar *decoded=g_new(gchar, strlen(email));
9801                 int start = 0;
9802
9803                 email += strlen("mailto:");
9804                 decode_uri(decoded, email); /* will fit */
9805                 gtk_editable_delete_text(entry, 0, -1);
9806                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
9807                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9808                 g_free(decoded);
9809                 return;
9810         }
9811         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9812 }
9813
9814 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
9815                                              GtkWidget *widget)
9816 {
9817         Compose *compose = (Compose *)data;
9818
9819         if (GTK_CHECK_MENU_ITEM(widget)->active)
9820                 compose->return_receipt = TRUE;
9821         else
9822                 compose->return_receipt = FALSE;
9823 }
9824
9825 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
9826                                              GtkWidget *widget)
9827 {
9828         Compose *compose = (Compose *)data;
9829
9830         if (GTK_CHECK_MENU_ITEM(widget)->active)
9831                 compose->remove_references = TRUE;
9832         else
9833                 compose->remove_references = FALSE;
9834 }
9835
9836 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
9837                                             GdkEventKey *event,
9838                                             ComposeHeaderEntry *headerentry)
9839 {
9840         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
9841             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
9842             !(event->state & GDK_MODIFIER_MASK) &&
9843             (event->keyval == GDK_BackSpace) &&
9844             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
9845                 gtk_container_remove
9846                         (GTK_CONTAINER(headerentry->compose->header_table),
9847                          headerentry->combo);
9848                 gtk_container_remove
9849                         (GTK_CONTAINER(headerentry->compose->header_table),
9850                          headerentry->entry);
9851                 headerentry->compose->header_list =
9852                         g_slist_remove(headerentry->compose->header_list,
9853                                        headerentry);
9854                 g_free(headerentry);
9855         } else  if (event->keyval == GDK_Tab) {
9856                 if (headerentry->compose->header_last == headerentry) {
9857                         /* Override default next focus, and give it to subject_entry
9858                          * instead of notebook tabs
9859                          */
9860                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
9861                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
9862                         return TRUE;
9863                 }
9864         }
9865         return FALSE;
9866 }
9867
9868 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
9869                                     ComposeHeaderEntry *headerentry)
9870 {
9871         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
9872                 compose_create_header_entry(headerentry->compose);
9873                 g_signal_handlers_disconnect_matched
9874                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
9875                          0, 0, NULL, NULL, headerentry);
9876                 
9877                 /* Automatically scroll down */
9878                 compose_show_first_last_header(headerentry->compose, FALSE);
9879                 
9880         }
9881         return FALSE;
9882 }
9883
9884 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
9885 {
9886         GtkAdjustment *vadj;
9887
9888         g_return_if_fail(compose);
9889         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
9890         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
9891
9892         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
9893         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
9894         gtk_adjustment_changed(vadj);
9895 }
9896
9897 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
9898                           const gchar *text, gint len, Compose *compose)
9899 {
9900         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
9901                                 (G_OBJECT(compose->text), "paste_as_quotation"));
9902         GtkTextMark *mark;
9903
9904         g_return_if_fail(text != NULL);
9905
9906         g_signal_handlers_block_by_func(G_OBJECT(buffer),
9907                                         G_CALLBACK(text_inserted),
9908                                         compose);
9909         if (paste_as_quotation) {
9910                 gchar *new_text;
9911                 const gchar *qmark;
9912                 guint pos = 0;
9913                 GtkTextIter start_iter;
9914
9915                 if (len < 0)
9916                         len = strlen(text);
9917
9918                 new_text = g_strndup(text, len);
9919
9920                 qmark = compose_quote_char_from_context(compose);
9921
9922                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9923                 gtk_text_buffer_place_cursor(buffer, iter);
9924
9925                 pos = gtk_text_iter_get_offset(iter);
9926
9927                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
9928                                                   _("Quote format error at line %d."));
9929                 quote_fmt_reset_vartable();
9930                 g_free(new_text);
9931                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
9932                                   GINT_TO_POINTER(paste_as_quotation - 1));
9933                                   
9934                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9935                 gtk_text_buffer_place_cursor(buffer, iter);
9936                 gtk_text_buffer_delete_mark(buffer, mark);
9937
9938                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
9939                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
9940                 compose_beautify_paragraph(compose, &start_iter, FALSE);
9941                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
9942                 gtk_text_buffer_delete_mark(buffer, mark);
9943         } else {
9944                 if (strcmp(text, "\n") || compose->automatic_break
9945                 || gtk_text_iter_starts_line(iter))
9946                         gtk_text_buffer_insert(buffer, iter, text, len);
9947                 else {
9948                         debug_print("insert nowrap \\n\n");
9949                         gtk_text_buffer_insert_with_tags_by_name(buffer, 
9950                                 iter, text, len, "no_join", NULL);
9951                 }
9952         }
9953         
9954         if (!paste_as_quotation) {
9955                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9956                 compose_beautify_paragraph(compose, iter, FALSE);
9957                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9958                 gtk_text_buffer_delete_mark(buffer, mark);
9959         }
9960
9961         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
9962                                           G_CALLBACK(text_inserted),
9963                                           compose);
9964         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
9965
9966         if (prefs_common.autosave && 
9967             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
9968             compose->draft_timeout_tag != -2 /* disabled while loading */)
9969                 compose->draft_timeout_tag = g_timeout_add
9970                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
9971 }
9972 static gint compose_defer_auto_save_draft(Compose *compose)
9973 {
9974         compose->draft_timeout_tag = -1;
9975         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
9976         return FALSE;
9977 }
9978
9979 #if USE_ASPELL
9980 static void compose_check_all(Compose *compose)
9981 {
9982         if (compose->gtkaspell)
9983                 gtkaspell_check_all(compose->gtkaspell);
9984 }
9985
9986 static void compose_highlight_all(Compose *compose)
9987 {
9988         if (compose->gtkaspell)
9989                 gtkaspell_highlight_all(compose->gtkaspell);
9990 }
9991
9992 static void compose_check_backwards(Compose *compose)
9993 {
9994         if (compose->gtkaspell) 
9995                 gtkaspell_check_backwards(compose->gtkaspell);
9996         else {
9997                 GtkItemFactory *ifactory;
9998                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9999                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
10000                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
10001         }
10002 }
10003
10004 static void compose_check_forwards_go(Compose *compose)
10005 {
10006         if (compose->gtkaspell) 
10007                 gtkaspell_check_forwards_go(compose->gtkaspell);
10008         else {
10009                 GtkItemFactory *ifactory;
10010                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
10011                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
10012                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
10013         }
10014 }
10015 #endif
10016
10017 /*!
10018  *\brief        Guess originating forward account from MsgInfo and several 
10019  *              "common preference" settings. Return NULL if no guess. 
10020  */
10021 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10022 {
10023         PrefsAccount *account = NULL;
10024         
10025         g_return_val_if_fail(msginfo, NULL);
10026         g_return_val_if_fail(msginfo->folder, NULL);
10027         g_return_val_if_fail(msginfo->folder->prefs, NULL);
10028
10029         if (msginfo->folder->prefs->enable_default_account)
10030                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10031                 
10032         if (!account) 
10033                 account = msginfo->folder->folder->account;
10034                 
10035         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10036                 gchar *to;
10037                 Xstrdup_a(to, msginfo->to, return NULL);
10038                 extract_address(to);
10039                 account = account_find_from_address(to);
10040         }
10041
10042         if (!account && prefs_common.forward_account_autosel) {
10043                 gchar cc[BUFFSIZE];
10044                 if (!procheader_get_header_from_msginfo
10045                         (msginfo, cc,sizeof cc , "Cc:")) { 
10046                         gchar *buf = cc + strlen("Cc:");
10047                         extract_address(buf);
10048                         account = account_find_from_address(buf);
10049                 }
10050         }
10051         
10052         if (!account && prefs_common.forward_account_autosel) {
10053                 gchar deliveredto[BUFFSIZE];
10054                 if (!procheader_get_header_from_msginfo
10055                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10056                         gchar *buf = deliveredto + strlen("Delivered-To:");
10057                         extract_address(buf);
10058                         account = account_find_from_address(buf);
10059                 }
10060         }
10061         
10062         return account;
10063 }
10064
10065 gboolean compose_close(Compose *compose)
10066 {
10067         gint x, y;
10068
10069         if (!g_mutex_trylock(compose->mutex)) {
10070                 /* we have to wait for the (possibly deferred by auto-save)
10071                  * drafting to be done, before destroying the compose under
10072                  * it. */
10073                 debug_print("waiting for drafting to finish...\n");
10074                 compose_allow_user_actions(compose, FALSE);
10075                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10076                 return FALSE;
10077         }
10078         g_return_val_if_fail(compose, FALSE);
10079         gtkut_widget_get_uposition(compose->window, &x, &y);
10080         prefs_common.compose_x = x;
10081         prefs_common.compose_y = y;
10082         g_mutex_unlock(compose->mutex);
10083         compose_destroy(compose);
10084         return FALSE;
10085 }
10086
10087 /**
10088  * Add entry field for each address in list.
10089  * \param compose     E-Mail composition object.
10090  * \param listAddress List of (formatted) E-Mail addresses.
10091  */
10092 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10093         GList *node;
10094         gchar *addr;
10095         node = listAddress;
10096         while( node ) {
10097                 addr = ( gchar * ) node->data;
10098                 compose_entry_append( compose, addr, COMPOSE_TO );
10099                 node = g_list_next( node );
10100         }
10101 }
10102
10103 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10104                                     guint action, gboolean opening_multiple)
10105 {
10106         gchar *body = NULL;
10107         GSList *new_msglist = NULL;
10108         MsgInfo *tmp_msginfo = NULL;
10109         gboolean originally_enc = FALSE;
10110         Compose *compose = NULL;
10111
10112         g_return_if_fail(msgview != NULL);
10113
10114         g_return_if_fail(msginfo_list != NULL);
10115
10116         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10117                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10118                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10119
10120                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10121                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10122                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10123                                                 orig_msginfo, mimeinfo);
10124                         if (tmp_msginfo != NULL) {
10125                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10126
10127                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10128                                 tmp_msginfo->folder = orig_msginfo->folder;
10129                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10130                                 if (orig_msginfo->tags)
10131                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10132                         }
10133                 }
10134         }
10135
10136         if (!opening_multiple)
10137                 body = messageview_get_selection(msgview);
10138
10139         if (new_msglist) {
10140                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10141                 procmsg_msginfo_free(tmp_msginfo);
10142                 g_slist_free(new_msglist);
10143         } else
10144                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10145
10146         if (compose && originally_enc) {
10147                 compose_force_encryption(compose, compose->account, FALSE);
10148         }
10149
10150         g_free(body);
10151 }
10152
10153 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10154                                     guint action)
10155 {
10156         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10157         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10158                 GSList *cur = msginfo_list;
10159                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10160                                                "messages. Opening the windows "
10161                                                "could take some time. Do you "
10162                                                "want to continue?"), 
10163                                                g_slist_length(msginfo_list));
10164                 if (g_slist_length(msginfo_list) > 9
10165                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10166                     != G_ALERTALTERNATE) {
10167                         g_free(msg);
10168                         return;
10169                 }
10170                 g_free(msg);
10171                 /* We'll open multiple compose windows */
10172                 /* let the WM place the next windows */
10173                 compose_force_window_origin = FALSE;
10174                 for (; cur; cur = cur->next) {
10175                         GSList tmplist;
10176                         tmplist.data = cur->data;
10177                         tmplist.next = NULL;
10178                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10179                 }
10180                 compose_force_window_origin = TRUE;
10181         } else {
10182                 /* forwarding multiple mails as attachments is done via a
10183                  * single compose window */
10184                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10185         }
10186 }
10187
10188 void compose_set_position(Compose *compose, gint pos)
10189 {
10190         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10191
10192         gtkut_text_view_set_position(text, pos);
10193 }
10194
10195 gboolean compose_search_string(Compose *compose,
10196                                 const gchar *str, gboolean case_sens)
10197 {
10198         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10199
10200         return gtkut_text_view_search_string(text, str, case_sens);
10201 }
10202
10203 gboolean compose_search_string_backward(Compose *compose,
10204                                 const gchar *str, gboolean case_sens)
10205 {
10206         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10207
10208         return gtkut_text_view_search_string_backward(text, str, case_sens);
10209 }
10210
10211 /* allocate a msginfo structure and populate its data from a compose data structure */
10212 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10213 {
10214         MsgInfo *newmsginfo;
10215         GSList *list;
10216         gchar buf[BUFFSIZE];
10217
10218         g_return_val_if_fail( compose != NULL, NULL );
10219
10220         newmsginfo = procmsg_msginfo_new();
10221
10222         /* date is now */
10223         get_rfc822_date(buf, sizeof(buf));
10224         newmsginfo->date = g_strdup(buf);
10225
10226         /* from */
10227         if (compose->from_name) {
10228                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10229                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10230         }
10231
10232         /* subject */
10233         if (compose->subject_entry)
10234                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10235
10236         /* to, cc, reply-to, newsgroups */
10237         for (list = compose->header_list; list; list = list->next) {
10238                 gchar *header = gtk_editable_get_chars(
10239                                                                 GTK_EDITABLE(
10240                                                                 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10241                 gchar *entry = gtk_editable_get_chars(
10242                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10243
10244                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10245                         if ( newmsginfo->to == NULL ) {
10246                                 newmsginfo->to = g_strdup(entry);
10247                         } else if (entry && *entry) {
10248                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10249                                 g_free(newmsginfo->to);
10250                                 newmsginfo->to = tmp;
10251                         }
10252                 } else
10253                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10254                         if ( newmsginfo->cc == NULL ) {
10255                                 newmsginfo->cc = g_strdup(entry);
10256                         } else if (entry && *entry) {
10257                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10258                                 g_free(newmsginfo->cc);
10259                                 newmsginfo->cc = tmp;
10260                         }
10261                 } else
10262                 if ( strcasecmp(header,
10263                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10264                         if ( newmsginfo->newsgroups == NULL ) {
10265                                 newmsginfo->newsgroups = g_strdup(entry);
10266                         } else if (entry && *entry) {
10267                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10268                                 g_free(newmsginfo->newsgroups);
10269                                 newmsginfo->newsgroups = tmp;
10270                         }
10271                 }
10272
10273                 g_free(header);
10274                 g_free(entry);  
10275         }
10276
10277         /* other data is unset */
10278
10279         return newmsginfo;
10280 }
10281
10282 #ifdef USE_ASPELL
10283 /* update compose's dictionaries from folder dict settings */
10284 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10285                                                 FolderItem *folder_item)
10286 {
10287         g_return_if_fail(compose != NULL);
10288
10289         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10290                 FolderItemPrefs *prefs = folder_item->prefs;
10291
10292                 if (prefs->enable_default_dictionary)
10293                         gtkaspell_change_dict(compose->gtkaspell,
10294                                         prefs->default_dictionary, FALSE);
10295                 if (folder_item->prefs->enable_default_alt_dictionary)
10296                         gtkaspell_change_alt_dict(compose->gtkaspell,
10297                                         prefs->default_alt_dictionary);
10298                 if (prefs->enable_default_dictionary
10299                         || prefs->enable_default_alt_dictionary)
10300                         compose_spell_menu_changed(compose);
10301         }
10302 }
10303 #endif
10304
10305 /*
10306  * End of Source.
10307  */