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