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