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