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