2007-10-07 [colin] 3.0.2cvs31
[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         gboolean err = FALSE;
4704
4705         debug_print("Writing redirect header\n");
4706
4707         cc_hdr = prefs_common_translated_header_name("Cc:");
4708         to_hdr = prefs_common_translated_header_name("To:");
4709
4710         first_to_address = TRUE;
4711         for (list = compose->header_list; list; list = list->next) {
4712                 headerentry = ((ComposeHeaderEntry *)list->data);
4713                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
4714
4715                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4716                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4717                         Xstrdup_a(str, entstr, return -1);
4718                         g_strstrip(str);
4719                         if (str[0] != '\0') {
4720                                 compose_convert_header
4721                                         (compose, buf, sizeof(buf), str,
4722                                         strlen("Resent-To") + 2, TRUE);
4723
4724                                 if (first_to_address) {
4725                                         err |= (fprintf(fp, "Resent-To: ") < 0);
4726                                         first_to_address = FALSE;
4727                                 } else {
4728                                         err |= (fprintf(fp, ",") < 0);
4729                                 }
4730                                 err |= (fprintf(fp, "%s", buf) < 0);
4731                         }
4732                 }
4733         }
4734         if (!first_to_address) {
4735                 err |= (fprintf(fp, "\n") < 0);
4736         }
4737
4738         first_cc_address = TRUE;
4739         for (list = compose->header_list; list; list = list->next) {
4740                 headerentry = ((ComposeHeaderEntry *)list->data);
4741                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
4742
4743                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4744                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4745                         Xstrdup_a(str, strg, return -1);
4746                         g_strstrip(str);
4747                         if (str[0] != '\0') {
4748                                 compose_convert_header
4749                                         (compose, buf, sizeof(buf), str,
4750                                         strlen("Resent-Cc") + 2, TRUE);
4751
4752                                 if (first_cc_address) {
4753                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
4754                                         first_cc_address = FALSE;
4755                                 } else {
4756                                         err |= (fprintf(fp, ",") < 0);
4757                                 }
4758                                 err |= (fprintf(fp, "%s", buf) < 0);
4759                         }
4760                 }
4761         }
4762         if (!first_cc_address) {
4763                 err |= (fprintf(fp, "\n") < 0);
4764         }
4765         
4766         return (err ? -1:0);
4767 }
4768
4769 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4770 {
4771         gchar buf[BUFFSIZE];
4772         gchar *str;
4773         const gchar *entstr;
4774         /* struct utsname utsbuf; */
4775         gboolean err = FALSE;
4776
4777         g_return_val_if_fail(fp != NULL, -1);
4778         g_return_val_if_fail(compose->account != NULL, -1);
4779         g_return_val_if_fail(compose->account->address != NULL, -1);
4780
4781         /* Resent-Date */
4782         get_rfc822_date(buf, sizeof(buf));
4783         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4784
4785         /* Resent-From */
4786         if (compose->account->name && *compose->account->name) {
4787                 compose_convert_header
4788                         (compose, buf, sizeof(buf), compose->account->name,
4789                          strlen("From: "), TRUE);
4790                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4791                         buf, compose->account->address) < 0);
4792         } else
4793                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4794
4795         /* Subject */
4796         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4797         if (*entstr != '\0') {
4798                 Xstrdup_a(str, entstr, return -1);
4799                 g_strstrip(str);
4800                 if (*str != '\0') {
4801                         compose_convert_header(compose, buf, sizeof(buf), str,
4802                                                strlen("Subject: "), FALSE);
4803                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4804                 }
4805         }
4806
4807         /* Resent-Message-ID */
4808         if (compose->account->set_domain && compose->account->domain) {
4809                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
4810         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4811                 g_snprintf(buf, sizeof(buf), "%s", 
4812                         strchr(compose->account->address, '@') ?
4813                                 strchr(compose->account->address, '@')+1 :
4814                                 compose->account->address);
4815         } else {
4816                 g_snprintf(buf, sizeof(buf), "%s", "");
4817         }
4818         generate_msgid(buf, sizeof(buf));
4819         err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4820         compose->msgid = g_strdup(buf);
4821
4822         if (compose_redirect_write_headers_from_headerlist(compose, fp))
4823                 return -1;
4824
4825         /* separator between header and body */
4826         err |= (fputs("\n", fp) == EOF);
4827
4828         return (err ? -1:0);
4829 }
4830
4831 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4832 {
4833         FILE *fp;
4834         size_t len;
4835         gchar buf[BUFFSIZE];
4836         int i = 0;
4837         gboolean skip = FALSE;
4838         gboolean err = FALSE;
4839         gchar *not_included[]={
4840                 "Return-Path:",         "Delivered-To:",        "Received:",
4841                 "Subject:",             "X-UIDL:",              "AF:",
4842                 "NF:",                  "PS:",                  "SRH:",
4843                 "SFN:",                 "DSR:",                 "MID:",
4844                 "CFG:",                 "PT:",                  "S:",
4845                 "RQ:",                  "SSV:",                 "NSV:",
4846                 "SSH:",                 "R:",                   "MAID:",
4847                 "NAID:",                "RMID:",                "FMID:",
4848                 "SCF:",                 "RRCPT:",               "NG:",
4849                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
4850                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
4851                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
4852                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
4853                 NULL
4854                 };
4855         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4856                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4857                 return -1;
4858         }
4859
4860         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4861                 skip = FALSE;
4862                 for (i = 0; not_included[i] != NULL; i++) {
4863                         if (g_ascii_strncasecmp(buf, not_included[i],
4864                                                 strlen(not_included[i])) == 0) {
4865                                 skip = TRUE;
4866                                 break;
4867                         }
4868                 }
4869                 if (skip)
4870                         continue;
4871                 if (fputs(buf, fdest) == -1)
4872                         goto error;
4873
4874                 if (!prefs_common.redirect_keep_from) {
4875                         if (g_ascii_strncasecmp(buf, "From:",
4876                                           strlen("From:")) == 0) {
4877                                 err |= (fputs(" (by way of ", fdest) == EOF);
4878                                 if (compose->account->name
4879                                     && *compose->account->name) {
4880                                         compose_convert_header
4881                                                 (compose, buf, sizeof(buf),
4882                                                  compose->account->name,
4883                                                  strlen("From: "),
4884                                                  FALSE);
4885                                         err |= (fprintf(fdest, "%s <%s>",
4886                                                 buf,
4887                                                 compose->account->address) < 0);
4888                                 } else
4889                                         err |= (fprintf(fdest, "%s",
4890                                                 compose->account->address) < 0);
4891                                 err |= (fputs(")", fdest) == EOF);
4892                         }
4893                 }
4894
4895                 if (fputs("\n", fdest) == -1)
4896                         goto error;
4897         }
4898
4899         if (err)
4900                 goto error;
4901
4902         if (compose_redirect_write_headers(compose, fdest))
4903                 goto error;
4904
4905         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4906                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
4907                         goto error;
4908         }
4909
4910         fclose(fp);
4911
4912         return 0;
4913 error:
4914         fclose(fp);
4915
4916         return -1;
4917 }
4918
4919 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
4920 {
4921         GtkTextBuffer *buffer;
4922         GtkTextIter start, end;
4923         gchar *chars;
4924         gchar *buf;
4925         const gchar *out_codeset;
4926         EncodingType encoding;
4927         MimeInfo *mimemsg, *mimetext;
4928         gint line;
4929
4930         if (action == COMPOSE_WRITE_FOR_SEND)
4931                 attach_parts = TRUE;
4932
4933         /* create message MimeInfo */
4934         mimemsg = procmime_mimeinfo_new();
4935         mimemsg->type = MIMETYPE_MESSAGE;
4936         mimemsg->subtype = g_strdup("rfc822");
4937         mimemsg->content = MIMECONTENT_MEM;
4938         mimemsg->tmp = TRUE; /* must free content later */
4939         mimemsg->data.mem = compose_get_header(compose);
4940
4941         /* Create text part MimeInfo */
4942         /* get all composed text */
4943         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4944         gtk_text_buffer_get_start_iter(buffer, &start);
4945         gtk_text_buffer_get_end_iter(buffer, &end);
4946         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4947         if (is_ascii_str(chars)) {
4948                 buf = chars;
4949                 chars = NULL;
4950                 out_codeset = CS_US_ASCII;
4951                 encoding = ENC_7BIT;
4952         } else {
4953                 const gchar *src_codeset = CS_INTERNAL;
4954
4955                 out_codeset = conv_get_charset_str(compose->out_encoding);
4956
4957                 if (!out_codeset) {
4958                         gchar *test_conv_global_out = NULL;
4959                         gchar *test_conv_reply = NULL;
4960
4961                         /* automatic mode. be automatic. */
4962                         codeconv_set_strict(TRUE);
4963                         
4964                         out_codeset = conv_get_outgoing_charset_str();
4965                         if (out_codeset) {
4966                                 debug_print("trying to convert to %s\n", out_codeset);
4967                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
4968                         }
4969                         
4970                         if (!test_conv_global_out && compose->orig_charset
4971                         &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
4972                                 out_codeset = compose->orig_charset;
4973                                 debug_print("failure; trying to convert to %s\n", out_codeset);
4974                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
4975                         }
4976                         
4977                         if (!test_conv_global_out && !test_conv_reply) {
4978                                 /* we're lost */
4979                                 out_codeset = CS_INTERNAL;
4980                                 debug_print("failure; finally using %s\n", out_codeset);
4981                         }
4982                         g_free(test_conv_global_out);
4983                         g_free(test_conv_reply);
4984                         codeconv_set_strict(FALSE);
4985                 }
4986
4987                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
4988                         out_codeset = CS_ISO_8859_1;
4989
4990                 if (prefs_common.encoding_method == CTE_BASE64)
4991                         encoding = ENC_BASE64;
4992                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
4993                         encoding = ENC_QUOTED_PRINTABLE;
4994                 else if (prefs_common.encoding_method == CTE_8BIT)
4995                         encoding = ENC_8BIT;
4996                 else
4997                         encoding = procmime_get_encoding_for_charset(out_codeset);
4998
4999                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5000                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5001
5002                 if (action == COMPOSE_WRITE_FOR_SEND) {
5003                         codeconv_set_strict(TRUE);
5004                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5005                         codeconv_set_strict(FALSE);
5006
5007                         if (!buf) {
5008                                 AlertValue aval;
5009                                 gchar *msg;
5010
5011                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5012                                                         "to the specified %s charset.\n"
5013                                                         "Send it as %s?"), out_codeset, src_codeset);
5014                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5015                                                       NULL, ALERT_ERROR, G_ALERTDEFAULT);
5016                                 g_free(msg);
5017
5018                                 if (aval != G_ALERTALTERNATE) {
5019                                         g_free(chars);
5020                                         return -3;
5021                                 } else {
5022                                         buf = chars;
5023                                         out_codeset = src_codeset;
5024                                         chars = NULL;
5025                                 }
5026                         }
5027                 } else {
5028                         buf = chars;
5029                         out_codeset = src_codeset;
5030                         chars = NULL;
5031                 }
5032         }
5033         g_free(chars);
5034
5035         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5036                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5037                     strstr(buf, "\nFrom ") != NULL) {
5038                         encoding = ENC_QUOTED_PRINTABLE;
5039                 }
5040         }
5041
5042         mimetext = procmime_mimeinfo_new();
5043         mimetext->content = MIMECONTENT_MEM;
5044         mimetext->tmp = TRUE; /* must free content later */
5045         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5046          * and free the data, which we need later. */
5047         mimetext->data.mem = g_strdup(buf); 
5048         mimetext->type = MIMETYPE_TEXT;
5049         mimetext->subtype = g_strdup("plain");
5050         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5051                             g_strdup(out_codeset));
5052                             
5053         /* protect trailing spaces when signing message */
5054         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5055             privacy_system_can_sign(compose->privacy_system)) {
5056                 encoding = ENC_QUOTED_PRINTABLE;
5057         }
5058         
5059         debug_print("main text: %zd bytes encoded as %s in %d\n",
5060                 strlen(buf), out_codeset, encoding);
5061
5062         /* check for line length limit */
5063         if (action == COMPOSE_WRITE_FOR_SEND &&
5064             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5065             check_line_length(buf, 1000, &line) < 0) {
5066                 AlertValue aval;
5067                 gchar *msg;
5068
5069                 msg = g_strdup_printf
5070                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5071                            "The contents of the message might be broken on the way to the delivery.\n"
5072                            "\n"
5073                            "Send it anyway?"), line + 1);
5074                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5075                 g_free(msg);
5076                 if (aval != G_ALERTALTERNATE) {
5077                         g_free(buf);
5078                         return -1;
5079                 }
5080         }
5081         
5082         if (encoding != ENC_UNKNOWN)
5083                 procmime_encode_content(mimetext, encoding);
5084
5085         /* append attachment parts */
5086         if (compose_use_attach(compose) && attach_parts) {
5087                 MimeInfo *mimempart;
5088                 gchar *boundary = NULL;
5089                 mimempart = procmime_mimeinfo_new();
5090                 mimempart->content = MIMECONTENT_EMPTY;
5091                 mimempart->type = MIMETYPE_MULTIPART;
5092                 mimempart->subtype = g_strdup("mixed");
5093
5094                 do {
5095                         g_free(boundary);
5096                         boundary = generate_mime_boundary(NULL);
5097                 } while (strstr(buf, boundary) != NULL);
5098
5099                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5100                                     boundary);
5101
5102                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5103
5104                 g_node_append(mimempart->node, mimetext->node);
5105                 g_node_append(mimemsg->node, mimempart->node);
5106
5107                 compose_add_attachments(compose, mimempart);
5108         } else
5109                 g_node_append(mimemsg->node, mimetext->node);
5110
5111         g_free(buf);
5112
5113         /* sign message if sending */
5114         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5115             privacy_system_can_sign(compose->privacy_system))
5116                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5117                         return -2;
5118
5119         procmime_write_mimeinfo(mimemsg, fp);
5120         
5121         procmime_mimeinfo_free_all(mimemsg);
5122
5123         return 0;
5124 }
5125
5126 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5127 {
5128         GtkTextBuffer *buffer;
5129         GtkTextIter start, end;
5130         FILE *fp;
5131         size_t len;
5132         gchar *chars, *tmp;
5133
5134         if ((fp = g_fopen(file, "wb")) == NULL) {
5135                 FILE_OP_ERROR(file, "fopen");
5136                 return -1;
5137         }
5138
5139         /* chmod for security */
5140         if (change_file_mode_rw(fp, file) < 0) {
5141                 FILE_OP_ERROR(file, "chmod");
5142                 g_warning("can't change file mode\n");
5143         }
5144
5145         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5146         gtk_text_buffer_get_start_iter(buffer, &start);
5147         gtk_text_buffer_get_end_iter(buffer, &end);
5148         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5149
5150         chars = conv_codeset_strdup
5151                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5152
5153         g_free(tmp);
5154         if (!chars) return -1;
5155
5156         /* write body */
5157         len = strlen(chars);
5158         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5159                 FILE_OP_ERROR(file, "fwrite");
5160                 g_free(chars);
5161                 fclose(fp);
5162                 g_unlink(file);
5163                 return -1;
5164         }
5165
5166         g_free(chars);
5167
5168         if (fclose(fp) == EOF) {
5169                 FILE_OP_ERROR(file, "fclose");
5170                 g_unlink(file);
5171                 return -1;
5172         }
5173         return 0;
5174 }
5175
5176 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5177 {
5178         FolderItem *item;
5179         MsgInfo *msginfo = compose->targetinfo;
5180
5181         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5182         if (!msginfo) return -1;
5183
5184         if (!force && MSG_IS_LOCKED(msginfo->flags))
5185                 return 0;
5186
5187         item = msginfo->folder;
5188         g_return_val_if_fail(item != NULL, -1);
5189
5190         if (procmsg_msg_exist(msginfo) &&
5191             (folder_has_parent_of_type(item, F_QUEUE) ||
5192              folder_has_parent_of_type(item, F_DRAFT) 
5193              || msginfo == compose->autosaved_draft)) {
5194                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5195                         g_warning("can't remove the old message\n");
5196                         return -1;
5197                 } else {
5198                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5199                 }
5200         }
5201
5202         return 0;
5203 }
5204
5205 static void compose_remove_draft(Compose *compose)
5206 {
5207         FolderItem *drafts;
5208         MsgInfo *msginfo = compose->targetinfo;
5209         drafts = account_get_special_folder(compose->account, F_DRAFT);
5210
5211         if (procmsg_msg_exist(msginfo)) {
5212                 folder_item_remove_msg(drafts, msginfo->msgnum);
5213         }
5214
5215 }
5216
5217 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5218                    gboolean remove_reedit_target)
5219 {
5220         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5221 }
5222
5223 static gboolean compose_warn_encryption(Compose *compose)
5224 {
5225         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5226         AlertValue val = G_ALERTALTERNATE;
5227         
5228         if (warning == NULL)
5229                 return TRUE;
5230
5231         val = alertpanel_full(_("Encryption warning"), warning,
5232                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5233                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5234         if (val & G_ALERTDISABLE) {
5235                 val &= ~G_ALERTDISABLE;
5236                 if (val == G_ALERTALTERNATE)
5237                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5238                                 TRUE);
5239         }
5240
5241         if (val == G_ALERTALTERNATE) {
5242                 return TRUE;
5243         } else {
5244                 return FALSE;
5245         } 
5246 }
5247
5248 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5249                               gchar **msgpath, gboolean check_subject,
5250                               gboolean remove_reedit_target)
5251 {
5252         FolderItem *queue;
5253         gchar *tmp;
5254         FILE *fp;
5255         GSList *cur;
5256         gint num;
5257         static gboolean lock = FALSE;
5258         PrefsAccount *mailac = NULL, *newsac = NULL;
5259         gboolean err = FALSE;
5260
5261         debug_print("queueing message...\n");
5262         g_return_val_if_fail(compose->account != NULL, -1);
5263
5264         lock = TRUE;
5265         
5266         if (compose_check_entries(compose, check_subject) == FALSE) {
5267                 lock = FALSE;
5268                 if (compose->batch) {
5269                         gtk_widget_show_all(compose->window);
5270                 }
5271                 return -1;
5272         }
5273
5274         if (!compose->to_list && !compose->newsgroup_list) {
5275                 g_warning("can't get recipient list.");
5276                 lock = FALSE;
5277                 return -1;
5278         }
5279
5280         if (compose->to_list) {
5281                 if (compose->account->protocol != A_NNTP)
5282                         mailac = compose->account;
5283                 else if (cur_account && cur_account->protocol != A_NNTP)
5284                         mailac = cur_account;
5285                 else if (!(mailac = compose_current_mail_account())) {
5286                         lock = FALSE;
5287                         alertpanel_error(_("No account for sending mails available!"));
5288                         return -1;
5289                 }
5290         }
5291
5292         if (compose->newsgroup_list) {
5293                 if (compose->account->protocol == A_NNTP)
5294                         newsac = compose->account;
5295                 else if (!newsac->protocol != A_NNTP) {
5296                         lock = FALSE;
5297                         alertpanel_error(_("No account for posting news available!"));
5298                         return -1;
5299                 }                       
5300         }
5301
5302         /* write queue header */
5303         tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
5304                               G_DIR_SEPARATOR, compose);
5305         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5306                 FILE_OP_ERROR(tmp, "fopen");
5307                 g_free(tmp);
5308                 lock = FALSE;
5309                 return -2;
5310         }
5311
5312         if (change_file_mode_rw(fp, tmp) < 0) {
5313                 FILE_OP_ERROR(tmp, "chmod");
5314                 g_warning("can't change file mode\n");
5315         }
5316
5317         /* queueing variables */
5318         err |= (fprintf(fp, "AF:\n") < 0);
5319         err |= (fprintf(fp, "NF:0\n") < 0);
5320         err |= (fprintf(fp, "PS:10\n") < 0);
5321         err |= (fprintf(fp, "SRH:1\n") < 0);
5322         err |= (fprintf(fp, "SFN:\n") < 0);
5323         err |= (fprintf(fp, "DSR:\n") < 0);
5324         if (compose->msgid)
5325                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5326         else
5327                 err |= (fprintf(fp, "MID:\n") < 0);
5328         err |= (fprintf(fp, "CFG:\n") < 0);
5329         err |= (fprintf(fp, "PT:0\n") < 0);
5330         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5331         err |= (fprintf(fp, "RQ:\n") < 0);
5332         if (mailac)
5333                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5334         else
5335                 err |= (fprintf(fp, "SSV:\n") < 0);
5336         if (newsac)
5337                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5338         else
5339                 err |= (fprintf(fp, "NSV:\n") < 0);
5340         err |= (fprintf(fp, "SSH:\n") < 0);
5341         /* write recepient list */
5342         if (compose->to_list) {
5343                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5344                 for (cur = compose->to_list->next; cur != NULL;
5345                      cur = cur->next)
5346                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5347                 err |= (fprintf(fp, "\n") < 0);
5348         }
5349         /* write newsgroup list */
5350         if (compose->newsgroup_list) {
5351                 err |= (fprintf(fp, "NG:") < 0);
5352                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5353                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5354                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5355                 err |= (fprintf(fp, "\n") < 0);
5356         }
5357         /* Sylpheed account IDs */
5358         if (mailac)
5359                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5360         if (newsac)
5361                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5362
5363         
5364         if (compose->privacy_system != NULL) {
5365                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5366                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5367                 if (compose->use_encryption) {
5368                         gchar *encdata;
5369                         if (!compose_warn_encryption(compose)) {
5370                                 lock = FALSE;
5371                                 fclose(fp);
5372                                 g_unlink(tmp);
5373                                 g_free(tmp);
5374                                 return -6;
5375                         }
5376                         if (mailac && mailac->encrypt_to_self) {
5377                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5378                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5379                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5380                                 g_slist_free(tmp_list);
5381                         } else {
5382                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5383                         }
5384                         if (encdata != NULL) {
5385                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5386                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5387                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5388                                                 encdata) < 0);
5389                                 } /* else we finally dont want to encrypt */
5390                         } else {
5391                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5392                                 /* and if encdata was null, it means there's been a problem in 
5393                                  * key selection */
5394                                 lock = FALSE;
5395                                 fclose(fp);
5396                                 g_unlink(tmp);
5397                                 g_free(tmp);
5398                                 return -5;
5399                         }
5400                         g_free(encdata);
5401                 }
5402         }
5403
5404         /* Save copy folder */
5405         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5406                 gchar *savefolderid;
5407                 
5408                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5409                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5410                 g_free(savefolderid);
5411         }
5412         /* Save copy folder */
5413         if (compose->return_receipt) {
5414                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5415         }
5416         /* Message-ID of message replying to */
5417         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5418                 gchar *folderid;
5419                 
5420                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5421                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5422                 g_free(folderid);
5423         }
5424         /* Message-ID of message forwarding to */
5425         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5426                 gchar *folderid;
5427                 
5428                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5429                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5430                 g_free(folderid);
5431         }
5432
5433         /* end of headers */
5434         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5435
5436         if (compose->redirect_filename != NULL) {
5437                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5438                         lock = FALSE;
5439                         fclose(fp);
5440                         g_unlink(tmp);
5441                         g_free(tmp);
5442                         return -2;
5443                 }
5444         } else {
5445                 gint result = 0;
5446                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5447                         lock = FALSE;
5448                         fclose(fp);
5449                         g_unlink(tmp);
5450                         g_free(tmp);
5451                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5452                 }
5453         }
5454         if (err == TRUE) {
5455                 g_warning("failed to write queue message\n");
5456                 fclose(fp);
5457                 g_unlink(tmp);
5458                 g_free(tmp);
5459                 lock = FALSE;
5460                 return -2;
5461         }
5462         if (fclose(fp) == EOF) {
5463                 FILE_OP_ERROR(tmp, "fclose");
5464                 g_unlink(tmp);
5465                 g_free(tmp);
5466                 lock = FALSE;
5467                 return -2;
5468         }
5469
5470         if (item && *item) {
5471                 queue = *item;
5472         } else {
5473                 queue = account_get_special_folder(compose->account, F_QUEUE);
5474         }
5475         if (!queue) {
5476                 g_warning("can't find queue folder\n");
5477                 g_unlink(tmp);
5478                 g_free(tmp);
5479                 lock = FALSE;
5480                 return -1;
5481         }
5482         folder_item_scan(queue);
5483         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5484                 g_warning("can't queue the message\n");
5485                 g_unlink(tmp);
5486                 g_free(tmp);
5487                 lock = FALSE;
5488                 return -1;
5489         }
5490         
5491         if (msgpath == NULL) {
5492                 g_unlink(tmp);
5493                 g_free(tmp);
5494         } else
5495                 *msgpath = tmp;
5496
5497         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5498                 compose_remove_reedit_target(compose, FALSE);
5499         }
5500
5501         if ((msgnum != NULL) && (item != NULL)) {
5502                 *msgnum = num;
5503                 *item = queue;
5504         }
5505
5506         return 0;
5507 }
5508
5509 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5510 {
5511         AttachInfo *ainfo;
5512         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5513         MimeInfo *mimepart;
5514         struct stat statbuf;
5515         gchar *type, *subtype;
5516         GtkTreeModel *model;
5517         GtkTreeIter iter;
5518
5519         model = gtk_tree_view_get_model(tree_view);
5520         
5521         if (!gtk_tree_model_get_iter_first(model, &iter))
5522                 return;
5523         do {
5524                 gtk_tree_model_get(model, &iter,
5525                                    COL_DATA, &ainfo,
5526                                    -1);
5527                                                            
5528                 mimepart = procmime_mimeinfo_new();
5529                 mimepart->content = MIMECONTENT_FILE;
5530                 mimepart->data.filename = g_strdup(ainfo->file);
5531                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5532                 mimepart->offset = 0;
5533
5534                 stat(ainfo->file, &statbuf);
5535                 mimepart->length = statbuf.st_size;
5536
5537                 type = g_strdup(ainfo->content_type);
5538
5539                 if (!strchr(type, '/')) {
5540                         g_free(type);
5541                         type = g_strdup("application/octet-stream");
5542                 }
5543
5544                 subtype = strchr(type, '/') + 1;
5545                 *(subtype - 1) = '\0';
5546                 mimepart->type = procmime_get_media_type(type);
5547                 mimepart->subtype = g_strdup(subtype);
5548                 g_free(type);
5549
5550                 if (mimepart->type == MIMETYPE_MESSAGE && 
5551                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5552                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5553                 } else {
5554                         if (ainfo->name) {
5555                                 g_hash_table_insert(mimepart->typeparameters,
5556                                             g_strdup("name"), g_strdup(ainfo->name));
5557                                 g_hash_table_insert(mimepart->dispositionparameters,
5558                                             g_strdup("filename"), g_strdup(ainfo->name));
5559                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5560                         }
5561                 }
5562
5563                 if (compose->use_signing) {
5564                         if (ainfo->encoding == ENC_7BIT)
5565                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5566                         else if (ainfo->encoding == ENC_8BIT)
5567                                 ainfo->encoding = ENC_BASE64;
5568                 }
5569                 
5570                 procmime_encode_content(mimepart, ainfo->encoding);
5571
5572                 g_node_append(parent->node, mimepart->node);
5573         } while (gtk_tree_model_iter_next(model, &iter));
5574 }
5575
5576 #define IS_IN_CUSTOM_HEADER(header) \
5577         (compose->account->add_customhdr && \
5578          custom_header_find(compose->account->customhdr_list, header) != NULL)
5579
5580 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5581                                                     GString *header, 
5582                                                     const gchar *fieldname,
5583                                                     const gchar *seperator)
5584 {
5585         gchar *str, *fieldname_w_colon;
5586         gboolean add_field = FALSE;
5587         GSList *list;
5588         ComposeHeaderEntry *headerentry;
5589         const gchar *headerentryname;
5590         const gchar *trans_fieldname;
5591         GString *fieldstr;
5592
5593         if (IS_IN_CUSTOM_HEADER(fieldname))
5594                 return;
5595
5596         debug_print("Adding %s-fields\n", fieldname);
5597
5598         fieldstr = g_string_sized_new(64);
5599
5600         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5601         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5602
5603         for (list = compose->header_list; list; list = list->next) {
5604                 headerentry = ((ComposeHeaderEntry *)list->data);
5605                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry));
5606
5607                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5608                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5609                         g_strstrip(str);
5610                         if (str[0] != '\0') {
5611                                 if (add_field)
5612                                         g_string_append(fieldstr, seperator);
5613                                 g_string_append(fieldstr, str);
5614                                 add_field = TRUE;
5615                         }
5616                         g_free(str);
5617                 }
5618         }
5619         if (add_field) {
5620                 gchar *buf;
5621
5622                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5623                 compose_convert_header
5624                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5625                         strlen(fieldname) + 2, TRUE);
5626                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5627                 g_free(buf);
5628         }
5629
5630         g_free(fieldname_w_colon);
5631         g_string_free(fieldstr, TRUE);
5632
5633         return;
5634 }
5635
5636 static gchar *compose_get_header(Compose *compose)
5637 {
5638         gchar buf[BUFFSIZE];
5639         const gchar *entry_str;
5640         gchar *str;
5641         gchar *name;
5642         GSList *list;
5643         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5644         GString *header;
5645         gchar *from_name = NULL, *from_address = NULL;
5646         gchar *tmp;
5647
5648         g_return_val_if_fail(compose->account != NULL, NULL);
5649         g_return_val_if_fail(compose->account->address != NULL, NULL);
5650
5651         header = g_string_sized_new(64);
5652
5653         /* Date */
5654         get_rfc822_date(buf, sizeof(buf));
5655         g_string_append_printf(header, "Date: %s\n", buf);
5656
5657         /* From */
5658         
5659         if (compose->account->name && *compose->account->name) {
5660                 gchar *buf;
5661                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5662                 tmp = g_strdup_printf("%s <%s>",
5663                         buf, compose->account->address);
5664         } else {
5665                 tmp = g_strdup_printf("%s",
5666                         compose->account->address);
5667         }
5668         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5669         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5670                 /* use default */
5671                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5672                 from_address = g_strdup(compose->account->address);
5673         } else {
5674                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5675                 /* extract name and address */
5676                 if (strstr(spec, " <") && strstr(spec, ">")) {
5677                         from_address = g_strdup(strrchr(spec, '<')+1);
5678                         *(strrchr(from_address, '>')) = '\0';
5679                         from_name = g_strdup(spec);
5680                         *(strrchr(from_name, '<')) = '\0';
5681                 } else {
5682                         from_name = NULL;
5683                         from_address = g_strdup(spec);
5684                 }
5685                 g_free(spec);
5686         }
5687         g_free(tmp);
5688         
5689         
5690         if (from_name && *from_name) {
5691                 compose_convert_header
5692                         (compose, buf, sizeof(buf), from_name,
5693                          strlen("From: "), TRUE);
5694                 QUOTE_IF_REQUIRED(name, buf);
5695                 
5696                 g_string_append_printf(header, "From: %s <%s>\n",
5697                         name, from_address);
5698         } else
5699                 g_string_append_printf(header, "From: %s\n", from_address);
5700         
5701         g_free(from_name);
5702         g_free(from_address);
5703
5704         /* To */
5705         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5706
5707         /* Newsgroups */
5708         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5709
5710         /* Cc */
5711         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5712
5713         /* Bcc */
5714         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5715
5716         /* Subject */
5717         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5718
5719         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5720                 g_strstrip(str);
5721                 if (*str != '\0') {
5722                         compose_convert_header(compose, buf, sizeof(buf), str,
5723                                                strlen("Subject: "), FALSE);
5724                         g_string_append_printf(header, "Subject: %s\n", buf);
5725                 }
5726         }
5727         g_free(str);
5728
5729         /* Message-ID */
5730         if (compose->account->set_domain && compose->account->domain) {
5731                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5732         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5733                 g_snprintf(buf, sizeof(buf), "%s", 
5734                         strchr(compose->account->address, '@') ?
5735                                 strchr(compose->account->address, '@')+1 :
5736                                 compose->account->address);
5737         } else {
5738                 g_snprintf(buf, sizeof(buf), "%s", "");
5739         }
5740         generate_msgid(buf, sizeof(buf));
5741         g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5742         compose->msgid = g_strdup(buf);
5743
5744         if (compose->remove_references == FALSE) {
5745                 /* In-Reply-To */
5746                 if (compose->inreplyto && compose->to_list)
5747                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5748         
5749                 /* References */
5750                 if (compose->references)
5751                         g_string_append_printf(header, "References: %s\n", compose->references);
5752         }
5753
5754         /* Followup-To */
5755         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5756
5757         /* Reply-To */
5758         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5759
5760         /* Organization */
5761         if (compose->account->organization &&
5762             strlen(compose->account->organization) &&
5763             !IS_IN_CUSTOM_HEADER("Organization")) {
5764                 compose_convert_header(compose, buf, sizeof(buf),
5765                                        compose->account->organization,
5766                                        strlen("Organization: "), FALSE);
5767                 g_string_append_printf(header, "Organization: %s\n", buf);
5768         }
5769
5770         /* Program version and system info */
5771         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5772             !compose->newsgroup_list) {
5773                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5774                         prog_version,
5775                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5776                         TARGET_ALIAS);
5777         }
5778         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5779                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5780                         prog_version,
5781                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5782                         TARGET_ALIAS);
5783         }
5784
5785         /* custom headers */
5786         if (compose->account->add_customhdr) {
5787                 GSList *cur;
5788
5789                 for (cur = compose->account->customhdr_list; cur != NULL;
5790                      cur = cur->next) {
5791                         CustomHeader *chdr = (CustomHeader *)cur->data;
5792
5793                         if (custom_header_is_allowed(chdr->name)) {
5794                                 compose_convert_header
5795                                         (compose, buf, sizeof(buf),
5796                                          chdr->value ? chdr->value : "",
5797                                          strlen(chdr->name) + 2, FALSE);
5798                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5799                         }
5800                 }
5801         }
5802
5803         /* PRIORITY */
5804         switch (compose->priority) {
5805                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5806                                                    "X-Priority: 1 (Highest)\n");
5807                         break;
5808                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5809                                                 "X-Priority: 2 (High)\n");
5810                         break;
5811                 case PRIORITY_NORMAL: break;
5812                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5813                                                "X-Priority: 4 (Low)\n");
5814                         break;
5815                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5816                                                   "X-Priority: 5 (Lowest)\n");
5817                         break;
5818                 default: debug_print("compose: priority unknown : %d\n",
5819                                      compose->priority);
5820         }
5821
5822         /* Request Return Receipt */
5823         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5824                 if (compose->return_receipt) {
5825                         if (compose->account->name
5826                             && *compose->account->name) {
5827                                 compose_convert_header(compose, buf, sizeof(buf), 
5828                                                        compose->account->name, 
5829                                                        strlen("Disposition-Notification-To: "),
5830                                                        TRUE);
5831                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5832                         } else
5833                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5834                 }
5835         }
5836
5837         /* get special headers */
5838         for (list = compose->header_list; list; list = list->next) {
5839                 ComposeHeaderEntry *headerentry;
5840                 gchar *tmp;
5841                 gchar *headername;
5842                 gchar *headername_wcolon;
5843                 const gchar *headername_trans;
5844                 gchar *headervalue;
5845                 gchar **string;
5846                 gboolean standard_header = FALSE;
5847
5848                 headerentry = ((ComposeHeaderEntry *)list->data);
5849                 
5850                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(headerentry->combo)->entry)));
5851                 if (strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5852                         g_free(tmp);
5853                         continue;
5854                 }
5855
5856                 if (!strstr(tmp, ":")) {
5857                         headername_wcolon = g_strconcat(tmp, ":", NULL);
5858                         headername = g_strdup(tmp);
5859                 } else {
5860                         headername_wcolon = g_strdup(tmp);
5861                         headername = g_strdup(strtok(tmp, ":"));
5862                 }
5863                 g_free(tmp);
5864                 
5865                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5866                 Xstrdup_a(headervalue, entry_str, return NULL);
5867                 subst_char(headervalue, '\r', ' ');
5868                 subst_char(headervalue, '\n', ' ');
5869                 string = std_headers;
5870                 while (*string != NULL) {
5871                         headername_trans = prefs_common_translated_header_name(*string);
5872                         if (!strcmp(headername_trans, headername_wcolon))
5873                                 standard_header = TRUE;
5874                         string++;
5875                 }
5876                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
5877                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
5878                                 
5879                 g_free(headername);
5880                 g_free(headername_wcolon);              
5881         }
5882
5883         str = header->str;
5884         g_string_free(header, FALSE);
5885
5886         return str;
5887 }
5888
5889 #undef IS_IN_CUSTOM_HEADER
5890
5891 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
5892                                    gint header_len, gboolean addr_field)
5893 {
5894         gchar *tmpstr = NULL;
5895         const gchar *out_codeset = NULL;
5896
5897         g_return_if_fail(src != NULL);
5898         g_return_if_fail(dest != NULL);
5899
5900         if (len < 1) return;
5901
5902         tmpstr = g_strdup(src);
5903
5904         subst_char(tmpstr, '\n', ' ');
5905         subst_char(tmpstr, '\r', ' ');
5906         g_strchomp(tmpstr);
5907
5908         if (!g_utf8_validate(tmpstr, -1, NULL)) {
5909                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
5910                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
5911                 g_free(tmpstr);
5912                 tmpstr = mybuf;
5913         }
5914
5915         codeconv_set_strict(TRUE);
5916         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5917                 conv_get_charset_str(compose->out_encoding));
5918         codeconv_set_strict(FALSE);
5919         
5920         if (!dest || *dest == '\0') {
5921                 gchar *test_conv_global_out = NULL;
5922                 gchar *test_conv_reply = NULL;
5923
5924                 /* automatic mode. be automatic. */
5925                 codeconv_set_strict(TRUE);
5926
5927                 out_codeset = conv_get_outgoing_charset_str();
5928                 if (out_codeset) {
5929                         debug_print("trying to convert to %s\n", out_codeset);
5930                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5931                 }
5932
5933                 if (!test_conv_global_out && compose->orig_charset
5934                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5935                         out_codeset = compose->orig_charset;
5936                         debug_print("failure; trying to convert to %s\n", out_codeset);
5937                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5938                 }
5939
5940                 if (!test_conv_global_out && !test_conv_reply) {
5941                         /* we're lost */
5942                         out_codeset = CS_INTERNAL;
5943                         debug_print("finally using %s\n", out_codeset);
5944                 }
5945                 g_free(test_conv_global_out);
5946                 g_free(test_conv_reply);
5947                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5948                                         out_codeset);
5949                 codeconv_set_strict(FALSE);
5950         }
5951         g_free(tmpstr);
5952 }
5953
5954 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
5955 {
5956         gchar *address;
5957
5958         g_return_if_fail(user_data != NULL);
5959
5960         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
5961         g_strstrip(address);
5962         if (*address != '\0') {
5963                 gchar *name = procheader_get_fromname(address);
5964                 extract_address(address);
5965                 addressbook_add_contact(name, address, NULL);
5966         }
5967         g_free(address);
5968 }
5969
5970 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
5971 {
5972         GtkWidget *menuitem;
5973         gchar *address;
5974
5975         g_return_if_fail(menu != NULL);
5976         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
5977
5978         menuitem = gtk_separator_menu_item_new();
5979         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
5980         gtk_widget_show(menuitem);
5981
5982         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
5983         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
5984
5985         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
5986         g_strstrip(address);
5987         if (*address == '\0') {
5988                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
5989         }
5990
5991         g_signal_connect(G_OBJECT(menuitem), "activate",
5992                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
5993         gtk_widget_show(menuitem);
5994 }
5995
5996 static void compose_create_header_entry(Compose *compose) 
5997 {
5998         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5999
6000         GtkWidget *combo;
6001         GtkWidget *entry;
6002         GList *combo_list = NULL;
6003         gchar **string;
6004         const gchar *header = NULL;
6005         ComposeHeaderEntry *headerentry;
6006         gboolean standard_header = FALSE;
6007
6008         headerentry = g_new0(ComposeHeaderEntry, 1);
6009
6010         /* Combo box */
6011         combo = gtk_combo_new();
6012         string = headers; 
6013         while(*string != NULL) {
6014                 combo_list = g_list_append(combo_list, (gchar*)prefs_common_translated_header_name(*string));
6015                 string++;
6016         }
6017         gtk_combo_set_popdown_strings(GTK_COMBO(combo), combo_list);
6018         g_list_free(combo_list);
6019         gtk_editable_set_editable(GTK_EDITABLE(GTK_COMBO(combo)->entry), TRUE);
6020         g_signal_connect(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
6021                          G_CALLBACK(compose_grab_focus_cb), compose);
6022         gtk_widget_show(combo);
6023         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6024                         compose->header_nextrow, compose->header_nextrow+1,
6025                         GTK_SHRINK, GTK_FILL, 0, 0);
6026         if (compose->header_last) {     
6027                 const gchar *last_header_entry = gtk_entry_get_text(
6028                                 GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
6029                 string = headers;
6030                 while (*string != NULL) {
6031                         if (!strcmp(*string, last_header_entry))
6032                                 standard_header = TRUE;
6033                         string++;
6034                 }
6035                 if (standard_header)
6036                         header = gtk_entry_get_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry));
6037         }
6038         if (!compose->header_last || !standard_header) {
6039                 switch(compose->account->protocol) {
6040                         case A_NNTP:
6041                                 header = prefs_common_translated_header_name("Newsgroups:");
6042                                 break;
6043                         default:
6044                                 header = prefs_common_translated_header_name("To:");
6045                                 break;
6046                 }                                                                   
6047         }
6048         if (header)
6049                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(combo)->entry), header);
6050
6051         g_signal_connect_after(G_OBJECT(GTK_COMBO(combo)->entry), "grab_focus",
6052                          G_CALLBACK(compose_grab_focus_cb), compose);
6053
6054         /* Entry field */
6055         entry = gtk_entry_new(); 
6056         gtk_widget_show(entry);
6057         gtk_tooltips_set_tip(compose->tooltips, entry,
6058                 _("Use <tab> to autocomplete from addressbook"), NULL);
6059         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6060                         compose->header_nextrow, compose->header_nextrow+1,
6061                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6062
6063         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6064                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6065                          headerentry);
6066         g_signal_connect(G_OBJECT(entry), "changed", 
6067                          G_CALLBACK(compose_headerentry_changed_cb), 
6068                          headerentry);
6069         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6070                          G_CALLBACK(compose_grab_focus_cb), compose);
6071                          
6072         /* email dnd */
6073         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6074                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6075                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6076         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6077                          G_CALLBACK(compose_header_drag_received_cb),
6078                          entry);
6079         g_signal_connect(G_OBJECT(entry), "drag-drop",
6080                          G_CALLBACK(compose_drag_drop),
6081                          compose);
6082         g_signal_connect(G_OBJECT(entry), "populate-popup",
6083                          G_CALLBACK(compose_entry_popup_extend),
6084                          NULL);
6085         
6086         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6087
6088         headerentry->compose = compose;
6089         headerentry->combo = combo;
6090         headerentry->entry = entry;
6091         headerentry->headernum = compose->header_nextrow;
6092
6093         compose->header_nextrow++;
6094         compose->header_last = headerentry;             
6095         compose->header_list =
6096                 g_slist_append(compose->header_list,
6097                                headerentry);
6098 }
6099
6100 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6101 {
6102         ComposeHeaderEntry *last_header;
6103         
6104         last_header = compose->header_last;
6105
6106         gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(last_header->combo)->entry), header);
6107         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6108 }
6109
6110 static void compose_remove_header_entries(Compose *compose) 
6111 {
6112         GSList *list;
6113         for (list = compose->header_list; list; list = list->next) {
6114                 ComposeHeaderEntry *headerentry = 
6115                         (ComposeHeaderEntry *)list->data;
6116                 gtk_widget_destroy(headerentry->combo);
6117                 gtk_widget_destroy(headerentry->entry);
6118                 g_free(headerentry);
6119         }
6120         compose->header_last = NULL;
6121         g_slist_free(compose->header_list);
6122         compose->header_list = NULL;
6123         compose->header_nextrow = 1;
6124         compose_create_header_entry(compose);
6125 }
6126
6127 static GtkWidget *compose_create_header(Compose *compose) 
6128 {
6129         GtkWidget *from_optmenu_hbox;
6130         GtkWidget *header_scrolledwin;
6131         GtkWidget *header_table;
6132
6133         gint count = 0;
6134
6135         /* header labels and entries */
6136         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6137         gtk_widget_show(header_scrolledwin);
6138         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6139
6140         header_table = gtk_table_new(2, 2, FALSE);
6141         gtk_widget_show(header_table);
6142         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6143         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6144         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_ETCHED_IN);
6145         count = 0;
6146
6147         /* option menu for selecting accounts */
6148         from_optmenu_hbox = compose_account_option_menu_create(compose);
6149         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6150                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6151         count++;
6152
6153         compose->header_table = header_table;
6154         compose->header_list = NULL;
6155         compose->header_nextrow = count;
6156
6157         compose_create_header_entry(compose);
6158
6159         compose->table            = NULL;
6160
6161         return header_scrolledwin ;
6162 }
6163
6164 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6165 {
6166         Compose *compose = (Compose *)data;
6167         GdkEventButton event;
6168         
6169         event.button = 3;
6170         event.time = gtk_get_current_event_time();
6171
6172         return attach_button_pressed(compose->attach_clist, &event, compose);
6173 }
6174
6175 static GtkWidget *compose_create_attach(Compose *compose)
6176 {
6177         GtkWidget *attach_scrwin;
6178         GtkWidget *attach_clist;
6179
6180         GtkListStore *store;
6181         GtkCellRenderer *renderer;
6182         GtkTreeViewColumn *column;
6183         GtkTreeSelection *selection;
6184
6185         /* attachment list */
6186         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6187         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6188                                        GTK_POLICY_AUTOMATIC,
6189                                        GTK_POLICY_AUTOMATIC);
6190         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6191
6192         store = gtk_list_store_new(N_ATTACH_COLS, 
6193                                    G_TYPE_STRING,
6194                                    G_TYPE_STRING,
6195                                    G_TYPE_STRING,
6196                                    G_TYPE_POINTER,
6197                                    G_TYPE_AUTO_POINTER,
6198                                    -1);
6199         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6200                                         (GTK_TREE_MODEL(store)));
6201         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6202         g_object_unref(store);
6203         
6204         renderer = gtk_cell_renderer_text_new();
6205         column = gtk_tree_view_column_new_with_attributes
6206                         (_("Mime type"), renderer, "text", 
6207                          COL_MIMETYPE, NULL);
6208         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6209         
6210         renderer = gtk_cell_renderer_text_new();
6211         column = gtk_tree_view_column_new_with_attributes
6212                         (_("Size"), renderer, "text", 
6213                          COL_SIZE, NULL);
6214         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6215         
6216         renderer = gtk_cell_renderer_text_new();
6217         column = gtk_tree_view_column_new_with_attributes
6218                         (_("Name"), renderer, "text", 
6219                          COL_NAME, NULL);
6220         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6221
6222         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6223                                      prefs_common.use_stripes_everywhere);
6224         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6225         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6226
6227         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6228                          G_CALLBACK(attach_selected), compose);
6229         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6230                          G_CALLBACK(attach_button_pressed), compose);
6231 #ifndef MAEMO
6232         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6233                          G_CALLBACK(popup_attach_button_pressed), compose);
6234 #else
6235         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6236                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6237         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6238                          G_CALLBACK(popup_attach_button_pressed), compose);
6239 #endif
6240         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6241                          G_CALLBACK(attach_key_pressed), compose);
6242
6243         /* drag and drop */
6244         gtk_drag_dest_set(attach_clist,
6245                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6246                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6247                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6248         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6249                          G_CALLBACK(compose_attach_drag_received_cb),
6250                          compose);
6251         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6252                          G_CALLBACK(compose_drag_drop),
6253                          compose);
6254
6255         compose->attach_scrwin = attach_scrwin;
6256         compose->attach_clist  = attach_clist;
6257
6258         return attach_scrwin;
6259 }
6260
6261 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6262 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6263
6264 static GtkWidget *compose_create_others(Compose *compose)
6265 {
6266         GtkWidget *table;
6267         GtkWidget *savemsg_checkbtn;
6268         GtkWidget *savemsg_entry;
6269         GtkWidget *savemsg_select;
6270         
6271         guint rowcount = 0;
6272         gchar *folderidentifier;
6273
6274         /* Table for settings */
6275         table = gtk_table_new(3, 1, FALSE);
6276         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6277         gtk_widget_show(table);
6278         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6279         rowcount = 0;
6280
6281         /* Save Message to folder */
6282         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6283         gtk_widget_show(savemsg_checkbtn);
6284         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6285         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6286                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6287         }
6288         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6289                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6290
6291         savemsg_entry = gtk_entry_new();
6292         gtk_widget_show(savemsg_entry);
6293         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6294         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6295         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6296                          G_CALLBACK(compose_grab_focus_cb), compose);
6297         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6298                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6299                                   (compose->account, F_OUTBOX));
6300                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6301                 g_free(folderidentifier);
6302         }
6303
6304         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6305         gtk_widget_show(savemsg_select);
6306         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6307         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6308                          G_CALLBACK(compose_savemsg_select_cb),
6309                          compose);
6310
6311         rowcount++;
6312
6313         compose->savemsg_checkbtn = savemsg_checkbtn;
6314         compose->savemsg_entry = savemsg_entry;
6315
6316         return table;   
6317 }
6318
6319 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6320 {
6321         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6322                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6323 }
6324
6325 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6326 {
6327         FolderItem *dest;
6328         gchar * path;
6329
6330         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
6331         if (!dest) return;
6332
6333         path = folder_item_get_identifier(dest);
6334
6335         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6336         g_free(path);
6337 }
6338
6339 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6340                                   GdkAtom clip, GtkTextIter *insert_place);
6341
6342
6343 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6344                                        Compose *compose)
6345 {
6346         gint prev_autowrap;
6347         GtkTextBuffer *buffer;
6348 #if USE_ASPELL
6349         if (event->button == 3) {
6350                 GtkTextIter iter;
6351                 GtkTextIter sel_start, sel_end;
6352                 gboolean stuff_selected;
6353                 gint x, y;
6354                 /* move the cursor to allow GtkAspell to check the word
6355                  * under the mouse */
6356                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6357                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6358                         &x, &y);
6359                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6360                         &iter, x, y);
6361                 /* get selection */
6362                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6363                                 GTK_TEXT_VIEW(text)->buffer,
6364                                 &sel_start, &sel_end);
6365
6366                 gtk_text_buffer_place_cursor (GTK_TEXT_VIEW(text)->buffer, &iter);
6367                 /* reselect stuff */
6368                 if (stuff_selected 
6369                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6370                         gtk_text_buffer_select_range(GTK_TEXT_VIEW(text)->buffer,
6371                                 &sel_start, &sel_end);
6372                 }
6373                 return FALSE; /* pass the event so that the right-click goes through */
6374         }
6375 #endif
6376         if (event->button == 2) {
6377                 GtkTextIter iter;
6378                 gint x, y;
6379                 BLOCK_WRAP();
6380                 
6381                 /* get the middle-click position to paste at the correct place */
6382                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6383                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6384                         &x, &y);
6385                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6386                         &iter, x, y);
6387                 
6388                 entry_paste_clipboard(compose, text, 
6389                                 prefs_common.linewrap_pastes,
6390                                 GDK_SELECTION_PRIMARY, &iter);
6391                 UNBLOCK_WRAP();
6392                 return TRUE;
6393         }
6394         return FALSE;
6395 }
6396
6397 #if USE_ASPELL
6398 static void compose_spell_menu_changed(void *data)
6399 {
6400         Compose *compose = (Compose *)data;
6401         GSList *items;
6402         GtkWidget *menuitem;
6403         GtkWidget *parent_item;
6404         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6405         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6406         GSList *spell_menu;
6407
6408         if (compose->gtkaspell == NULL)
6409                 return;
6410
6411         parent_item = gtk_item_factory_get_item(ifactory, 
6412                         "/Spelling/Options");
6413
6414         /* setting the submenu removes /Spelling/Options from the factory 
6415          * so we need to save it */
6416
6417         if (parent_item == NULL) {
6418                 parent_item = compose->aspell_options_menu;
6419                 gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
6420         } else
6421                 compose->aspell_options_menu = parent_item;
6422
6423         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6424
6425         spell_menu = g_slist_reverse(spell_menu);
6426         for (items = spell_menu;
6427              items; items = items->next) {
6428                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6429                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6430                 gtk_widget_show(GTK_WIDGET(menuitem));
6431         }
6432         g_slist_free(spell_menu);
6433
6434         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6435         
6436 }
6437 #endif
6438
6439 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6440 {
6441         Compose *compose = (Compose *)data;
6442         GdkEventButton event;
6443         
6444         event.button = 3;
6445         event.time = gtk_get_current_event_time();
6446
6447         return text_clicked(compose->text, &event, compose);
6448 }
6449
6450 static gboolean compose_force_window_origin = TRUE;
6451 static Compose *compose_create(PrefsAccount *account,
6452                                                  FolderItem *folder,
6453                                                  ComposeMode mode,
6454                                                  gboolean batch)
6455 {
6456         Compose   *compose;
6457         GtkWidget *window;
6458         GtkWidget *vbox;
6459         GtkWidget *menubar;
6460         GtkWidget *handlebox;
6461
6462         GtkWidget *notebook;
6463
6464         GtkWidget *vbox2;
6465
6466         GtkWidget *label;
6467         GtkWidget *subject_hbox;
6468         GtkWidget *subject_frame;
6469         GtkWidget *subject_entry;
6470         GtkWidget *subject;
6471         GtkWidget *paned;
6472
6473         GtkWidget *edit_vbox;
6474         GtkWidget *ruler_hbox;
6475         GtkWidget *ruler;
6476         GtkWidget *scrolledwin;
6477         GtkWidget *text;
6478         GtkTextBuffer *buffer;
6479         GtkClipboard *clipboard;
6480
6481         UndoMain *undostruct;
6482
6483         gchar *titles[N_ATTACH_COLS];
6484         guint n_menu_entries;
6485         GtkWidget *popupmenu;
6486         GtkItemFactory *popupfactory;
6487         GtkItemFactory *ifactory;
6488         GtkWidget *tmpl_menu;
6489         gint n_entries;
6490         GtkWidget *menuitem;
6491
6492 #if USE_ASPELL
6493         GtkAspell * gtkaspell = NULL;
6494 #endif
6495
6496         static GdkGeometry geometry;
6497
6498         g_return_val_if_fail(account != NULL, NULL);
6499
6500         debug_print("Creating compose window...\n");
6501         compose = g_new0(Compose, 1);
6502
6503         titles[COL_MIMETYPE] = _("MIME type");
6504         titles[COL_SIZE]     = _("Size");
6505         titles[COL_NAME]     = _("Name");
6506
6507         compose->batch = batch;
6508         compose->account = account;
6509         compose->folder = folder;
6510         
6511         compose->mutex = g_mutex_new();
6512         compose->set_cursor_pos = -1;
6513
6514         compose->tooltips = gtk_tooltips_new();
6515
6516         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6517
6518         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6519         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6520
6521         if (!geometry.max_width) {
6522                 geometry.max_width = gdk_screen_width();
6523                 geometry.max_height = gdk_screen_height();
6524         }
6525
6526         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6527                                       &geometry, GDK_HINT_MAX_SIZE);
6528         if (!geometry.min_width) {
6529                 geometry.min_width = 600;
6530                 geometry.min_height = 480;
6531         }
6532         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6533                                       &geometry, GDK_HINT_MIN_SIZE);
6534
6535 #ifndef MAEMO   
6536         if (compose_force_window_origin)
6537                 gtk_widget_set_uposition(window, prefs_common.compose_x, 
6538                                  prefs_common.compose_y);
6539 #endif
6540         g_signal_connect(G_OBJECT(window), "delete_event",
6541                          G_CALLBACK(compose_delete_cb), compose);
6542         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6543         gtk_widget_realize(window);
6544
6545         gtkut_widget_set_composer_icon(window);
6546
6547         vbox = gtk_vbox_new(FALSE, 0);
6548         gtk_container_add(GTK_CONTAINER(window), vbox);
6549
6550         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
6551         menubar = menubar_create(window, compose_entries,
6552                                  n_menu_entries, "<Compose>", compose);
6553         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6554
6555         if (prefs_common.toolbar_detachable) {
6556                 handlebox = gtk_handle_box_new();
6557         } else {
6558                 handlebox = gtk_hbox_new(FALSE, 0);
6559         }
6560         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6561
6562         gtk_widget_realize(handlebox);
6563 #ifdef MAEMO
6564         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6565                                           (gpointer)compose);
6566 #else
6567         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6568                                           (gpointer)compose);
6569 #endif
6570
6571         vbox2 = gtk_vbox_new(FALSE, 2);
6572         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6573         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6574         
6575         /* Notebook */
6576         notebook = gtk_notebook_new();
6577         gtk_widget_set_size_request(notebook, -1, 130);
6578         gtk_widget_show(notebook);
6579
6580         /* header labels and entries */
6581         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6582                         compose_create_header(compose),
6583                         gtk_label_new_with_mnemonic(_("Hea_der")));
6584         /* attachment list */
6585         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6586                         compose_create_attach(compose),
6587                         gtk_label_new_with_mnemonic(_("_Attachments")));
6588         /* Others Tab */
6589         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6590                         compose_create_others(compose),
6591                         gtk_label_new_with_mnemonic(_("Othe_rs")));
6592
6593         /* Subject */
6594         subject_hbox = gtk_hbox_new(FALSE, 0);
6595         gtk_widget_show(subject_hbox);
6596
6597         subject_frame = gtk_frame_new(NULL);
6598         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6599         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6600         gtk_widget_show(subject_frame);
6601
6602         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6603         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6604         gtk_widget_show(subject);
6605
6606         label = gtk_label_new(_("Subject:"));
6607         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6608         gtk_widget_show(label);
6609
6610         subject_entry = gtk_entry_new();
6611         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6612         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6613                          G_CALLBACK(compose_grab_focus_cb), compose);
6614         gtk_widget_show(subject_entry);
6615         compose->subject_entry = subject_entry;
6616         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6617         
6618         edit_vbox = gtk_vbox_new(FALSE, 0);
6619
6620         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6621
6622         /* ruler */
6623         ruler_hbox = gtk_hbox_new(FALSE, 0);
6624         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6625
6626         ruler = gtk_shruler_new();
6627         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6628         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6629                            BORDER_WIDTH);
6630
6631         /* text widget */
6632         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6633         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6634                                        GTK_POLICY_AUTOMATIC,
6635                                        GTK_POLICY_AUTOMATIC);
6636         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6637                                             GTK_SHADOW_IN);
6638         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6639         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6640
6641         text = gtk_text_view_new();
6642         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6643         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6644         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6645         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6646         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6647         
6648         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6649
6650         g_signal_connect_after(G_OBJECT(text), "size_allocate",
6651                                G_CALLBACK(compose_edit_size_alloc),
6652                                ruler);
6653         g_signal_connect(G_OBJECT(buffer), "changed",
6654                          G_CALLBACK(compose_changed_cb), compose);
6655         g_signal_connect(G_OBJECT(text), "grab_focus",
6656                          G_CALLBACK(compose_grab_focus_cb), compose);
6657         g_signal_connect(G_OBJECT(buffer), "insert_text",
6658                          G_CALLBACK(text_inserted), compose);
6659         g_signal_connect(G_OBJECT(text), "button_press_event",
6660                          G_CALLBACK(text_clicked), compose);
6661 #ifndef MAEMO
6662         g_signal_connect(G_OBJECT(text), "popup-menu",
6663                          G_CALLBACK(compose_popup_menu), compose);
6664 #else
6665         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6666                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6667         g_signal_connect(G_OBJECT(text), "tap-and-hold",
6668                          G_CALLBACK(compose_popup_menu), compose);
6669 #endif
6670         g_signal_connect(G_OBJECT(subject_entry), "changed",
6671                          G_CALLBACK(compose_changed_cb), compose);
6672
6673         /* drag and drop */
6674         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6675                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6676                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6677         g_signal_connect(G_OBJECT(text), "drag_data_received",
6678                          G_CALLBACK(compose_insert_drag_received_cb),
6679                          compose);
6680         g_signal_connect(G_OBJECT(text), "drag-drop",
6681                          G_CALLBACK(compose_drag_drop),
6682                          compose);
6683         gtk_widget_show_all(vbox);
6684
6685         /* pane between attach clist and text */
6686         paned = gtk_vpaned_new();
6687         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
6688         gtk_container_add(GTK_CONTAINER(vbox2), paned);
6689 #ifdef MAEMO
6690         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
6691                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
6692         else
6693                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
6694 #endif
6695         gtk_paned_add1(GTK_PANED(paned), notebook);
6696         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
6697         gtk_widget_show_all(paned);
6698
6699
6700         if (prefs_common.textfont) {
6701                 PangoFontDescription *font_desc;
6702
6703                 font_desc = pango_font_description_from_string
6704                         (prefs_common.textfont);
6705                 if (font_desc) {
6706                         gtk_widget_modify_font(text, font_desc);
6707                         pango_font_description_free(font_desc);
6708                 }
6709         }
6710
6711         n_entries = sizeof(compose_popup_entries) /
6712                 sizeof(compose_popup_entries[0]);
6713         popupmenu = menu_create_items(compose_popup_entries, n_entries,
6714                                       "<Compose>", &popupfactory,
6715                                       compose);
6716
6717         ifactory = gtk_item_factory_from_widget(menubar);
6718         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6719         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6720         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
6721
6722         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6723
6724         undostruct = undo_init(text);
6725         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
6726                                    menubar);
6727
6728         address_completion_start(window);
6729
6730         compose->window        = window;
6731         compose->vbox          = vbox;
6732         compose->menubar       = menubar;
6733         compose->handlebox     = handlebox;
6734
6735         compose->vbox2         = vbox2;
6736
6737         compose->paned = paned;
6738
6739         compose->notebook      = notebook;
6740         compose->edit_vbox     = edit_vbox;
6741         compose->ruler_hbox    = ruler_hbox;
6742         compose->ruler         = ruler;
6743         compose->scrolledwin   = scrolledwin;
6744         compose->text          = text;
6745
6746         compose->focused_editable = NULL;
6747
6748         compose->popupmenu    = popupmenu;
6749         compose->popupfactory = popupfactory;
6750
6751         compose->tmpl_menu = tmpl_menu;
6752
6753         compose->mode = mode;
6754         compose->rmode = mode;
6755
6756         compose->targetinfo = NULL;
6757         compose->replyinfo  = NULL;
6758         compose->fwdinfo    = NULL;
6759
6760         compose->replyto     = NULL;
6761         compose->cc          = NULL;
6762         compose->bcc         = NULL;
6763         compose->followup_to = NULL;
6764
6765         compose->ml_post     = NULL;
6766
6767         compose->inreplyto   = NULL;
6768         compose->references  = NULL;
6769         compose->msgid       = NULL;
6770         compose->boundary    = NULL;
6771
6772         compose->autowrap       = prefs_common.autowrap;
6773
6774         compose->use_signing    = FALSE;
6775         compose->use_encryption = FALSE;
6776         compose->privacy_system = NULL;
6777
6778         compose->modified = FALSE;
6779
6780         compose->return_receipt = FALSE;
6781
6782         compose->to_list        = NULL;
6783         compose->newsgroup_list = NULL;
6784
6785         compose->undostruct = undostruct;
6786
6787         compose->sig_str = NULL;
6788
6789         compose->exteditor_file    = NULL;
6790         compose->exteditor_pid     = -1;
6791         compose->exteditor_tag     = -1;
6792         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
6793
6794 #if USE_ASPELL
6795         menu_set_sensitive(ifactory, "/Spelling", FALSE);
6796         if (mode != COMPOSE_REDIRECT) {
6797                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
6798                     strcmp(prefs_common.dictionary, "")) {
6799                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
6800                                                   prefs_common.dictionary,
6801                                                   prefs_common.alt_dictionary,
6802                                                   conv_get_locale_charset_str(),
6803                                                   prefs_common.misspelled_col,
6804                                                   prefs_common.check_while_typing,
6805                                                   prefs_common.recheck_when_changing_dict,
6806                                                   prefs_common.use_alternate,
6807                                                   prefs_common.use_both_dicts,
6808                                                   GTK_TEXT_VIEW(text),
6809                                                   GTK_WINDOW(compose->window),
6810                                                   compose_spell_menu_changed,
6811                                                   compose);
6812                         if (!gtkaspell) {
6813                                 alertpanel_error(_("Spell checker could not "
6814                                                 "be started.\n%s"),
6815                                                 gtkaspell_checkers_strerror());
6816                                 gtkaspell_checkers_reset_error();
6817                         } else {
6818                                 if (!gtkaspell_set_sug_mode(gtkaspell,
6819                                                 prefs_common.aspell_sugmode)) {
6820                                         debug_print("Aspell: could not set "
6821                                                     "suggestion mode %s\n",
6822                                                     gtkaspell_checkers_strerror());
6823                                         gtkaspell_checkers_reset_error();
6824                                 }
6825
6826                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
6827                         }
6828                 }
6829         }
6830         compose->gtkaspell = gtkaspell;
6831         compose_spell_menu_changed(compose);
6832 #endif
6833
6834         compose_select_account(compose, account, TRUE);
6835
6836         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
6837         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
6838                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
6839
6840         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
6841                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
6842         
6843         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
6844                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
6845
6846         menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
6847
6848         if (account->protocol != A_NNTP)
6849                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
6850                                 prefs_common_translated_header_name("To:"));
6851         else
6852                 gtk_entry_set_text(GTK_ENTRY(GTK_COMBO(compose->header_last->combo)->entry),
6853                                 prefs_common_translated_header_name("Newsgroups:"));
6854
6855         addressbook_set_target_compose(compose);
6856         
6857         if (mode != COMPOSE_REDIRECT)
6858                 compose_set_template_menu(compose);
6859         else {
6860                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6861                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
6862         }
6863
6864         compose_list = g_list_append(compose_list, compose);
6865
6866         if (!prefs_common.show_ruler)
6867                 gtk_widget_hide(ruler_hbox);
6868                 
6869         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
6870         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
6871                                        prefs_common.show_ruler);
6872
6873         /* Priority */
6874         compose->priority = PRIORITY_NORMAL;
6875         compose_update_priority_menu_item(compose);
6876
6877         compose_set_out_encoding(compose);
6878         
6879         /* Actions menu */
6880         compose_update_actions_menu(compose);
6881
6882         /* Privacy Systems menu */
6883         compose_update_privacy_systems_menu(compose);
6884
6885         activate_privacy_system(compose, account, TRUE);
6886         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
6887         if (batch) {
6888                 gtk_widget_realize(window);
6889         } else {
6890                 gtk_widget_show(window);
6891 #ifdef MAEMO
6892                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
6893                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
6894 #endif
6895         }
6896         
6897         return compose;
6898 }
6899
6900 static GtkWidget *compose_account_option_menu_create(Compose *compose)
6901 {
6902         GList *accounts;
6903         GtkWidget *hbox;
6904         GtkWidget *optmenu;
6905         GtkWidget *optmenubox;
6906         GtkListStore *menu;
6907         GtkTreeIter iter;
6908         GtkWidget *from_name = NULL;
6909
6910         gint num = 0, def_menu = 0;
6911         
6912         accounts = account_get_list();
6913         g_return_val_if_fail(accounts != NULL, NULL);
6914
6915         optmenubox = gtk_event_box_new();
6916         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
6917         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
6918
6919         hbox = gtk_hbox_new(FALSE, 6);
6920         from_name = gtk_entry_new();
6921         
6922         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
6923                          G_CALLBACK(compose_grab_focus_cb), compose);
6924
6925         for (; accounts != NULL; accounts = accounts->next, num++) {
6926                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
6927                 gchar *name, *from = NULL;
6928
6929                 if (ac == compose->account) def_menu = num;
6930
6931                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
6932                                        ac->account_name);
6933                 
6934                 if (ac == compose->account) {
6935                         if (ac->name && *ac->name) {
6936                                 gchar *buf;
6937                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
6938                                 from = g_strdup_printf("%s <%s>",
6939                                                        buf, ac->address);
6940                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
6941                         } else {
6942                                 from = g_strdup_printf("%s",
6943                                                        ac->address);
6944                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
6945                         }
6946                 }
6947                 COMBOBOX_ADD(menu, name, ac->account_id);
6948                 g_free(name);
6949                 g_free(from);
6950         }
6951
6952         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
6953
6954         g_signal_connect(G_OBJECT(optmenu), "changed",
6955                         G_CALLBACK(account_activated),
6956                         compose);
6957         g_signal_connect(G_OBJECT(from_name), "populate-popup",
6958                          G_CALLBACK(compose_entry_popup_extend),
6959                          NULL);
6960
6961         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
6962         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
6963         
6964         gtk_tooltips_set_tip(compose->tooltips, optmenubox,
6965                 _("Account to use for this email"), NULL);
6966         gtk_tooltips_set_tip(compose->tooltips, from_name,
6967                 _("Sender address to be used"), NULL);
6968
6969         compose->from_name = from_name;
6970         
6971         return hbox;
6972 }
6973
6974 static void compose_set_priority_cb(gpointer data,
6975                                     guint action,
6976                                     GtkWidget *widget)
6977 {
6978         Compose *compose = (Compose *) data;
6979         compose->priority = action;
6980 }
6981
6982 static void compose_reply_change_mode(gpointer data,
6983                                     ComposeMode action,
6984                                     GtkWidget *widget)
6985 {
6986         Compose *compose = (Compose *) data;
6987         gboolean was_modified = compose->modified;
6988
6989         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
6990         
6991         g_return_if_fail(compose->replyinfo != NULL);
6992         
6993         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
6994                 ml = TRUE;
6995         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
6996                 followup = TRUE;
6997         if (action == COMPOSE_REPLY_TO_ALL)
6998                 all = TRUE;
6999         if (action == COMPOSE_REPLY_TO_SENDER)
7000                 sender = TRUE;
7001         if (action == COMPOSE_REPLY_TO_LIST)
7002                 ml = TRUE;
7003
7004         compose_remove_header_entries(compose);
7005         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7006         if (compose->account->set_autocc && compose->account->auto_cc)
7007                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7008
7009         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7010                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7011         
7012         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7013                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7014         compose_show_first_last_header(compose, TRUE);
7015         compose->modified = was_modified;
7016         compose_set_title(compose);
7017 }
7018
7019 static void compose_update_priority_menu_item(Compose * compose)
7020 {
7021         GtkItemFactory *ifactory;
7022         GtkWidget *menuitem = NULL;
7023
7024         ifactory = gtk_item_factory_from_widget(compose->menubar);
7025         
7026         switch (compose->priority) {
7027                 case PRIORITY_HIGHEST:
7028                         menuitem = gtk_item_factory_get_item
7029                                 (ifactory, "/Options/Priority/Highest");
7030                         break;
7031                 case PRIORITY_HIGH:
7032                         menuitem = gtk_item_factory_get_item
7033                                 (ifactory, "/Options/Priority/High");
7034                         break;
7035                 case PRIORITY_NORMAL:
7036                         menuitem = gtk_item_factory_get_item
7037                                 (ifactory, "/Options/Priority/Normal");
7038                         break;
7039                 case PRIORITY_LOW:
7040                         menuitem = gtk_item_factory_get_item
7041                                 (ifactory, "/Options/Priority/Low");
7042                         break;
7043                 case PRIORITY_LOWEST:
7044                         menuitem = gtk_item_factory_get_item
7045                                 (ifactory, "/Options/Priority/Lowest");
7046                         break;
7047         }
7048         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7049 }       
7050
7051 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7052 {
7053         Compose *compose = (Compose *) data;
7054         gchar *systemid;
7055         GtkItemFactory *ifactory;
7056         gboolean can_sign = FALSE, can_encrypt = FALSE;
7057
7058         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7059
7060         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7061                 return;
7062
7063         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7064         g_free(compose->privacy_system);
7065         compose->privacy_system = NULL;
7066         if (systemid != NULL) {
7067                 compose->privacy_system = g_strdup(systemid);
7068
7069                 can_sign = privacy_system_can_sign(systemid);
7070                 can_encrypt = privacy_system_can_encrypt(systemid);
7071         }
7072
7073         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7074
7075         ifactory = gtk_item_factory_from_widget(compose->menubar);
7076         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7077         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7078 }
7079
7080 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7081 {
7082         static gchar *branch_path = "/Options/Privacy System";
7083         GtkItemFactory *ifactory;
7084         GtkWidget *menuitem = NULL;
7085         GList *amenu;
7086         gboolean can_sign = FALSE, can_encrypt = FALSE;
7087         gboolean found = FALSE;
7088
7089         ifactory = gtk_item_factory_from_widget(compose->menubar);
7090
7091         if (compose->privacy_system != NULL) {
7092                 gchar *systemid;
7093
7094                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7095                 g_return_if_fail(menuitem != NULL);
7096
7097                 amenu = GTK_MENU_SHELL(menuitem)->children;
7098                 menuitem = NULL;
7099                 while (amenu != NULL) {
7100                         GList *alist = amenu->next;
7101
7102                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7103                         if (systemid != NULL) {
7104                                 if (strcmp(systemid, compose->privacy_system) == 0) {
7105                                         menuitem = GTK_WIDGET(amenu->data);
7106
7107                                         can_sign = privacy_system_can_sign(systemid);
7108                                         can_encrypt = privacy_system_can_encrypt(systemid);
7109                                         found = TRUE;
7110                                         break;
7111                                 } 
7112                         } else if (strlen(compose->privacy_system) == 0) {
7113                                         menuitem = GTK_WIDGET(amenu->data);
7114
7115                                         can_sign = FALSE;
7116                                         can_encrypt = FALSE;
7117                                         found = TRUE;
7118                                         break;
7119                         }
7120
7121                         amenu = alist;
7122                 }
7123                 if (menuitem != NULL)
7124                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7125                 
7126                 if (warn && !found && strlen(compose->privacy_system)) {
7127                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7128                                   "will not be able to sign or encrypt this message."),
7129                                   compose->privacy_system);
7130                 }
7131         } 
7132
7133         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7134         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7135 }       
7136  
7137 static void compose_set_out_encoding(Compose *compose)
7138 {
7139         GtkItemFactoryEntry *entry;
7140         GtkItemFactory *ifactory;
7141         CharSet out_encoding;
7142         gchar *path, *p, *q;
7143         GtkWidget *item;
7144
7145         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7146         ifactory = gtk_item_factory_from_widget(compose->menubar);
7147
7148         for (entry = compose_entries; entry->callback != compose_address_cb;
7149              entry++) {
7150                 if (entry->callback == compose_set_encoding_cb &&
7151                     (CharSet)entry->callback_action == out_encoding) {
7152                         p = q = path = g_strdup(entry->path);
7153                         while (*p) {
7154                                 if (*p == '_') {
7155                                         if (p[1] == '_') {
7156                                                 p++;
7157                                                 *q++ = '_';
7158                                         }
7159                                 } else
7160                                         *q++ = *p;
7161                                 p++;
7162                         }
7163                         *q = '\0';
7164                         item = gtk_item_factory_get_item(ifactory, path);
7165                         gtk_widget_activate(item);
7166                         g_free(path);
7167                         break;
7168                 }
7169         }
7170 }
7171
7172 static void compose_set_template_menu(Compose *compose)
7173 {
7174         GSList *tmpl_list, *cur;
7175         GtkWidget *menu;
7176         GtkWidget *item;
7177
7178         tmpl_list = template_get_config();
7179
7180         menu = gtk_menu_new();
7181
7182         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7183                 Template *tmpl = (Template *)cur->data;
7184
7185                 item = gtk_menu_item_new_with_label(tmpl->name);
7186                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7187                 g_signal_connect(G_OBJECT(item), "activate",
7188                                  G_CALLBACK(compose_template_activate_cb),
7189                                  compose);
7190                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7191                 gtk_widget_show(item);
7192         }
7193
7194         gtk_widget_show(menu);
7195         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7196 }
7197
7198 void compose_update_actions_menu(Compose *compose)
7199 {
7200         GtkItemFactory *ifactory;
7201
7202         ifactory = gtk_item_factory_from_widget(compose->menubar);
7203         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
7204 }
7205
7206 static void compose_update_privacy_systems_menu(Compose *compose)
7207 {
7208         static gchar *branch_path = "/Options/Privacy System";
7209         GtkItemFactory *ifactory;
7210         GtkWidget *menuitem;
7211         GSList *systems, *cur;
7212         GList *amenu;
7213         GtkWidget *widget;
7214         GtkWidget *system_none;
7215         GSList *group;
7216
7217         ifactory = gtk_item_factory_from_widget(compose->menubar);
7218
7219         /* remove old entries */
7220         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7221         g_return_if_fail(menuitem != NULL);
7222
7223         amenu = GTK_MENU_SHELL(menuitem)->children->next;
7224         while (amenu != NULL) {
7225                 GList *alist = amenu->next;
7226                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
7227                 amenu = alist;
7228         }
7229
7230         system_none = gtk_item_factory_get_widget(ifactory,
7231                 "/Options/Privacy System/None");
7232
7233         g_signal_connect(G_OBJECT(system_none), "activate",
7234                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7235
7236         systems = privacy_get_system_ids();
7237         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7238                 gchar *systemid = cur->data;
7239
7240                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7241                 widget = gtk_radio_menu_item_new_with_label(group,
7242                         privacy_system_get_name(systemid));
7243                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7244                                        g_strdup(systemid), g_free);
7245                 g_signal_connect(G_OBJECT(widget), "activate",
7246                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7247
7248                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
7249                 gtk_widget_show(widget);
7250                 g_free(systemid);
7251         }
7252         g_slist_free(systems);
7253 }
7254
7255 void compose_reflect_prefs_all(void)
7256 {
7257         GList *cur;
7258         Compose *compose;
7259
7260         for (cur = compose_list; cur != NULL; cur = cur->next) {
7261                 compose = (Compose *)cur->data;
7262                 compose_set_template_menu(compose);
7263         }
7264 }
7265
7266 void compose_reflect_prefs_pixmap_theme(void)
7267 {
7268         GList *cur;
7269         Compose *compose;
7270
7271         for (cur = compose_list; cur != NULL; cur = cur->next) {
7272                 compose = (Compose *)cur->data;
7273                 toolbar_update(TOOLBAR_COMPOSE, compose);
7274         }
7275 }
7276
7277 static const gchar *compose_quote_char_from_context(Compose *compose)
7278 {
7279         const gchar *qmark = NULL;
7280
7281         g_return_val_if_fail(compose != NULL, NULL);
7282
7283         switch (compose->mode) {
7284                 /* use forward-specific quote char */
7285                 case COMPOSE_FORWARD:
7286                 case COMPOSE_FORWARD_AS_ATTACH:
7287                 case COMPOSE_FORWARD_INLINE:
7288                         if (compose->folder && compose->folder->prefs &&
7289                                         compose->folder->prefs->forward_with_format)
7290                                 qmark = compose->folder->prefs->forward_quotemark;
7291                         else if (compose->account->forward_with_format)
7292                                 qmark = compose->account->forward_quotemark;
7293                         else
7294                                 qmark = prefs_common.fw_quotemark;
7295                         break;
7296
7297                 /* use reply-specific quote char in all other modes */
7298                 default:
7299                         if (compose->folder && compose->folder->prefs &&
7300                                         compose->folder->prefs->reply_with_format)
7301                                 qmark = compose->folder->prefs->reply_quotemark;
7302                         else if (compose->account->reply_with_format)
7303                                 qmark = compose->account->reply_quotemark;
7304                         else
7305                                 qmark = prefs_common.quotemark;
7306                         break;
7307         }
7308
7309         if (qmark == NULL || *qmark == '\0')
7310                 qmark = "> ";
7311
7312         return qmark;
7313 }
7314
7315 static void compose_template_apply(Compose *compose, Template *tmpl,
7316                                    gboolean replace)
7317 {
7318         GtkTextView *text;
7319         GtkTextBuffer *buffer;
7320         GtkTextMark *mark;
7321         GtkTextIter iter;
7322         const gchar *qmark;
7323         gchar *parsed_str = NULL;
7324         gint cursor_pos = 0;
7325         const gchar *err_msg = _("Template body format error at line %d.");
7326         if (!tmpl) return;
7327
7328         /* process the body */
7329
7330         text = GTK_TEXT_VIEW(compose->text);
7331         buffer = gtk_text_view_get_buffer(text);
7332
7333         if (tmpl->value) {
7334                 qmark = compose_quote_char_from_context(compose);
7335
7336                 if (compose->replyinfo != NULL) {
7337
7338                         if (replace)
7339                                 gtk_text_buffer_set_text(buffer, "", -1);
7340                         mark = gtk_text_buffer_get_insert(buffer);
7341                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7342
7343                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7344                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7345
7346                 } else if (compose->fwdinfo != NULL) {
7347
7348                         if (replace)
7349                                 gtk_text_buffer_set_text(buffer, "", -1);
7350                         mark = gtk_text_buffer_get_insert(buffer);
7351                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7352
7353                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7354                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7355
7356                 } else {
7357                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7358
7359                         GtkTextIter start, end;
7360                         gchar *tmp = NULL;
7361
7362                         gtk_text_buffer_get_start_iter(buffer, &start);
7363                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7364                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7365
7366                         /* clear the buffer now */
7367                         if (replace)
7368                                 gtk_text_buffer_set_text(buffer, "", -1);
7369
7370                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7371                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7372                         procmsg_msginfo_free( dummyinfo );
7373
7374                         g_free( tmp );
7375                 } 
7376         } else {
7377                 if (replace)
7378                         gtk_text_buffer_set_text(buffer, "", -1);
7379                 mark = gtk_text_buffer_get_insert(buffer);
7380                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7381         }       
7382
7383         if (replace && parsed_str && compose->account->auto_sig)
7384                 compose_insert_sig(compose, FALSE);
7385
7386         if (replace && parsed_str) {
7387                 gtk_text_buffer_get_start_iter(buffer, &iter);
7388                 gtk_text_buffer_place_cursor(buffer, &iter);
7389         }
7390         
7391         if (parsed_str) {
7392                 cursor_pos = quote_fmt_get_cursor_pos();
7393                 compose->set_cursor_pos = cursor_pos;
7394                 if (cursor_pos == -1)
7395                         cursor_pos = 0;
7396                 gtk_text_buffer_get_start_iter(buffer, &iter);
7397                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7398                 gtk_text_buffer_place_cursor(buffer, &iter);
7399         }
7400
7401         /* process the other fields */
7402
7403         compose_template_apply_fields(compose, tmpl);
7404         quote_fmt_reset_vartable();
7405         compose_changed_cb(NULL, compose);
7406 }
7407
7408 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7409 {
7410         MsgInfo* dummyinfo = NULL;
7411         MsgInfo *msginfo = NULL;
7412         gchar *buf = NULL;
7413
7414         if (compose->replyinfo != NULL)
7415                 msginfo = compose->replyinfo;
7416         else if (compose->fwdinfo != NULL)
7417                 msginfo = compose->fwdinfo;
7418         else {
7419                 dummyinfo = compose_msginfo_new_from_compose(compose);
7420                 msginfo = dummyinfo;
7421         }
7422
7423         if (tmpl->to && *tmpl->to != '\0') {
7424 #ifdef USE_ASPELL
7425                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7426                                 compose->gtkaspell);
7427 #else
7428                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7429 #endif
7430                 quote_fmt_scan_string(tmpl->to);
7431                 quote_fmt_parse();
7432
7433                 buf = quote_fmt_get_buffer();
7434                 if (buf == NULL) {
7435                         alertpanel_error(_("Template To format error."));
7436                 } else {
7437                         compose_entry_append(compose, buf, COMPOSE_TO);
7438                 }
7439         }
7440
7441         if (tmpl->cc && *tmpl->cc != '\0') {
7442 #ifdef USE_ASPELL
7443                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7444                                 compose->gtkaspell);
7445 #else
7446                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7447 #endif
7448                 quote_fmt_scan_string(tmpl->cc);
7449                 quote_fmt_parse();
7450
7451                 buf = quote_fmt_get_buffer();
7452                 if (buf == NULL) {
7453                         alertpanel_error(_("Template Cc format error."));
7454                 } else {
7455                         compose_entry_append(compose, buf, COMPOSE_CC);
7456                 }
7457         }
7458
7459         if (tmpl->bcc && *tmpl->bcc != '\0') {
7460 #ifdef USE_ASPELL
7461                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7462                                 compose->gtkaspell);
7463 #else
7464                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7465 #endif
7466                 quote_fmt_scan_string(tmpl->bcc);
7467                 quote_fmt_parse();
7468
7469                 buf = quote_fmt_get_buffer();
7470                 if (buf == NULL) {
7471                         alertpanel_error(_("Template Bcc format error."));
7472                 } else {
7473                         compose_entry_append(compose, buf, COMPOSE_BCC);
7474                 }
7475         }
7476
7477         /* process the subject */
7478         if (tmpl->subject && *tmpl->subject != '\0') {
7479 #ifdef USE_ASPELL
7480                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7481                                 compose->gtkaspell);
7482 #else
7483                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7484 #endif
7485                 quote_fmt_scan_string(tmpl->subject);
7486                 quote_fmt_parse();
7487
7488                 buf = quote_fmt_get_buffer();
7489                 if (buf == NULL) {
7490                         alertpanel_error(_("Template subject format error."));
7491                 } else {
7492                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7493                 }
7494         }
7495
7496         procmsg_msginfo_free( dummyinfo );
7497 }
7498
7499 static void compose_destroy(Compose *compose)
7500 {
7501         GtkTextBuffer *buffer;
7502         GtkClipboard *clipboard;
7503
7504         compose_list = g_list_remove(compose_list, compose);
7505
7506         if (compose->updating) {
7507                 debug_print("danger, not destroying anything now\n");
7508                 compose->deferred_destroy = TRUE;
7509                 return;
7510         }
7511         /* NOTE: address_completion_end() does nothing with the window
7512          * however this may change. */
7513         address_completion_end(compose->window);
7514
7515         slist_free_strings(compose->to_list);
7516         g_slist_free(compose->to_list);
7517         slist_free_strings(compose->newsgroup_list);
7518         g_slist_free(compose->newsgroup_list);
7519         slist_free_strings(compose->header_list);
7520         g_slist_free(compose->header_list);
7521
7522         procmsg_msginfo_free(compose->targetinfo);
7523         procmsg_msginfo_free(compose->replyinfo);
7524         procmsg_msginfo_free(compose->fwdinfo);
7525
7526         g_free(compose->replyto);
7527         g_free(compose->cc);
7528         g_free(compose->bcc);
7529         g_free(compose->newsgroups);
7530         g_free(compose->followup_to);
7531
7532         g_free(compose->ml_post);
7533
7534         g_free(compose->inreplyto);
7535         g_free(compose->references);
7536         g_free(compose->msgid);
7537         g_free(compose->boundary);
7538
7539         g_free(compose->redirect_filename);
7540         if (compose->undostruct)
7541                 undo_destroy(compose->undostruct);
7542
7543         g_free(compose->sig_str);
7544
7545         g_free(compose->exteditor_file);
7546
7547         g_free(compose->orig_charset);
7548
7549         g_free(compose->privacy_system);
7550
7551         if (addressbook_get_target_compose() == compose)
7552                 addressbook_set_target_compose(NULL);
7553
7554 #if USE_ASPELL
7555         if (compose->gtkaspell) {
7556                 gtkaspell_delete(compose->gtkaspell);
7557                 compose->gtkaspell = NULL;
7558         }
7559 #endif
7560
7561         prefs_common.compose_width = compose->scrolledwin->allocation.width;
7562         prefs_common.compose_height = compose->window->allocation.height;
7563
7564         if (!gtk_widget_get_parent(compose->paned))
7565                 gtk_widget_destroy(compose->paned);
7566         gtk_widget_destroy(compose->popupmenu);
7567
7568         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7569         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7570         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7571
7572         gtk_widget_destroy(compose->window);
7573         toolbar_destroy(compose->toolbar);
7574         g_free(compose->toolbar);
7575         g_mutex_free(compose->mutex);
7576         g_free(compose);
7577 }
7578
7579 static void compose_attach_info_free(AttachInfo *ainfo)
7580 {
7581         g_free(ainfo->file);
7582         g_free(ainfo->content_type);
7583         g_free(ainfo->name);
7584         g_free(ainfo);
7585 }
7586
7587 static void compose_attach_remove_selected(Compose *compose)
7588 {
7589         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7590         GtkTreeSelection *selection;
7591         GList *sel, *cur;
7592         GtkTreeModel *model;
7593
7594         selection = gtk_tree_view_get_selection(tree_view);
7595         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7596
7597         if (!sel) 
7598                 return;
7599
7600         for (cur = sel; cur != NULL; cur = cur->next) {
7601                 GtkTreePath *path = cur->data;
7602                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7603                                                 (model, cur->data);
7604                 cur->data = ref;
7605                 gtk_tree_path_free(path);
7606         }
7607
7608         for (cur = sel; cur != NULL; cur = cur->next) {
7609                 GtkTreeRowReference *ref = cur->data;
7610                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7611                 GtkTreeIter iter;
7612
7613                 if (gtk_tree_model_get_iter(model, &iter, path))
7614                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7615                 
7616                 gtk_tree_path_free(path);
7617                 gtk_tree_row_reference_free(ref);
7618         }
7619
7620         g_list_free(sel);
7621 }
7622
7623 static struct _AttachProperty
7624 {
7625         GtkWidget *window;
7626         GtkWidget *mimetype_entry;
7627         GtkWidget *encoding_optmenu;
7628         GtkWidget *path_entry;
7629         GtkWidget *filename_entry;
7630         GtkWidget *ok_btn;
7631         GtkWidget *cancel_btn;
7632 } attach_prop;
7633
7634 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
7635 {       
7636         gtk_tree_path_free((GtkTreePath *)ptr);
7637 }
7638
7639 static void compose_attach_property(Compose *compose)
7640 {
7641         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7642         AttachInfo *ainfo;
7643         GtkComboBox *optmenu;
7644         GtkTreeSelection *selection;
7645         GList *sel;
7646         GtkTreeModel *model;
7647         GtkTreeIter iter;
7648         GtkTreePath *path;
7649         static gboolean cancelled;
7650
7651         /* only if one selected */
7652         selection = gtk_tree_view_get_selection(tree_view);
7653         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
7654                 return;
7655
7656         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7657         if (!sel)
7658                 return;
7659
7660         path = (GtkTreePath *) sel->data;
7661         gtk_tree_model_get_iter(model, &iter, path);
7662         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
7663         
7664         if (!ainfo) {
7665                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
7666                 g_list_free(sel);
7667                 return;
7668         }               
7669         g_list_free(sel);
7670
7671         if (!attach_prop.window)
7672                 compose_attach_property_create(&cancelled);
7673         gtk_widget_grab_focus(attach_prop.ok_btn);
7674         gtk_widget_show(attach_prop.window);
7675         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
7676
7677         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
7678         if (ainfo->encoding == ENC_UNKNOWN)
7679                 combobox_select_by_data(optmenu, ENC_BASE64);
7680         else
7681                 combobox_select_by_data(optmenu, ainfo->encoding);
7682
7683         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
7684                            ainfo->content_type ? ainfo->content_type : "");
7685         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
7686                            ainfo->file ? ainfo->file : "");
7687         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
7688                            ainfo->name ? ainfo->name : "");
7689
7690         for (;;) {
7691                 const gchar *entry_text;
7692                 gchar *text;
7693                 gchar *cnttype = NULL;
7694                 gchar *file = NULL;
7695                 off_t size = 0;
7696
7697                 cancelled = FALSE;
7698                 gtk_main();
7699
7700                 gtk_widget_hide(attach_prop.window);
7701                 
7702                 if (cancelled) 
7703                         break;
7704
7705                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
7706                 if (*entry_text != '\0') {
7707                         gchar *p;
7708
7709                         text = g_strstrip(g_strdup(entry_text));
7710                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
7711                                 cnttype = g_strdup(text);
7712                                 g_free(text);
7713                         } else {
7714                                 alertpanel_error(_("Invalid MIME type."));
7715                                 g_free(text);
7716                                 continue;
7717                         }
7718                 }
7719
7720                 ainfo->encoding = combobox_get_active_data(optmenu);
7721
7722                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
7723                 if (*entry_text != '\0') {
7724                         if (is_file_exist(entry_text) &&
7725                             (size = get_file_size(entry_text)) > 0)
7726                                 file = g_strdup(entry_text);
7727                         else {
7728                                 alertpanel_error
7729                                         (_("File doesn't exist or is empty."));
7730                                 g_free(cnttype);
7731                                 continue;
7732                         }
7733                 }
7734
7735                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
7736                 if (*entry_text != '\0') {
7737                         g_free(ainfo->name);
7738                         ainfo->name = g_strdup(entry_text);
7739                 }
7740
7741                 if (cnttype) {
7742                         g_free(ainfo->content_type);
7743                         ainfo->content_type = cnttype;
7744                 }
7745                 if (file) {
7746                         g_free(ainfo->file);
7747                         ainfo->file = file;
7748                 }
7749                 if (size)
7750                         ainfo->size = size;
7751
7752                 /* update tree store */
7753                 text = to_human_readable(ainfo->size);
7754                 gtk_tree_model_get_iter(model, &iter, path);
7755                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
7756                                    COL_MIMETYPE, ainfo->content_type,
7757                                    COL_SIZE, text,
7758                                    COL_NAME, ainfo->name,
7759                                    -1);
7760                 
7761                 break;
7762         }
7763
7764         gtk_tree_path_free(path);
7765 }
7766
7767 #define SET_LABEL_AND_ENTRY(str, entry, top) \
7768 { \
7769         label = gtk_label_new(str); \
7770         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
7771                          GTK_FILL, 0, 0, 0); \
7772         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
7773  \
7774         entry = gtk_entry_new(); \
7775         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
7776                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
7777 }
7778
7779 static void compose_attach_property_create(gboolean *cancelled)
7780 {
7781         GtkWidget *window;
7782         GtkWidget *vbox;
7783         GtkWidget *table;
7784         GtkWidget *label;
7785         GtkWidget *mimetype_entry;
7786         GtkWidget *hbox;
7787         GtkWidget *optmenu;
7788         GtkListStore *optmenu_menu;
7789         GtkWidget *path_entry;
7790         GtkWidget *filename_entry;
7791         GtkWidget *hbbox;
7792         GtkWidget *ok_btn;
7793         GtkWidget *cancel_btn;
7794         GList     *mime_type_list, *strlist;
7795         GtkTreeIter iter;
7796
7797         debug_print("Creating attach_property window...\n");
7798
7799         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
7800         gtk_widget_set_size_request(window, 480, -1);
7801         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
7802         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
7803         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
7804         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
7805         g_signal_connect(G_OBJECT(window), "delete_event",
7806                          G_CALLBACK(attach_property_delete_event),
7807                          cancelled);
7808         g_signal_connect(G_OBJECT(window), "key_press_event",
7809                          G_CALLBACK(attach_property_key_pressed),
7810                          cancelled);
7811
7812         vbox = gtk_vbox_new(FALSE, 8);
7813         gtk_container_add(GTK_CONTAINER(window), vbox);
7814
7815         table = gtk_table_new(4, 2, FALSE);
7816         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
7817         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
7818         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
7819
7820         label = gtk_label_new(_("MIME type")); 
7821         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
7822                          GTK_FILL, 0, 0, 0); 
7823         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
7824         mimetype_entry = gtk_combo_new(); 
7825         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
7826                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7827                          
7828         /* stuff with list */
7829         mime_type_list = procmime_get_mime_type_list();
7830         strlist = NULL;
7831         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
7832                 MimeType *type = (MimeType *) mime_type_list->data;
7833                 gchar *tmp;
7834
7835                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
7836
7837                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
7838                         g_free(tmp);
7839                 else
7840                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
7841                                         (GCompareFunc)strcmp2);
7842         }
7843
7844         gtk_combo_set_popdown_strings(GTK_COMBO(mimetype_entry), strlist);
7845
7846         for (mime_type_list = strlist; mime_type_list != NULL; 
7847                 mime_type_list = mime_type_list->next)
7848                 g_free(mime_type_list->data);
7849         g_list_free(strlist);
7850                          
7851         mimetype_entry = GTK_COMBO(mimetype_entry)->entry;                       
7852
7853         label = gtk_label_new(_("Encoding"));
7854         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
7855                          GTK_FILL, 0, 0, 0);
7856         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
7857
7858         hbox = gtk_hbox_new(FALSE, 0);
7859         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
7860                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7861
7862         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
7863         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7864
7865         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
7866         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
7867         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
7868         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
7869         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
7870
7871         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
7872
7873         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
7874         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
7875
7876         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
7877                                       &ok_btn, GTK_STOCK_OK,
7878                                       NULL, NULL);
7879         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
7880         gtk_widget_grab_default(ok_btn);
7881
7882         g_signal_connect(G_OBJECT(ok_btn), "clicked",
7883                          G_CALLBACK(attach_property_ok),
7884                          cancelled);
7885         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
7886                          G_CALLBACK(attach_property_cancel),
7887                          cancelled);
7888
7889         gtk_widget_show_all(vbox);
7890
7891         attach_prop.window           = window;
7892         attach_prop.mimetype_entry   = mimetype_entry;
7893         attach_prop.encoding_optmenu = optmenu;
7894         attach_prop.path_entry       = path_entry;
7895         attach_prop.filename_entry   = filename_entry;
7896         attach_prop.ok_btn           = ok_btn;
7897         attach_prop.cancel_btn       = cancel_btn;
7898 }
7899
7900 #undef SET_LABEL_AND_ENTRY
7901
7902 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
7903 {
7904         *cancelled = FALSE;
7905         gtk_main_quit();
7906 }
7907
7908 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
7909 {
7910         *cancelled = TRUE;
7911         gtk_main_quit();
7912 }
7913
7914 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
7915                                          gboolean *cancelled)
7916 {
7917         *cancelled = TRUE;
7918         gtk_main_quit();
7919
7920         return TRUE;
7921 }
7922
7923 static gboolean attach_property_key_pressed(GtkWidget *widget,
7924                                             GdkEventKey *event,
7925                                             gboolean *cancelled)
7926 {
7927         if (event && event->keyval == GDK_Escape) {
7928                 *cancelled = TRUE;
7929                 gtk_main_quit();
7930         }
7931         return FALSE;
7932 }
7933
7934 static void compose_exec_ext_editor(Compose *compose)
7935 {
7936 #ifdef G_OS_UNIX
7937         gchar *tmp;
7938         pid_t pid;
7939         gint pipe_fds[2];
7940
7941         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
7942                               G_DIR_SEPARATOR, compose);
7943
7944         if (pipe(pipe_fds) < 0) {
7945                 perror("pipe");
7946                 g_free(tmp);
7947                 return;
7948         }
7949
7950         if ((pid = fork()) < 0) {
7951                 perror("fork");
7952                 g_free(tmp);
7953                 return;
7954         }
7955
7956         if (pid != 0) {
7957                 /* close the write side of the pipe */
7958                 close(pipe_fds[1]);
7959
7960                 compose->exteditor_file    = g_strdup(tmp);
7961                 compose->exteditor_pid     = pid;
7962
7963                 compose_set_ext_editor_sensitive(compose, FALSE);
7964
7965                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
7966                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
7967                                                         G_IO_IN,
7968                                                         compose_input_cb,
7969                                                         compose);
7970         } else {        /* process-monitoring process */
7971                 pid_t pid_ed;
7972
7973                 if (setpgid(0, 0))
7974                         perror("setpgid");
7975
7976                 /* close the read side of the pipe */
7977                 close(pipe_fds[0]);
7978
7979                 if (compose_write_body_to_file(compose, tmp) < 0) {
7980                         fd_write_all(pipe_fds[1], "2\n", 2);
7981                         _exit(1);
7982                 }
7983
7984                 pid_ed = compose_exec_ext_editor_real(tmp);
7985                 if (pid_ed < 0) {
7986                         fd_write_all(pipe_fds[1], "1\n", 2);
7987                         _exit(1);
7988                 }
7989
7990                 /* wait until editor is terminated */
7991                 waitpid(pid_ed, NULL, 0);
7992
7993                 fd_write_all(pipe_fds[1], "0\n", 2);
7994
7995                 close(pipe_fds[1]);
7996                 _exit(0);
7997         }
7998
7999         g_free(tmp);
8000 #endif /* G_OS_UNIX */
8001 }
8002
8003 #ifdef G_OS_UNIX
8004 static gint compose_exec_ext_editor_real(const gchar *file)
8005 {
8006         gchar buf[1024];
8007         gchar *p;
8008         gchar **cmdline;
8009         pid_t pid;
8010
8011         g_return_val_if_fail(file != NULL, -1);
8012
8013         if ((pid = fork()) < 0) {
8014                 perror("fork");
8015                 return -1;
8016         }
8017
8018         if (pid != 0) return pid;
8019
8020         /* grandchild process */
8021
8022         if (setpgid(0, getppid()))
8023                 perror("setpgid");
8024
8025         if (prefs_common.ext_editor_cmd &&
8026             (p = strchr(prefs_common.ext_editor_cmd, '%')) &&
8027             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8028                 g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, file);
8029         } else {
8030                 if (prefs_common.ext_editor_cmd)
8031                         g_warning("External editor command line is invalid: '%s'\n",
8032                                   prefs_common.ext_editor_cmd);
8033                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8034         }
8035
8036         cmdline = strsplit_with_quote(buf, " ", 1024);
8037         execvp(cmdline[0], cmdline);
8038
8039         perror("execvp");
8040         g_strfreev(cmdline);
8041
8042         _exit(1);
8043 }
8044
8045 static gboolean compose_ext_editor_kill(Compose *compose)
8046 {
8047         pid_t pgid = compose->exteditor_pid * -1;
8048         gint ret;
8049
8050         ret = kill(pgid, 0);
8051
8052         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8053                 AlertValue val;
8054                 gchar *msg;
8055
8056                 msg = g_strdup_printf
8057                         (_("The external editor is still working.\n"
8058                            "Force terminating the process?\n"
8059                            "process group id: %d"), -pgid);
8060                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8061                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8062                         
8063                 g_free(msg);
8064
8065                 if (val == G_ALERTALTERNATE) {
8066                         g_source_remove(compose->exteditor_tag);
8067                         g_io_channel_shutdown(compose->exteditor_ch,
8068                                               FALSE, NULL);
8069                         g_io_channel_unref(compose->exteditor_ch);
8070
8071                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8072                         waitpid(compose->exteditor_pid, NULL, 0);
8073
8074                         g_warning("Terminated process group id: %d", -pgid);
8075                         g_warning("Temporary file: %s",
8076                                   compose->exteditor_file);
8077
8078                         compose_set_ext_editor_sensitive(compose, TRUE);
8079
8080                         g_free(compose->exteditor_file);
8081                         compose->exteditor_file    = NULL;
8082                         compose->exteditor_pid     = -1;
8083                         compose->exteditor_ch      = NULL;
8084                         compose->exteditor_tag     = -1;
8085                 } else
8086                         return FALSE;
8087         }
8088
8089         return TRUE;
8090 }
8091
8092 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8093                                  gpointer data)
8094 {
8095         gchar buf[3] = "3";
8096         Compose *compose = (Compose *)data;
8097         gsize bytes_read;
8098
8099         debug_print(_("Compose: input from monitoring process\n"));
8100
8101         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8102
8103         g_io_channel_shutdown(source, FALSE, NULL);
8104         g_io_channel_unref(source);
8105
8106         waitpid(compose->exteditor_pid, NULL, 0);
8107
8108         if (buf[0] == '0') {            /* success */
8109                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8110                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8111
8112                 gtk_text_buffer_set_text(buffer, "", -1);
8113                 compose_insert_file(compose, compose->exteditor_file);
8114                 compose_changed_cb(NULL, compose);
8115
8116                 if (g_unlink(compose->exteditor_file) < 0)
8117                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8118         } else if (buf[0] == '1') {     /* failed */
8119                 g_warning("Couldn't exec external editor\n");
8120                 if (g_unlink(compose->exteditor_file) < 0)
8121                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8122         } else if (buf[0] == '2') {
8123                 g_warning("Couldn't write to file\n");
8124         } else if (buf[0] == '3') {
8125                 g_warning("Pipe read failed\n");
8126         }
8127
8128         compose_set_ext_editor_sensitive(compose, TRUE);
8129
8130         g_free(compose->exteditor_file);
8131         compose->exteditor_file    = NULL;
8132         compose->exteditor_pid     = -1;
8133         compose->exteditor_ch      = NULL;
8134         compose->exteditor_tag     = -1;
8135
8136         return FALSE;
8137 }
8138
8139 static void compose_set_ext_editor_sensitive(Compose *compose,
8140                                              gboolean sensitive)
8141 {
8142         GtkItemFactory *ifactory;
8143
8144         ifactory = gtk_item_factory_from_widget(compose->menubar);
8145
8146         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
8147         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
8148         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
8149         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
8150         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
8151         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
8152         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
8153                            sensitive);
8154
8155         gtk_widget_set_sensitive(compose->text,                       sensitive);
8156         if (compose->toolbar->send_btn)
8157                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8158         if (compose->toolbar->sendl_btn)
8159                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8160         if (compose->toolbar->draft_btn)
8161                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8162         if (compose->toolbar->insert_btn)
8163                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8164         if (compose->toolbar->sig_btn)
8165                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8166         if (compose->toolbar->exteditor_btn)
8167                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8168         if (compose->toolbar->linewrap_current_btn)
8169                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8170         if (compose->toolbar->linewrap_all_btn)
8171                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8172 }
8173 #endif /* G_OS_UNIX */
8174
8175 /**
8176  * compose_undo_state_changed:
8177  *
8178  * Change the sensivity of the menuentries undo and redo
8179  **/
8180 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8181                                        gint redo_state, gpointer data)
8182 {
8183         GtkWidget *widget = GTK_WIDGET(data);
8184         GtkItemFactory *ifactory;
8185
8186         g_return_if_fail(widget != NULL);
8187
8188         ifactory = gtk_item_factory_from_widget(widget);
8189
8190         switch (undo_state) {
8191         case UNDO_STATE_TRUE:
8192                 if (!undostruct->undo_state) {
8193                         undostruct->undo_state = TRUE;
8194                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
8195                 }
8196                 break;
8197         case UNDO_STATE_FALSE:
8198                 if (undostruct->undo_state) {
8199                         undostruct->undo_state = FALSE;
8200                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
8201                 }
8202                 break;
8203         case UNDO_STATE_UNCHANGED:
8204                 break;
8205         case UNDO_STATE_REFRESH:
8206                 menu_set_sensitive(ifactory, "/Edit/Undo",
8207                                    undostruct->undo_state);
8208                 break;
8209         default:
8210                 g_warning("Undo state not recognized");
8211                 break;
8212         }
8213
8214         switch (redo_state) {
8215         case UNDO_STATE_TRUE:
8216                 if (!undostruct->redo_state) {
8217                         undostruct->redo_state = TRUE;
8218                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
8219                 }
8220                 break;
8221         case UNDO_STATE_FALSE:
8222                 if (undostruct->redo_state) {
8223                         undostruct->redo_state = FALSE;
8224                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
8225                 }
8226                 break;
8227         case UNDO_STATE_UNCHANGED:
8228                 break;
8229         case UNDO_STATE_REFRESH:
8230                 menu_set_sensitive(ifactory, "/Edit/Redo",
8231                                    undostruct->redo_state);
8232                 break;
8233         default:
8234                 g_warning("Redo state not recognized");
8235                 break;
8236         }
8237 }
8238
8239 /* callback functions */
8240
8241 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8242  * includes "non-client" (windows-izm) in calculation, so this calculation
8243  * may not be accurate.
8244  */
8245 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8246                                         GtkAllocation *allocation,
8247                                         GtkSHRuler *shruler)
8248 {
8249         if (prefs_common.show_ruler) {
8250                 gint char_width = 0, char_height = 0;
8251                 gint line_width_in_chars;
8252
8253                 gtkut_get_font_size(GTK_WIDGET(widget),
8254                                     &char_width, &char_height);
8255                 line_width_in_chars =
8256                         (allocation->width - allocation->x) / char_width;
8257
8258                 /* got the maximum */
8259                 gtk_ruler_set_range(GTK_RULER(shruler),
8260                                     0.0, line_width_in_chars, 0,
8261                                     /*line_width_in_chars*/ char_width);
8262         }
8263
8264         return TRUE;
8265 }
8266
8267 static void account_activated(GtkComboBox *optmenu, gpointer data)
8268 {
8269         Compose *compose = (Compose *)data;
8270
8271         PrefsAccount *ac;
8272         gchar *folderidentifier;
8273         gint account_id = 0;
8274         GtkTreeModel *menu;
8275         GtkTreeIter iter;
8276
8277         /* Get ID of active account in the combo box */
8278         menu = gtk_combo_box_get_model(optmenu);
8279         gtk_combo_box_get_active_iter(optmenu, &iter);
8280         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8281
8282         ac = account_find_from_id(account_id);
8283         g_return_if_fail(ac != NULL);
8284
8285         if (ac != compose->account)
8286                 compose_select_account(compose, ac, FALSE);
8287
8288         /* Set message save folder */
8289         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8290                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8291         }
8292         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8293                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8294                            
8295         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8296         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8297                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8298                                   (compose->account, F_OUTBOX));
8299                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8300                 g_free(folderidentifier);
8301         }
8302 }
8303
8304 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8305                             GtkTreeViewColumn *column, Compose *compose)
8306 {
8307         compose_attach_property(compose);
8308 }
8309
8310 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8311                                       gpointer data)
8312 {
8313         Compose *compose = (Compose *)data;
8314         GtkTreeSelection *attach_selection;
8315         gint attach_nr_selected;
8316         GtkItemFactory *ifactory;
8317         
8318         if (!event) return FALSE;
8319
8320         if (event->button == 3) {
8321                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8322                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8323                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
8324                         
8325                 if (attach_nr_selected > 0)
8326                 {
8327                         menu_set_sensitive(ifactory, "/Remove", TRUE);
8328                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
8329                 } else {
8330                         menu_set_sensitive(ifactory, "/Remove", FALSE);
8331                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
8332                 }
8333                         
8334                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8335                                NULL, NULL, event->button, event->time);
8336                 return TRUE;                           
8337         }
8338
8339         return FALSE;
8340 }
8341
8342 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8343                                    gpointer data)
8344 {
8345         Compose *compose = (Compose *)data;
8346
8347         if (!event) return FALSE;
8348
8349         switch (event->keyval) {
8350         case GDK_Delete:
8351                 compose_attach_remove_selected(compose);
8352                 break;
8353         }
8354         return FALSE;
8355 }
8356
8357 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8358 {
8359         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
8360         toolbar_comp_set_sensitive(compose, allow);
8361         menu_set_sensitive(ifactory, "/Message", allow);
8362         menu_set_sensitive(ifactory, "/Edit", allow);
8363 #if USE_ASPELL
8364         menu_set_sensitive(ifactory, "/Spelling", allow);
8365 #endif  
8366         menu_set_sensitive(ifactory, "/Options", allow);
8367         menu_set_sensitive(ifactory, "/Tools", allow);
8368         menu_set_sensitive(ifactory, "/Help", allow);
8369         
8370         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8371
8372 }
8373
8374 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
8375 {
8376         Compose *compose = (Compose *)data;
8377         
8378         if (prefs_common.work_offline && 
8379             !inc_offline_should_override(TRUE,
8380                 _("Claws Mail needs network access in order "
8381                   "to send this email.")))
8382                 return;
8383         
8384         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8385                 g_source_remove(compose->draft_timeout_tag);
8386                 compose->draft_timeout_tag = -1;
8387         }
8388
8389         compose_send(compose);
8390 }
8391
8392 static void compose_send_later_cb(gpointer data, guint action,
8393                                   GtkWidget *widget)
8394 {
8395         Compose *compose = (Compose *)data;
8396         gint val;
8397
8398         inc_lock();
8399         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8400         inc_unlock();
8401
8402         if (!val) {
8403                 compose_close(compose);
8404         } else if (val == -1) {
8405                 alertpanel_error(_("Could not queue message."));
8406         } else if (val == -2) {
8407                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8408         } else if (val == -3) {
8409                 if (privacy_peek_error())
8410                 alertpanel_error(_("Could not queue message for sending:\n\n"
8411                                    "Signature failed: %s"), privacy_get_error());
8412         } else if (val == -4) {
8413                 alertpanel_error(_("Could not queue message for sending:\n\n"
8414                                    "Charset conversion failed."));
8415         } else if (val == -5) {
8416                 alertpanel_error(_("Could not queue message for sending:\n\n"
8417                                    "Couldn't get recipient encryption key."));
8418         } else if (val == -6) {
8419                 /* silent error */
8420         }
8421         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8422 }
8423
8424 #define DRAFTED_AT_EXIT "drafted_at_exit"
8425 static void compose_register_draft(MsgInfo *info)
8426 {
8427         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8428                                       DRAFTED_AT_EXIT, NULL);
8429         FILE *fp = fopen(filepath, "ab");
8430         
8431         if (fp) {
8432                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8433                                 info->msgnum);
8434                 fclose(fp);
8435         }
8436                 
8437         g_free(filepath);       
8438 }
8439
8440 gboolean compose_draft (gpointer data, guint action) 
8441 {
8442         Compose *compose = (Compose *)data;
8443         FolderItem *draft;
8444         gchar *tmp;
8445         gint msgnum;
8446         MsgFlags flag = {0, 0};
8447         static gboolean lock = FALSE;
8448         MsgInfo *newmsginfo;
8449         FILE *fp;
8450         gboolean target_locked = FALSE;
8451         gboolean err = FALSE;
8452
8453         if (lock) return FALSE;
8454
8455         if (compose->sending)
8456                 return TRUE;
8457
8458         draft = account_get_special_folder(compose->account, F_DRAFT);
8459         g_return_val_if_fail(draft != NULL, FALSE);
8460         
8461         if (!g_mutex_trylock(compose->mutex)) {
8462                 /* we don't want to lock the mutex once it's available,
8463                  * because as the only other part of compose.c locking
8464                  * it is compose_close - which means once unlocked,
8465                  * the compose struct will be freed */
8466                 debug_print("couldn't lock mutex, probably sending\n");
8467                 return FALSE;
8468         }
8469         
8470         lock = TRUE;
8471
8472         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8473                               G_DIR_SEPARATOR, compose);
8474         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8475                 FILE_OP_ERROR(tmp, "fopen");
8476                 goto warn_err;
8477         }
8478
8479         /* chmod for security */
8480         if (change_file_mode_rw(fp, tmp) < 0) {
8481                 FILE_OP_ERROR(tmp, "chmod");
8482                 g_warning("can't change file mode\n");
8483         }
8484
8485         /* Save draft infos */
8486         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8487         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8488
8489         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8490                 gchar *savefolderid;
8491
8492                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8493                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8494                 g_free(savefolderid);
8495         }
8496         if (compose->return_receipt) {
8497                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8498         }
8499         if (compose->privacy_system) {
8500                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8501                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8502                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8503         }
8504
8505         /* Message-ID of message replying to */
8506         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8507                 gchar *folderid;
8508                 
8509                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8510                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8511                 g_free(folderid);
8512         }
8513         /* Message-ID of message forwarding to */
8514         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8515                 gchar *folderid;
8516                 
8517                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8518                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8519                 g_free(folderid);
8520         }
8521
8522         /* end of headers */
8523         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8524
8525         if (err) {
8526                 fclose(fp);
8527                 goto warn_err;
8528         }
8529
8530         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8531                 fclose(fp);
8532                 goto warn_err;
8533         }
8534         if (fclose(fp) == EOF) {
8535                 goto warn_err;
8536         }
8537         
8538         if (compose->targetinfo) {
8539                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8540                 flag.perm_flags = target_locked?MSG_LOCKED:0;
8541         }
8542         flag.tmp_flags = MSG_DRAFT;
8543
8544         folder_item_scan(draft);
8545         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8546                 MsgInfo *tmpinfo = NULL;
8547                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8548                 if (compose->msgid) {
8549                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8550                 }
8551                 if (tmpinfo) {
8552                         msgnum = tmpinfo->msgnum;
8553                         procmsg_msginfo_free(tmpinfo);
8554                         debug_print("got draft msgnum %d from scanning\n", msgnum);
8555                 } else {
8556                         debug_print("didn't get draft msgnum after scanning\n");
8557                 }
8558         } else {
8559                 debug_print("got draft msgnum %d from adding\n", msgnum);
8560         }
8561         if (msgnum < 0) {
8562 warn_err:
8563                 g_unlink(tmp);
8564                 g_free(tmp);
8565                 if (action != COMPOSE_AUTO_SAVE) {
8566                         if (action != COMPOSE_DRAFT_FOR_EXIT)
8567                                 alertpanel_error(_("Could not save draft."));
8568                         else {
8569                                 AlertValue val;
8570                                 gtkut_window_popup(compose->window);
8571                                 val = alertpanel_full(_("Could not save draft"),
8572                                         _("Could not save draft.\n"
8573                                         "Do you want to cancel exit or discard this email?"),
8574                                           _("_Cancel exit"), _("_Discard email"), NULL,
8575                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8576                                 if (val == G_ALERTALTERNATE) {
8577                                         lock = FALSE;
8578                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8579                                         compose_close(compose);
8580                                         return TRUE;
8581                                 } else {
8582                                         lock = FALSE;
8583                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8584                                         return FALSE;
8585                                 }
8586                         }
8587                 }
8588                 goto unlock;
8589         }
8590         g_free(tmp);
8591
8592         if (compose->mode == COMPOSE_REEDIT) {
8593                 compose_remove_reedit_target(compose, TRUE);
8594         }
8595
8596         newmsginfo = folder_item_get_msginfo(draft, msgnum);
8597
8598         if (newmsginfo) {
8599                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8600                 if (target_locked)
8601                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8602                 else
8603                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8604                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8605                         procmsg_msginfo_set_flags(newmsginfo, 0,
8606                                                   MSG_HAS_ATTACHMENT);
8607
8608                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8609                         compose_register_draft(newmsginfo);
8610                 }
8611                 procmsg_msginfo_free(newmsginfo);
8612         }
8613         
8614         folder_item_scan(draft);
8615         
8616         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8617                 lock = FALSE;
8618                 g_mutex_unlock(compose->mutex); /* must be done before closing */
8619                 compose_close(compose);
8620                 return TRUE;
8621         } else {
8622                 struct stat s;
8623                 gchar *path;
8624
8625                 path = folder_item_fetch_msg(draft, msgnum);
8626                 if (path == NULL) {
8627                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
8628                         goto unlock;
8629                 }
8630                 if (g_stat(path, &s) < 0) {
8631                         FILE_OP_ERROR(path, "stat");
8632                         g_free(path);
8633                         goto unlock;
8634                 }
8635                 g_free(path);
8636
8637                 procmsg_msginfo_free(compose->targetinfo);
8638                 compose->targetinfo = procmsg_msginfo_new();
8639                 compose->targetinfo->msgnum = msgnum;
8640                 compose->targetinfo->size = s.st_size;
8641                 compose->targetinfo->mtime = s.st_mtime;
8642                 compose->targetinfo->folder = draft;
8643                 if (target_locked)
8644                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
8645                 compose->mode = COMPOSE_REEDIT;
8646                 
8647                 if (action == COMPOSE_AUTO_SAVE) {
8648                         compose->autosaved_draft = compose->targetinfo;
8649                 }
8650                 compose->modified = FALSE;
8651                 compose_set_title(compose);
8652         }
8653 unlock:
8654         lock = FALSE;
8655         g_mutex_unlock(compose->mutex);
8656         return TRUE;
8657 }
8658
8659 void compose_clear_exit_drafts(void)
8660 {
8661         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8662                                       DRAFTED_AT_EXIT, NULL);
8663         if (is_file_exist(filepath))
8664                 g_unlink(filepath);
8665         
8666         g_free(filepath);
8667 }
8668
8669 void compose_reopen_exit_drafts(void)
8670 {
8671         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8672                                       DRAFTED_AT_EXIT, NULL);
8673         FILE *fp = fopen(filepath, "rb");
8674         gchar buf[1024];
8675         
8676         if (fp) {
8677                 while (fgets(buf, sizeof(buf), fp)) {
8678                         gchar **parts = g_strsplit(buf, "\t", 2);
8679                         const gchar *folder = parts[0];
8680                         int msgnum = parts[1] ? atoi(parts[1]):-1;
8681                         
8682                         if (folder && *folder && msgnum > -1) {
8683                                 FolderItem *item = folder_find_item_from_identifier(folder);
8684                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
8685                                 if (info)
8686                                         compose_reedit(info, FALSE);
8687                         }
8688                         g_strfreev(parts);
8689                 }       
8690                 fclose(fp);
8691         }       
8692         g_free(filepath);
8693         compose_clear_exit_drafts();
8694 }
8695
8696 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
8697 {
8698         compose_draft(data, action);
8699 }
8700
8701 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
8702 {
8703         Compose *compose = (Compose *)data;
8704         GList *file_list;
8705
8706         if (compose->redirect_filename != NULL)
8707                 return;
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 *utf8_filename = conv_filename_to_utf8(file);
8717                         compose_attach_append(compose, file, utf8_filename, NULL);
8718                         compose_changed_cb(NULL, compose);
8719                         g_free(file);
8720                         g_free(utf8_filename);
8721                 }
8722                 g_list_free(file_list);
8723         }               
8724 }
8725
8726 static void compose_insert_file_cb(gpointer data, guint action,
8727                                    GtkWidget *widget)
8728 {
8729         Compose *compose = (Compose *)data;
8730         GList *file_list;
8731
8732         file_list = filesel_select_multiple_files_open(_("Select file"));
8733
8734         if (file_list) {
8735                 GList *tmp;
8736
8737                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8738                         gchar *file = (gchar *) tmp->data;
8739                         gchar *filedup = g_strdup(file);
8740                         gchar *shortfile = g_path_get_basename(filedup);
8741                         ComposeInsertResult res;
8742
8743                         res = compose_insert_file(compose, file);
8744                         if (res == COMPOSE_INSERT_READ_ERROR) {
8745                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
8746                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
8747                                 alertpanel_error(_("File '%s' contained invalid characters\n"
8748                                                    "for the current encoding, insertion may be incorrect."), shortfile);
8749                         }
8750                         g_free(shortfile);
8751                         g_free(filedup);
8752                         g_free(file);
8753                 }
8754                 g_list_free(file_list);
8755         }
8756 }
8757
8758 static void compose_insert_sig_cb(gpointer data, guint action,
8759                                   GtkWidget *widget)
8760 {
8761         Compose *compose = (Compose *)data;
8762
8763         compose_insert_sig(compose, FALSE);
8764 }
8765
8766 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
8767                               gpointer data)
8768 {
8769         gint x, y;
8770         Compose *compose = (Compose *)data;
8771
8772         gtkut_widget_get_uposition(widget, &x, &y);
8773         prefs_common.compose_x = x;
8774         prefs_common.compose_y = y;
8775
8776         if (compose->sending || compose->updating)
8777                 return TRUE;
8778         compose_close_cb(compose, 0, NULL);
8779         return TRUE;
8780 }
8781
8782 void compose_close_toolbar(Compose *compose)
8783 {
8784         compose_close_cb(compose, 0, NULL);
8785 }
8786
8787 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
8788 {
8789         Compose *compose = (Compose *)data;
8790         AlertValue val;
8791
8792 #ifdef G_OS_UNIX
8793         if (compose->exteditor_tag != -1) {
8794                 if (!compose_ext_editor_kill(compose))
8795                         return;
8796         }
8797 #endif
8798
8799         if (compose->modified) {
8800                 val = alertpanel(_("Discard message"),
8801                                  _("This message has been modified. Discard it?"),
8802                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
8803
8804                 switch (val) {
8805                 case G_ALERTDEFAULT:
8806                         if (prefs_common.autosave)
8807                                 compose_remove_draft(compose);                  
8808                         break;
8809                 case G_ALERTALTERNATE:
8810                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
8811                         return;
8812                 default:
8813                         return;
8814                 }
8815         }
8816
8817         compose_close(compose);
8818 }
8819
8820 static void compose_set_encoding_cb(gpointer data, guint action,
8821                                     GtkWidget *widget)
8822 {
8823         Compose *compose = (Compose *)data;
8824
8825         if (GTK_CHECK_MENU_ITEM(widget)->active)
8826                 compose->out_encoding = (CharSet)action;
8827 }
8828
8829 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
8830 {
8831         Compose *compose = (Compose *)data;
8832
8833         addressbook_open(compose);
8834 }
8835
8836 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
8837 {
8838         Compose *compose = (Compose *)data;
8839         Template *tmpl;
8840         gchar *msg;
8841         AlertValue val;
8842
8843         tmpl = g_object_get_data(G_OBJECT(widget), "template");
8844         g_return_if_fail(tmpl != NULL);
8845
8846         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
8847                               tmpl->name);
8848         val = alertpanel(_("Apply template"), msg,
8849                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
8850         g_free(msg);
8851
8852         if (val == G_ALERTDEFAULT)
8853                 compose_template_apply(compose, tmpl, TRUE);
8854         else if (val == G_ALERTALTERNATE)
8855                 compose_template_apply(compose, tmpl, FALSE);
8856 }
8857
8858 static void compose_ext_editor_cb(gpointer data, guint action,
8859                                   GtkWidget *widget)
8860 {
8861         Compose *compose = (Compose *)data;
8862
8863         compose_exec_ext_editor(compose);
8864 }
8865
8866 static void compose_undo_cb(Compose *compose)
8867 {
8868         gboolean prev_autowrap = compose->autowrap;
8869
8870         compose->autowrap = FALSE;
8871         undo_undo(compose->undostruct);
8872         compose->autowrap = prev_autowrap;
8873 }
8874
8875 static void compose_redo_cb(Compose *compose)
8876 {
8877         gboolean prev_autowrap = compose->autowrap;
8878         
8879         compose->autowrap = FALSE;
8880         undo_redo(compose->undostruct);
8881         compose->autowrap = prev_autowrap;
8882 }
8883
8884 static void entry_cut_clipboard(GtkWidget *entry)
8885 {
8886         if (GTK_IS_EDITABLE(entry))
8887                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
8888         else if (GTK_IS_TEXT_VIEW(entry))
8889                 gtk_text_buffer_cut_clipboard(
8890                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8891                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
8892                         TRUE);
8893 }
8894
8895 static void entry_copy_clipboard(GtkWidget *entry)
8896 {
8897         if (GTK_IS_EDITABLE(entry))
8898                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
8899         else if (GTK_IS_TEXT_VIEW(entry))
8900                 gtk_text_buffer_copy_clipboard(
8901                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8902                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
8903 }
8904
8905 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
8906                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
8907 {
8908         if (GTK_IS_TEXT_VIEW(entry)) {
8909                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
8910                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
8911                 GtkTextIter start_iter, end_iter;
8912                 gint start, end;
8913                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
8914
8915                 if (contents == NULL)
8916                         return;
8917
8918                 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
8919
8920                 /* we shouldn't delete the selection when middle-click-pasting, or we
8921                  * can't mid-click-paste our own selection */
8922                 if (clip != GDK_SELECTION_PRIMARY) {
8923                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
8924                 }
8925                 
8926                 if (insert_place == NULL) {
8927                         /* if insert_place isn't specified, insert at the cursor.
8928                          * used for Ctrl-V pasting */
8929                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
8930                         start = gtk_text_iter_get_offset(&start_iter);
8931                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
8932                 } else {
8933                         /* if insert_place is specified, paste here.
8934                          * used for mid-click-pasting */
8935                         start = gtk_text_iter_get_offset(insert_place);
8936                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
8937                 }
8938                 
8939                 if (!wrap) {
8940                         /* paste unwrapped: mark the paste so it's not wrapped later */
8941                         end = start + strlen(contents);
8942                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
8943                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
8944                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
8945                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
8946                         /* rewrap paragraph now (after a mid-click-paste) */
8947                         mark_start = gtk_text_buffer_get_insert(buffer);
8948                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
8949                         gtk_text_iter_backward_char(&start_iter);
8950                         compose_beautify_paragraph(compose, &start_iter, TRUE);
8951                 }
8952         } else if (GTK_IS_EDITABLE(entry))
8953                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
8954         
8955 }
8956
8957 static void entry_allsel(GtkWidget *entry)
8958 {
8959         if (GTK_IS_EDITABLE(entry))
8960                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
8961         else if (GTK_IS_TEXT_VIEW(entry)) {
8962                 GtkTextIter startiter, enditer;
8963                 GtkTextBuffer *textbuf;
8964
8965                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
8966                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
8967                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
8968
8969                 gtk_text_buffer_move_mark_by_name(textbuf, 
8970                         "selection_bound", &startiter);
8971                 gtk_text_buffer_move_mark_by_name(textbuf, 
8972                         "insert", &enditer);
8973         }
8974 }
8975
8976 static void compose_cut_cb(Compose *compose)
8977 {
8978         if (compose->focused_editable 
8979 #ifndef MAEMO
8980             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8981 #endif
8982             )
8983                 entry_cut_clipboard(compose->focused_editable);
8984 }
8985
8986 static void compose_copy_cb(Compose *compose)
8987 {
8988         if (compose->focused_editable 
8989 #ifndef MAEMO
8990             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
8991 #endif
8992             )
8993                 entry_copy_clipboard(compose->focused_editable);
8994 }
8995
8996 static void compose_paste_cb(Compose *compose)
8997 {
8998         gint prev_autowrap;
8999         GtkTextBuffer *buffer;
9000         BLOCK_WRAP();
9001         if (compose->focused_editable &&
9002             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9003                 entry_paste_clipboard(compose, compose->focused_editable, 
9004                                 prefs_common.linewrap_pastes,
9005                                 GDK_SELECTION_CLIPBOARD, NULL);
9006         UNBLOCK_WRAP();
9007 }
9008
9009 static void compose_paste_as_quote_cb(Compose *compose)
9010 {
9011         gint wrap_quote = prefs_common.linewrap_quote;
9012         if (compose->focused_editable 
9013 #ifndef MAEMO
9014             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9015 #endif
9016             ) {
9017                 /* let text_insert() (called directly or at a later time
9018                  * after the gtk_editable_paste_clipboard) know that 
9019                  * text is to be inserted as a quotation. implemented
9020                  * by using a simple refcount... */
9021                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9022                                                 G_OBJECT(compose->focused_editable),
9023                                                 "paste_as_quotation"));
9024                 g_object_set_data(G_OBJECT(compose->focused_editable),
9025                                     "paste_as_quotation",
9026                                     GINT_TO_POINTER(paste_as_quotation + 1));
9027                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9028                 entry_paste_clipboard(compose, compose->focused_editable, 
9029                                 prefs_common.linewrap_pastes,
9030                                 GDK_SELECTION_CLIPBOARD, NULL);
9031                 prefs_common.linewrap_quote = wrap_quote;
9032         }
9033 }
9034
9035 static void compose_paste_no_wrap_cb(Compose *compose)
9036 {
9037         gint prev_autowrap;
9038         GtkTextBuffer *buffer;
9039         BLOCK_WRAP();
9040         if (compose->focused_editable 
9041 #ifndef MAEMO
9042             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9043 #endif
9044             )
9045                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9046                         GDK_SELECTION_CLIPBOARD, NULL);
9047         UNBLOCK_WRAP();
9048 }
9049
9050 static void compose_paste_wrap_cb(Compose *compose)
9051 {
9052         gint prev_autowrap;
9053         GtkTextBuffer *buffer;
9054         BLOCK_WRAP();
9055         if (compose->focused_editable 
9056 #ifndef MAEMO
9057             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9058 #endif
9059             )
9060                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9061                         GDK_SELECTION_CLIPBOARD, NULL);
9062         UNBLOCK_WRAP();
9063 }
9064
9065 static void compose_allsel_cb(Compose *compose)
9066 {
9067         if (compose->focused_editable 
9068 #ifndef MAEMO
9069             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9070 #endif
9071             )
9072                 entry_allsel(compose->focused_editable);
9073 }
9074
9075 static void textview_move_beginning_of_line (GtkTextView *text)
9076 {
9077         GtkTextBuffer *buffer;
9078         GtkTextMark *mark;
9079         GtkTextIter ins;
9080
9081         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9082
9083         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9084         mark = gtk_text_buffer_get_insert(buffer);
9085         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9086         gtk_text_iter_set_line_offset(&ins, 0);
9087         gtk_text_buffer_place_cursor(buffer, &ins);
9088 }
9089
9090 static void textview_move_forward_character (GtkTextView *text)
9091 {
9092         GtkTextBuffer *buffer;
9093         GtkTextMark *mark;
9094         GtkTextIter ins;
9095
9096         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9097
9098         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9099         mark = gtk_text_buffer_get_insert(buffer);
9100         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9101         if (gtk_text_iter_forward_cursor_position(&ins))
9102                 gtk_text_buffer_place_cursor(buffer, &ins);
9103 }
9104
9105 static void textview_move_backward_character (GtkTextView *text)
9106 {
9107         GtkTextBuffer *buffer;
9108         GtkTextMark *mark;
9109         GtkTextIter ins;
9110
9111         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9112
9113         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9114         mark = gtk_text_buffer_get_insert(buffer);
9115         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9116         if (gtk_text_iter_backward_cursor_position(&ins))
9117                 gtk_text_buffer_place_cursor(buffer, &ins);
9118 }
9119
9120 static void textview_move_forward_word (GtkTextView *text)
9121 {
9122         GtkTextBuffer *buffer;
9123         GtkTextMark *mark;
9124         GtkTextIter ins;
9125         gint count;
9126
9127         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9128
9129         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9130         mark = gtk_text_buffer_get_insert(buffer);
9131         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9132         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9133         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9134                 gtk_text_iter_backward_word_start(&ins);
9135                 gtk_text_buffer_place_cursor(buffer, &ins);
9136         }
9137 }
9138
9139 static void textview_move_backward_word (GtkTextView *text)
9140 {
9141         GtkTextBuffer *buffer;
9142         GtkTextMark *mark;
9143         GtkTextIter ins;
9144         gint count;
9145
9146         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9147
9148         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9149         mark = gtk_text_buffer_get_insert(buffer);
9150         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9151         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9152         if (gtk_text_iter_backward_word_starts(&ins, 1))
9153                 gtk_text_buffer_place_cursor(buffer, &ins);
9154 }
9155
9156 static void textview_move_end_of_line (GtkTextView *text)
9157 {
9158         GtkTextBuffer *buffer;
9159         GtkTextMark *mark;
9160         GtkTextIter ins;
9161
9162         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9163
9164         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9165         mark = gtk_text_buffer_get_insert(buffer);
9166         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9167         if (gtk_text_iter_forward_to_line_end(&ins))
9168                 gtk_text_buffer_place_cursor(buffer, &ins);
9169 }
9170
9171 static void textview_move_next_line (GtkTextView *text)
9172 {
9173         GtkTextBuffer *buffer;
9174         GtkTextMark *mark;
9175         GtkTextIter ins;
9176         gint offset;
9177
9178         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9179
9180         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9181         mark = gtk_text_buffer_get_insert(buffer);
9182         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9183         offset = gtk_text_iter_get_line_offset(&ins);
9184         if (gtk_text_iter_forward_line(&ins)) {
9185                 gtk_text_iter_set_line_offset(&ins, offset);
9186                 gtk_text_buffer_place_cursor(buffer, &ins);
9187         }
9188 }
9189
9190 static void textview_move_previous_line (GtkTextView *text)
9191 {
9192         GtkTextBuffer *buffer;
9193         GtkTextMark *mark;
9194         GtkTextIter ins;
9195         gint offset;
9196
9197         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9198
9199         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9200         mark = gtk_text_buffer_get_insert(buffer);
9201         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9202         offset = gtk_text_iter_get_line_offset(&ins);
9203         if (gtk_text_iter_backward_line(&ins)) {
9204                 gtk_text_iter_set_line_offset(&ins, offset);
9205                 gtk_text_buffer_place_cursor(buffer, &ins);
9206         }
9207 }
9208
9209 static void textview_delete_forward_character (GtkTextView *text)
9210 {
9211         GtkTextBuffer *buffer;
9212         GtkTextMark *mark;
9213         GtkTextIter ins, end_iter;
9214
9215         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9216
9217         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9218         mark = gtk_text_buffer_get_insert(buffer);
9219         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9220         end_iter = ins;
9221         if (gtk_text_iter_forward_char(&end_iter)) {
9222                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9223         }
9224 }
9225
9226 static void textview_delete_backward_character (GtkTextView *text)
9227 {
9228         GtkTextBuffer *buffer;
9229         GtkTextMark *mark;
9230         GtkTextIter ins, end_iter;
9231
9232         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9233
9234         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9235         mark = gtk_text_buffer_get_insert(buffer);
9236         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9237         end_iter = ins;
9238         if (gtk_text_iter_backward_char(&end_iter)) {
9239                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9240         }
9241 }
9242
9243 static void textview_delete_forward_word (GtkTextView *text)
9244 {
9245         GtkTextBuffer *buffer;
9246         GtkTextMark *mark;
9247         GtkTextIter ins, end_iter;
9248
9249         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9250
9251         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9252         mark = gtk_text_buffer_get_insert(buffer);
9253         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9254         end_iter = ins;
9255         if (gtk_text_iter_forward_word_end(&end_iter)) {
9256                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9257         }
9258 }
9259
9260 static void textview_delete_backward_word (GtkTextView *text)
9261 {
9262         GtkTextBuffer *buffer;
9263         GtkTextMark *mark;
9264         GtkTextIter ins, end_iter;
9265
9266         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9267
9268         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9269         mark = gtk_text_buffer_get_insert(buffer);
9270         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9271         end_iter = ins;
9272         if (gtk_text_iter_backward_word_start(&end_iter)) {
9273                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9274         }
9275 }
9276
9277 static void textview_delete_line (GtkTextView *text)
9278 {
9279         GtkTextBuffer *buffer;
9280         GtkTextMark *mark;
9281         GtkTextIter ins, start_iter, end_iter;
9282         gboolean found;
9283
9284         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9285
9286         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9287         mark = gtk_text_buffer_get_insert(buffer);
9288         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9289
9290         start_iter = ins;
9291         gtk_text_iter_set_line_offset(&start_iter, 0);
9292
9293         end_iter = ins;
9294         if (gtk_text_iter_ends_line(&end_iter))
9295                 found = gtk_text_iter_forward_char(&end_iter);
9296         else
9297                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9298
9299         if (found)
9300                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9301 }
9302
9303 static void textview_delete_to_line_end (GtkTextView *text)
9304 {
9305         GtkTextBuffer *buffer;
9306         GtkTextMark *mark;
9307         GtkTextIter ins, end_iter;
9308         gboolean found;
9309
9310         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9311
9312         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9313         mark = gtk_text_buffer_get_insert(buffer);
9314         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9315         end_iter = ins;
9316         if (gtk_text_iter_ends_line(&end_iter))
9317                 found = gtk_text_iter_forward_char(&end_iter);
9318         else
9319                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9320         if (found)
9321                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9322 }
9323
9324 static void compose_advanced_action_cb(Compose *compose,
9325                                         ComposeCallAdvancedAction action)
9326 {
9327         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9328         static struct {
9329                 void (*do_action) (GtkTextView *text);
9330         } action_table[] = {
9331                 {textview_move_beginning_of_line},
9332                 {textview_move_forward_character},
9333                 {textview_move_backward_character},
9334                 {textview_move_forward_word},
9335                 {textview_move_backward_word},
9336                 {textview_move_end_of_line},
9337                 {textview_move_next_line},
9338                 {textview_move_previous_line},
9339                 {textview_delete_forward_character},
9340                 {textview_delete_backward_character},
9341                 {textview_delete_forward_word},
9342                 {textview_delete_backward_word},
9343                 {textview_delete_line},
9344                 {NULL}, /* gtk_stext_delete_line_n */
9345                 {textview_delete_to_line_end}
9346         };
9347
9348         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9349
9350         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9351             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9352                 if (action_table[action].do_action)
9353                         action_table[action].do_action(text);
9354                 else
9355                         g_warning("Not implemented yet.");
9356         }
9357 }
9358
9359 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9360 {
9361         gchar *str = NULL;
9362         
9363         if (GTK_IS_EDITABLE(widget)) {
9364                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9365                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9366                         strlen(str));
9367                 g_free(str);
9368                 if (widget->parent && widget->parent->parent
9369                  && widget->parent->parent->parent) {
9370                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9371                                 gint y = widget->allocation.y;
9372                                 gint height = widget->allocation.height;
9373                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9374                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9375
9376                                 if (y < (int)shown->value) {
9377                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9378                                 }
9379                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9380                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9381                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9382                                                         y + height - (int)shown->page_size - 1);
9383                                         } else {
9384                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9385                                                         (int)shown->upper - (int)shown->page_size - 1);
9386                                         }
9387                                 }
9388                         }
9389                 }
9390         }
9391
9392         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9393                 compose->focused_editable = widget;
9394         
9395 #ifdef MAEMO
9396         if (GTK_IS_TEXT_VIEW(widget) 
9397             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9398                 gtk_widget_ref(compose->notebook);
9399                 gtk_widget_ref(compose->edit_vbox);
9400                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9401                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9402                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9403                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9404                 gtk_widget_unref(compose->notebook);
9405                 gtk_widget_unref(compose->edit_vbox);
9406                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9407                                         G_CALLBACK(compose_grab_focus_cb),
9408                                         compose);
9409                 gtk_widget_grab_focus(widget);
9410                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9411                                         G_CALLBACK(compose_grab_focus_cb),
9412                                         compose);
9413         } else if (!GTK_IS_TEXT_VIEW(widget) 
9414                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9415                 gtk_widget_ref(compose->notebook);
9416                 gtk_widget_ref(compose->edit_vbox);
9417                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9418                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9419                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9420                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9421                 gtk_widget_unref(compose->notebook);
9422                 gtk_widget_unref(compose->edit_vbox);
9423                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9424                                         G_CALLBACK(compose_grab_focus_cb),
9425                                         compose);
9426                 gtk_widget_grab_focus(widget);
9427                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9428                                         G_CALLBACK(compose_grab_focus_cb),
9429                                         compose);
9430         }
9431 #endif
9432 }
9433
9434 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9435 {
9436         compose->modified = TRUE;
9437 #ifndef MAEMO
9438         compose_set_title(compose);
9439 #endif
9440 }
9441
9442 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
9443 {
9444         Compose *compose = (Compose *)data;
9445
9446         if (action == 1)
9447                 compose_wrap_all_full(compose, TRUE);
9448         else
9449                 compose_beautify_paragraph(compose, NULL, TRUE);
9450 }
9451
9452 static void compose_find_cb(gpointer data, guint action, GtkWidget *widget)
9453 {
9454         Compose *compose = (Compose *)data;
9455
9456         message_search_compose(compose);
9457 }
9458
9459 static void compose_toggle_autowrap_cb(gpointer data, guint action,
9460                                        GtkWidget *widget)
9461 {
9462         Compose *compose = (Compose *)data;
9463         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9464         if (compose->autowrap)
9465                 compose_wrap_all_full(compose, TRUE);
9466         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9467 }
9468
9469 static void compose_toggle_sign_cb(gpointer data, guint action,
9470                                    GtkWidget *widget)
9471 {
9472         Compose *compose = (Compose *)data;
9473
9474         if (GTK_CHECK_MENU_ITEM(widget)->active)
9475                 compose->use_signing = TRUE;
9476         else
9477                 compose->use_signing = FALSE;
9478 }
9479
9480 static void compose_toggle_encrypt_cb(gpointer data, guint action,
9481                                       GtkWidget *widget)
9482 {
9483         Compose *compose = (Compose *)data;
9484
9485         if (GTK_CHECK_MENU_ITEM(widget)->active)
9486                 compose->use_encryption = TRUE;
9487         else
9488                 compose->use_encryption = FALSE;
9489 }
9490
9491 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
9492 {
9493         g_free(compose->privacy_system);
9494
9495         compose->privacy_system = g_strdup(account->default_privacy_system);
9496         compose_update_privacy_system_menu_item(compose, warn);
9497 }
9498
9499 static void compose_toggle_ruler_cb(gpointer data, guint action,
9500                                     GtkWidget *widget)
9501 {
9502         Compose *compose = (Compose *)data;
9503
9504         if (GTK_CHECK_MENU_ITEM(widget)->active) {
9505                 gtk_widget_show(compose->ruler_hbox);
9506                 prefs_common.show_ruler = TRUE;
9507         } else {
9508                 gtk_widget_hide(compose->ruler_hbox);
9509                 gtk_widget_queue_resize(compose->edit_vbox);
9510                 prefs_common.show_ruler = FALSE;
9511         }
9512 }
9513
9514 static void compose_attach_drag_received_cb (GtkWidget          *widget,
9515                                              GdkDragContext     *context,
9516                                              gint                x,
9517                                              gint                y,
9518                                              GtkSelectionData   *data,
9519                                              guint               info,
9520                                              guint               time,
9521                                              gpointer            user_data)
9522 {
9523         Compose *compose = (Compose *)user_data;
9524         GList *list, *tmp;
9525
9526         if (gdk_atom_name(data->type) && 
9527             !strcmp(gdk_atom_name(data->type), "text/uri-list")
9528             && gtk_drag_get_source_widget(context) != 
9529                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9530                 list = uri_list_extract_filenames((const gchar *)data->data);
9531                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9532                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9533                         compose_attach_append
9534                                 (compose, (const gchar *)tmp->data,
9535                                  utf8_filename, NULL);
9536                         g_free(utf8_filename);
9537                 }
9538                 if (list) compose_changed_cb(NULL, compose);
9539                 list_free_strings(list);
9540                 g_list_free(list);
9541         } else if (gtk_drag_get_source_widget(context) 
9542                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9543                 /* comes from our summaryview */
9544                 SummaryView * summaryview = NULL;
9545                 GSList * list = NULL, *cur = NULL;
9546                 
9547                 if (mainwindow_get_mainwindow())
9548                         summaryview = mainwindow_get_mainwindow()->summaryview;
9549                 
9550                 if (summaryview)
9551                         list = summary_get_selected_msg_list(summaryview);
9552                 
9553                 for (cur = list; cur; cur = cur->next) {
9554                         MsgInfo *msginfo = (MsgInfo *)cur->data;
9555                         gchar *file = NULL;
9556                         if (msginfo)
9557                                 file = procmsg_get_message_file_full(msginfo, 
9558                                         TRUE, TRUE);
9559                         if (file) {
9560                                 compose_attach_append(compose, (const gchar *)file, 
9561                                         (const gchar *)file, "message/rfc822");
9562                                 g_free(file);
9563                         }
9564                 }
9565                 g_slist_free(list);
9566         }
9567 }
9568
9569 static gboolean compose_drag_drop(GtkWidget *widget,
9570                                   GdkDragContext *drag_context,
9571                                   gint x, gint y,
9572                                   guint time, gpointer user_data)
9573 {
9574         /* not handling this signal makes compose_insert_drag_received_cb
9575          * called twice */
9576         return TRUE;                                     
9577 }
9578
9579 static void compose_insert_drag_received_cb (GtkWidget          *widget,
9580                                              GdkDragContext     *drag_context,
9581                                              gint                x,
9582                                              gint                y,
9583                                              GtkSelectionData   *data,
9584                                              guint               info,
9585                                              guint               time,
9586                                              gpointer            user_data)
9587 {
9588         Compose *compose = (Compose *)user_data;
9589         GList *list, *tmp;
9590
9591         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
9592          * does not work */
9593         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
9594                 AlertValue val = G_ALERTDEFAULT;
9595
9596                 switch (prefs_common.compose_dnd_mode) {
9597                         case COMPOSE_DND_ASK:
9598                                 val = alertpanel_full(_("Insert or attach?"),
9599                                          _("Do you want to insert the contents of the file(s) "
9600                                            "into the message body, or attach it to the email?"),
9601                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
9602                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
9603                                 break;
9604                         case COMPOSE_DND_INSERT:
9605                                 val = G_ALERTALTERNATE;
9606                                 break;
9607                         case COMPOSE_DND_ATTACH:
9608                                 val = G_ALERTOTHER;
9609                                 break;
9610                         default:
9611                                 /* unexpected case */
9612                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
9613                 }
9614
9615                 if (val & G_ALERTDISABLE) {
9616                         val &= ~G_ALERTDISABLE;
9617                         /* remember what action to perform by default, only if we don't click Cancel */
9618                         if (val == G_ALERTALTERNATE)
9619                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
9620                         else if (val == G_ALERTOTHER)
9621                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
9622                 }
9623
9624                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
9625                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
9626                         return;
9627                 } else if (val == G_ALERTOTHER) {
9628                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
9629                         return;
9630                 } 
9631                 list = uri_list_extract_filenames((const gchar *)data->data);
9632                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9633                         compose_insert_file(compose, (const gchar *)tmp->data);
9634                 }
9635                 list_free_strings(list);
9636                 g_list_free(list);
9637                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9638                 return;
9639         } else {
9640 #if GTK_CHECK_VERSION(2, 8, 0)
9641                 /* do nothing, handled by GTK */
9642 #else
9643                 gchar *tmpfile = get_tmp_file();
9644                 str_write_to_file((const gchar *)data->data, tmpfile);
9645                 compose_insert_file(compose, tmpfile);
9646                 g_unlink(tmpfile);
9647                 g_free(tmpfile);
9648                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9649 #endif
9650                 return;
9651         }
9652         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9653 }
9654
9655 static void compose_header_drag_received_cb (GtkWidget          *widget,
9656                                              GdkDragContext     *drag_context,
9657                                              gint                x,
9658                                              gint                y,
9659                                              GtkSelectionData   *data,
9660                                              guint               info,
9661                                              guint               time,
9662                                              gpointer            user_data)
9663 {
9664         GtkEditable *entry = (GtkEditable *)user_data;
9665         gchar *email = (gchar *)data->data;
9666
9667         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
9668          * does not work */
9669
9670         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
9671                 gchar *decoded=g_new(gchar, strlen(email));
9672                 int start = 0;
9673
9674                 email += strlen("mailto:");
9675                 decode_uri(decoded, email); /* will fit */
9676                 gtk_editable_delete_text(entry, 0, -1);
9677                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
9678                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9679                 g_free(decoded);
9680                 return;
9681         }
9682         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9683 }
9684
9685 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
9686                                              GtkWidget *widget)
9687 {
9688         Compose *compose = (Compose *)data;
9689
9690         if (GTK_CHECK_MENU_ITEM(widget)->active)
9691                 compose->return_receipt = TRUE;
9692         else
9693                 compose->return_receipt = FALSE;
9694 }
9695
9696 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
9697                                              GtkWidget *widget)
9698 {
9699         Compose *compose = (Compose *)data;
9700
9701         if (GTK_CHECK_MENU_ITEM(widget)->active)
9702                 compose->remove_references = TRUE;
9703         else
9704                 compose->remove_references = FALSE;
9705 }
9706
9707 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
9708                                             GdkEventKey *event,
9709                                             ComposeHeaderEntry *headerentry)
9710 {
9711         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
9712             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
9713             !(event->state & GDK_MODIFIER_MASK) &&
9714             (event->keyval == GDK_BackSpace) &&
9715             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
9716                 gtk_container_remove
9717                         (GTK_CONTAINER(headerentry->compose->header_table),
9718                          headerentry->combo);
9719                 gtk_container_remove
9720                         (GTK_CONTAINER(headerentry->compose->header_table),
9721                          headerentry->entry);
9722                 headerentry->compose->header_list =
9723                         g_slist_remove(headerentry->compose->header_list,
9724                                        headerentry);
9725                 g_free(headerentry);
9726         } else  if (event->keyval == GDK_Tab) {
9727                 if (headerentry->compose->header_last == headerentry) {
9728                         /* Override default next focus, and give it to subject_entry
9729                          * instead of notebook tabs
9730                          */
9731                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
9732                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
9733                         return TRUE;
9734                 }
9735         }
9736         return FALSE;
9737 }
9738
9739 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
9740                                     ComposeHeaderEntry *headerentry)
9741 {
9742         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
9743                 compose_create_header_entry(headerentry->compose);
9744                 g_signal_handlers_disconnect_matched
9745                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
9746                          0, 0, NULL, NULL, headerentry);
9747                 
9748                 /* Automatically scroll down */
9749                 compose_show_first_last_header(headerentry->compose, FALSE);
9750                 
9751         }
9752         return FALSE;
9753 }
9754
9755 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
9756 {
9757         GtkAdjustment *vadj;
9758
9759         g_return_if_fail(compose);
9760         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
9761         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
9762
9763         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
9764         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
9765         gtk_adjustment_changed(vadj);
9766 }
9767
9768 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
9769                           const gchar *text, gint len, Compose *compose)
9770 {
9771         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
9772                                 (G_OBJECT(compose->text), "paste_as_quotation"));
9773         GtkTextMark *mark;
9774
9775         g_return_if_fail(text != NULL);
9776
9777         g_signal_handlers_block_by_func(G_OBJECT(buffer),
9778                                         G_CALLBACK(text_inserted),
9779                                         compose);
9780         if (paste_as_quotation) {
9781                 gchar *new_text;
9782                 const gchar *qmark;
9783                 guint pos = 0;
9784                 GtkTextIter start_iter;
9785
9786                 if (len < 0)
9787                         len = strlen(text);
9788
9789                 new_text = g_strndup(text, len);
9790
9791                 qmark = compose_quote_char_from_context(compose);
9792
9793                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9794                 gtk_text_buffer_place_cursor(buffer, iter);
9795
9796                 pos = gtk_text_iter_get_offset(iter);
9797
9798                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
9799                                                   _("Quote format error at line %d."));
9800                 quote_fmt_reset_vartable();
9801                 g_free(new_text);
9802                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
9803                                   GINT_TO_POINTER(paste_as_quotation - 1));
9804                                   
9805                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9806                 gtk_text_buffer_place_cursor(buffer, iter);
9807                 gtk_text_buffer_delete_mark(buffer, mark);
9808
9809                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
9810                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
9811                 compose_beautify_paragraph(compose, &start_iter, FALSE);
9812                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
9813                 gtk_text_buffer_delete_mark(buffer, mark);
9814         } else {
9815                 if (strcmp(text, "\n") || compose->automatic_break
9816                 || gtk_text_iter_starts_line(iter))
9817                         gtk_text_buffer_insert(buffer, iter, text, len);
9818                 else {
9819                         debug_print("insert nowrap \\n\n");
9820                         gtk_text_buffer_insert_with_tags_by_name(buffer, 
9821                                 iter, text, len, "no_join", NULL);
9822                 }
9823         }
9824         
9825         if (!paste_as_quotation) {
9826                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9827                 compose_beautify_paragraph(compose, iter, FALSE);
9828                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9829                 gtk_text_buffer_delete_mark(buffer, mark);
9830         }
9831
9832         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
9833                                           G_CALLBACK(text_inserted),
9834                                           compose);
9835         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
9836
9837         if (prefs_common.autosave && 
9838             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
9839             compose->draft_timeout_tag != -2 /* disabled while loading */)
9840                 compose->draft_timeout_tag = g_timeout_add
9841                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
9842 }
9843 static gint compose_defer_auto_save_draft(Compose *compose)
9844 {
9845         compose->draft_timeout_tag = -1;
9846         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
9847         return FALSE;
9848 }
9849
9850 #if USE_ASPELL
9851 static void compose_check_all(Compose *compose)
9852 {
9853         if (compose->gtkaspell)
9854                 gtkaspell_check_all(compose->gtkaspell);
9855 }
9856
9857 static void compose_highlight_all(Compose *compose)
9858 {
9859         if (compose->gtkaspell)
9860                 gtkaspell_highlight_all(compose->gtkaspell);
9861 }
9862
9863 static void compose_check_backwards(Compose *compose)
9864 {
9865         if (compose->gtkaspell) 
9866                 gtkaspell_check_backwards(compose->gtkaspell);
9867         else {
9868                 GtkItemFactory *ifactory;
9869                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9870                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9871                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9872         }
9873 }
9874
9875 static void compose_check_forwards_go(Compose *compose)
9876 {
9877         if (compose->gtkaspell) 
9878                 gtkaspell_check_forwards_go(compose->gtkaspell);
9879         else {
9880                 GtkItemFactory *ifactory;
9881                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9882                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9883                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9884         }
9885 }
9886 #endif
9887
9888 /*!
9889  *\brief        Guess originating forward account from MsgInfo and several 
9890  *              "common preference" settings. Return NULL if no guess. 
9891  */
9892 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
9893 {
9894         PrefsAccount *account = NULL;
9895         
9896         g_return_val_if_fail(msginfo, NULL);
9897         g_return_val_if_fail(msginfo->folder, NULL);
9898         g_return_val_if_fail(msginfo->folder->prefs, NULL);
9899
9900         if (msginfo->folder->prefs->enable_default_account)
9901                 account = account_find_from_id(msginfo->folder->prefs->default_account);
9902                 
9903         if (!account) 
9904                 account = msginfo->folder->folder->account;
9905                 
9906         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
9907                 gchar *to;
9908                 Xstrdup_a(to, msginfo->to, return NULL);
9909                 extract_address(to);
9910                 account = account_find_from_address(to);
9911         }
9912
9913         if (!account && prefs_common.forward_account_autosel) {
9914                 gchar cc[BUFFSIZE];
9915                 if (!procheader_get_header_from_msginfo
9916                         (msginfo, cc,sizeof cc , "Cc:")) { 
9917                         gchar *buf = cc + strlen("Cc:");
9918                         extract_address(buf);
9919                         account = account_find_from_address(buf);
9920                 }
9921         }
9922         
9923         if (!account && prefs_common.forward_account_autosel) {
9924                 gchar deliveredto[BUFFSIZE];
9925                 if (!procheader_get_header_from_msginfo
9926                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
9927                         gchar *buf = deliveredto + strlen("Delivered-To:");
9928                         extract_address(buf);
9929                         account = account_find_from_address(buf);
9930                 }
9931         }
9932         
9933         return account;
9934 }
9935
9936 gboolean compose_close(Compose *compose)
9937 {
9938         gint x, y;
9939
9940         if (!g_mutex_trylock(compose->mutex)) {
9941                 /* we have to wait for the (possibly deferred by auto-save)
9942                  * drafting to be done, before destroying the compose under
9943                  * it. */
9944                 debug_print("waiting for drafting to finish...\n");
9945                 compose_allow_user_actions(compose, FALSE);
9946                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
9947                 return FALSE;
9948         }
9949         g_return_val_if_fail(compose, FALSE);
9950         gtkut_widget_get_uposition(compose->window, &x, &y);
9951         prefs_common.compose_x = x;
9952         prefs_common.compose_y = y;
9953         g_mutex_unlock(compose->mutex);
9954         compose_destroy(compose);
9955         return FALSE;
9956 }
9957
9958 /**
9959  * Add entry field for each address in list.
9960  * \param compose     E-Mail composition object.
9961  * \param listAddress List of (formatted) E-Mail addresses.
9962  */
9963 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
9964         GList *node;
9965         gchar *addr;
9966         node = listAddress;
9967         while( node ) {
9968                 addr = ( gchar * ) node->data;
9969                 compose_entry_append( compose, addr, COMPOSE_TO );
9970                 node = g_list_next( node );
9971         }
9972 }
9973
9974 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
9975                                     guint action, gboolean opening_multiple)
9976 {
9977         gchar *body = NULL;
9978         GSList *new_msglist = NULL;
9979         MsgInfo *tmp_msginfo = NULL;
9980         gboolean originally_enc = FALSE;
9981         Compose *compose = NULL;
9982
9983         g_return_if_fail(msgview != NULL);
9984
9985         g_return_if_fail(msginfo_list != NULL);
9986
9987         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
9988                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
9989                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
9990
9991                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
9992                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
9993                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
9994                                                 orig_msginfo, mimeinfo);
9995                         if (tmp_msginfo != NULL) {
9996                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
9997
9998                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
9999                                 tmp_msginfo->folder = orig_msginfo->folder;
10000                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10001                                 if (orig_msginfo->tags)
10002                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10003                         }
10004                 }
10005         }
10006
10007         if (!opening_multiple)
10008                 body = messageview_get_selection(msgview);
10009
10010         if (new_msglist) {
10011                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10012                 procmsg_msginfo_free(tmp_msginfo);
10013                 g_slist_free(new_msglist);
10014         } else
10015                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10016
10017         if (compose && originally_enc) {
10018                 compose_force_encryption(compose, compose->account, FALSE);
10019         }
10020
10021         g_free(body);
10022 }
10023
10024 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10025                                     guint action)
10026 {
10027         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10028         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10029                 GSList *cur = msginfo_list;
10030                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10031                                                "messages. Opening the windows "
10032                                                "could take some time. Do you "
10033                                                "want to continue?"), 
10034                                                g_slist_length(msginfo_list));
10035                 if (g_slist_length(msginfo_list) > 9
10036                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10037                     != G_ALERTALTERNATE) {
10038                         g_free(msg);
10039                         return;
10040                 }
10041                 g_free(msg);
10042                 /* We'll open multiple compose windows */
10043                 /* let the WM place the next windows */
10044                 compose_force_window_origin = FALSE;
10045                 for (; cur; cur = cur->next) {
10046                         GSList tmplist;
10047                         tmplist.data = cur->data;
10048                         tmplist.next = NULL;
10049                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10050                 }
10051                 compose_force_window_origin = TRUE;
10052         } else {
10053                 /* forwarding multiple mails as attachments is done via a
10054                  * single compose window */
10055                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10056         }
10057 }
10058
10059 void compose_set_position(Compose *compose, gint pos)
10060 {
10061         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10062
10063         gtkut_text_view_set_position(text, pos);
10064 }
10065
10066 gboolean compose_search_string(Compose *compose,
10067                                 const gchar *str, gboolean case_sens)
10068 {
10069         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10070
10071         return gtkut_text_view_search_string(text, str, case_sens);
10072 }
10073
10074 gboolean compose_search_string_backward(Compose *compose,
10075                                 const gchar *str, gboolean case_sens)
10076 {
10077         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10078
10079         return gtkut_text_view_search_string_backward(text, str, case_sens);
10080 }
10081
10082 /* allocate a msginfo structure and populate its data from a compose data structure */
10083 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10084 {
10085         MsgInfo *newmsginfo;
10086         GSList *list;
10087         gchar buf[BUFFSIZE];
10088
10089         g_return_val_if_fail( compose != NULL, NULL );
10090
10091         newmsginfo = procmsg_msginfo_new();
10092
10093         /* date is now */
10094         get_rfc822_date(buf, sizeof(buf));
10095         newmsginfo->date = g_strdup(buf);
10096
10097         /* from */
10098         if (compose->from_name) {
10099                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10100                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10101         }
10102
10103         /* subject */
10104         if (compose->subject_entry)
10105                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10106
10107         /* to, cc, reply-to, newsgroups */
10108         for (list = compose->header_list; list; list = list->next) {
10109                 gchar *header = gtk_editable_get_chars(
10110                                                                 GTK_EDITABLE(
10111                                                                 GTK_COMBO(((ComposeHeaderEntry *)list->data)->combo)->entry), 0, -1);
10112                 gchar *entry = gtk_editable_get_chars(
10113                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10114
10115                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10116                         if ( newmsginfo->to == NULL ) {
10117                                 newmsginfo->to = g_strdup(entry);
10118                         } else if (entry && *entry) {
10119                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10120                                 g_free(newmsginfo->to);
10121                                 newmsginfo->to = tmp;
10122                         }
10123                 } else
10124                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10125                         if ( newmsginfo->cc == NULL ) {
10126                                 newmsginfo->cc = g_strdup(entry);
10127                         } else if (entry && *entry) {
10128                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10129                                 g_free(newmsginfo->cc);
10130                                 newmsginfo->cc = tmp;
10131                         }
10132                 } else
10133                 if ( strcasecmp(header,
10134                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10135                         if ( newmsginfo->newsgroups == NULL ) {
10136                                 newmsginfo->newsgroups = g_strdup(entry);
10137                         } else if (entry && *entry) {
10138                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10139                                 g_free(newmsginfo->newsgroups);
10140                                 newmsginfo->newsgroups = tmp;
10141                         }
10142                 }
10143
10144                 g_free(header);
10145                 g_free(entry);  
10146         }
10147
10148         /* other data is unset */
10149
10150         return newmsginfo;
10151 }
10152
10153 #ifdef USE_ASPELL
10154 /* update compose's dictionaries from folder dict settings */
10155 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10156                                                 FolderItem *folder_item)
10157 {
10158         g_return_if_fail(compose != NULL);
10159
10160         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10161                 FolderItemPrefs *prefs = folder_item->prefs;
10162
10163                 if (prefs->enable_default_dictionary)
10164                         gtkaspell_change_dict(compose->gtkaspell,
10165                                         prefs->default_dictionary, FALSE);
10166                 if (folder_item->prefs->enable_default_alt_dictionary)
10167                         gtkaspell_change_alt_dict(compose->gtkaspell,
10168                                         prefs->default_alt_dictionary);
10169                 if (prefs->enable_default_dictionary
10170                         || prefs->enable_default_alt_dictionary)
10171                         compose_spell_menu_changed(compose);
10172         }
10173 }
10174 #endif
10175
10176 /*
10177  * End of Source.
10178  */