2007-12-06 [colin] 3.1.0cvs62
[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, att = 0;
2434                 gchar *warn_files = NULL;
2435                 while (attach[i] != NULL) {
2436                         gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2437                         if (utf8_filename) {
2438                                 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2439                                         gchar *tmp = g_strdup_printf("%s%s\n",
2440                                                         warn_files?warn_files:"",
2441                                                         utf8_filename);
2442                                         g_free(warn_files);
2443                                         warn_files = tmp;
2444                                         att++;
2445                                 }
2446                                 g_free(utf8_filename);
2447                         } else {
2448                                 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2449                         }
2450                         i++;
2451                 }
2452                 if (warn_files) {
2453                         alertpanel_notice(ngettext(
2454                         "The following file has been attached: \n%s",
2455                         "The following files have been attached: \n%s", att), warn_files);
2456                         g_free(warn_files);
2457                 }
2458         }
2459         g_free(to);
2460         g_free(cc);
2461         g_free(bcc);
2462         g_free(subject);
2463         g_free(body);
2464         g_strfreev(attach);
2465 }
2466
2467 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2468 {
2469         static HeaderEntry hentry[] = {{"Reply-To:",    NULL, TRUE},
2470                                        {"Cc:",          NULL, TRUE},
2471                                        {"References:",  NULL, FALSE},
2472                                        {"Bcc:",         NULL, TRUE},
2473                                        {"Newsgroups:",  NULL, TRUE},
2474                                        {"Followup-To:", NULL, TRUE},
2475                                        {"List-Post:",   NULL, FALSE},
2476                                        {"X-Priority:",  NULL, FALSE},
2477                                        {NULL,           NULL, FALSE}};
2478
2479         enum
2480         {
2481                 H_REPLY_TO      = 0,
2482                 H_CC            = 1,
2483                 H_REFERENCES    = 2,
2484                 H_BCC           = 3,
2485                 H_NEWSGROUPS    = 4,
2486                 H_FOLLOWUP_TO   = 5,
2487                 H_LIST_POST     = 6,
2488                 H_X_PRIORITY    = 7
2489         };
2490
2491         FILE *fp;
2492
2493         g_return_val_if_fail(msginfo != NULL, -1);
2494
2495         if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2496         procheader_get_header_fields(fp, hentry);
2497         fclose(fp);
2498
2499         if (hentry[H_REPLY_TO].body != NULL) {
2500                 if (hentry[H_REPLY_TO].body[0] != '\0') {
2501                         compose->replyto =
2502                                 conv_unmime_header(hentry[H_REPLY_TO].body,
2503                                                    NULL);
2504                 }
2505                 g_free(hentry[H_REPLY_TO].body);
2506                 hentry[H_REPLY_TO].body = NULL;
2507         }
2508         if (hentry[H_CC].body != NULL) {
2509                 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2510                 g_free(hentry[H_CC].body);
2511                 hentry[H_CC].body = NULL;
2512         }
2513         if (hentry[H_REFERENCES].body != NULL) {
2514                 if (compose->mode == COMPOSE_REEDIT)
2515                         compose->references = hentry[H_REFERENCES].body;
2516                 else {
2517                         compose->references = compose_parse_references
2518                                 (hentry[H_REFERENCES].body, msginfo->msgid);
2519                         g_free(hentry[H_REFERENCES].body);
2520                 }
2521                 hentry[H_REFERENCES].body = NULL;
2522         }
2523         if (hentry[H_BCC].body != NULL) {
2524                 if (compose->mode == COMPOSE_REEDIT)
2525                         compose->bcc =
2526                                 conv_unmime_header(hentry[H_BCC].body, NULL);
2527                 g_free(hentry[H_BCC].body);
2528                 hentry[H_BCC].body = NULL;
2529         }
2530         if (hentry[H_NEWSGROUPS].body != NULL) {
2531                 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2532                 hentry[H_NEWSGROUPS].body = NULL;
2533         }
2534         if (hentry[H_FOLLOWUP_TO].body != NULL) {
2535                 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2536                         compose->followup_to =
2537                                 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2538                                                    NULL);
2539                 }
2540                 g_free(hentry[H_FOLLOWUP_TO].body);
2541                 hentry[H_FOLLOWUP_TO].body = NULL;
2542         }
2543         if (hentry[H_LIST_POST].body != NULL) {
2544                 gchar *to = NULL;
2545
2546                 extract_address(hentry[H_LIST_POST].body);
2547                 if (hentry[H_LIST_POST].body[0] != '\0') {
2548                         scan_mailto_url(hentry[H_LIST_POST].body,
2549                                         &to, NULL, NULL, NULL, NULL, NULL);
2550                         if (to) {
2551                                 g_free(compose->ml_post);
2552                                 compose->ml_post = to;
2553                         }
2554                 }
2555                 g_free(hentry[H_LIST_POST].body);
2556                 hentry[H_LIST_POST].body = NULL;
2557         }
2558
2559         /* CLAWS - X-Priority */
2560         if (compose->mode == COMPOSE_REEDIT)
2561                 if (hentry[H_X_PRIORITY].body != NULL) {
2562                         gint priority;
2563                         
2564                         priority = atoi(hentry[H_X_PRIORITY].body);
2565                         g_free(hentry[H_X_PRIORITY].body);
2566                         
2567                         hentry[H_X_PRIORITY].body = NULL;
2568                         
2569                         if (priority < PRIORITY_HIGHEST || 
2570                             priority > PRIORITY_LOWEST)
2571                                 priority = PRIORITY_NORMAL;
2572                         
2573                         compose->priority =  priority;
2574                 }
2575  
2576         if (compose->mode == COMPOSE_REEDIT) {
2577                 if (msginfo->inreplyto && *msginfo->inreplyto)
2578                         compose->inreplyto = g_strdup(msginfo->inreplyto);
2579                 return 0;
2580         }
2581
2582         if (msginfo->msgid && *msginfo->msgid)
2583                 compose->inreplyto = g_strdup(msginfo->msgid);
2584
2585         if (!compose->references) {
2586                 if (msginfo->msgid && *msginfo->msgid) {
2587                         if (msginfo->inreplyto && *msginfo->inreplyto)
2588                                 compose->references =
2589                                         g_strdup_printf("<%s>\n\t<%s>",
2590                                                         msginfo->inreplyto,
2591                                                         msginfo->msgid);
2592                         else
2593                                 compose->references =
2594                                         g_strconcat("<", msginfo->msgid, ">",
2595                                                     NULL);
2596                 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2597                         compose->references =
2598                                 g_strconcat("<", msginfo->inreplyto, ">",
2599                                             NULL);
2600                 }
2601         }
2602
2603         return 0;
2604 }
2605
2606 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2607 {
2608         GSList *ref_id_list, *cur;
2609         GString *new_ref;
2610         gchar *new_ref_str;
2611
2612         ref_id_list = references_list_append(NULL, ref);
2613         if (!ref_id_list) return NULL;
2614         if (msgid && *msgid)
2615                 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2616
2617         for (;;) {
2618                 gint len = 0;
2619
2620                 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2621                         /* "<" + Message-ID + ">" + CR+LF+TAB */
2622                         len += strlen((gchar *)cur->data) + 5;
2623
2624                 if (len > MAX_REFERENCES_LEN) {
2625                         /* remove second message-ID */
2626                         if (ref_id_list && ref_id_list->next &&
2627                             ref_id_list->next->next) {
2628                                 g_free(ref_id_list->next->data);
2629                                 ref_id_list = g_slist_remove
2630                                         (ref_id_list, ref_id_list->next->data);
2631                         } else {
2632                                 slist_free_strings(ref_id_list);
2633                                 g_slist_free(ref_id_list);
2634                                 return NULL;
2635                         }
2636                 } else
2637                         break;
2638         }
2639
2640         new_ref = g_string_new("");
2641         for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2642                 if (new_ref->len > 0)
2643                         g_string_append(new_ref, "\n\t");
2644                 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2645         }
2646
2647         slist_free_strings(ref_id_list);
2648         g_slist_free(ref_id_list);
2649
2650         new_ref_str = new_ref->str;
2651         g_string_free(new_ref, FALSE);
2652
2653         return new_ref_str;
2654 }
2655
2656 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2657                                 const gchar *fmt, const gchar *qmark,
2658                                 const gchar *body, gboolean rewrap,
2659                                 gboolean need_unescape,
2660                                 const gchar *err_msg)
2661 {
2662         MsgInfo* dummyinfo = NULL;
2663         gchar *quote_str = NULL;
2664         gchar *buf;
2665         gboolean prev_autowrap;
2666         const gchar *trimmed_body = body;
2667         gint cursor_pos = -1;
2668         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2669         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2670         GtkTextIter iter;
2671         GtkTextMark *mark;
2672         
2673
2674         SIGNAL_BLOCK(buffer);
2675
2676         if (!msginfo) {
2677                 dummyinfo = compose_msginfo_new_from_compose(compose);
2678                 msginfo = dummyinfo;
2679         }
2680
2681         if (qmark != NULL) {
2682 #ifdef USE_ASPELL
2683                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
2684                                 compose->gtkaspell);
2685 #else
2686                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
2687 #endif
2688                 quote_fmt_scan_string(qmark);
2689                 quote_fmt_parse();
2690
2691                 buf = quote_fmt_get_buffer();
2692                 if (buf == NULL)
2693                         alertpanel_error(_("Quote mark format error."));
2694                 else
2695                         Xstrdup_a(quote_str, buf, goto error)
2696         }
2697
2698         if (fmt && *fmt != '\0') {
2699
2700                 if (trimmed_body)
2701                         while (*trimmed_body == '\n')
2702                                 trimmed_body++;
2703
2704 #ifdef USE_ASPELL
2705                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account,
2706                                 compose->gtkaspell);
2707 #else
2708                 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account);
2709 #endif
2710                 if (need_unescape) {
2711                         gchar *tmp = NULL;
2712
2713                         /* decode \-escape sequences in the internal representation of the quote format */
2714                         tmp = malloc(strlen(fmt)+1);
2715                         pref_get_unescaped_pref(tmp, fmt);
2716                         quote_fmt_scan_string(tmp);
2717                         quote_fmt_parse();
2718                         g_free(tmp);
2719                 } else {
2720                         quote_fmt_scan_string(fmt);
2721                         quote_fmt_parse();
2722                 }
2723
2724                 buf = quote_fmt_get_buffer();
2725                 if (buf == NULL) {
2726                         gint line = quote_fmt_get_line();
2727                         alertpanel_error(err_msg, line);
2728                         goto error;
2729                 }
2730         } else
2731                 buf = "";
2732
2733         prev_autowrap = compose->autowrap;
2734         compose->autowrap = FALSE;
2735
2736         mark = gtk_text_buffer_get_insert(buffer);
2737         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2738         if (g_utf8_validate(buf, -1, NULL)) { 
2739                 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2740         } else {
2741                 gchar *tmpout = NULL;
2742                 tmpout = conv_codeset_strdup
2743                         (buf, conv_get_locale_charset_str_no_utf8(),
2744                          CS_INTERNAL);
2745                 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2746                         g_free(tmpout);
2747                         tmpout = g_malloc(strlen(buf)*2+1);
2748                         conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2749                 }
2750                 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2751                 g_free(tmpout);
2752         }
2753
2754         cursor_pos = quote_fmt_get_cursor_pos();
2755         compose->set_cursor_pos = cursor_pos;
2756         if (cursor_pos == -1) {
2757                 cursor_pos = 0;
2758         }
2759         gtk_text_buffer_get_start_iter(buffer, &iter);
2760         gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2761         gtk_text_buffer_place_cursor(buffer, &iter);
2762
2763         compose->autowrap = prev_autowrap;
2764         if (compose->autowrap && rewrap)
2765                 compose_wrap_all(compose);
2766
2767         goto ok;
2768
2769 error:
2770         buf = NULL;
2771 ok:
2772         SIGNAL_UNBLOCK(buffer);
2773
2774         procmsg_msginfo_free( dummyinfo );
2775
2776         return buf;
2777 }
2778
2779 /* if ml_post is of type addr@host and from is of type
2780  * addr-anything@host, return TRUE
2781  */
2782 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2783 {
2784         gchar *left_ml = NULL;
2785         gchar *right_ml = NULL;
2786         gchar *left_from = NULL;
2787         gchar *right_from = NULL;
2788         gboolean result = FALSE;
2789         
2790         if (!ml_post || !from)
2791                 return FALSE;
2792         
2793         left_ml = g_strdup(ml_post);
2794         if (strstr(left_ml, "@")) {
2795                 right_ml = strstr(left_ml, "@")+1;
2796                 *(strstr(left_ml, "@")) = '\0';
2797         }
2798         
2799         left_from = g_strdup(from);
2800         if (strstr(left_from, "@")) {
2801                 right_from = strstr(left_from, "@")+1;
2802                 *(strstr(left_from, "@")) = '\0';
2803         }
2804         
2805         if (left_ml && left_from && right_ml && right_from
2806         &&  !strncmp(left_from, left_ml, strlen(left_ml))
2807         &&  !strcmp(right_from, right_ml)) {
2808                 result = TRUE;
2809         }
2810         g_free(left_ml);
2811         g_free(left_from);
2812         
2813         return result;
2814 }
2815
2816 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2817 {
2818         gchar *my_addr1, *my_addr2;
2819         
2820         if (!addr1 || !addr2)
2821                 return FALSE;
2822
2823         Xstrdup_a(my_addr1, addr1, return FALSE);
2824         Xstrdup_a(my_addr2, addr2, return FALSE);
2825         
2826         extract_address(my_addr1);
2827         extract_address(my_addr2);
2828         
2829         return !strcasecmp(my_addr1, my_addr2);
2830 }
2831
2832 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2833                                     gboolean to_all, gboolean to_ml,
2834                                     gboolean to_sender,
2835                                     gboolean followup_and_reply_to)
2836 {
2837         GSList *cc_list = NULL;
2838         GSList *cur;
2839         gchar *from = NULL;
2840         gchar *replyto = NULL;
2841         GHashTable *to_table;
2842
2843         gboolean reply_to_ml = FALSE;
2844         gboolean default_reply_to = FALSE;
2845
2846         g_return_if_fail(compose->account != NULL);
2847         g_return_if_fail(msginfo != NULL);
2848
2849         reply_to_ml = to_ml && compose->ml_post;
2850
2851         default_reply_to = msginfo->folder && 
2852                 msginfo->folder->prefs->enable_default_reply_to;
2853
2854         if (compose->account->protocol != A_NNTP) {
2855                 if (reply_to_ml && !default_reply_to) {
2856                         
2857                         gboolean is_subscr = is_subscription(compose->ml_post,
2858                                                              msginfo->from);
2859                         if (!is_subscr) {
2860                                 /* normal answer to ml post with a reply-to */
2861                                 compose_entry_append(compose,
2862                                            compose->ml_post,
2863                                            COMPOSE_TO);
2864                                 if (compose->replyto
2865                                 &&  !same_address(compose->ml_post, compose->replyto))
2866                                         compose_entry_append(compose,
2867                                                 compose->replyto,
2868                                                 COMPOSE_CC);
2869                         } else {
2870                                 /* answer to subscription confirmation */
2871                                 if (compose->replyto)
2872                                         compose_entry_append(compose,
2873                                                 compose->replyto,
2874                                                 COMPOSE_TO);
2875                                 else if (msginfo->from)
2876                                         compose_entry_append(compose,
2877                                                 msginfo->from,
2878                                                 COMPOSE_TO);
2879                         }
2880                 }
2881                 else if (!(to_all || to_sender) && default_reply_to) {
2882                         compose_entry_append(compose,
2883                             msginfo->folder->prefs->default_reply_to,
2884                             COMPOSE_TO);
2885                         compose_entry_mark_default_to(compose,
2886                                 msginfo->folder->prefs->default_reply_to);
2887                 } else {
2888                         gchar *tmp1 = NULL;
2889                         if (!msginfo->from)
2890                                 return;
2891                         Xstrdup_a(tmp1, msginfo->from, return);
2892                         extract_address(tmp1);
2893                         if (to_all || to_sender ||
2894                             !account_find_from_address(tmp1))
2895                                 compose_entry_append(compose,
2896                                  (compose->replyto && !to_sender)
2897                                           ? compose->replyto :
2898                                           msginfo->from ? msginfo->from : "",
2899                                           COMPOSE_TO);
2900                         else if (!to_all && !to_sender) {
2901                                 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2902                                     !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2903                                     !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2904                                         compose_entry_append(compose,
2905                                                   msginfo->from ? msginfo->from : "",
2906                                                   COMPOSE_TO);
2907                                 } else {
2908                                         /* replying to own mail, use original recp */
2909                                         compose_entry_append(compose,
2910                                                   msginfo->to ? msginfo->to : "",
2911                                                   COMPOSE_TO);
2912                                         compose_entry_append(compose,
2913                                                   msginfo->cc ? msginfo->cc : "",
2914                                                   COMPOSE_CC);
2915                                 }
2916                         }
2917                 }
2918         } else {
2919                 if (to_sender || (compose->followup_to && 
2920                         !strncmp(compose->followup_to, "poster", 6)))
2921                         compose_entry_append
2922                                 (compose, 
2923                                  (compose->replyto ? compose->replyto :
2924                                         msginfo->from ? msginfo->from : ""),
2925                                  COMPOSE_TO);
2926                                  
2927                 else if (followup_and_reply_to || to_all) {
2928                         compose_entry_append
2929                                 (compose,
2930                                  (compose->replyto ? compose->replyto :
2931                                  msginfo->from ? msginfo->from : ""),
2932                                  COMPOSE_TO);                           
2933                 
2934                         compose_entry_append
2935                                 (compose,
2936                                  compose->followup_to ? compose->followup_to :
2937                                  compose->newsgroups ? compose->newsgroups : "",
2938                                  COMPOSE_NEWSGROUPS);
2939                 } 
2940                 else 
2941                         compose_entry_append
2942                                 (compose,
2943                                  compose->followup_to ? compose->followup_to :
2944                                  compose->newsgroups ? compose->newsgroups : "",
2945                                  COMPOSE_NEWSGROUPS);
2946         }
2947
2948         if (msginfo->subject && *msginfo->subject) {
2949                 gchar *buf, *buf2;
2950                 gchar *p;
2951
2952                 buf = p = g_strdup(msginfo->subject);
2953                 p += subject_get_prefix_length(p);
2954                 memmove(buf, p, strlen(p) + 1);
2955
2956                 buf2 = g_strdup_printf("Re: %s", buf);
2957                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
2958
2959                 g_free(buf2);
2960                 g_free(buf);
2961         } else
2962                 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
2963
2964         if (to_ml && compose->ml_post) return;
2965         if (!to_all || compose->account->protocol == A_NNTP) return;
2966
2967         if (compose->replyto) {
2968                 Xstrdup_a(replyto, compose->replyto, return);
2969                 extract_address(replyto);
2970         }
2971         if (msginfo->from) {
2972                 Xstrdup_a(from, msginfo->from, return);
2973                 extract_address(from);
2974         }
2975
2976         if (replyto && from)
2977                 cc_list = address_list_append_with_comments(cc_list, from);
2978         if (to_all && msginfo->folder && 
2979             msginfo->folder->prefs->enable_default_reply_to)
2980                 cc_list = address_list_append_with_comments(cc_list,
2981                                 msginfo->folder->prefs->default_reply_to);
2982         cc_list = address_list_append_with_comments(cc_list, msginfo->to);
2983         cc_list = address_list_append_with_comments(cc_list, compose->cc);
2984
2985         to_table = g_hash_table_new(g_str_hash, g_str_equal);
2986         if (replyto)
2987                 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
2988         if (compose->account) {
2989                 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
2990                                     GINT_TO_POINTER(1));
2991         }
2992         /* remove address on To: and that of current account */
2993         for (cur = cc_list; cur != NULL; ) {
2994                 GSList *next = cur->next;
2995                 gchar *addr;
2996
2997                 addr = g_utf8_strdown(cur->data, -1);
2998                 extract_address(addr);
2999
3000                 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3001                         cc_list = g_slist_remove(cc_list, cur->data);
3002                 else
3003                         g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3004
3005                 cur = next;
3006         }
3007         hash_free_strings(to_table);
3008         g_hash_table_destroy(to_table);
3009
3010         if (cc_list) {
3011                 for (cur = cc_list; cur != NULL; cur = cur->next)
3012                         compose_entry_append(compose, (gchar *)cur->data,
3013                                              COMPOSE_CC);
3014                 slist_free_strings(cc_list);
3015                 g_slist_free(cc_list);
3016         }
3017
3018 }
3019
3020 #define SET_ENTRY(entry, str) \
3021 { \
3022         if (str && *str) \
3023                 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3024 }
3025
3026 #define SET_ADDRESS(type, str) \
3027 { \
3028         if (str && *str) \
3029                 compose_entry_append(compose, str, type); \
3030 }
3031
3032 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3033 {
3034         g_return_if_fail(msginfo != NULL);
3035
3036         SET_ENTRY(subject_entry, msginfo->subject);
3037         SET_ENTRY(from_name, msginfo->from);
3038         SET_ADDRESS(COMPOSE_TO, msginfo->to);
3039         SET_ADDRESS(COMPOSE_CC, compose->cc);
3040         SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3041         SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3042         SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3043         SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3044
3045         compose_update_priority_menu_item(compose);
3046         compose_update_privacy_system_menu_item(compose, FALSE);
3047         compose_show_first_last_header(compose, TRUE);
3048 }
3049
3050 #undef SET_ENTRY
3051 #undef SET_ADDRESS
3052
3053 static void compose_insert_sig(Compose *compose, gboolean replace)
3054 {
3055         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3056         GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3057         GtkTextMark *mark;
3058         GtkTextIter iter, iter_end;
3059         gint cur_pos;
3060         gboolean prev_autowrap;
3061         gboolean found = FALSE;
3062         gboolean exists = FALSE;
3063         
3064         g_return_if_fail(compose->account != NULL);
3065
3066         BLOCK_WRAP();
3067
3068         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3069                                         G_CALLBACK(compose_changed_cb),
3070                                         compose);
3071         
3072         mark = gtk_text_buffer_get_insert(buffer);
3073         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3074         cur_pos = gtk_text_iter_get_offset (&iter);
3075
3076         gtk_text_buffer_get_end_iter(buffer, &iter);
3077
3078         exists = (compose->sig_str != NULL);
3079
3080         if (replace) {
3081                 GtkTextIter first_iter, start_iter, end_iter;
3082
3083                 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3084
3085                 if (!exists || compose->sig_str[0] == '\0')
3086                         found = FALSE;
3087                 else
3088                         found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3089                                         compose->signature_tag);
3090
3091                 if (found) {
3092                         /* include previous \n\n */
3093                         gtk_text_iter_backward_chars(&first_iter, 2);
3094                         start_iter = first_iter;
3095                         end_iter = first_iter;
3096                         /* skip re-start */
3097                         found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3098                                         compose->signature_tag);
3099                         found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3100                                         compose->signature_tag);
3101                         if (found) {
3102                                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3103                                 iter = start_iter;
3104                         }
3105                 } 
3106         } 
3107
3108         g_free(compose->sig_str);
3109         compose->sig_str = compose_get_signature_str(compose);
3110
3111         cur_pos = gtk_text_iter_get_offset(&iter);
3112
3113         if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3114                 g_free(compose->sig_str);
3115                 compose->sig_str = NULL;
3116         } else {
3117                 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3118                 /* remove \n\n */
3119                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3120                 gtk_text_iter_forward_chars(&iter, 2);
3121                 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3122                 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3123
3124                 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3125                         cur_pos = gtk_text_buffer_get_char_count (buffer);
3126         }
3127         /* put the cursor where it should be 
3128          * either where the quote_fmt says, either before the signature */
3129         if (compose->set_cursor_pos < 0)
3130                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3131         else
3132                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, 
3133                         compose->set_cursor_pos);
3134                 
3135         gtk_text_buffer_place_cursor(buffer, &iter);
3136         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3137                                         G_CALLBACK(compose_changed_cb),
3138                                         compose);
3139                 
3140         UNBLOCK_WRAP();
3141 }
3142
3143 static gchar *compose_get_signature_str(Compose *compose)
3144 {
3145         gchar *sig_body = NULL;
3146         gchar *sig_str = NULL;
3147         gchar *utf8_sig_str = NULL;
3148
3149         g_return_val_if_fail(compose->account != NULL, NULL);
3150
3151         if (!compose->account->sig_path)
3152                 return NULL;
3153
3154         if (compose->account->sig_type == SIG_FILE) {
3155                 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3156                         g_warning("can't open signature file: %s\n",
3157                                   compose->account->sig_path);
3158                         return NULL;
3159                 }
3160         }
3161
3162         if (compose->account->sig_type == SIG_COMMAND)
3163                 sig_body = get_command_output(compose->account->sig_path);
3164         else {
3165                 gchar *tmp;
3166
3167                 tmp = file_read_to_str(compose->account->sig_path);
3168                 if (!tmp)
3169                         return NULL;
3170                 sig_body = normalize_newlines(tmp);
3171                 g_free(tmp);
3172         }
3173
3174         if (compose->account->sig_sep) {
3175                 sig_str = g_strconcat("\n\n", compose->account->sig_sep, "\n", sig_body,
3176                                       NULL);
3177                 g_free(sig_body);
3178         } else
3179                 sig_str = g_strconcat("\n\n", sig_body, NULL);
3180
3181         if (sig_str) {
3182                 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3183                         utf8_sig_str = sig_str;
3184                 else {
3185                         utf8_sig_str = conv_codeset_strdup
3186                                 (sig_str, conv_get_locale_charset_str_no_utf8(),
3187                                  CS_INTERNAL);
3188                         g_free(sig_str);
3189                 }
3190         }
3191
3192         return utf8_sig_str;
3193 }
3194
3195 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3196 {
3197         GtkTextView *text;
3198         GtkTextBuffer *buffer;
3199         GtkTextMark *mark;
3200         GtkTextIter iter;
3201         const gchar *cur_encoding;
3202         gchar buf[BUFFSIZE];
3203         gint len;
3204         FILE *fp;
3205         gboolean prev_autowrap;
3206         gboolean badtxt = FALSE;
3207
3208         g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3209
3210         if ((fp = g_fopen(file, "rb")) == NULL) {
3211                 FILE_OP_ERROR(file, "fopen");
3212                 return COMPOSE_INSERT_READ_ERROR;
3213         }
3214
3215         prev_autowrap = compose->autowrap;
3216         compose->autowrap = FALSE;
3217
3218         text = GTK_TEXT_VIEW(compose->text);
3219         buffer = gtk_text_view_get_buffer(text);
3220         mark = gtk_text_buffer_get_insert(buffer);
3221         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3222
3223         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3224                                         G_CALLBACK(text_inserted),
3225                                         compose);
3226
3227         cur_encoding = conv_get_locale_charset_str_no_utf8();
3228
3229         while (fgets(buf, sizeof(buf), fp) != NULL) {
3230                 gchar *str;
3231
3232                 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3233                         str = g_strdup(buf);
3234                 else
3235                         str = conv_codeset_strdup
3236                                 (buf, cur_encoding, CS_INTERNAL);
3237                 if (!str) continue;
3238
3239                 /* strip <CR> if DOS/Windows file,
3240                    replace <CR> with <LF> if Macintosh file. */
3241                 strcrchomp(str);
3242                 len = strlen(str);
3243                 if (len > 0 && str[len - 1] != '\n') {
3244                         while (--len >= 0)
3245                                 if (str[len] == '\r') str[len] = '\n';
3246                 }
3247
3248                 gtk_text_buffer_insert(buffer, &iter, str, -1);
3249                 g_free(str);
3250         }
3251
3252         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3253                                           G_CALLBACK(text_inserted),
3254                                           compose);
3255         compose->autowrap = prev_autowrap;
3256         if (compose->autowrap)
3257                 compose_wrap_all(compose);
3258
3259         fclose(fp);
3260
3261         if (badtxt)
3262                 return COMPOSE_INSERT_INVALID_CHARACTER;
3263         else 
3264                 return COMPOSE_INSERT_SUCCESS;
3265 }
3266
3267 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3268                                   const gchar *filename,
3269                                   const gchar *content_type)
3270 {
3271         AttachInfo *ainfo;
3272         GtkTreeIter iter;
3273         FILE *fp;
3274         off_t size;
3275         GAuto *auto_ainfo;
3276         gchar *size_text;
3277         GtkListStore *store;
3278         gchar *name;
3279         gboolean has_binary = FALSE;
3280
3281         if (!is_file_exist(file)) {
3282                 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3283                 gboolean result = FALSE;
3284                 if (file_from_uri && is_file_exist(file_from_uri)) {
3285                         result = compose_attach_append(
3286                                                 compose, file_from_uri,
3287                                                 filename,
3288                                                 content_type);
3289                 }
3290                 g_free(file_from_uri);
3291                 if (result)
3292                         return TRUE;
3293                 alertpanel_error("File %s doesn't exist\n", filename);
3294                 return FALSE;
3295         }
3296         if ((size = get_file_size(file)) < 0) {
3297                 alertpanel_error("Can't get file size of %s\n", filename);
3298                 return FALSE;
3299         }
3300         if (size == 0) {
3301                 alertpanel_error(_("File %s is empty."), filename);
3302                 return FALSE;
3303         }
3304         if ((fp = g_fopen(file, "rb")) == NULL) {
3305                 alertpanel_error(_("Can't read %s."), filename);
3306                 return FALSE;
3307         }
3308         fclose(fp);
3309
3310         ainfo = g_new0(AttachInfo, 1);
3311         auto_ainfo = g_auto_pointer_new_with_free
3312                         (ainfo, (GFreeFunc) compose_attach_info_free); 
3313         ainfo->file = g_strdup(file);
3314
3315         if (content_type) {
3316                 ainfo->content_type = g_strdup(content_type);
3317                 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3318                         MsgInfo *msginfo;
3319                         MsgFlags flags = {0, 0};
3320
3321                         if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3322                                 ainfo->encoding = ENC_7BIT;
3323                         else
3324                                 ainfo->encoding = ENC_8BIT;
3325
3326                         msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3327                         if (msginfo && msginfo->subject)
3328                                 name = g_strdup(msginfo->subject);
3329                         else
3330                                 name = g_path_get_basename(filename ? filename : file);
3331
3332                         ainfo->name = g_strdup_printf(_("Message: %s"), name);
3333
3334                         procmsg_msginfo_free(msginfo);
3335                 } else {
3336                         if (!g_ascii_strncasecmp(content_type, "text", 4))
3337                                 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3338                         else
3339                                 ainfo->encoding = ENC_BASE64;
3340                         name = g_path_get_basename(filename ? filename : file);
3341                         ainfo->name = g_strdup(name);
3342                 }
3343                 g_free(name);
3344         } else {
3345                 ainfo->content_type = procmime_get_mime_type(file);
3346                 if (!ainfo->content_type) {
3347                         ainfo->content_type =
3348                                 g_strdup("application/octet-stream");
3349                         ainfo->encoding = ENC_BASE64;
3350                 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3351                         ainfo->encoding =
3352                                 procmime_get_encoding_for_text_file(file, &has_binary);
3353                 else
3354                         ainfo->encoding = ENC_BASE64;
3355                 name = g_path_get_basename(filename ? filename : file);
3356                 ainfo->name = g_strdup(name);   
3357                 g_free(name);
3358         }
3359
3360         if (ainfo->name != NULL
3361         &&  !strcmp(ainfo->name, ".")) {
3362                 g_free(ainfo->name);
3363                 ainfo->name = NULL;
3364         }
3365
3366         if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3367                 g_free(ainfo->content_type);
3368                 ainfo->content_type = g_strdup("application/octet-stream");
3369         }
3370
3371         ainfo->size = size;
3372         size_text = to_human_readable(size);
3373
3374         store = GTK_LIST_STORE(gtk_tree_view_get_model
3375                         (GTK_TREE_VIEW(compose->attach_clist)));
3376                 
3377         gtk_list_store_append(store, &iter);
3378         gtk_list_store_set(store, &iter, 
3379                            COL_MIMETYPE, ainfo->content_type,
3380                            COL_SIZE, size_text,
3381                            COL_NAME, ainfo->name,
3382                            COL_DATA, ainfo,
3383                            COL_AUTODATA, auto_ainfo,
3384                            -1);
3385         
3386         g_auto_pointer_free(auto_ainfo);
3387         compose_attach_update_label(compose);
3388         return TRUE;
3389 }
3390
3391 static void compose_use_signing(Compose *compose, gboolean use_signing)
3392 {
3393         GtkItemFactory *ifactory;
3394         GtkWidget *menuitem = NULL;
3395
3396         compose->use_signing = use_signing;
3397         ifactory = gtk_item_factory_from_widget(compose->menubar);
3398         menuitem = gtk_item_factory_get_item
3399                 (ifactory, "/Options/Sign");
3400         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3401                                        use_signing);
3402 }
3403
3404 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3405 {
3406         GtkItemFactory *ifactory;
3407         GtkWidget *menuitem = NULL;
3408
3409         compose->use_encryption = use_encryption;
3410         ifactory = gtk_item_factory_from_widget(compose->menubar);
3411         menuitem = gtk_item_factory_get_item
3412                 (ifactory, "/Options/Encrypt");
3413
3414         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), 
3415                                        use_encryption);
3416 }
3417
3418 #define NEXT_PART_NOT_CHILD(info)  \
3419 {  \
3420         node = info->node;  \
3421         while (node->children)  \
3422                 node = g_node_last_child(node);  \
3423         info = procmime_mimeinfo_next((MimeInfo *)node->data);  \
3424 }
3425
3426 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3427 {
3428         MimeInfo *mimeinfo;
3429         MimeInfo *child;
3430         MimeInfo *firsttext = NULL;
3431         MimeInfo *encrypted = NULL;
3432         GNode    *node;
3433         gchar *outfile;
3434         const gchar *partname = NULL;
3435
3436         mimeinfo = procmime_scan_message(msginfo);
3437         if (!mimeinfo) return;
3438
3439         if (mimeinfo->node->children == NULL) {
3440                 procmime_mimeinfo_free_all(mimeinfo);
3441                 return;
3442         }
3443
3444         /* find first content part */
3445         child = (MimeInfo *) mimeinfo->node->children->data;
3446         while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3447                 child = (MimeInfo *)child->node->children->data;
3448
3449         if (child->type == MIMETYPE_TEXT) {
3450                 firsttext = child;
3451                 debug_print("First text part found\n");
3452         } else if (compose->mode == COMPOSE_REEDIT &&
3453                  child->type == MIMETYPE_APPLICATION &&
3454                  !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3455                 encrypted = (MimeInfo *)child->node->parent->data;
3456         }
3457      
3458         child = (MimeInfo *) mimeinfo->node->children->data;
3459         while (child != NULL) {
3460                 gint err;
3461
3462                 if (child == encrypted) {
3463                         /* skip this part of tree */
3464                         NEXT_PART_NOT_CHILD(child);
3465                         continue;
3466                 }
3467
3468                 if (child->type == MIMETYPE_MULTIPART) {
3469                         /* get the actual content */
3470                         child = procmime_mimeinfo_next(child);
3471                         continue;
3472                 }
3473                     
3474                 if (child == firsttext) {
3475                         child = procmime_mimeinfo_next(child);
3476                         continue;
3477                 }
3478
3479                 outfile = procmime_get_tmp_file_name(child);
3480                 if ((err = procmime_get_part(outfile, child)) < 0)
3481                         g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3482                 else {
3483                         gchar *content_type;
3484
3485                         content_type = procmime_get_content_type_str(child->type, child->subtype);
3486
3487                         /* if we meet a pgp signature, we don't attach it, but
3488                          * we force signing. */
3489                         if ((strcmp(content_type, "application/pgp-signature") &&
3490                             strcmp(content_type, "application/pkcs7-signature") &&
3491                             strcmp(content_type, "application/x-pkcs7-signature"))
3492                             || compose->mode == COMPOSE_REDIRECT) {
3493                                 partname = procmime_mimeinfo_get_parameter(child, "filename");
3494                                 if (partname == NULL)
3495                                         partname = procmime_mimeinfo_get_parameter(child, "name");
3496                                 if (partname == NULL)
3497                                         partname = "";
3498                                 compose_attach_append(compose, outfile, 
3499                                                       partname, content_type);
3500                         } else {
3501                                 compose_force_signing(compose, compose->account);
3502                         }
3503                         g_free(content_type);
3504                 }
3505                 g_free(outfile);
3506                 NEXT_PART_NOT_CHILD(child);
3507         }
3508         procmime_mimeinfo_free_all(mimeinfo);
3509 }
3510
3511 #undef NEXT_PART_NOT_CHILD
3512
3513
3514
3515 typedef enum {
3516         WAIT_FOR_INDENT_CHAR,
3517         WAIT_FOR_INDENT_CHAR_OR_SPACE,
3518 } IndentState;
3519
3520 /* return indent length, we allow:
3521    indent characters followed by indent characters or spaces/tabs,
3522    alphabets and numbers immediately followed by indent characters,
3523    and the repeating sequences of the above
3524    If quote ends with multiple spaces, only the first one is included. */
3525 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3526                                     const GtkTextIter *start, gint *len)
3527 {
3528         GtkTextIter iter = *start;
3529         gunichar wc;
3530         gchar ch[6];
3531         gint clen;
3532         IndentState state = WAIT_FOR_INDENT_CHAR;
3533         gboolean is_space;
3534         gboolean is_indent;
3535         gint alnum_count = 0;
3536         gint space_count = 0;
3537         gint quote_len = 0;
3538
3539         if (prefs_common.quote_chars == NULL) {
3540                 return 0 ;
3541         }
3542
3543         while (!gtk_text_iter_ends_line(&iter)) {
3544                 wc = gtk_text_iter_get_char(&iter);
3545                 if (g_unichar_iswide(wc))
3546                         break;
3547                 clen = g_unichar_to_utf8(wc, ch);
3548                 if (clen != 1)
3549                         break;
3550
3551                 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3552                 is_space = g_unichar_isspace(wc);
3553
3554                 if (state == WAIT_FOR_INDENT_CHAR) {
3555                         if (!is_indent && !g_unichar_isalnum(wc))
3556                                 break;
3557                         if (is_indent) {
3558                                 quote_len += alnum_count + space_count + 1;
3559                                 alnum_count = space_count = 0;
3560                                 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3561                         } else
3562                                 alnum_count++;
3563                 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3564                         if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3565                                 break;
3566                         if (is_space)
3567                                 space_count++;
3568                         else if (is_indent) {
3569                                 quote_len += alnum_count + space_count + 1;
3570                                 alnum_count = space_count = 0;
3571                         } else {
3572                                 alnum_count++;
3573                                 state = WAIT_FOR_INDENT_CHAR;
3574                         }
3575                 }
3576
3577                 gtk_text_iter_forward_char(&iter);
3578         }
3579
3580         if (quote_len > 0 && space_count > 0)
3581                 quote_len++;
3582
3583         if (len)
3584                 *len = quote_len;
3585
3586         if (quote_len > 0) {
3587                 iter = *start;
3588                 gtk_text_iter_forward_chars(&iter, quote_len);
3589                 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3590         }
3591
3592         return NULL;
3593 }
3594
3595 /* return TRUE if the line is itemized */
3596 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3597                                     const GtkTextIter *start)
3598 {
3599         GtkTextIter iter = *start;
3600         gunichar wc;
3601         gchar ch[6];
3602         gint clen;
3603
3604         if (gtk_text_iter_ends_line(&iter))
3605                 return FALSE;
3606
3607         while (1) {
3608                 wc = gtk_text_iter_get_char(&iter);
3609                 if (!g_unichar_isspace(wc))
3610                         break;
3611                 gtk_text_iter_forward_char(&iter);
3612                 if (gtk_text_iter_ends_line(&iter))
3613                         return FALSE;
3614         }
3615
3616         clen = g_unichar_to_utf8(wc, ch);
3617         if (clen != 1)
3618                 return FALSE;
3619
3620         if (!strchr("*-+", ch[0]))
3621                 return FALSE;
3622
3623         gtk_text_iter_forward_char(&iter);
3624         if (gtk_text_iter_ends_line(&iter))
3625                 return FALSE;
3626         wc = gtk_text_iter_get_char(&iter);
3627         if (g_unichar_isspace(wc))
3628                 return TRUE;
3629
3630         return FALSE;
3631 }
3632
3633 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
3634                                            const GtkTextIter *start,
3635                                            GtkTextIter *break_pos,
3636                                            gint max_col,
3637                                            gint quote_len)
3638 {
3639         GtkTextIter iter = *start, line_end = *start;
3640         PangoLogAttr *attrs;
3641         gchar *str;
3642         gchar *p;
3643         gint len;
3644         gint i;
3645         gint col = 0;
3646         gint pos = 0;
3647         gboolean can_break = FALSE;
3648         gboolean do_break = FALSE;
3649         gboolean was_white = FALSE;
3650         gboolean prev_dont_break = FALSE;
3651
3652         gtk_text_iter_forward_to_line_end(&line_end);
3653         str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
3654         len = g_utf8_strlen(str, -1);
3655         
3656         if (len == 0) {
3657                 g_free(str);
3658                 g_warning("compose_get_line_break_pos: len = 0!\n");
3659                 return FALSE;
3660         }
3661
3662         /* g_print("breaking line: %d: %s (len = %d)\n",
3663                 gtk_text_iter_get_line(&iter), str, len); */
3664
3665         attrs = g_new(PangoLogAttr, len + 1);
3666
3667         pango_default_break(str, -1, NULL, attrs, len + 1);
3668
3669         p = str;
3670
3671         /* skip quote and leading spaces */
3672         for (i = 0; *p != '\0' && i < len; i++) {
3673                 gunichar wc;
3674
3675                 wc = g_utf8_get_char(p);
3676                 if (i >= quote_len && !g_unichar_isspace(wc))
3677                         break;
3678                 if (g_unichar_iswide(wc))
3679                         col += 2;
3680                 else if (*p == '\t')
3681                         col += 8;
3682                 else
3683                         col++;
3684                 p = g_utf8_next_char(p);
3685         }
3686
3687         for (; *p != '\0' && i < len; i++) {
3688                 PangoLogAttr *attr = attrs + i;
3689                 gunichar wc;
3690                 gint uri_len;
3691
3692                 if (attr->is_line_break && can_break && was_white && !prev_dont_break)
3693                         pos = i;
3694                 
3695                 was_white = attr->is_white;
3696
3697                 /* don't wrap URI */
3698                 if ((uri_len = get_uri_len(p)) > 0) {
3699                         col += uri_len;
3700                         if (pos > 0 && col > max_col) {
3701                                 do_break = TRUE;
3702                                 break;
3703                         }
3704                         i += uri_len - 1;
3705                         p += uri_len;
3706                         can_break = TRUE;
3707                         continue;
3708                 }
3709
3710                 wc = g_utf8_get_char(p);
3711                 if (g_unichar_iswide(wc)) {
3712                         col += 2;
3713                         if (prev_dont_break && can_break && attr->is_line_break)
3714                                 pos = i;
3715                 } else if (*p == '\t')
3716                         col += 8;
3717                 else
3718                         col++;
3719                 if (pos > 0 && col > max_col) {
3720                         do_break = TRUE;
3721                         break;
3722                 }
3723
3724                 if (*p == '-' || *p == '/')
3725                         prev_dont_break = TRUE;
3726                 else
3727                         prev_dont_break = FALSE;
3728
3729                 p = g_utf8_next_char(p);
3730                 can_break = TRUE;
3731         }
3732
3733         debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
3734
3735         g_free(attrs);
3736         g_free(str);
3737
3738         *break_pos = *start;
3739         gtk_text_iter_set_line_offset(break_pos, pos);
3740
3741         return do_break;
3742 }
3743
3744 static gboolean compose_join_next_line(Compose *compose,
3745                                        GtkTextBuffer *buffer,
3746                                        GtkTextIter *iter,
3747                                        const gchar *quote_str)
3748 {
3749         GtkTextIter iter_ = *iter, cur, prev, next, end;
3750         PangoLogAttr attrs[3];
3751         gchar *str;
3752         gchar *next_quote_str;
3753         gunichar wc1, wc2;
3754         gint quote_len;
3755         gboolean keep_cursor = FALSE;
3756
3757         if (!gtk_text_iter_forward_line(&iter_) ||
3758             gtk_text_iter_ends_line(&iter_))
3759                 return FALSE;
3760
3761         next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
3762
3763         if ((quote_str || next_quote_str) &&
3764             strcmp2(quote_str, next_quote_str) != 0) {
3765                 g_free(next_quote_str);
3766                 return FALSE;
3767         }
3768         g_free(next_quote_str);
3769
3770         end = iter_;
3771         if (quote_len > 0) {
3772                 gtk_text_iter_forward_chars(&end, quote_len);
3773                 if (gtk_text_iter_ends_line(&end))
3774                         return FALSE;
3775         }
3776
3777         /* don't join itemized lines */
3778         if (compose_is_itemized(buffer, &end))
3779                 return FALSE;
3780
3781         /* don't join signature separator */
3782         if (compose_is_sig_separator(compose, buffer, &iter_))
3783                 return FALSE;
3784
3785         /* delete quote str */
3786         if (quote_len > 0)
3787                 gtk_text_buffer_delete(buffer, &iter_, &end);
3788
3789         /* don't join line breaks put by the user */
3790         prev = cur = iter_;
3791         gtk_text_iter_backward_char(&cur);
3792         if (gtk_text_iter_has_tag(&cur, compose->no_join_tag)) {
3793                 gtk_text_iter_forward_char(&cur);
3794                 *iter = cur;
3795                 return FALSE;
3796         }
3797         gtk_text_iter_forward_char(&cur);
3798         /* delete linebreak and extra spaces */
3799         while (gtk_text_iter_backward_char(&cur)) {
3800                 wc1 = gtk_text_iter_get_char(&cur);
3801                 if (!g_unichar_isspace(wc1))
3802                         break;
3803                 prev = cur;
3804         }
3805         next = cur = iter_;
3806         while (!gtk_text_iter_ends_line(&cur)) {
3807                 wc1 = gtk_text_iter_get_char(&cur);
3808                 if (!g_unichar_isspace(wc1))
3809                         break;
3810                 gtk_text_iter_forward_char(&cur);
3811                 next = cur;
3812         }
3813         if (!gtk_text_iter_equal(&prev, &next)) {
3814                 GtkTextMark *mark;
3815
3816                 mark = gtk_text_buffer_get_insert(buffer);
3817                 gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
3818                 if (gtk_text_iter_equal(&prev, &cur))
3819                         keep_cursor = TRUE;
3820                 gtk_text_buffer_delete(buffer, &prev, &next);
3821         }
3822         iter_ = prev;
3823
3824         /* insert space if required */
3825         gtk_text_iter_backward_char(&prev);
3826         wc1 = gtk_text_iter_get_char(&prev);
3827         wc2 = gtk_text_iter_get_char(&next);
3828         gtk_text_iter_forward_char(&next);
3829         str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
3830         pango_default_break(str, -1, NULL, attrs, 3);
3831         if (!attrs[1].is_line_break ||
3832             (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
3833                 gtk_text_buffer_insert(buffer, &iter_, " ", 1);
3834                 if (keep_cursor) {
3835                         gtk_text_iter_backward_char(&iter_);
3836                         gtk_text_buffer_place_cursor(buffer, &iter_);
3837                 }
3838         }
3839         g_free(str);
3840
3841         *iter = iter_;
3842         return TRUE;
3843 }
3844
3845 #define ADD_TXT_POS(bp_, ep_, pti_) \
3846         if ((last->next = alloca(sizeof(struct txtpos))) != NULL) { \
3847                 last = last->next; \
3848                 last->bp = (bp_); last->ep = (ep_); last->pti = (pti_); \
3849                 last->next = NULL; \
3850         } else { \
3851                 g_warning("alloc error scanning URIs\n"); \
3852         }
3853
3854 static gboolean compose_beautify_paragraph(Compose *compose, GtkTextIter *par_iter, gboolean force)
3855 {
3856         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3857         GtkTextBuffer *buffer;
3858         GtkTextIter iter, break_pos, end_of_line;
3859         gchar *quote_str = NULL;
3860         gint quote_len;
3861         gboolean wrap_quote = prefs_common.linewrap_quote;
3862         gboolean prev_autowrap = compose->autowrap;
3863         gint startq_offset = -1, noq_offset = -1;
3864         gint uri_start = -1, uri_stop = -1;
3865         gint nouri_start = -1, nouri_stop = -1;
3866         gint num_blocks = 0;
3867         gint quotelevel = -1;
3868         gboolean modified = force;
3869         gboolean removed = FALSE;
3870         gboolean modified_before_remove = FALSE;
3871         gint lines = 0;
3872         gboolean start = TRUE;
3873
3874         if (force) {
3875                 modified = TRUE;
3876         }
3877         if (compose->draft_timeout_tag == -2) {
3878                 modified = TRUE;
3879         }
3880
3881         compose->autowrap = FALSE;
3882
3883         buffer = gtk_text_view_get_buffer(text);
3884         undo_wrapping(compose->undostruct, TRUE);
3885         if (par_iter) {
3886                 iter = *par_iter;
3887         } else {
3888                 GtkTextMark *mark;
3889                 mark = gtk_text_buffer_get_insert(buffer);
3890                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3891         }
3892
3893
3894         if (compose->draft_timeout_tag == -2) {
3895                 if (gtk_text_iter_ends_line(&iter)) {
3896                         while (gtk_text_iter_ends_line(&iter) &&
3897                                gtk_text_iter_forward_line(&iter))
3898                                 ;
3899                 } else {
3900                         while (gtk_text_iter_backward_line(&iter)) {
3901                                 if (gtk_text_iter_ends_line(&iter)) {
3902                                         gtk_text_iter_forward_line(&iter);
3903                                         break;
3904                                 }
3905                         }
3906                 }
3907         } else {
3908                 /* move to line start */
3909                 gtk_text_iter_set_line_offset(&iter, 0);
3910         }
3911         /* go until paragraph end (empty line) */
3912         while (start || !gtk_text_iter_ends_line(&iter)) {
3913                 gchar *scanpos = NULL;
3914                 /* parse table - in order of priority */
3915                 struct table {
3916                         const gchar *needle; /* token */
3917
3918                         /* token search function */
3919                         gchar    *(*search)     (const gchar *haystack,
3920                                                  const gchar *needle);
3921                         /* part parsing function */
3922                         gboolean  (*parse)      (const gchar *start,
3923                                                  const gchar *scanpos,
3924                                                  const gchar **bp_,
3925                                                  const gchar **ep_,
3926                                                  gboolean hdr);
3927                         /* part to URI function */
3928                         gchar    *(*build_uri)  (const gchar *bp,
3929                                                  const gchar *ep);
3930                 };
3931
3932                 static struct table parser[] = {
3933                         {"http://",  strcasestr, get_uri_part,   make_uri_string},
3934                         {"https://", strcasestr, get_uri_part,   make_uri_string},
3935                         {"ftp://",   strcasestr, get_uri_part,   make_uri_string},
3936                         {"sftp://",  strcasestr, get_uri_part,   make_uri_string},
3937                         {"www.",     strcasestr, get_uri_part,   make_http_string},
3938                         {"mailto:",  strcasestr, get_uri_part,   make_uri_string},
3939                         {"@",        strcasestr, get_email_part, make_email_string}
3940                 };
3941                 const gint PARSE_ELEMS = sizeof parser / sizeof parser[0];
3942                 gint last_index = PARSE_ELEMS;
3943                 gint  n;
3944                 gchar *o_walk = NULL, *walk = NULL, *bp = NULL, *ep = NULL;
3945                 gint walk_pos;
3946                 
3947                 start = FALSE;
3948                 if (!prev_autowrap && num_blocks == 0) {
3949                         num_blocks++;
3950                         g_signal_handlers_block_by_func(G_OBJECT(buffer),
3951                                         G_CALLBACK(text_inserted),
3952                                         compose);
3953                 }
3954                 if (gtk_text_iter_has_tag(&iter, compose->no_wrap_tag) && !force)
3955                         goto colorize;
3956
3957                 uri_start = uri_stop = -1;
3958                 quote_len = 0;
3959                 quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
3960
3961                 if (quote_str) {
3962                         debug_print("compose_beautify_paragraph(): quote_str = '%s'\n", quote_str);
3963                         if (startq_offset == -1) 
3964                                 startq_offset = gtk_text_iter_get_offset(&iter);
3965                         quotelevel = get_quote_level(quote_str, prefs_common.quote_chars);
3966                         if (quotelevel > 2) {
3967                                 /* recycle colors */
3968                                 if (prefs_common.recycle_quote_colors)
3969                                         quotelevel %= 3;
3970                                 else
3971                                         quotelevel = 2;
3972                         }
3973                         if (!wrap_quote) {
3974                                 goto colorize;
3975                         }
3976                 } else {
3977                         if (startq_offset == -1)
3978                                 noq_offset = gtk_text_iter_get_offset(&iter);
3979                         quotelevel = -1;
3980                 }
3981
3982                 if (prev_autowrap == FALSE && !force && !wrap_quote) {
3983                         goto colorize;
3984                 }
3985                 if (gtk_text_iter_ends_line(&iter)) {
3986                         goto colorize;
3987                 } else if (compose_get_line_break_pos(buffer, &iter, &break_pos,
3988                                                prefs_common.linewrap_len,
3989                                                quote_len)) {
3990                         GtkTextIter prev, next, cur;
3991
3992                         if (prev_autowrap != FALSE || force) {
3993                                 compose->automatic_break = TRUE;
3994                                 modified = TRUE;
3995                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
3996                                 compose->automatic_break = FALSE;
3997                         } else if (quote_str && wrap_quote) {
3998                                 compose->automatic_break = TRUE;
3999                                 modified = TRUE;
4000                                 gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
4001                                 compose->automatic_break = FALSE;
4002                         } else 
4003                                 goto colorize;
4004                         /* remove trailing spaces */
4005                         cur = break_pos;
4006                         gtk_text_iter_backward_char(&cur);
4007                         prev = next = cur;
4008                         while (!gtk_text_iter_starts_line(&cur)) {
4009                                 gunichar wc;
4010
4011                                 gtk_text_iter_backward_char(&cur);
4012                                 wc = gtk_text_iter_get_char(&cur);
4013                                 if (!g_unichar_isspace(wc))
4014                                         break;
4015                                 prev = cur;
4016                         }
4017                         if (!gtk_text_iter_equal(&prev, &next)) {
4018                                 gtk_text_buffer_delete(buffer, &prev, &next);
4019                                 break_pos = next;
4020                                 gtk_text_iter_forward_char(&break_pos);
4021                         }
4022
4023                         if (quote_str)
4024                                 gtk_text_buffer_insert(buffer, &break_pos,
4025                                                        quote_str, -1);
4026
4027                         iter = break_pos;
4028                         modified |= compose_join_next_line(compose, buffer, &iter, quote_str);
4029
4030                         /* move iter to current line start */
4031                         gtk_text_iter_set_line_offset(&iter, 0);
4032                         if (quote_str) {
4033                                 g_free(quote_str);
4034                                 quote_str = NULL;
4035                         }
4036                         continue;       
4037                 } else {
4038                         /* move iter to next line start */
4039                         iter = break_pos;
4040                         lines++;
4041                 }
4042
4043 colorize:
4044                 if (!prev_autowrap && num_blocks > 0) {
4045                         num_blocks--;
4046                         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
4047                                         G_CALLBACK(text_inserted),
4048                                         compose);
4049                 }
4050                 end_of_line = iter;
4051                 while (!gtk_text_iter_ends_line(&end_of_line)) {
4052                         gtk_text_iter_forward_char(&end_of_line);
4053                 }
4054                 o_walk = walk = gtk_text_buffer_get_text(buffer, &iter, &end_of_line, FALSE);
4055
4056                 nouri_start = gtk_text_iter_get_offset(&iter);
4057                 nouri_stop = gtk_text_iter_get_offset(&end_of_line);
4058
4059                 walk_pos = gtk_text_iter_get_offset(&iter);
4060                 /* FIXME: this looks phony. scanning for anything in the parse table */
4061                 for (n = 0; n < PARSE_ELEMS; n++) {
4062                         gchar *tmp;
4063
4064                         tmp = parser[n].search(walk, parser[n].needle);
4065                         if (tmp) {
4066                                 if (scanpos == NULL || tmp < scanpos) {
4067                                         scanpos = tmp;
4068                                         last_index = n;
4069                                 }
4070                         }                                       
4071                 }
4072
4073                 bp = ep = 0;
4074                 if (scanpos) {
4075                         /* check if URI can be parsed */
4076                         if (parser[last_index].parse(walk, scanpos, (const gchar **)&bp,
4077                                         (const gchar **)&ep, FALSE)
4078                             && (size_t) (ep - bp - 1) > strlen(parser[last_index].needle)) {
4079                                         walk = ep;
4080                         } else
4081                                 walk = scanpos +
4082                                         strlen(parser[last_index].needle);
4083                 } 
4084                 if (bp && ep) {
4085                         uri_start = walk_pos + (bp - o_walk);
4086                         uri_stop  = walk_pos + (ep - o_walk);
4087                 }
4088                 g_free(o_walk);
4089                 o_walk = NULL;
4090                 gtk_text_iter_forward_line(&iter);
4091                 g_free(quote_str);
4092                 quote_str = NULL;
4093                 if (startq_offset != -1) {
4094                         GtkTextIter startquote, endquote;
4095                         gtk_text_buffer_get_iter_at_offset(
4096                                 buffer, &startquote, startq_offset);
4097                         endquote = iter;
4098
4099                         switch (quotelevel) {
4100                         case 0: 
4101                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote0_tag) ||
4102                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) {
4103                                         gtk_text_buffer_apply_tag_by_name(
4104                                                 buffer, "quote0", &startquote, &endquote);
4105                                         gtk_text_buffer_remove_tag_by_name(
4106                                                 buffer, "quote1", &startquote, &endquote);
4107                                         gtk_text_buffer_remove_tag_by_name(
4108                                                 buffer, "quote2", &startquote, &endquote);
4109                                         modified = TRUE;
4110                                 }
4111                                 break;
4112                         case 1: 
4113                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote1_tag) ||
4114                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) {
4115                                         gtk_text_buffer_apply_tag_by_name(
4116                                                 buffer, "quote1", &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, "quote2", &startquote, &endquote);
4121                                         modified = TRUE;
4122                                 }
4123                                 break;
4124                         case 2: 
4125                                 if (!gtk_text_iter_has_tag(&startquote, compose->quote2_tag) ||
4126                                     !gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag)) {
4127                                         gtk_text_buffer_apply_tag_by_name(
4128                                                 buffer, "quote2", &startquote, &endquote);
4129                                         gtk_text_buffer_remove_tag_by_name(
4130                                                 buffer, "quote0", &startquote, &endquote);
4131                                         gtk_text_buffer_remove_tag_by_name(
4132                                                 buffer, "quote1", &startquote, &endquote);
4133                                         modified = TRUE;
4134                                 }
4135                                 break;
4136                         }
4137                         startq_offset = -1;
4138                 } else if (noq_offset != -1) {
4139                         GtkTextIter startnoquote, endnoquote;
4140                         gtk_text_buffer_get_iter_at_offset(
4141                                 buffer, &startnoquote, noq_offset);
4142                         endnoquote = iter;
4143
4144                         if ((gtk_text_iter_has_tag(&startnoquote, compose->quote0_tag)
4145                           && gtk_text_iter_has_tag(&end_of_line, compose->quote0_tag)) ||
4146                             (gtk_text_iter_has_tag(&startnoquote, compose->quote1_tag)
4147                           && gtk_text_iter_has_tag(&end_of_line, compose->quote1_tag)) ||
4148                             (gtk_text_iter_has_tag(&startnoquote, compose->quote2_tag)
4149                           && gtk_text_iter_has_tag(&end_of_line, compose->quote2_tag))) {
4150                                 gtk_text_buffer_remove_tag_by_name(
4151                                         buffer, "quote0", &startnoquote, &endnoquote);
4152                                 gtk_text_buffer_remove_tag_by_name(
4153                                         buffer, "quote1", &startnoquote, &endnoquote);
4154                                 gtk_text_buffer_remove_tag_by_name(
4155                                         buffer, "quote2", &startnoquote, &endnoquote);
4156                                 modified = TRUE;
4157                         }
4158                         noq_offset = -1;
4159                 }
4160                 
4161                 if (uri_start != nouri_start && uri_stop != nouri_stop) {
4162                         GtkTextIter nouri_start_iter, nouri_end_iter;
4163                         gtk_text_buffer_get_iter_at_offset(
4164                                 buffer, &nouri_start_iter, nouri_start);
4165                         gtk_text_buffer_get_iter_at_offset(
4166                                 buffer, &nouri_end_iter, nouri_stop);
4167                         if (gtk_text_iter_has_tag(&nouri_start_iter, compose->uri_tag) &&
4168                             gtk_text_iter_has_tag(&nouri_end_iter, compose->uri_tag)) {
4169                                 gtk_text_buffer_remove_tag_by_name(
4170                                         buffer, "link", &nouri_start_iter, &nouri_end_iter);
4171                                 modified_before_remove = modified;
4172                                 modified = TRUE;
4173                                 removed = TRUE;
4174                         }
4175                 }
4176                 if (uri_start > 0 && uri_stop > 0) {
4177                         GtkTextIter uri_start_iter, uri_end_iter, back;
4178                         gtk_text_buffer_get_iter_at_offset(
4179                                 buffer, &uri_start_iter, uri_start);
4180                         gtk_text_buffer_get_iter_at_offset(
4181                                 buffer, &uri_end_iter, uri_stop);
4182                         back = uri_end_iter;
4183                         gtk_text_iter_backward_char(&back);
4184                         if (!gtk_text_iter_has_tag(&uri_start_iter, compose->uri_tag) ||
4185                             !gtk_text_iter_has_tag(&back, compose->uri_tag)) {
4186                                 gtk_text_buffer_apply_tag_by_name(
4187                                         buffer, "link", &uri_start_iter, &uri_end_iter);
4188                                 modified = TRUE;
4189                                 if (removed && !modified_before_remove) {
4190                                         modified = FALSE;
4191                                 } 
4192                         }
4193                 }
4194                 if (!modified) {
4195                         debug_print("not modified, out after %d lines\n", lines);
4196                         goto end;
4197                 }
4198         }
4199
4200         debug_print("modified, out after %d lines\n", lines);
4201 end:
4202         if (par_iter)
4203                 *par_iter = iter;
4204         undo_wrapping(compose->undostruct, FALSE);
4205         compose->autowrap = prev_autowrap;
4206         
4207         return modified;
4208 }
4209
4210 void compose_action_cb(void *data)
4211 {
4212         Compose *compose = (Compose *)data;
4213         compose_wrap_all(compose);
4214 }
4215
4216 static void compose_wrap_all(Compose *compose)
4217 {
4218         compose_wrap_all_full(compose, FALSE);
4219 }
4220
4221 static void compose_wrap_all_full(Compose *compose, gboolean force)
4222 {
4223         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
4224         GtkTextBuffer *buffer;
4225         GtkTextIter iter;
4226         gboolean modified = TRUE;
4227
4228         buffer = gtk_text_view_get_buffer(text);
4229
4230         gtk_text_buffer_get_start_iter(buffer, &iter);
4231         while (!gtk_text_iter_is_end(&iter) && modified)
4232                 modified = compose_beautify_paragraph(compose, &iter, force);
4233
4234 }
4235
4236 static void compose_set_title(Compose *compose)
4237 {
4238         gchar *str;
4239         gchar *edited;
4240         gchar *subject;
4241         
4242         edited = compose->modified ? _(" [Edited]") : "";
4243         
4244         subject = gtk_editable_get_chars(
4245                         GTK_EDITABLE(compose->subject_entry), 0, -1);
4246
4247 #ifndef MAEMO
4248         if (subject && strlen(subject))
4249                 str = g_strdup_printf(_("%s - Compose message%s"),
4250                                       subject, edited); 
4251         else
4252                 str = g_strdup_printf(_("[no subject] - Compose message%s"), edited);
4253 #else
4254         str = g_strdup(_("Compose message"));
4255 #endif
4256
4257         gtk_window_set_title(GTK_WINDOW(compose->window), str);
4258         g_free(str);
4259         g_free(subject);
4260 }
4261
4262 /**
4263  * compose_current_mail_account:
4264  * 
4265  * Find a current mail account (the currently selected account, or the
4266  * default account, if a news account is currently selected).  If a
4267  * mail account cannot be found, display an error message.
4268  * 
4269  * Return value: Mail account, or NULL if not found.
4270  **/
4271 static PrefsAccount *
4272 compose_current_mail_account(void)
4273 {
4274         PrefsAccount *ac;
4275
4276         if (cur_account && cur_account->protocol != A_NNTP)
4277                 ac = cur_account;
4278         else {
4279                 ac = account_get_default();
4280                 if (!ac || ac->protocol == A_NNTP) {
4281                         alertpanel_error(_("Account for sending mail is not specified.\n"
4282                                            "Please select a mail account before sending."));
4283                         return NULL;
4284                 }
4285         }
4286         return ac;
4287 }
4288
4289 #define QUOTE_IF_REQUIRED(out, str)                                     \
4290 {                                                                       \
4291         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4292                 gchar *__tmp;                                           \
4293                 gint len;                                               \
4294                                                                         \
4295                 len = strlen(str) + 3;                                  \
4296                 if ((__tmp = alloca(len)) == NULL) {                    \
4297                         g_warning("can't allocate memory\n");           \
4298                         g_string_free(header, TRUE);                    \
4299                         return NULL;                                    \
4300                 }                                                       \
4301                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4302                 out = __tmp;                                            \
4303         } else {                                                        \
4304                 gchar *__tmp;                                           \
4305                                                                         \
4306                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4307                         g_warning("can't allocate memory\n");           \
4308                         g_string_free(header, TRUE);                    \
4309                         return NULL;                                    \
4310                 } else                                                  \
4311                         strcpy(__tmp, str);                             \
4312                                                                         \
4313                 out = __tmp;                                            \
4314         }                                                               \
4315 }
4316
4317 #define QUOTE_IF_REQUIRED_NORMAL(out, str, errret)                      \
4318 {                                                                       \
4319         if (*str != '"' && strpbrk(str, ",.[]<>")) {                    \
4320                 gchar *__tmp;                                           \
4321                 gint len;                                               \
4322                                                                         \
4323                 len = strlen(str) + 3;                                  \
4324                 if ((__tmp = alloca(len)) == NULL) {                    \
4325                         g_warning("can't allocate memory\n");           \
4326                         errret;                                         \
4327                 }                                                       \
4328                 g_snprintf(__tmp, len, "\"%s\"", str);                  \
4329                 out = __tmp;                                            \
4330         } else {                                                        \
4331                 gchar *__tmp;                                           \
4332                                                                         \
4333                 if ((__tmp = alloca(strlen(str) + 1)) == NULL) {        \
4334                         g_warning("can't allocate memory\n");           \
4335                         errret;                                         \
4336                 } else                                                  \
4337                         strcpy(__tmp, str);                             \
4338                                                                         \
4339                 out = __tmp;                                            \
4340         }                                                               \
4341 }
4342
4343 static void compose_select_account(Compose *compose, PrefsAccount *account,
4344                                    gboolean init)
4345 {
4346         GtkItemFactory *ifactory;
4347         gchar *from = NULL;
4348
4349         g_return_if_fail(account != NULL);
4350
4351         compose->account = account;
4352
4353         if (account->name && *account->name) {
4354                 gchar *buf;
4355                 QUOTE_IF_REQUIRED_NORMAL(buf, account->name, return);
4356                 from = g_strdup_printf("%s <%s>",
4357                                        buf, account->address);
4358                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4359         } else {
4360                 from = g_strdup_printf("<%s>",
4361                                        account->address);
4362                 gtk_entry_set_text(GTK_ENTRY(compose->from_name), from);
4363         }
4364
4365         g_free(from);
4366
4367         compose_set_title(compose);
4368
4369         ifactory = gtk_item_factory_from_widget(compose->menubar);
4370
4371         if (account->default_sign && compose->mode != COMPOSE_REDIRECT)
4372                 menu_set_active(ifactory, "/Options/Sign", TRUE);
4373         else
4374                 menu_set_active(ifactory, "/Options/Sign", FALSE);
4375         if (account->default_encrypt && compose->mode != COMPOSE_REDIRECT)
4376                 menu_set_active(ifactory, "/Options/Encrypt", TRUE);
4377         else
4378                 menu_set_active(ifactory, "/Options/Encrypt", FALSE);
4379                                        
4380         activate_privacy_system(compose, account, FALSE);
4381
4382         if (!init && compose->mode != COMPOSE_REDIRECT) {
4383                 undo_block(compose->undostruct);
4384                 compose_insert_sig(compose, TRUE);
4385                 undo_unblock(compose->undostruct);
4386         }
4387
4388 #ifdef USE_ASPELL
4389         /* use account's dict info if set */
4390         if (compose->gtkaspell) {
4391                 if (account->enable_default_dictionary)
4392                         gtkaspell_change_dict(compose->gtkaspell,
4393                                         account->default_dictionary, FALSE);
4394                 if (account->enable_default_alt_dictionary)
4395                         gtkaspell_change_alt_dict(compose->gtkaspell,
4396                                         account->default_alt_dictionary);
4397                 if (account->enable_default_dictionary
4398                         || account->enable_default_alt_dictionary)
4399                         compose_spell_menu_changed(compose);
4400         }
4401 #endif
4402 }
4403
4404 gboolean compose_check_for_valid_recipient(Compose *compose) {
4405         gchar *recipient_headers_mail[] = {"To:", "Cc:", "Bcc:", NULL};
4406         gchar *recipient_headers_news[] = {"Newsgroups:", NULL};
4407         gboolean recipient_found = FALSE;
4408         GSList *list;
4409         gchar **strptr;
4410
4411         /* free to and newsgroup list */
4412         slist_free_strings(compose->to_list);
4413         g_slist_free(compose->to_list);
4414         compose->to_list = NULL;
4415                         
4416         slist_free_strings(compose->newsgroup_list);
4417         g_slist_free(compose->newsgroup_list);
4418         compose->newsgroup_list = NULL;
4419
4420         /* search header entries for to and newsgroup entries */
4421         for (list = compose->header_list; list; list = list->next) {
4422                 gchar *header;
4423                 gchar *entry;
4424                 header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4425                 entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4426
4427                 g_strstrip(entry);
4428                 if (entry[0] != '\0') {
4429                         for (strptr = recipient_headers_mail; *strptr != NULL; strptr++) {
4430                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4431                                         compose->to_list = address_list_append(compose->to_list, entry);
4432                                         recipient_found = TRUE;
4433                                 }
4434                         }
4435                         for (strptr = recipient_headers_news; *strptr != NULL; strptr++) {
4436                                 if (!strcmp(header, prefs_common_translated_header_name(*strptr))) {
4437                                         compose->newsgroup_list = newsgroup_list_append(compose->newsgroup_list, entry);
4438                                         recipient_found = TRUE;
4439                                 }
4440                         }
4441                 }
4442                 g_free(header);
4443                 g_free(entry);
4444         }
4445         return recipient_found;
4446 }
4447
4448 static gboolean compose_check_for_set_recipients(Compose *compose)
4449 {
4450         if (compose->account->set_autocc && compose->account->auto_cc) {
4451                 gboolean found_other = FALSE;
4452                 GSList *list;
4453                 /* search header entries for to and newsgroup entries */
4454                 for (list = compose->header_list; list; list = list->next) {
4455                         gchar *entry;
4456                         gchar *header;
4457                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4458                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4459                         g_strstrip(entry);
4460                         if (strcmp(entry, compose->account->auto_cc)
4461                         ||  strcmp(header, prefs_common_translated_header_name("Cc:"))) {
4462                                 found_other = TRUE;
4463                                 g_free(entry);
4464                                 break;
4465                         }
4466                         g_free(entry);
4467                         g_free(header);
4468                 }
4469                 if (!found_other) {
4470                         AlertValue aval;
4471                         if (compose->batch) {
4472                                 gtk_widget_show_all(compose->window);
4473                         }
4474                         aval = alertpanel(_("Send"),
4475                                           _("The only recipient is the default CC address. Send anyway?"),
4476                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4477                         if (aval != G_ALERTALTERNATE)
4478                                 return FALSE;
4479                 }
4480         }
4481         if (compose->account->set_autobcc && compose->account->auto_bcc) {
4482                 gboolean found_other = FALSE;
4483                 GSList *list;
4484                 /* search header entries for to and newsgroup entries */
4485                 for (list = compose->header_list; list; list = list->next) {
4486                         gchar *entry;
4487                         gchar *header;
4488                         entry = gtk_editable_get_chars(GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
4489                         header = gtk_editable_get_chars(GTK_EDITABLE(GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
4490                         g_strstrip(entry);
4491                         if (strcmp(entry, compose->account->auto_bcc)
4492                         ||  strcmp(header, prefs_common_translated_header_name("Bcc:"))) {
4493                                 found_other = TRUE;
4494                                 g_free(entry);
4495                                 break;
4496                         }
4497                         g_free(entry);
4498                         g_free(header);
4499                 }
4500                 if (!found_other) {
4501                         AlertValue aval;
4502                         if (compose->batch) {
4503                                 gtk_widget_show_all(compose->window);
4504                         }
4505                         aval = alertpanel(_("Send"),
4506                                           _("The only recipient is the default BCC address. Send anyway?"),
4507                                           GTK_STOCK_CANCEL, _("+_Send"), NULL);
4508                         if (aval != G_ALERTALTERNATE)
4509                                 return FALSE;
4510                 }
4511         }
4512         return TRUE;
4513 }
4514
4515 static gboolean compose_check_entries(Compose *compose, gboolean check_everything)
4516 {
4517         const gchar *str;
4518
4519         if (compose_check_for_valid_recipient(compose) == FALSE) {
4520                 if (compose->batch) {
4521                         gtk_widget_show_all(compose->window);
4522                 }
4523                 alertpanel_error(_("Recipient is not specified."));
4524                 return FALSE;
4525         }
4526
4527         if (compose_check_for_set_recipients(compose) == FALSE) {
4528                 return FALSE;
4529         }
4530
4531         if (!compose->batch) {
4532                 str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4533                 if (*str == '\0' && check_everything == TRUE && 
4534                     compose->mode != COMPOSE_REDIRECT) {
4535                         AlertValue aval;
4536                         gchar *button_label;
4537                         gchar *message;
4538
4539                         if (compose->sending)
4540                                 button_label = _("+_Send");
4541                         else
4542                                 button_label = _("+_Queue");
4543                         message = g_strdup_printf(_("Subject is empty. %s"),
4544                                         compose->sending?_("Send it anyway?"):
4545                                         _("Queue it anyway?"));
4546
4547                         aval = alertpanel(compose->sending?_("Send"):_("Send later"), message,
4548                                           GTK_STOCK_CANCEL, button_label, NULL);
4549                         g_free(message);
4550                         if (aval != G_ALERTALTERNATE)
4551                                 return FALSE;
4552                 }
4553         }
4554
4555         if (check_everything && hooks_invoke(COMPOSE_CHECK_BEFORE_SEND_HOOKLIST, compose))
4556                 return FALSE;
4557
4558         return TRUE;
4559 }
4560
4561 gint compose_send(Compose *compose)
4562 {
4563         gint msgnum;
4564         FolderItem *folder = NULL;
4565         gint val = -1;
4566         gchar *msgpath = NULL;
4567         gboolean discard_window = FALSE;
4568         gchar *errstr = NULL;
4569         gchar *tmsgid = NULL;
4570         MainWindow *mainwin = mainwindow_get_mainwindow();
4571         gboolean queued_removed = FALSE;
4572
4573         if (prefs_common.send_dialog_invisible
4574                         || compose->batch == TRUE)
4575                 discard_window = TRUE;
4576
4577         compose_allow_user_actions (compose, FALSE);
4578         compose->sending = TRUE;
4579
4580         if (compose_check_entries(compose, TRUE) == FALSE) {
4581                 if (compose->batch) {
4582                         gtk_widget_show_all(compose->window);
4583                 }
4584                 goto bail;
4585         }
4586
4587         inc_lock();
4588         val = compose_queue(compose, &msgnum, &folder, &msgpath, TRUE);
4589
4590         if (val) {
4591                 if (compose->batch) {
4592                         gtk_widget_show_all(compose->window);
4593                 }
4594                 if (val == -4) {
4595                         alertpanel_error(_("Could not queue message for sending:\n\n"
4596                                            "Charset conversion failed."));
4597                 } else if (val == -5) {
4598                         alertpanel_error(_("Could not queue message for sending:\n\n"
4599                                            "Couldn't get recipient encryption key."));
4600                 } else if (val == -6) {
4601                         /* silent error */
4602                 } else if (val == -3) {
4603                         if (privacy_peek_error())
4604                         alertpanel_error(_("Could not queue message for sending:\n\n"
4605                                            "Signature failed: %s"), privacy_get_error());
4606                 } else if (val == -2 && errno != 0) {
4607                         alertpanel_error(_("Could not queue message for sending:\n\n%s."), strerror(errno));
4608                 } else {
4609                         alertpanel_error(_("Could not queue message for sending."));
4610                 }
4611                 goto bail;
4612         }
4613
4614         tmsgid = compose->msgid ? g_strdup(compose->msgid) : NULL;
4615         if (discard_window) {
4616                 compose->sending = FALSE;
4617                 compose_close(compose);
4618                 /* No more compose access in the normal codepath 
4619                  * after this point! */
4620                 compose = NULL;
4621         }
4622
4623         if (msgnum == 0) {
4624                 alertpanel_error(_("The message was queued but could not be "
4625                                    "sent.\nUse \"Send queued messages\" from "
4626                                    "the main window to retry."));
4627                 if (!discard_window) {
4628                         goto bail;
4629                 }
4630                 inc_unlock();
4631                 g_free(tmsgid);
4632                 return -1;
4633         }
4634         if (msgpath == NULL) {
4635                 msgpath = folder_item_fetch_msg(folder, msgnum);
4636                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4637                 g_free(msgpath);
4638         } else {
4639                 val = procmsg_send_message_queue(msgpath, &errstr, folder, msgnum, &queued_removed);
4640                 g_unlink(msgpath);
4641                 g_free(msgpath);
4642         }
4643         if (!discard_window) {
4644                 if (val != 0) {
4645                         if (!queued_removed)
4646                                 folder_item_remove_msg(folder, msgnum);
4647                         folder_item_scan(folder);
4648                         if (tmsgid) {
4649                                 /* make sure we delete that */
4650                                 MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4651                                 if (tmp) {
4652                                         debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4653                                         folder_item_remove_msg(folder, tmp->msgnum);
4654                                         procmsg_msginfo_free(tmp);
4655                                 } 
4656                         }
4657                 }
4658         }
4659
4660         if (val == 0) {
4661                 if (!queued_removed)
4662                         folder_item_remove_msg(folder, msgnum);
4663                 folder_item_scan(folder);
4664                 if (tmsgid) {
4665                         /* make sure we delete that */
4666                         MsgInfo *tmp = folder_item_get_msginfo_by_msgid(folder, tmsgid);
4667                         if (tmp) {
4668                                 debug_print("removing %d via %s\n", tmp->msgnum, tmsgid);
4669                                 folder_item_remove_msg(folder, tmp->msgnum);
4670                                 procmsg_msginfo_free(tmp);
4671                         }
4672                 }
4673                 if (!discard_window) {
4674                         compose->sending = FALSE;
4675                         compose_allow_user_actions (compose, TRUE);
4676                         compose_close(compose);
4677                 }
4678         } else {
4679                 if (errstr) {
4680                         alertpanel_error_log(_("%s\nUse \"Send queued messages\" from "
4681                                    "the main window to retry."), errstr);
4682                         g_free(errstr);
4683                 } else {
4684                         alertpanel_error_log(_("The message was queued but could not be "
4685                                    "sent.\nUse \"Send queued messages\" from "
4686                                    "the main window to retry."));
4687                 }
4688                 if (!discard_window) {
4689                         goto bail;              
4690                 }
4691                 inc_unlock();
4692                 g_free(tmsgid);
4693                 return -1;
4694         }
4695         g_free(tmsgid);
4696         inc_unlock();
4697         toolbar_main_set_sensitive(mainwin);
4698         main_window_set_menu_sensitive(mainwin);
4699         return 0;
4700
4701 bail:
4702         inc_unlock();
4703         g_free(tmsgid);
4704         compose_allow_user_actions (compose, TRUE);
4705         compose->sending = FALSE;
4706         compose->modified = TRUE; 
4707         toolbar_main_set_sensitive(mainwin);
4708         main_window_set_menu_sensitive(mainwin);
4709
4710         return -1;
4711 }
4712
4713 static gboolean compose_use_attach(Compose *compose) 
4714 {
4715         GtkTreeModel *model = gtk_tree_view_get_model
4716                                 (GTK_TREE_VIEW(compose->attach_clist));
4717         return gtk_tree_model_iter_n_children(model, NULL) > 0;
4718 }
4719
4720 static gint compose_redirect_write_headers_from_headerlist(Compose *compose, 
4721                                                            FILE *fp)
4722 {
4723         gchar buf[BUFFSIZE];
4724         gchar *str;
4725         gboolean first_to_address;
4726         gboolean first_cc_address;
4727         GSList *list;
4728         ComposeHeaderEntry *headerentry;
4729         const gchar *headerentryname;
4730         const gchar *cc_hdr;
4731         const gchar *to_hdr;
4732         gboolean err = FALSE;
4733
4734         debug_print("Writing redirect header\n");
4735
4736         cc_hdr = prefs_common_translated_header_name("Cc:");
4737         to_hdr = prefs_common_translated_header_name("To:");
4738
4739         first_to_address = TRUE;
4740         for (list = compose->header_list; list; list = list->next) {
4741                 headerentry = ((ComposeHeaderEntry *)list->data);
4742                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4743
4744                 if (g_utf8_collate(headerentryname, to_hdr) == 0) {
4745                         const gchar *entstr = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4746                         Xstrdup_a(str, entstr, return -1);
4747                         g_strstrip(str);
4748                         if (str[0] != '\0') {
4749                                 compose_convert_header
4750                                         (compose, buf, sizeof(buf), str,
4751                                         strlen("Resent-To") + 2, TRUE);
4752
4753                                 if (first_to_address) {
4754                                         err |= (fprintf(fp, "Resent-To: ") < 0);
4755                                         first_to_address = FALSE;
4756                                 } else {
4757                                         err |= (fprintf(fp, ",") < 0);
4758                                 }
4759                                 err |= (fprintf(fp, "%s", buf) < 0);
4760                         }
4761                 }
4762         }
4763         if (!first_to_address) {
4764                 err |= (fprintf(fp, "\n") < 0);
4765         }
4766
4767         first_cc_address = TRUE;
4768         for (list = compose->header_list; list; list = list->next) {
4769                 headerentry = ((ComposeHeaderEntry *)list->data);
4770                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
4771
4772                 if (g_utf8_collate(headerentryname, cc_hdr) == 0) {
4773                         const gchar *strg = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
4774                         Xstrdup_a(str, strg, return -1);
4775                         g_strstrip(str);
4776                         if (str[0] != '\0') {
4777                                 compose_convert_header
4778                                         (compose, buf, sizeof(buf), str,
4779                                         strlen("Resent-Cc") + 2, TRUE);
4780
4781                                 if (first_cc_address) {
4782                                         err |= (fprintf(fp, "Resent-Cc: ") < 0);
4783                                         first_cc_address = FALSE;
4784                                 } else {
4785                                         err |= (fprintf(fp, ",") < 0);
4786                                 }
4787                                 err |= (fprintf(fp, "%s", buf) < 0);
4788                         }
4789                 }
4790         }
4791         if (!first_cc_address) {
4792                 err |= (fprintf(fp, "\n") < 0);
4793         }
4794         
4795         return (err ? -1:0);
4796 }
4797
4798 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
4799 {
4800         gchar buf[BUFFSIZE];
4801         gchar *str;
4802         const gchar *entstr;
4803         /* struct utsname utsbuf; */
4804         gboolean err = FALSE;
4805
4806         g_return_val_if_fail(fp != NULL, -1);
4807         g_return_val_if_fail(compose->account != NULL, -1);
4808         g_return_val_if_fail(compose->account->address != NULL, -1);
4809
4810         /* Resent-Date */
4811         get_rfc822_date(buf, sizeof(buf));
4812         err |= (fprintf(fp, "Resent-Date: %s\n", buf) < 0);
4813
4814         /* Resent-From */
4815         if (compose->account->name && *compose->account->name) {
4816                 compose_convert_header
4817                         (compose, buf, sizeof(buf), compose->account->name,
4818                          strlen("From: "), TRUE);
4819                 err |= (fprintf(fp, "Resent-From: %s <%s>\n",
4820                         buf, compose->account->address) < 0);
4821         } else
4822                 err |= (fprintf(fp, "Resent-From: %s\n", compose->account->address) < 0);
4823
4824         /* Subject */
4825         entstr = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4826         if (*entstr != '\0') {
4827                 Xstrdup_a(str, entstr, return -1);
4828                 g_strstrip(str);
4829                 if (*str != '\0') {
4830                         compose_convert_header(compose, buf, sizeof(buf), str,
4831                                                strlen("Subject: "), FALSE);
4832                         err |= (fprintf(fp, "Subject: %s\n", buf) < 0);
4833                 }
4834         }
4835
4836         /* Resent-Message-ID */
4837         if (compose->account->set_domain && compose->account->domain) {
4838                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
4839         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
4840                 g_snprintf(buf, sizeof(buf), "%s", 
4841                         strchr(compose->account->address, '@') ?
4842                                 strchr(compose->account->address, '@')+1 :
4843                                 compose->account->address);
4844         } else {
4845                 g_snprintf(buf, sizeof(buf), "%s", "");
4846         }
4847
4848         if (compose->account->gen_msgid) {
4849                 generate_msgid(buf, sizeof(buf));
4850                 err |= (fprintf(fp, "Resent-Message-ID: <%s>\n", buf) < 0);
4851                 compose->msgid = g_strdup(buf);
4852         } else {
4853                 compose->msgid = NULL;
4854         }
4855
4856         if (compose_redirect_write_headers_from_headerlist(compose, fp))
4857                 return -1;
4858
4859         /* separator between header and body */
4860         err |= (fputs("\n", fp) == EOF);
4861
4862         return (err ? -1:0);
4863 }
4864
4865 static gint compose_redirect_write_to_file(Compose *compose, FILE *fdest)
4866 {
4867         FILE *fp;
4868         size_t len;
4869         gchar buf[BUFFSIZE];
4870         int i = 0;
4871         gboolean skip = FALSE;
4872         gboolean err = FALSE;
4873         gchar *not_included[]={
4874                 "Return-Path:",         "Delivered-To:",        "Received:",
4875                 "Subject:",             "X-UIDL:",              "AF:",
4876                 "NF:",                  "PS:",                  "SRH:",
4877                 "SFN:",                 "DSR:",                 "MID:",
4878                 "CFG:",                 "PT:",                  "S:",
4879                 "RQ:",                  "SSV:",                 "NSV:",
4880                 "SSH:",                 "R:",                   "MAID:",
4881                 "NAID:",                "RMID:",                "FMID:",
4882                 "SCF:",                 "RRCPT:",               "NG:",
4883                 "X-Claws-Privacy",      "X-Claws-Sign:",        "X-Claws-Encrypt",
4884                 "X-Claws-End-Special-Headers:",                 "X-Claws-Account-Id:",
4885                 "X-Sylpheed-Privacy",   "X-Sylpheed-Sign:",     "X-Sylpheed-Encrypt",
4886                 "X-Sylpheed-End-Special-Headers:",              "X-Sylpheed-Account-Id:",
4887                 NULL
4888                 };
4889         if ((fp = g_fopen(compose->redirect_filename, "rb")) == NULL) {
4890                 FILE_OP_ERROR(compose->redirect_filename, "fopen");
4891                 return -1;
4892         }
4893
4894         while (procheader_get_one_field_asis(buf, sizeof(buf), fp) != -1) {
4895                 skip = FALSE;
4896                 for (i = 0; not_included[i] != NULL; i++) {
4897                         if (g_ascii_strncasecmp(buf, not_included[i],
4898                                                 strlen(not_included[i])) == 0) {
4899                                 skip = TRUE;
4900                                 break;
4901                         }
4902                 }
4903                 if (skip)
4904                         continue;
4905                 if (fputs(buf, fdest) == -1)
4906                         goto error;
4907
4908                 if (!prefs_common.redirect_keep_from) {
4909                         if (g_ascii_strncasecmp(buf, "From:",
4910                                           strlen("From:")) == 0) {
4911                                 err |= (fputs(" (by way of ", fdest) == EOF);
4912                                 if (compose->account->name
4913                                     && *compose->account->name) {
4914                                         compose_convert_header
4915                                                 (compose, buf, sizeof(buf),
4916                                                  compose->account->name,
4917                                                  strlen("From: "),
4918                                                  FALSE);
4919                                         err |= (fprintf(fdest, "%s <%s>",
4920                                                 buf,
4921                                                 compose->account->address) < 0);
4922                                 } else
4923                                         err |= (fprintf(fdest, "%s",
4924                                                 compose->account->address) < 0);
4925                                 err |= (fputs(")", fdest) == EOF);
4926                         }
4927                 }
4928
4929                 if (fputs("\n", fdest) == -1)
4930                         goto error;
4931         }
4932
4933         if (err)
4934                 goto error;
4935
4936         if (compose_redirect_write_headers(compose, fdest))
4937                 goto error;
4938
4939         while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4940                 if (fwrite(buf, sizeof(gchar), len, fdest) != len)
4941                         goto error;
4942         }
4943
4944         fclose(fp);
4945
4946         return 0;
4947 error:
4948         fclose(fp);
4949
4950         return -1;
4951 }
4952
4953 static gint compose_write_to_file(Compose *compose, FILE *fp, gint action, gboolean attach_parts)
4954 {
4955         GtkTextBuffer *buffer;
4956         GtkTextIter start, end;
4957         gchar *chars;
4958         gchar *buf;
4959         const gchar *out_codeset;
4960         EncodingType encoding;
4961         MimeInfo *mimemsg, *mimetext;
4962         gint line;
4963
4964         if (action == COMPOSE_WRITE_FOR_SEND)
4965                 attach_parts = TRUE;
4966
4967         /* create message MimeInfo */
4968         mimemsg = procmime_mimeinfo_new();
4969         mimemsg->type = MIMETYPE_MESSAGE;
4970         mimemsg->subtype = g_strdup("rfc822");
4971         mimemsg->content = MIMECONTENT_MEM;
4972         mimemsg->tmp = TRUE; /* must free content later */
4973         mimemsg->data.mem = compose_get_header(compose);
4974
4975         /* Create text part MimeInfo */
4976         /* get all composed text */
4977         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4978         gtk_text_buffer_get_start_iter(buffer, &start);
4979         gtk_text_buffer_get_end_iter(buffer, &end);
4980         chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4981         if (is_ascii_str(chars)) {
4982                 buf = chars;
4983                 chars = NULL;
4984                 out_codeset = CS_US_ASCII;
4985                 encoding = ENC_7BIT;
4986         } else {
4987                 const gchar *src_codeset = CS_INTERNAL;
4988
4989                 out_codeset = conv_get_charset_str(compose->out_encoding);
4990
4991                 if (!out_codeset) {
4992                         gchar *test_conv_global_out = NULL;
4993                         gchar *test_conv_reply = NULL;
4994
4995                         /* automatic mode. be automatic. */
4996                         codeconv_set_strict(TRUE);
4997                         
4998                         out_codeset = conv_get_outgoing_charset_str();
4999                         if (out_codeset) {
5000                                 debug_print("trying to convert to %s\n", out_codeset);
5001                                 test_conv_global_out = conv_codeset_strdup(chars, src_codeset, out_codeset);
5002                         }
5003                         
5004                         if (!test_conv_global_out && compose->orig_charset
5005                         &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5006                                 out_codeset = compose->orig_charset;
5007                                 debug_print("failure; trying to convert to %s\n", out_codeset);
5008                                 test_conv_reply = conv_codeset_strdup(chars, src_codeset, out_codeset);
5009                         }
5010                         
5011                         if (!test_conv_global_out && !test_conv_reply) {
5012                                 /* we're lost */
5013                                 out_codeset = CS_INTERNAL;
5014                                 debug_print("failure; finally using %s\n", out_codeset);
5015                         }
5016                         g_free(test_conv_global_out);
5017                         g_free(test_conv_reply);
5018                         codeconv_set_strict(FALSE);
5019                 }
5020
5021                 if (!g_ascii_strcasecmp(out_codeset, CS_US_ASCII))
5022                         out_codeset = CS_ISO_8859_1;
5023
5024                 if (prefs_common.encoding_method == CTE_BASE64)
5025                         encoding = ENC_BASE64;
5026                 else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
5027                         encoding = ENC_QUOTED_PRINTABLE;
5028                 else if (prefs_common.encoding_method == CTE_8BIT)
5029                         encoding = ENC_8BIT;
5030                 else
5031                         encoding = procmime_get_encoding_for_charset(out_codeset);
5032
5033                 debug_print("src encoding = %s, out encoding = %s, transfer encoding = %s\n",
5034                             src_codeset, out_codeset, procmime_get_encoding_str(encoding));
5035
5036                 if (action == COMPOSE_WRITE_FOR_SEND) {
5037                         codeconv_set_strict(TRUE);
5038                         buf = conv_codeset_strdup(chars, src_codeset, out_codeset);
5039                         codeconv_set_strict(FALSE);
5040
5041                         if (!buf) {
5042                                 AlertValue aval;
5043                                 gchar *msg;
5044
5045                                 msg = g_strdup_printf(_("Can't convert the character encoding of the message \n"
5046                                                         "to the specified %s charset.\n"
5047                                                         "Send it as %s?"), out_codeset, src_codeset);
5048                                 aval = alertpanel_full(_("Error"), msg, GTK_STOCK_CANCEL, _("+_Send"), NULL, FALSE,
5049                                                       NULL, ALERT_ERROR, G_ALERTDEFAULT);
5050                                 g_free(msg);
5051
5052                                 if (aval != G_ALERTALTERNATE) {
5053                                         g_free(chars);
5054                                         return -3;
5055                                 } else {
5056                                         buf = chars;
5057                                         out_codeset = src_codeset;
5058                                         chars = NULL;
5059                                 }
5060                         }
5061                 } else {
5062                         buf = chars;
5063                         out_codeset = src_codeset;
5064                         chars = NULL;
5065                 }
5066         }
5067         g_free(chars);
5068
5069         if (encoding == ENC_8BIT || encoding == ENC_7BIT) {
5070                 if (!strncmp(buf, "From ", sizeof("From ")-1) ||
5071                     strstr(buf, "\nFrom ") != NULL) {
5072                         encoding = ENC_QUOTED_PRINTABLE;
5073                 }
5074         }
5075
5076         mimetext = procmime_mimeinfo_new();
5077         mimetext->content = MIMECONTENT_MEM;
5078         mimetext->tmp = TRUE; /* must free content later */
5079         /* dup'ed because procmime_encode_content can turn it into a tmpfile
5080          * and free the data, which we need later. */
5081         mimetext->data.mem = g_strdup(buf); 
5082         mimetext->type = MIMETYPE_TEXT;
5083         mimetext->subtype = g_strdup("plain");
5084         g_hash_table_insert(mimetext->typeparameters, g_strdup("charset"),
5085                             g_strdup(out_codeset));
5086                             
5087         /* protect trailing spaces when signing message */
5088         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5089             privacy_system_can_sign(compose->privacy_system)) {
5090                 encoding = ENC_QUOTED_PRINTABLE;
5091         }
5092         
5093         debug_print("main text: %zd bytes encoded as %s in %d\n",
5094                 strlen(buf), out_codeset, encoding);
5095
5096         /* check for line length limit */
5097         if (action == COMPOSE_WRITE_FOR_SEND &&
5098             encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
5099             check_line_length(buf, 1000, &line) < 0) {
5100                 AlertValue aval;
5101                 gchar *msg;
5102
5103                 msg = g_strdup_printf
5104                         (_("Line %d exceeds the line length limit (998 bytes).\n"
5105                            "The contents of the message might be broken on the way to the delivery.\n"
5106                            "\n"
5107                            "Send it anyway?"), line + 1);
5108                 aval = alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, GTK_STOCK_OK, NULL);
5109                 g_free(msg);
5110                 if (aval != G_ALERTALTERNATE) {
5111                         g_free(buf);
5112                         return -1;
5113                 }
5114         }
5115         
5116         if (encoding != ENC_UNKNOWN)
5117                 procmime_encode_content(mimetext, encoding);
5118
5119         /* append attachment parts */
5120         if (compose_use_attach(compose) && attach_parts) {
5121                 MimeInfo *mimempart;
5122                 gchar *boundary = NULL;
5123                 mimempart = procmime_mimeinfo_new();
5124                 mimempart->content = MIMECONTENT_EMPTY;
5125                 mimempart->type = MIMETYPE_MULTIPART;
5126                 mimempart->subtype = g_strdup("mixed");
5127
5128                 do {
5129                         g_free(boundary);
5130                         boundary = generate_mime_boundary(NULL);
5131                 } while (strstr(buf, boundary) != NULL);
5132
5133                 g_hash_table_insert(mimempart->typeparameters, g_strdup("boundary"),
5134                                     boundary);
5135
5136                 mimetext->disposition = DISPOSITIONTYPE_INLINE;
5137
5138                 g_node_append(mimempart->node, mimetext->node);
5139                 g_node_append(mimemsg->node, mimempart->node);
5140
5141                 compose_add_attachments(compose, mimempart);
5142         } else
5143                 g_node_append(mimemsg->node, mimetext->node);
5144
5145         g_free(buf);
5146
5147         /* sign message if sending */
5148         if (action == COMPOSE_WRITE_FOR_SEND && compose->use_signing && 
5149             privacy_system_can_sign(compose->privacy_system))
5150                 if (!privacy_sign(compose->privacy_system, mimemsg, compose->account))
5151                         return -2;
5152
5153         procmime_write_mimeinfo(mimemsg, fp);
5154         
5155         procmime_mimeinfo_free_all(mimemsg);
5156
5157         return 0;
5158 }
5159
5160 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
5161 {
5162         GtkTextBuffer *buffer;
5163         GtkTextIter start, end;
5164         FILE *fp;
5165         size_t len;
5166         gchar *chars, *tmp;
5167
5168         if ((fp = g_fopen(file, "wb")) == NULL) {
5169                 FILE_OP_ERROR(file, "fopen");
5170                 return -1;
5171         }
5172
5173         /* chmod for security */
5174         if (change_file_mode_rw(fp, file) < 0) {
5175                 FILE_OP_ERROR(file, "chmod");
5176                 g_warning("can't change file mode\n");
5177         }
5178
5179         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
5180         gtk_text_buffer_get_start_iter(buffer, &start);
5181         gtk_text_buffer_get_end_iter(buffer, &end);
5182         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
5183
5184         chars = conv_codeset_strdup
5185                 (tmp, CS_INTERNAL, conv_get_locale_charset_str());
5186
5187         g_free(tmp);
5188         if (!chars) return -1;
5189
5190         /* write body */
5191         len = strlen(chars);
5192         if (fwrite(chars, sizeof(gchar), len, fp) != len) {
5193                 FILE_OP_ERROR(file, "fwrite");
5194                 g_free(chars);
5195                 fclose(fp);
5196                 g_unlink(file);
5197                 return -1;
5198         }
5199
5200         g_free(chars);
5201
5202         if (fclose(fp) == EOF) {
5203                 FILE_OP_ERROR(file, "fclose");
5204                 g_unlink(file);
5205                 return -1;
5206         }
5207         return 0;
5208 }
5209
5210 static gint compose_remove_reedit_target(Compose *compose, gboolean force)
5211 {
5212         FolderItem *item;
5213         MsgInfo *msginfo = compose->targetinfo;
5214
5215         g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
5216         if (!msginfo) return -1;
5217
5218         if (!force && MSG_IS_LOCKED(msginfo->flags))
5219                 return 0;
5220
5221         item = msginfo->folder;
5222         g_return_val_if_fail(item != NULL, -1);
5223
5224         if (procmsg_msg_exist(msginfo) &&
5225             (folder_has_parent_of_type(item, F_QUEUE) ||
5226              folder_has_parent_of_type(item, F_DRAFT) 
5227              || msginfo == compose->autosaved_draft)) {
5228                 if (folder_item_remove_msg(item, msginfo->msgnum) < 0) {
5229                         g_warning("can't remove the old message\n");
5230                         return -1;
5231                 } else {
5232                         debug_print("removed reedit target %d\n", msginfo->msgnum);
5233                 }
5234         }
5235
5236         return 0;
5237 }
5238
5239 static void compose_remove_draft(Compose *compose)
5240 {
5241         FolderItem *drafts;
5242         MsgInfo *msginfo = compose->targetinfo;
5243         drafts = account_get_special_folder(compose->account, F_DRAFT);
5244
5245         if (procmsg_msg_exist(msginfo)) {
5246                 folder_item_remove_msg(drafts, msginfo->msgnum);
5247         }
5248
5249 }
5250
5251 gint compose_queue(Compose *compose, gint *msgnum, FolderItem **item, gchar **msgpath,
5252                    gboolean remove_reedit_target)
5253 {
5254         return compose_queue_sub (compose, msgnum, item, msgpath, FALSE, remove_reedit_target);
5255 }
5256
5257 static gboolean compose_warn_encryption(Compose *compose)
5258 {
5259         const gchar *warning = privacy_get_encrypt_warning(compose->privacy_system);
5260         AlertValue val = G_ALERTALTERNATE;
5261         
5262         if (warning == NULL)
5263                 return TRUE;
5264
5265         val = alertpanel_full(_("Encryption warning"), warning,
5266                   GTK_STOCK_CANCEL, _("+C_ontinue"), NULL,
5267                   TRUE, NULL, ALERT_WARNING, G_ALERTALTERNATE);
5268         if (val & G_ALERTDISABLE) {
5269                 val &= ~G_ALERTDISABLE;
5270                 if (val == G_ALERTALTERNATE)
5271                         privacy_inhibit_encrypt_warning(compose->privacy_system,
5272                                 TRUE);
5273         }
5274
5275         if (val == G_ALERTALTERNATE) {
5276                 return TRUE;
5277         } else {
5278                 return FALSE;
5279         } 
5280 }
5281
5282 static gint compose_queue_sub(Compose *compose, gint *msgnum, FolderItem **item, 
5283                               gchar **msgpath, gboolean check_subject,
5284                               gboolean remove_reedit_target)
5285 {
5286         FolderItem *queue;
5287         gchar *tmp;
5288         FILE *fp;
5289         GSList *cur;
5290         gint num;
5291         static gboolean lock = FALSE;
5292         PrefsAccount *mailac = NULL, *newsac = NULL;
5293         gboolean err = FALSE;
5294
5295         debug_print("queueing message...\n");
5296         g_return_val_if_fail(compose->account != NULL, -1);
5297
5298         lock = TRUE;
5299         
5300         if (compose_check_entries(compose, check_subject) == FALSE) {
5301                 lock = FALSE;
5302                 if (compose->batch) {
5303                         gtk_widget_show_all(compose->window);
5304                 }
5305                 return -1;
5306         }
5307
5308         if (!compose->to_list && !compose->newsgroup_list) {
5309                 g_warning("can't get recipient list.");
5310                 lock = FALSE;
5311                 return -1;
5312         }
5313
5314         if (compose->to_list) {
5315                 if (compose->account->protocol != A_NNTP)
5316                         mailac = compose->account;
5317                 else if (cur_account && cur_account->protocol != A_NNTP)
5318                         mailac = cur_account;
5319                 else if (!(mailac = compose_current_mail_account())) {
5320                         lock = FALSE;
5321                         alertpanel_error(_("No account for sending mails available!"));
5322                         return -1;
5323                 }
5324         }
5325
5326         if (compose->newsgroup_list) {
5327                 if (compose->account->protocol == A_NNTP)
5328                         newsac = compose->account;
5329                 else if (!newsac->protocol != A_NNTP) {
5330                         lock = FALSE;
5331                         alertpanel_error(_("No account for posting news available!"));
5332                         return -1;
5333                 }                       
5334         }
5335
5336         /* write queue header */
5337         tmp = g_strdup_printf("%s%cqueue.%p%08x", get_tmp_dir(),
5338                               G_DIR_SEPARATOR, compose, (guint) rand());
5339         debug_print("queuing to %s\n", tmp);
5340         if ((fp = g_fopen(tmp, "wb")) == NULL) {
5341                 FILE_OP_ERROR(tmp, "fopen");
5342                 g_free(tmp);
5343                 lock = FALSE;
5344                 return -2;
5345         }
5346
5347         if (change_file_mode_rw(fp, tmp) < 0) {
5348                 FILE_OP_ERROR(tmp, "chmod");
5349                 g_warning("can't change file mode\n");
5350         }
5351
5352         /* queueing variables */
5353         err |= (fprintf(fp, "AF:\n") < 0);
5354         err |= (fprintf(fp, "NF:0\n") < 0);
5355         err |= (fprintf(fp, "PS:10\n") < 0);
5356         err |= (fprintf(fp, "SRH:1\n") < 0);
5357         err |= (fprintf(fp, "SFN:\n") < 0);
5358         err |= (fprintf(fp, "DSR:\n") < 0);
5359         if (compose->msgid)
5360                 err |= (fprintf(fp, "MID:<%s>\n", compose->msgid) < 0);
5361         else
5362                 err |= (fprintf(fp, "MID:\n") < 0);
5363         err |= (fprintf(fp, "CFG:\n") < 0);
5364         err |= (fprintf(fp, "PT:0\n") < 0);
5365         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
5366         err |= (fprintf(fp, "RQ:\n") < 0);
5367         if (mailac)
5368                 err |= (fprintf(fp, "SSV:%s\n", mailac->smtp_server) < 0);
5369         else
5370                 err |= (fprintf(fp, "SSV:\n") < 0);
5371         if (newsac)
5372                 err |= (fprintf(fp, "NSV:%s\n", newsac->nntp_server) < 0);
5373         else
5374                 err |= (fprintf(fp, "NSV:\n") < 0);
5375         err |= (fprintf(fp, "SSH:\n") < 0);
5376         /* write recepient list */
5377         if (compose->to_list) {
5378                 err |= (fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data) < 0);
5379                 for (cur = compose->to_list->next; cur != NULL;
5380                      cur = cur->next)
5381                         err |= (fprintf(fp, ",<%s>", (gchar *)cur->data) < 0);
5382                 err |= (fprintf(fp, "\n") < 0);
5383         }
5384         /* write newsgroup list */
5385         if (compose->newsgroup_list) {
5386                 err |= (fprintf(fp, "NG:") < 0);
5387                 err |= (fprintf(fp, "%s", (gchar *)compose->newsgroup_list->data) < 0);
5388                 for (cur = compose->newsgroup_list->next; cur != NULL; cur = cur->next)
5389                         err |= (fprintf(fp, ",%s", (gchar *)cur->data) < 0);
5390                 err |= (fprintf(fp, "\n") < 0);
5391         }
5392         /* Sylpheed account IDs */
5393         if (mailac)
5394                 err |= (fprintf(fp, "MAID:%d\n", mailac->account_id) < 0);
5395         if (newsac)
5396                 err |= (fprintf(fp, "NAID:%d\n", newsac->account_id) < 0);
5397
5398         
5399         if (compose->privacy_system != NULL) {
5400                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
5401                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
5402                 if (compose->use_encryption) {
5403                         gchar *encdata;
5404                         if (!compose_warn_encryption(compose)) {
5405                                 lock = FALSE;
5406                                 fclose(fp);
5407                                 g_unlink(tmp);
5408                                 g_free(tmp);
5409                                 return -6;
5410                         }
5411                         if (mailac && mailac->encrypt_to_self) {
5412                                 GSList *tmp_list = g_slist_copy(compose->to_list);
5413                                 tmp_list = g_slist_append(tmp_list, compose->account->address);
5414                                 encdata = privacy_get_encrypt_data(compose->privacy_system, tmp_list);
5415                                 g_slist_free(tmp_list);
5416                         } else {
5417                                 encdata = privacy_get_encrypt_data(compose->privacy_system, compose->to_list);
5418                         }
5419                         if (encdata != NULL) {
5420                                 if (strcmp(encdata, "_DONT_ENCRYPT_")) {
5421                                         err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5422                                         err |= (fprintf(fp, "X-Claws-Encrypt-Data:%s\n", 
5423                                                 encdata) < 0);
5424                                 } /* else we finally dont want to encrypt */
5425                         } else {
5426                                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
5427                                 /* and if encdata was null, it means there's been a problem in 
5428                                  * key selection */
5429                                 lock = FALSE;
5430                                 fclose(fp);
5431                                 g_unlink(tmp);
5432                                 g_free(tmp);
5433                                 return -5;
5434                         }
5435                         g_free(encdata);
5436                 }
5437         }
5438
5439         /* Save copy folder */
5440         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
5441                 gchar *savefolderid;
5442                 
5443                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
5444                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
5445                 g_free(savefolderid);
5446         }
5447         /* Save copy folder */
5448         if (compose->return_receipt) {
5449                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
5450         }
5451         /* Message-ID of message replying to */
5452         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
5453                 gchar *folderid;
5454                 
5455                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
5456                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
5457                 g_free(folderid);
5458         }
5459         /* Message-ID of message forwarding to */
5460         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
5461                 gchar *folderid;
5462                 
5463                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
5464                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
5465                 g_free(folderid);
5466         }
5467
5468         /* end of headers */
5469         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
5470
5471         if (compose->redirect_filename != NULL) {
5472                 if (compose_redirect_write_to_file(compose, fp) < 0) {
5473                         lock = FALSE;
5474                         fclose(fp);
5475                         g_unlink(tmp);
5476                         g_free(tmp);
5477                         return -2;
5478                 }
5479         } else {
5480                 gint result = 0;
5481                 if ((result = compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_SEND, TRUE)) < 0) {
5482                         lock = FALSE;
5483                         fclose(fp);
5484                         g_unlink(tmp);
5485                         g_free(tmp);
5486                         return result - 1; /* -2 for a generic error, -3 for signing error, -4 for encoding */
5487                 }
5488         }
5489         if (err == TRUE) {
5490                 g_warning("failed to write queue message\n");
5491                 fclose(fp);
5492                 g_unlink(tmp);
5493                 g_free(tmp);
5494                 lock = FALSE;
5495                 return -2;
5496         }
5497         if (fclose(fp) == EOF) {
5498                 FILE_OP_ERROR(tmp, "fclose");
5499                 g_unlink(tmp);
5500                 g_free(tmp);
5501                 lock = FALSE;
5502                 return -2;
5503         }
5504
5505         if (item && *item) {
5506                 queue = *item;
5507         } else {
5508                 queue = account_get_special_folder(compose->account, F_QUEUE);
5509         }
5510         if (!queue) {
5511                 g_warning("can't find queue folder\n");
5512                 g_unlink(tmp);
5513                 g_free(tmp);
5514                 lock = FALSE;
5515                 return -1;
5516         }
5517         folder_item_scan(queue);
5518         if ((num = folder_item_add_msg(queue, tmp, NULL, FALSE)) < 0) {
5519                 g_warning("can't queue the message\n");
5520                 g_unlink(tmp);
5521                 g_free(tmp);
5522                 lock = FALSE;
5523                 return -1;
5524         }
5525         
5526         if (msgpath == NULL) {
5527                 g_unlink(tmp);
5528                 g_free(tmp);
5529         } else
5530                 *msgpath = tmp;
5531
5532         if (compose->mode == COMPOSE_REEDIT && remove_reedit_target) {
5533                 compose_remove_reedit_target(compose, FALSE);
5534         }
5535
5536         if ((msgnum != NULL) && (item != NULL)) {
5537                 *msgnum = num;
5538                 *item = queue;
5539         }
5540
5541         return 0;
5542 }
5543
5544 static void compose_add_attachments(Compose *compose, MimeInfo *parent)
5545 {
5546         AttachInfo *ainfo;
5547         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
5548         MimeInfo *mimepart;
5549         struct stat statbuf;
5550         gchar *type, *subtype;
5551         GtkTreeModel *model;
5552         GtkTreeIter iter;
5553
5554         model = gtk_tree_view_get_model(tree_view);
5555         
5556         if (!gtk_tree_model_get_iter_first(model, &iter))
5557                 return;
5558         do {
5559                 gtk_tree_model_get(model, &iter,
5560                                    COL_DATA, &ainfo,
5561                                    -1);
5562                                                            
5563                 mimepart = procmime_mimeinfo_new();
5564                 mimepart->content = MIMECONTENT_FILE;
5565                 mimepart->data.filename = g_strdup(ainfo->file);
5566                 mimepart->tmp = FALSE; /* or we destroy our attachment */
5567                 mimepart->offset = 0;
5568
5569                 stat(ainfo->file, &statbuf);
5570                 mimepart->length = statbuf.st_size;
5571
5572                 type = g_strdup(ainfo->content_type);
5573
5574                 if (!strchr(type, '/')) {
5575                         g_free(type);
5576                         type = g_strdup("application/octet-stream");
5577                 }
5578
5579                 subtype = strchr(type, '/') + 1;
5580                 *(subtype - 1) = '\0';
5581                 mimepart->type = procmime_get_media_type(type);
5582                 mimepart->subtype = g_strdup(subtype);
5583                 g_free(type);
5584
5585                 if (mimepart->type == MIMETYPE_MESSAGE && 
5586                     !g_ascii_strcasecmp(mimepart->subtype, "rfc822")) {
5587                         mimepart->disposition = DISPOSITIONTYPE_INLINE;
5588                 } else {
5589                         if (ainfo->name) {
5590                                 g_hash_table_insert(mimepart->typeparameters,
5591                                             g_strdup("name"), g_strdup(ainfo->name));
5592                                 g_hash_table_insert(mimepart->dispositionparameters,
5593                                             g_strdup("filename"), g_strdup(ainfo->name));
5594                                 mimepart->disposition = DISPOSITIONTYPE_ATTACHMENT;
5595                         }
5596                 }
5597
5598                 if (compose->use_signing) {
5599                         if (ainfo->encoding == ENC_7BIT)
5600                                 ainfo->encoding = ENC_QUOTED_PRINTABLE;
5601                         else if (ainfo->encoding == ENC_8BIT)
5602                                 ainfo->encoding = ENC_BASE64;
5603                 }
5604                 
5605                 procmime_encode_content(mimepart, ainfo->encoding);
5606
5607                 g_node_append(parent->node, mimepart->node);
5608         } while (gtk_tree_model_iter_next(model, &iter));
5609 }
5610
5611 #define IS_IN_CUSTOM_HEADER(header) \
5612         (compose->account->add_customhdr && \
5613          custom_header_find(compose->account->customhdr_list, header) != NULL)
5614
5615 static void compose_add_headerfield_from_headerlist(Compose *compose, 
5616                                                     GString *header, 
5617                                                     const gchar *fieldname,
5618                                                     const gchar *seperator)
5619 {
5620         gchar *str, *fieldname_w_colon;
5621         gboolean add_field = FALSE;
5622         GSList *list;
5623         ComposeHeaderEntry *headerentry;
5624         const gchar *headerentryname;
5625         const gchar *trans_fieldname;
5626         GString *fieldstr;
5627
5628         if (IS_IN_CUSTOM_HEADER(fieldname))
5629                 return;
5630
5631         debug_print("Adding %s-fields\n", fieldname);
5632
5633         fieldstr = g_string_sized_new(64);
5634
5635         fieldname_w_colon = g_strconcat(fieldname, ":", NULL);
5636         trans_fieldname = prefs_common_translated_header_name(fieldname_w_colon);
5637
5638         for (list = compose->header_list; list; list = list->next) {
5639                 headerentry = ((ComposeHeaderEntry *)list->data);
5640                 headerentryname = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child));
5641
5642                 if (!g_utf8_collate(trans_fieldname, headerentryname)) {
5643                         str = gtk_editable_get_chars(GTK_EDITABLE(headerentry->entry), 0, -1);
5644                         g_strstrip(str);
5645                         if (str[0] != '\0') {
5646                                 if (add_field)
5647                                         g_string_append(fieldstr, seperator);
5648                                 g_string_append(fieldstr, str);
5649                                 add_field = TRUE;
5650                         }
5651                         g_free(str);
5652                 }
5653         }
5654         if (add_field) {
5655                 gchar *buf;
5656
5657                 buf = g_new0(gchar, fieldstr->len * 4 + 256);
5658                 compose_convert_header
5659                         (compose, buf, fieldstr->len * 4  + 256, fieldstr->str,
5660                         strlen(fieldname) + 2, TRUE);
5661                 g_string_append_printf(header, "%s: %s\n", fieldname, buf);
5662                 g_free(buf);
5663         }
5664
5665         g_free(fieldname_w_colon);
5666         g_string_free(fieldstr, TRUE);
5667
5668         return;
5669 }
5670
5671 static gchar *compose_get_header(Compose *compose)
5672 {
5673         gchar buf[BUFFSIZE];
5674         const gchar *entry_str;
5675         gchar *str;
5676         gchar *name;
5677         GSList *list;
5678         gchar *std_headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
5679         GString *header;
5680         gchar *from_name = NULL, *from_address = NULL;
5681         gchar *tmp;
5682
5683         g_return_val_if_fail(compose->account != NULL, NULL);
5684         g_return_val_if_fail(compose->account->address != NULL, NULL);
5685
5686         header = g_string_sized_new(64);
5687
5688         /* Date */
5689         get_rfc822_date(buf, sizeof(buf));
5690         g_string_append_printf(header, "Date: %s\n", buf);
5691
5692         /* From */
5693         
5694         if (compose->account->name && *compose->account->name) {
5695                 gchar *buf;
5696                 QUOTE_IF_REQUIRED(buf, compose->account->name);
5697                 tmp = g_strdup_printf("%s <%s>",
5698                         buf, compose->account->address);
5699         } else {
5700                 tmp = g_strdup_printf("%s",
5701                         compose->account->address);
5702         }
5703         if (!strcmp(gtk_entry_get_text(GTK_ENTRY(compose->from_name)), tmp)
5704         ||  strlen(gtk_entry_get_text(GTK_ENTRY(compose->from_name))) == 0) {
5705                 /* use default */
5706                 from_name = compose->account->name ? g_strdup(compose->account->name):NULL;
5707                 from_address = g_strdup(compose->account->address);
5708         } else {
5709                 gchar *spec = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
5710                 /* extract name and address */
5711                 if (strstr(spec, " <") && strstr(spec, ">")) {
5712                         from_address = g_strdup(strrchr(spec, '<')+1);
5713                         *(strrchr(from_address, '>')) = '\0';
5714                         from_name = g_strdup(spec);
5715                         *(strrchr(from_name, '<')) = '\0';
5716                 } else {
5717                         from_name = NULL;
5718                         from_address = g_strdup(spec);
5719                 }
5720                 g_free(spec);
5721         }
5722         g_free(tmp);
5723         
5724         
5725         if (from_name && *from_name) {
5726                 compose_convert_header
5727                         (compose, buf, sizeof(buf), from_name,
5728                          strlen("From: "), TRUE);
5729                 QUOTE_IF_REQUIRED(name, buf);
5730                 
5731                 g_string_append_printf(header, "From: %s <%s>\n",
5732                         name, from_address);
5733         } else
5734                 g_string_append_printf(header, "From: %s\n", from_address);
5735         
5736         g_free(from_name);
5737         g_free(from_address);
5738
5739         /* To */
5740         compose_add_headerfield_from_headerlist(compose, header, "To", ", ");
5741
5742         /* Newsgroups */
5743         compose_add_headerfield_from_headerlist(compose, header, "Newsgroups", ",");
5744
5745         /* Cc */
5746         compose_add_headerfield_from_headerlist(compose, header, "Cc", ", ");
5747
5748         /* Bcc */
5749         compose_add_headerfield_from_headerlist(compose, header, "Bcc", ", ");
5750
5751         /* Subject */
5752         str = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
5753
5754         if (*str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
5755                 g_strstrip(str);
5756                 if (*str != '\0') {
5757                         compose_convert_header(compose, buf, sizeof(buf), str,
5758                                                strlen("Subject: "), FALSE);
5759                         g_string_append_printf(header, "Subject: %s\n", buf);
5760                 }
5761         }
5762         g_free(str);
5763
5764         /* Message-ID */
5765         if (compose->account->set_domain && compose->account->domain) {
5766                 g_snprintf(buf, sizeof(buf), "%s", compose->account->domain); 
5767         } else if (!strncmp(get_domain_name(), "localhost", strlen("localhost"))) {
5768                 g_snprintf(buf, sizeof(buf), "%s", 
5769                         strchr(compose->account->address, '@') ?
5770                                 strchr(compose->account->address, '@')+1 :
5771                                 compose->account->address);
5772         } else {
5773                 g_snprintf(buf, sizeof(buf), "%s", "");
5774         }
5775         
5776         if (compose->account->gen_msgid) {
5777                 generate_msgid(buf, sizeof(buf));
5778                 g_string_append_printf(header, "Message-ID: <%s>\n", buf);
5779                 compose->msgid = g_strdup(buf);
5780         } else {
5781                 compose->msgid = NULL;
5782         }
5783
5784         if (compose->remove_references == FALSE) {
5785                 /* In-Reply-To */
5786                 if (compose->inreplyto && compose->to_list)
5787                         g_string_append_printf(header, "In-Reply-To: <%s>\n", compose->inreplyto);
5788         
5789                 /* References */
5790                 if (compose->references)
5791                         g_string_append_printf(header, "References: %s\n", compose->references);
5792         }
5793
5794         /* Followup-To */
5795         compose_add_headerfield_from_headerlist(compose, header, "Followup-To", ",");
5796
5797         /* Reply-To */
5798         compose_add_headerfield_from_headerlist(compose, header, "Reply-To", ", ");
5799
5800         /* Organization */
5801         if (compose->account->organization &&
5802             strlen(compose->account->organization) &&
5803             !IS_IN_CUSTOM_HEADER("Organization")) {
5804                 compose_convert_header(compose, buf, sizeof(buf),
5805                                        compose->account->organization,
5806                                        strlen("Organization: "), FALSE);
5807                 g_string_append_printf(header, "Organization: %s\n", buf);
5808         }
5809
5810         /* Program version and system info */
5811         if (g_slist_length(compose->to_list) && !IS_IN_CUSTOM_HEADER("X-Mailer") &&
5812             !compose->newsgroup_list) {
5813                 g_string_append_printf(header, "X-Mailer: %s (GTK+ %d.%d.%d; %s)\n",
5814                         prog_version,
5815                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5816                         TARGET_ALIAS);
5817         }
5818         if (g_slist_length(compose->newsgroup_list) && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
5819                 g_string_append_printf(header, "X-Newsreader: %s (GTK+ %d.%d.%d; %s)\n",
5820                         prog_version,
5821                         gtk_major_version, gtk_minor_version, gtk_micro_version,
5822                         TARGET_ALIAS);
5823         }
5824
5825         /* custom headers */
5826         if (compose->account->add_customhdr) {
5827                 GSList *cur;
5828
5829                 for (cur = compose->account->customhdr_list; cur != NULL;
5830                      cur = cur->next) {
5831                         CustomHeader *chdr = (CustomHeader *)cur->data;
5832
5833                         if (custom_header_is_allowed(chdr->name)) {
5834                                 compose_convert_header
5835                                         (compose, buf, sizeof(buf),
5836                                          chdr->value ? chdr->value : "",
5837                                          strlen(chdr->name) + 2, FALSE);
5838                                 g_string_append_printf(header, "%s: %s\n", chdr->name, buf);
5839                         }
5840                 }
5841         }
5842
5843         /* PRIORITY */
5844         switch (compose->priority) {
5845                 case PRIORITY_HIGHEST: g_string_append_printf(header, "Importance: high\n"
5846                                                    "X-Priority: 1 (Highest)\n");
5847                         break;
5848                 case PRIORITY_HIGH: g_string_append_printf(header, "Importance: high\n"
5849                                                 "X-Priority: 2 (High)\n");
5850                         break;
5851                 case PRIORITY_NORMAL: break;
5852                 case PRIORITY_LOW: g_string_append_printf(header, "Importance: low\n"
5853                                                "X-Priority: 4 (Low)\n");
5854                         break;
5855                 case PRIORITY_LOWEST: g_string_append_printf(header, "Importance: low\n"
5856                                                   "X-Priority: 5 (Lowest)\n");
5857                         break;
5858                 default: debug_print("compose: priority unknown : %d\n",
5859                                      compose->priority);
5860         }
5861
5862         /* Request Return Receipt */
5863         if (!IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
5864                 if (compose->return_receipt) {
5865                         if (compose->account->name
5866                             && *compose->account->name) {
5867                                 compose_convert_header(compose, buf, sizeof(buf), 
5868                                                        compose->account->name, 
5869                                                        strlen("Disposition-Notification-To: "),
5870                                                        TRUE);
5871                                 g_string_append_printf(header, "Disposition-Notification-To: %s <%s>\n", buf, compose->account->address);
5872                         } else
5873                                 g_string_append_printf(header, "Disposition-Notification-To: %s\n", compose->account->address);
5874                 }
5875         }
5876
5877         /* get special headers */
5878         for (list = compose->header_list; list; list = list->next) {
5879                 ComposeHeaderEntry *headerentry;
5880                 gchar *tmp;
5881                 gchar *headername;
5882                 gchar *headername_wcolon;
5883                 const gchar *headername_trans;
5884                 gchar *headervalue;
5885                 gchar **string;
5886                 gboolean standard_header = FALSE;
5887
5888                 headerentry = ((ComposeHeaderEntry *)list->data);
5889                 
5890                 tmp = g_strdup(gtk_entry_get_text(GTK_ENTRY(GTK_BIN(headerentry->combo)->child)));
5891                 if (strchr(tmp, ' ') != NULL || strchr(tmp, '\r') != NULL || strchr(tmp, '\n') != NULL) {
5892                         g_free(tmp);
5893                         continue;
5894                 }
5895
5896                 if (!strstr(tmp, ":")) {
5897                         headername_wcolon = g_strconcat(tmp, ":", NULL);
5898                         headername = g_strdup(tmp);
5899                 } else {
5900                         headername_wcolon = g_strdup(tmp);
5901                         headername = g_strdup(strtok(tmp, ":"));
5902                 }
5903                 g_free(tmp);
5904                 
5905                 entry_str = gtk_entry_get_text(GTK_ENTRY(headerentry->entry));
5906                 Xstrdup_a(headervalue, entry_str, return NULL);
5907                 subst_char(headervalue, '\r', ' ');
5908                 subst_char(headervalue, '\n', ' ');
5909                 string = std_headers;
5910                 while (*string != NULL) {
5911                         headername_trans = prefs_common_translated_header_name(*string);
5912                         if (!strcmp(headername_trans, headername_wcolon))
5913                                 standard_header = TRUE;
5914                         string++;
5915                 }
5916                 if (!standard_header && !IS_IN_CUSTOM_HEADER(headername))
5917                         g_string_append_printf(header, "%s %s\n", headername_wcolon, headervalue);
5918                                 
5919                 g_free(headername);
5920                 g_free(headername_wcolon);              
5921         }
5922
5923         str = header->str;
5924         g_string_free(header, FALSE);
5925
5926         return str;
5927 }
5928
5929 #undef IS_IN_CUSTOM_HEADER
5930
5931 static void compose_convert_header(Compose *compose, gchar *dest, gint len, gchar *src,
5932                                    gint header_len, gboolean addr_field)
5933 {
5934         gchar *tmpstr = NULL;
5935         const gchar *out_codeset = NULL;
5936
5937         g_return_if_fail(src != NULL);
5938         g_return_if_fail(dest != NULL);
5939
5940         if (len < 1) return;
5941
5942         tmpstr = g_strdup(src);
5943
5944         subst_char(tmpstr, '\n', ' ');
5945         subst_char(tmpstr, '\r', ' ');
5946         g_strchomp(tmpstr);
5947
5948         if (!g_utf8_validate(tmpstr, -1, NULL)) {
5949                 gchar *mybuf = g_malloc(strlen(tmpstr)*2 +1);
5950                 conv_localetodisp(mybuf, strlen(tmpstr)*2 +1, tmpstr);
5951                 g_free(tmpstr);
5952                 tmpstr = mybuf;
5953         }
5954
5955         codeconv_set_strict(TRUE);
5956         conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5957                 conv_get_charset_str(compose->out_encoding));
5958         codeconv_set_strict(FALSE);
5959         
5960         if (!dest || *dest == '\0') {
5961                 gchar *test_conv_global_out = NULL;
5962                 gchar *test_conv_reply = NULL;
5963
5964                 /* automatic mode. be automatic. */
5965                 codeconv_set_strict(TRUE);
5966
5967                 out_codeset = conv_get_outgoing_charset_str();
5968                 if (out_codeset) {
5969                         debug_print("trying to convert to %s\n", out_codeset);
5970                         test_conv_global_out = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5971                 }
5972
5973                 if (!test_conv_global_out && compose->orig_charset
5974                 &&  strcmp(compose->orig_charset, CS_US_ASCII)) {
5975                         out_codeset = compose->orig_charset;
5976                         debug_print("failure; trying to convert to %s\n", out_codeset);
5977                         test_conv_reply = conv_codeset_strdup(src, CS_INTERNAL, out_codeset);
5978                 }
5979
5980                 if (!test_conv_global_out && !test_conv_reply) {
5981                         /* we're lost */
5982                         out_codeset = CS_INTERNAL;
5983                         debug_print("finally using %s\n", out_codeset);
5984                 }
5985                 g_free(test_conv_global_out);
5986                 g_free(test_conv_reply);
5987                 conv_encode_header_full(dest, len, tmpstr, header_len, addr_field, 
5988                                         out_codeset);
5989                 codeconv_set_strict(FALSE);
5990         }
5991         g_free(tmpstr);
5992 }
5993
5994 static void compose_add_to_addressbook_cb(GtkMenuItem *menuitem, gpointer user_data)
5995 {
5996         gchar *address;
5997
5998         g_return_if_fail(user_data != NULL);
5999
6000         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(user_data)));
6001         g_strstrip(address);
6002         if (*address != '\0') {
6003                 gchar *name = procheader_get_fromname(address);
6004                 extract_address(address);
6005                 addressbook_add_contact(name, address, NULL, NULL);
6006         }
6007         g_free(address);
6008 }
6009
6010 static void compose_entry_popup_extend(GtkEntry *entry, GtkMenu *menu, gpointer user_data)
6011 {
6012         GtkWidget *menuitem;
6013         gchar *address;
6014
6015         g_return_if_fail(menu != NULL);
6016         g_return_if_fail(GTK_IS_MENU_SHELL(menu));
6017
6018         menuitem = gtk_separator_menu_item_new();
6019         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6020         gtk_widget_show(menuitem);
6021
6022         menuitem = gtk_menu_item_new_with_mnemonic(_("Add to address _book"));
6023         gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), menuitem);
6024
6025         address = g_strdup(gtk_entry_get_text(GTK_ENTRY(entry)));
6026         g_strstrip(address);
6027         if (*address == '\0') {
6028                 gtk_widget_set_sensitive(GTK_WIDGET(menuitem), FALSE);
6029         }
6030
6031         g_signal_connect(G_OBJECT(menuitem), "activate",
6032                          G_CALLBACK(compose_add_to_addressbook_cb), entry);
6033         gtk_widget_show(menuitem);
6034 }
6035
6036 static void compose_create_header_entry(Compose *compose) 
6037 {
6038         gchar *headers[] = {"To:", "Cc:", "Bcc:", "Newsgroups:", "Reply-To:", "Followup-To:", NULL};
6039
6040         GtkWidget *combo;
6041         GtkWidget *entry;
6042         gchar **string;
6043         const gchar *header = NULL;
6044         ComposeHeaderEntry *headerentry;
6045         gboolean standard_header = FALSE;
6046         
6047         headerentry = g_new0(ComposeHeaderEntry, 1);
6048
6049         /* Combo box */
6050         combo = gtk_combo_box_entry_new_text();
6051         string = headers; 
6052         while(*string != NULL) {
6053                 gtk_combo_box_append_text(GTK_COMBO_BOX(combo),
6054                         (gchar*)prefs_common_translated_header_name(*string));
6055                 string++;
6056         }
6057         gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6058         g_signal_connect(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6059                          G_CALLBACK(compose_grab_focus_cb), compose);
6060         gtk_widget_show(combo);
6061         gtk_table_attach(GTK_TABLE(compose->header_table), combo, 0, 1,
6062                         compose->header_nextrow, compose->header_nextrow+1,
6063                         GTK_SHRINK, GTK_FILL, 0, 0);
6064         if (compose->header_last) {     
6065                 const gchar *last_header_entry = gtk_entry_get_text(
6066                                 GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6067                 string = headers;
6068                 while (*string != NULL) {
6069                         if (!strcmp(*string, last_header_entry))
6070                                 standard_header = TRUE;
6071                         string++;
6072                 }
6073                 if (standard_header)
6074                         header = gtk_entry_get_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child));
6075         }
6076         if (!compose->header_last || !standard_header) {
6077                 switch(compose->account->protocol) {
6078                         case A_NNTP:
6079                                 header = prefs_common_translated_header_name("Newsgroups:");
6080                                 break;
6081                         default:
6082                                 header = prefs_common_translated_header_name("To:");
6083                                 break;
6084                 }                                                                   
6085         }
6086         if (header)
6087                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(combo)->child), header);
6088
6089         g_signal_connect_after(G_OBJECT(GTK_BIN(combo)->child), "grab_focus",
6090                          G_CALLBACK(compose_grab_focus_cb), compose);
6091
6092         /* Entry field */
6093         entry = gtk_entry_new(); 
6094         gtk_widget_show(entry);
6095         gtk_tooltips_set_tip(compose->tooltips, entry,
6096                 _("Use <tab> to autocomplete from addressbook"), NULL);
6097         gtk_table_attach(GTK_TABLE(compose->header_table), entry, 1, 2,
6098                         compose->header_nextrow, compose->header_nextrow+1,
6099                         GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
6100
6101         g_signal_connect(G_OBJECT(entry), "key-press-event", 
6102                          G_CALLBACK(compose_headerentry_key_press_event_cb), 
6103                          headerentry);
6104         g_signal_connect(G_OBJECT(entry), "changed", 
6105                          G_CALLBACK(compose_headerentry_changed_cb), 
6106                          headerentry);
6107         g_signal_connect_after(G_OBJECT(entry), "grab_focus",
6108                          G_CALLBACK(compose_grab_focus_cb), compose);
6109                          
6110         /* email dnd */
6111         gtk_drag_dest_set(entry, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6112                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6113                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6114         g_signal_connect(G_OBJECT(entry), "drag_data_received",
6115                          G_CALLBACK(compose_header_drag_received_cb),
6116                          entry);
6117         g_signal_connect(G_OBJECT(entry), "drag-drop",
6118                          G_CALLBACK(compose_drag_drop),
6119                          compose);
6120         g_signal_connect(G_OBJECT(entry), "populate-popup",
6121                          G_CALLBACK(compose_entry_popup_extend),
6122                          NULL);
6123         
6124         address_completion_register_entry(GTK_ENTRY(entry), TRUE);
6125
6126         headerentry->compose = compose;
6127         headerentry->combo = combo;
6128         headerentry->entry = entry;
6129         headerentry->headernum = compose->header_nextrow;
6130
6131         compose->header_nextrow++;
6132         compose->header_last = headerentry;             
6133         compose->header_list =
6134                 g_slist_append(compose->header_list,
6135                                headerentry);
6136 }
6137
6138 static void compose_add_header_entry(Compose *compose, const gchar *header, gchar *text) 
6139 {
6140         ComposeHeaderEntry *last_header;
6141         
6142         last_header = compose->header_last;
6143
6144         gtk_entry_set_text(GTK_ENTRY(GTK_BIN(last_header->combo)->child), header);
6145         gtk_entry_set_text(GTK_ENTRY(last_header->entry), text);
6146 }
6147
6148 static void compose_remove_header_entries(Compose *compose) 
6149 {
6150         GSList *list;
6151         for (list = compose->header_list; list; list = list->next) {
6152                 ComposeHeaderEntry *headerentry = 
6153                         (ComposeHeaderEntry *)list->data;
6154                 gtk_widget_destroy(headerentry->combo);
6155                 gtk_widget_destroy(headerentry->entry);
6156                 g_free(headerentry);
6157         }
6158         compose->header_last = NULL;
6159         g_slist_free(compose->header_list);
6160         compose->header_list = NULL;
6161         compose->header_nextrow = 1;
6162         compose_create_header_entry(compose);
6163 }
6164
6165 static GtkWidget *compose_create_header(Compose *compose) 
6166 {
6167         GtkWidget *from_optmenu_hbox;
6168         GtkWidget *header_scrolledwin;
6169         GtkWidget *header_table;
6170
6171         gint count = 0;
6172
6173         /* header labels and entries */
6174         header_scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6175         gtk_widget_show(header_scrolledwin);
6176         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(header_scrolledwin), GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
6177
6178         header_table = gtk_table_new(2, 2, FALSE);
6179         gtk_widget_show(header_table);
6180         gtk_container_set_border_width(GTK_CONTAINER(header_table), BORDER_WIDTH);
6181         gtk_scrolled_window_add_with_viewport(GTK_SCROLLED_WINDOW(header_scrolledwin), header_table);
6182         gtk_viewport_set_shadow_type(GTK_VIEWPORT(GTK_BIN(header_scrolledwin)->child), GTK_SHADOW_NONE);
6183         count = 0;
6184
6185         /* option menu for selecting accounts */
6186         from_optmenu_hbox = compose_account_option_menu_create(compose);
6187         gtk_table_attach(GTK_TABLE(header_table), from_optmenu_hbox,
6188                                   0, 2, count, count + 1, GTK_EXPAND | GTK_FILL, GTK_SHRINK, 0, 0);
6189         count++;
6190
6191         compose->header_table = header_table;
6192         compose->header_list = NULL;
6193         compose->header_nextrow = count;
6194
6195         compose_create_header_entry(compose);
6196
6197         compose->table            = NULL;
6198
6199         return header_scrolledwin ;
6200 }
6201
6202 static gboolean popup_attach_button_pressed(GtkWidget *widget, gpointer data)
6203 {
6204         Compose *compose = (Compose *)data;
6205         GdkEventButton event;
6206         
6207         event.button = 3;
6208         event.time = gtk_get_current_event_time();
6209
6210         return attach_button_pressed(compose->attach_clist, &event, compose);
6211 }
6212
6213 static GtkWidget *compose_create_attach(Compose *compose)
6214 {
6215         GtkWidget *attach_scrwin;
6216         GtkWidget *attach_clist;
6217
6218         GtkListStore *store;
6219         GtkCellRenderer *renderer;
6220         GtkTreeViewColumn *column;
6221         GtkTreeSelection *selection;
6222
6223         /* attachment list */
6224         attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
6225         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
6226                                        GTK_POLICY_AUTOMATIC,
6227                                        GTK_POLICY_AUTOMATIC);
6228         gtk_widget_set_size_request(attach_scrwin, -1, 80);
6229
6230         store = gtk_list_store_new(N_ATTACH_COLS, 
6231                                    G_TYPE_STRING,
6232                                    G_TYPE_STRING,
6233                                    G_TYPE_STRING,
6234                                    G_TYPE_POINTER,
6235                                    G_TYPE_AUTO_POINTER,
6236                                    -1);
6237         attach_clist = GTK_WIDGET(gtk_tree_view_new_with_model
6238                                         (GTK_TREE_MODEL(store)));
6239         gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_clist);
6240         g_object_unref(store);
6241         
6242         renderer = gtk_cell_renderer_text_new();
6243         column = gtk_tree_view_column_new_with_attributes
6244                         (_("Mime type"), renderer, "text", 
6245                          COL_MIMETYPE, NULL);
6246         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6247         
6248         renderer = gtk_cell_renderer_text_new();
6249         column = gtk_tree_view_column_new_with_attributes
6250                         (_("Size"), renderer, "text", 
6251                          COL_SIZE, NULL);
6252         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);                        
6253         
6254         renderer = gtk_cell_renderer_text_new();
6255         column = gtk_tree_view_column_new_with_attributes
6256                         (_("Name"), renderer, "text", 
6257                          COL_NAME, NULL);
6258         gtk_tree_view_append_column(GTK_TREE_VIEW(attach_clist), column);
6259
6260         gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_clist),
6261                                      prefs_common.use_stripes_everywhere);
6262         selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_clist));
6263         gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
6264
6265         g_signal_connect(G_OBJECT(attach_clist), "row_activated",
6266                          G_CALLBACK(attach_selected), compose);
6267         g_signal_connect(G_OBJECT(attach_clist), "button_press_event",
6268                          G_CALLBACK(attach_button_pressed), compose);
6269 #ifndef MAEMO
6270         g_signal_connect(G_OBJECT(attach_clist), "popup-menu",
6271                          G_CALLBACK(popup_attach_button_pressed), compose);
6272 #else
6273         gtk_widget_tap_and_hold_setup(GTK_WIDGET(attach_clist), NULL, NULL,
6274                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6275         g_signal_connect(G_OBJECT(attach_clist), "tap-and-hold",
6276                          G_CALLBACK(popup_attach_button_pressed), compose);
6277 #endif
6278         g_signal_connect(G_OBJECT(attach_clist), "key_press_event",
6279                          G_CALLBACK(attach_key_pressed), compose);
6280
6281         /* drag and drop */
6282         gtk_drag_dest_set(attach_clist,
6283                           GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6284                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6285                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6286         g_signal_connect(G_OBJECT(attach_clist), "drag_data_received",
6287                          G_CALLBACK(compose_attach_drag_received_cb),
6288                          compose);
6289         g_signal_connect(G_OBJECT(attach_clist), "drag-drop",
6290                          G_CALLBACK(compose_drag_drop),
6291                          compose);
6292
6293         compose->attach_scrwin = attach_scrwin;
6294         compose->attach_clist  = attach_clist;
6295
6296         return attach_scrwin;
6297 }
6298
6299 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose);
6300 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose);
6301
6302 static GtkWidget *compose_create_others(Compose *compose)
6303 {
6304         GtkWidget *table;
6305         GtkWidget *savemsg_checkbtn;
6306         GtkWidget *savemsg_entry;
6307         GtkWidget *savemsg_select;
6308         
6309         guint rowcount = 0;
6310         gchar *folderidentifier;
6311
6312         /* Table for settings */
6313         table = gtk_table_new(3, 1, FALSE);
6314         gtk_container_set_border_width(GTK_CONTAINER(table), BORDER_WIDTH);
6315         gtk_widget_show(table);
6316         gtk_table_set_row_spacings(GTK_TABLE(table), VSPACING_NARROW);
6317         rowcount = 0;
6318
6319         /* Save Message to folder */
6320         savemsg_checkbtn = gtk_check_button_new_with_label(_("Save Message to "));
6321         gtk_widget_show(savemsg_checkbtn);
6322         gtk_table_attach(GTK_TABLE(table), savemsg_checkbtn, 0, 1, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6323         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6324                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(savemsg_checkbtn), prefs_common.savemsg);
6325         }
6326         g_signal_connect(G_OBJECT(savemsg_checkbtn), "toggled",
6327                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
6328
6329         savemsg_entry = gtk_entry_new();
6330         gtk_widget_show(savemsg_entry);
6331         gtk_table_attach_defaults(GTK_TABLE(table), savemsg_entry, 1, 2, rowcount, rowcount + 1);
6332         gtk_editable_set_editable(GTK_EDITABLE(savemsg_entry), prefs_common.savemsg);
6333         g_signal_connect_after(G_OBJECT(savemsg_entry), "grab_focus",
6334                          G_CALLBACK(compose_grab_focus_cb), compose);
6335         if (account_get_special_folder(compose->account, F_OUTBOX)) {
6336                 folderidentifier = folder_item_get_identifier(account_get_special_folder
6337                                   (compose->account, F_OUTBOX));
6338                 gtk_entry_set_text(GTK_ENTRY(savemsg_entry), folderidentifier);
6339                 g_free(folderidentifier);
6340         }
6341
6342         savemsg_select = gtkut_get_browse_file_btn(_("_Browse"));
6343         gtk_widget_show(savemsg_select);
6344         gtk_table_attach(GTK_TABLE(table), savemsg_select, 2, 3, rowcount, rowcount + 1, GTK_SHRINK | GTK_FILL, GTK_SHRINK, 0, 0);
6345         g_signal_connect(G_OBJECT(savemsg_select), "clicked",
6346                          G_CALLBACK(compose_savemsg_select_cb),
6347                          compose);
6348
6349         rowcount++;
6350
6351         compose->savemsg_checkbtn = savemsg_checkbtn;
6352         compose->savemsg_entry = savemsg_entry;
6353
6354         return table;   
6355 }
6356
6357 static void compose_savemsg_checkbtn_cb(GtkWidget *widget, Compose *compose) 
6358 {
6359         gtk_editable_set_editable(GTK_EDITABLE(compose->savemsg_entry),
6360                 gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn)));
6361 }
6362
6363 static void compose_savemsg_select_cb(GtkWidget *widget, Compose *compose)
6364 {
6365         FolderItem *dest;
6366         gchar * path;
6367
6368         dest = foldersel_folder_sel(NULL, FOLDER_SEL_COPY, NULL);
6369         if (!dest) return;
6370
6371         path = folder_item_get_identifier(dest);
6372
6373         gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), path);
6374         g_free(path);
6375 }
6376
6377 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, gboolean wrap,
6378                                   GdkAtom clip, GtkTextIter *insert_place);
6379
6380
6381 static gboolean text_clicked(GtkWidget *text, GdkEventButton *event,
6382                                        Compose *compose)
6383 {
6384         gint prev_autowrap;
6385         GtkTextBuffer *buffer;
6386 #if USE_ASPELL
6387         if (event->button == 3) {
6388                 GtkTextIter iter;
6389                 GtkTextIter sel_start, sel_end;
6390                 gboolean stuff_selected;
6391                 gint x, y;
6392                 /* move the cursor to allow GtkAspell to check the word
6393                  * under the mouse */
6394                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6395                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6396                         &x, &y);
6397                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6398                         &iter, x, y);
6399                 /* get selection */
6400                 stuff_selected = gtk_text_buffer_get_selection_bounds(
6401                                 GTK_TEXT_VIEW(text)->buffer,
6402                                 &sel_start, &sel_end);
6403
6404                 gtk_text_buffer_place_cursor (GTK_TEXT_VIEW(text)->buffer, &iter);
6405                 /* reselect stuff */
6406                 if (stuff_selected 
6407                 && gtk_text_iter_in_range(&iter, &sel_start, &sel_end)) {
6408                         gtk_text_buffer_select_range(GTK_TEXT_VIEW(text)->buffer,
6409                                 &sel_start, &sel_end);
6410                 }
6411                 return FALSE; /* pass the event so that the right-click goes through */
6412         }
6413 #endif
6414         if (event->button == 2) {
6415                 GtkTextIter iter;
6416                 gint x, y;
6417                 BLOCK_WRAP();
6418                 
6419                 /* get the middle-click position to paste at the correct place */
6420                 gtk_text_view_window_to_buffer_coords(GTK_TEXT_VIEW(text),
6421                         GTK_TEXT_WINDOW_TEXT, event->x, event->y,
6422                         &x, &y);
6423                 gtk_text_view_get_iter_at_location (GTK_TEXT_VIEW(text),
6424                         &iter, x, y);
6425                 
6426                 entry_paste_clipboard(compose, text, 
6427                                 prefs_common.linewrap_pastes,
6428                                 GDK_SELECTION_PRIMARY, &iter);
6429                 UNBLOCK_WRAP();
6430                 return TRUE;
6431         }
6432         return FALSE;
6433 }
6434
6435 #if USE_ASPELL
6436 static void compose_spell_menu_changed(void *data)
6437 {
6438         Compose *compose = (Compose *)data;
6439         GSList *items;
6440         GtkWidget *menuitem;
6441         GtkWidget *parent_item;
6442         GtkMenu *menu = GTK_MENU(gtk_menu_new());
6443         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
6444         GSList *spell_menu;
6445
6446         if (compose->gtkaspell == NULL)
6447                 return;
6448
6449         parent_item = gtk_item_factory_get_item(ifactory, 
6450                         "/Spelling/Options");
6451
6452         /* setting the submenu removes /Spelling/Options from the factory 
6453          * so we need to save it */
6454
6455         if (parent_item == NULL) {
6456                 parent_item = compose->aspell_options_menu;
6457                 gtk_menu_item_remove_submenu(GTK_MENU_ITEM(parent_item));
6458         } else
6459                 compose->aspell_options_menu = parent_item;
6460
6461         spell_menu = gtkaspell_make_config_menu(compose->gtkaspell);
6462
6463         spell_menu = g_slist_reverse(spell_menu);
6464         for (items = spell_menu;
6465              items; items = items->next) {
6466                 menuitem = GTK_WIDGET(GTK_MENU_ITEM(items->data));
6467                 gtk_menu_shell_prepend(GTK_MENU_SHELL(menu), GTK_WIDGET(menuitem));
6468                 gtk_widget_show(GTK_WIDGET(menuitem));
6469         }
6470         g_slist_free(spell_menu);
6471
6472         gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), GTK_WIDGET(menu));
6473         
6474 }
6475 #endif
6476
6477 static gboolean compose_popup_menu(GtkWidget *widget, gpointer data)
6478 {
6479         Compose *compose = (Compose *)data;
6480         GdkEventButton event;
6481         
6482         event.button = 3;
6483         event.time = gtk_get_current_event_time();
6484
6485         return text_clicked(compose->text, &event, compose);
6486 }
6487
6488 static gboolean compose_force_window_origin = TRUE;
6489 static Compose *compose_create(PrefsAccount *account,
6490                                                  FolderItem *folder,
6491                                                  ComposeMode mode,
6492                                                  gboolean batch)
6493 {
6494         Compose   *compose;
6495         GtkWidget *window;
6496         GtkWidget *vbox;
6497         GtkWidget *menubar;
6498         GtkWidget *handlebox;
6499
6500         GtkWidget *notebook;
6501         
6502         GtkWidget *attach_hbox;
6503         GtkWidget *attach_lab1;
6504         GtkWidget *attach_lab2;
6505
6506         GtkWidget *vbox2;
6507
6508         GtkWidget *label;
6509         GtkWidget *subject_hbox;
6510         GtkWidget *subject_frame;
6511         GtkWidget *subject_entry;
6512         GtkWidget *subject;
6513         GtkWidget *paned;
6514
6515         GtkWidget *edit_vbox;
6516         GtkWidget *ruler_hbox;
6517         GtkWidget *ruler;
6518         GtkWidget *scrolledwin;
6519         GtkWidget *text;
6520         GtkTextBuffer *buffer;
6521         GtkClipboard *clipboard;
6522
6523         UndoMain *undostruct;
6524
6525         gchar *titles[N_ATTACH_COLS];
6526         guint n_menu_entries;
6527         GtkWidget *popupmenu;
6528         GtkItemFactory *popupfactory;
6529         GtkItemFactory *ifactory;
6530         GtkWidget *tmpl_menu;
6531         gint n_entries;
6532         GtkWidget *menuitem;
6533
6534 #if USE_ASPELL
6535         GtkAspell * gtkaspell = NULL;
6536 #endif
6537
6538         static GdkGeometry geometry;
6539
6540         g_return_val_if_fail(account != NULL, NULL);
6541
6542         debug_print("Creating compose window...\n");
6543         compose = g_new0(Compose, 1);
6544
6545         titles[COL_MIMETYPE] = _("MIME type");
6546         titles[COL_SIZE]     = _("Size");
6547         titles[COL_NAME]     = _("Name");
6548
6549         compose->batch = batch;
6550         compose->account = account;
6551         compose->folder = folder;
6552         
6553         compose->mutex = g_mutex_new();
6554         compose->set_cursor_pos = -1;
6555
6556         compose->tooltips = gtk_tooltips_new();
6557
6558         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose");
6559
6560         gtk_window_set_resizable(GTK_WINDOW(window), TRUE);
6561         gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
6562
6563         if (!geometry.max_width) {
6564                 geometry.max_width = gdk_screen_width();
6565                 geometry.max_height = gdk_screen_height();
6566         }
6567
6568         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6569                                       &geometry, GDK_HINT_MAX_SIZE);
6570         if (!geometry.min_width) {
6571                 geometry.min_width = 600;
6572                 geometry.min_height = 480;
6573         }
6574         gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
6575                                       &geometry, GDK_HINT_MIN_SIZE);
6576
6577 #ifndef MAEMO   
6578         if (compose_force_window_origin)
6579                 gtk_widget_set_uposition(window, prefs_common.compose_x, 
6580                                  prefs_common.compose_y);
6581 #endif
6582         g_signal_connect(G_OBJECT(window), "delete_event",
6583                          G_CALLBACK(compose_delete_cb), compose);
6584         MANAGE_WINDOW_SIGNALS_CONNECT(window);
6585         gtk_widget_realize(window);
6586
6587         gtkut_widget_set_composer_icon(window);
6588
6589         vbox = gtk_vbox_new(FALSE, 0);
6590         gtk_container_add(GTK_CONTAINER(window), vbox);
6591
6592         n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
6593         menubar = menubar_create(window, compose_entries,
6594                                  n_menu_entries, "<Compose>", compose);
6595         gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
6596
6597         if (prefs_common.toolbar_detachable) {
6598                 handlebox = gtk_handle_box_new();
6599         } else {
6600                 handlebox = gtk_hbox_new(FALSE, 0);
6601         }
6602         gtk_box_pack_start(GTK_BOX(vbox), handlebox, FALSE, FALSE, 0);
6603
6604         gtk_widget_realize(handlebox);
6605 #ifdef MAEMO
6606         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, window,
6607                                           (gpointer)compose);
6608 #else
6609         compose->toolbar = toolbar_create(TOOLBAR_COMPOSE, handlebox,
6610                                           (gpointer)compose);
6611 #endif
6612
6613         vbox2 = gtk_vbox_new(FALSE, 2);
6614         gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
6615         gtk_container_set_border_width(GTK_CONTAINER(vbox2), 0);
6616         
6617         /* Notebook */
6618         notebook = gtk_notebook_new();
6619         gtk_widget_set_size_request(notebook, -1, 130);
6620         gtk_widget_show(notebook);
6621
6622         /* header labels and entries */
6623         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6624                         compose_create_header(compose),
6625                         gtk_label_new_with_mnemonic(_("Hea_der")));
6626         /* attachment list */
6627         attach_hbox = gtk_hbox_new(FALSE, 0);
6628         gtk_widget_show(attach_hbox);
6629         
6630         attach_lab1 = gtk_label_new_with_mnemonic(_("_Attachments"));
6631         gtk_widget_show(attach_lab1);
6632         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab1, TRUE, TRUE, 0);
6633         
6634         attach_lab2 = gtk_label_new("");
6635         gtk_widget_show(attach_lab2);
6636         gtk_box_pack_start(GTK_BOX(attach_hbox), attach_lab2, FALSE, FALSE, 0);
6637         
6638         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6639                         compose_create_attach(compose),
6640                         attach_hbox);
6641         /* Others Tab */
6642         gtk_notebook_append_page(GTK_NOTEBOOK(notebook),
6643                         compose_create_others(compose),
6644                         gtk_label_new_with_mnemonic(_("Othe_rs")));
6645
6646         /* Subject */
6647         subject_hbox = gtk_hbox_new(FALSE, 0);
6648         gtk_widget_show(subject_hbox);
6649
6650         subject_frame = gtk_frame_new(NULL);
6651         gtk_frame_set_shadow_type(GTK_FRAME(subject_frame), GTK_SHADOW_NONE);
6652         gtk_box_pack_start(GTK_BOX(subject_hbox), subject_frame, TRUE, TRUE, 0);
6653         gtk_widget_show(subject_frame);
6654
6655         subject = gtk_hbox_new(FALSE, HSPACING_NARROW);
6656         gtk_container_set_border_width(GTK_CONTAINER(subject), 0);
6657         gtk_widget_show(subject);
6658
6659         label = gtk_label_new(_("Subject:"));
6660         gtk_box_pack_start(GTK_BOX(subject), label, FALSE, FALSE, 0);
6661         gtk_widget_show(label);
6662
6663         subject_entry = gtk_entry_new();
6664         gtk_box_pack_start(GTK_BOX(subject), subject_entry, TRUE, TRUE, 0);
6665         g_signal_connect_after(G_OBJECT(subject_entry), "grab_focus",
6666                          G_CALLBACK(compose_grab_focus_cb), compose);
6667         gtk_widget_show(subject_entry);
6668         compose->subject_entry = subject_entry;
6669         gtk_container_add(GTK_CONTAINER(subject_frame), subject);
6670         
6671         edit_vbox = gtk_vbox_new(FALSE, 0);
6672
6673         gtk_box_pack_start(GTK_BOX(edit_vbox), subject_hbox, FALSE, FALSE, 0);
6674
6675         /* ruler */
6676         ruler_hbox = gtk_hbox_new(FALSE, 0);
6677         gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
6678
6679         ruler = gtk_shruler_new();
6680         gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
6681         gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE,
6682                            BORDER_WIDTH);
6683
6684         /* text widget */
6685         scrolledwin = gtk_scrolled_window_new(NULL, NULL);
6686         gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
6687                                        GTK_POLICY_AUTOMATIC,
6688                                        GTK_POLICY_AUTOMATIC);
6689         gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
6690                                             GTK_SHADOW_IN);
6691         gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
6692         gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width, -1);
6693
6694         text = gtk_text_view_new();
6695         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
6696         gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD_CHAR);
6697         gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
6698         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6699         gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
6700         
6701         gtk_container_add(GTK_CONTAINER(scrolledwin), text);
6702
6703         g_signal_connect_after(G_OBJECT(text), "size_allocate",
6704                                G_CALLBACK(compose_edit_size_alloc),
6705                                ruler);
6706         g_signal_connect(G_OBJECT(buffer), "changed",
6707                          G_CALLBACK(compose_changed_cb), compose);
6708         g_signal_connect(G_OBJECT(text), "grab_focus",
6709                          G_CALLBACK(compose_grab_focus_cb), compose);
6710         g_signal_connect(G_OBJECT(buffer), "insert_text",
6711                          G_CALLBACK(text_inserted), compose);
6712         g_signal_connect(G_OBJECT(text), "button_press_event",
6713                          G_CALLBACK(text_clicked), compose);
6714 #ifndef MAEMO
6715         g_signal_connect(G_OBJECT(text), "popup-menu",
6716                          G_CALLBACK(compose_popup_menu), compose);
6717 #else
6718         gtk_widget_tap_and_hold_setup(GTK_WIDGET(text), NULL, NULL,
6719                         GTK_TAP_AND_HOLD_NONE | GTK_TAP_AND_HOLD_NO_INTERNALS);
6720         g_signal_connect(G_OBJECT(text), "tap-and-hold",
6721                          G_CALLBACK(compose_popup_menu), compose);
6722 #endif
6723         g_signal_connect(G_OBJECT(subject_entry), "changed",
6724                          G_CALLBACK(compose_changed_cb), compose);
6725
6726         /* drag and drop */
6727         gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_mime_types, 
6728                           sizeof(compose_mime_types)/sizeof(compose_mime_types[0]),
6729                           GDK_ACTION_COPY | GDK_ACTION_MOVE);
6730         g_signal_connect(G_OBJECT(text), "drag_data_received",
6731                          G_CALLBACK(compose_insert_drag_received_cb),
6732                          compose);
6733         g_signal_connect(G_OBJECT(text), "drag-drop",
6734                          G_CALLBACK(compose_drag_drop),
6735                          compose);
6736         gtk_widget_show_all(vbox);
6737
6738         /* pane between attach clist and text */
6739         paned = gtk_vpaned_new();
6740         gtk_paned_set_gutter_size(GTK_PANED(paned), 12);
6741         gtk_container_add(GTK_CONTAINER(vbox2), paned);
6742 #ifdef MAEMO
6743         if( maemo_mainwindow_is_fullscreen(mainwindow_get_mainwindow()->window) )
6744                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 300 : 280);
6745         else
6746                 gtk_widget_set_size_request(edit_vbox, -1, mode == COMPOSE_NEW ? 250 : 230);
6747 #endif
6748         gtk_paned_add1(GTK_PANED(paned), notebook);
6749         gtk_paned_add2(GTK_PANED(paned), edit_vbox);
6750         gtk_widget_show_all(paned);
6751
6752
6753         if (prefs_common.textfont) {
6754                 PangoFontDescription *font_desc;
6755
6756                 font_desc = pango_font_description_from_string
6757                         (prefs_common.textfont);
6758                 if (font_desc) {
6759                         gtk_widget_modify_font(text, font_desc);
6760                         pango_font_description_free(font_desc);
6761                 }
6762         }
6763
6764         n_entries = sizeof(compose_popup_entries) /
6765                 sizeof(compose_popup_entries[0]);
6766         popupmenu = menu_create_items(compose_popup_entries, n_entries,
6767                                       "<Compose>", &popupfactory,
6768                                       compose);
6769
6770         ifactory = gtk_item_factory_from_widget(menubar);
6771         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
6772         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
6773         menu_set_sensitive(ifactory, "/Options/Remove references", FALSE);
6774
6775         tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6776
6777         undostruct = undo_init(text);
6778         undo_set_change_state_func(undostruct, &compose_undo_state_changed,
6779                                    menubar);
6780
6781         address_completion_start(window);
6782
6783         compose->window        = window;
6784         compose->vbox          = vbox;
6785         compose->menubar       = menubar;
6786         compose->handlebox     = handlebox;
6787
6788         compose->vbox2         = vbox2;
6789
6790         compose->paned = paned;
6791
6792         compose->attach_label  = attach_lab2;
6793
6794         compose->notebook      = notebook;
6795         compose->edit_vbox     = edit_vbox;
6796         compose->ruler_hbox    = ruler_hbox;
6797         compose->ruler         = ruler;
6798         compose->scrolledwin   = scrolledwin;
6799         compose->text          = text;
6800
6801         compose->focused_editable = NULL;
6802
6803         compose->popupmenu    = popupmenu;
6804         compose->popupfactory = popupfactory;
6805
6806         compose->tmpl_menu = tmpl_menu;
6807
6808         compose->mode = mode;
6809         compose->rmode = mode;
6810
6811         compose->targetinfo = NULL;
6812         compose->replyinfo  = NULL;
6813         compose->fwdinfo    = NULL;
6814
6815         compose->replyto     = NULL;
6816         compose->cc          = NULL;
6817         compose->bcc         = NULL;
6818         compose->followup_to = NULL;
6819
6820         compose->ml_post     = NULL;
6821
6822         compose->inreplyto   = NULL;
6823         compose->references  = NULL;
6824         compose->msgid       = NULL;
6825         compose->boundary    = NULL;
6826
6827         compose->autowrap       = prefs_common.autowrap;
6828
6829         compose->use_signing    = FALSE;
6830         compose->use_encryption = FALSE;
6831         compose->privacy_system = NULL;
6832
6833         compose->modified = FALSE;
6834
6835         compose->return_receipt = FALSE;
6836
6837         compose->to_list        = NULL;
6838         compose->newsgroup_list = NULL;
6839
6840         compose->undostruct = undostruct;
6841
6842         compose->sig_str = NULL;
6843
6844         compose->exteditor_file    = NULL;
6845         compose->exteditor_pid     = -1;
6846         compose->exteditor_tag     = -1;
6847         compose->draft_timeout_tag = -2; /* inhibit auto-drafting while loading */
6848
6849 #if USE_ASPELL
6850         menu_set_sensitive(ifactory, "/Spelling", FALSE);
6851         if (mode != COMPOSE_REDIRECT) {
6852                 if (prefs_common.enable_aspell && prefs_common.dictionary &&
6853                     strcmp(prefs_common.dictionary, "")) {
6854                         gtkaspell = gtkaspell_new(prefs_common.aspell_path,
6855                                                   prefs_common.dictionary,
6856                                                   prefs_common.alt_dictionary,
6857                                                   conv_get_locale_charset_str(),
6858                                                   prefs_common.misspelled_col,
6859                                                   prefs_common.check_while_typing,
6860                                                   prefs_common.recheck_when_changing_dict,
6861                                                   prefs_common.use_alternate,
6862                                                   prefs_common.use_both_dicts,
6863                                                   GTK_TEXT_VIEW(text),
6864                                                   GTK_WINDOW(compose->window),
6865                                                   compose_spell_menu_changed,
6866                                                   compose);
6867                         if (!gtkaspell) {
6868                                 alertpanel_error(_("Spell checker could not "
6869                                                 "be started.\n%s"),
6870                                                 gtkaspell_checkers_strerror());
6871                                 gtkaspell_checkers_reset_error();
6872                         } else {
6873                                 if (!gtkaspell_set_sug_mode(gtkaspell,
6874                                                 prefs_common.aspell_sugmode)) {
6875                                         debug_print("Aspell: could not set "
6876                                                     "suggestion mode %s\n",
6877                                                     gtkaspell_checkers_strerror());
6878                                         gtkaspell_checkers_reset_error();
6879                                 }
6880
6881                                 menu_set_sensitive(ifactory, "/Spelling", TRUE);
6882                         }
6883                 }
6884         }
6885         compose->gtkaspell = gtkaspell;
6886         compose_spell_menu_changed(compose);
6887 #endif
6888
6889         compose_select_account(compose, account, TRUE);
6890
6891         menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
6892         if (account->set_autocc && account->auto_cc && mode != COMPOSE_REEDIT)
6893                 compose_entry_append(compose, account->auto_cc, COMPOSE_CC);
6894
6895         if (account->set_autobcc && account->auto_bcc && mode != COMPOSE_REEDIT) 
6896                 compose_entry_append(compose, account->auto_bcc, COMPOSE_BCC);
6897         
6898         if (account->set_autoreplyto && account->auto_replyto && mode != COMPOSE_REEDIT)
6899                 compose_entry_append(compose, account->auto_replyto, COMPOSE_REPLYTO);
6900
6901         menu_set_sensitive(ifactory, "/Options/Reply mode", compose->mode == COMPOSE_REPLY);
6902
6903         if (account->protocol != A_NNTP)
6904                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
6905                                 prefs_common_translated_header_name("To:"));
6906         else
6907                 gtk_entry_set_text(GTK_ENTRY(GTK_BIN(compose->header_last->combo)->child),
6908                                 prefs_common_translated_header_name("Newsgroups:"));
6909
6910         addressbook_set_target_compose(compose);
6911         
6912         if (mode != COMPOSE_REDIRECT)
6913                 compose_set_template_menu(compose);
6914         else {
6915                 menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Template");
6916                 menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
6917         }
6918
6919         compose_list = g_list_append(compose_list, compose);
6920
6921         if (!prefs_common.show_ruler)
6922                 gtk_widget_hide(ruler_hbox);
6923                 
6924         menuitem = gtk_item_factory_get_item(ifactory, "/Tools/Show ruler");
6925         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),
6926                                        prefs_common.show_ruler);
6927
6928         /* Priority */
6929         compose->priority = PRIORITY_NORMAL;
6930         compose_update_priority_menu_item(compose);
6931
6932         compose_set_out_encoding(compose);
6933         
6934         /* Actions menu */
6935         compose_update_actions_menu(compose);
6936
6937         /* Privacy Systems menu */
6938         compose_update_privacy_systems_menu(compose);
6939
6940         activate_privacy_system(compose, account, TRUE);
6941         toolbar_set_style(compose->toolbar->toolbar, compose->handlebox, prefs_common.toolbar_style);
6942         if (batch) {
6943                 gtk_widget_realize(window);
6944         } else {
6945                 gtk_widget_show(window);
6946 #ifdef MAEMO
6947                 maemo_window_full_screen_if_needed(GTK_WINDOW(window));
6948                 maemo_connect_key_press_to_mainwindow(GTK_WINDOW(window));
6949 #endif
6950         }
6951         
6952         return compose;
6953 }
6954
6955 static GtkWidget *compose_account_option_menu_create(Compose *compose)
6956 {
6957         GList *accounts;
6958         GtkWidget *hbox;
6959         GtkWidget *optmenu;
6960         GtkWidget *optmenubox;
6961         GtkListStore *menu;
6962         GtkTreeIter iter;
6963         GtkWidget *from_name = NULL;
6964
6965         gint num = 0, def_menu = 0;
6966         
6967         accounts = account_get_list();
6968         g_return_val_if_fail(accounts != NULL, NULL);
6969
6970         optmenubox = gtk_event_box_new();
6971         optmenu = gtkut_sc_combobox_create(optmenubox, FALSE);
6972         menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
6973
6974         hbox = gtk_hbox_new(FALSE, 6);
6975         from_name = gtk_entry_new();
6976         
6977         g_signal_connect_after(G_OBJECT(from_name), "grab_focus",
6978                          G_CALLBACK(compose_grab_focus_cb), compose);
6979
6980         for (; accounts != NULL; accounts = accounts->next, num++) {
6981                 PrefsAccount *ac = (PrefsAccount *)accounts->data;
6982                 gchar *name, *from = NULL;
6983
6984                 if (ac == compose->account) def_menu = num;
6985
6986                 name = g_markup_printf_escaped(_("From: <i>%s</i>"),
6987                                        ac->account_name);
6988                 
6989                 if (ac == compose->account) {
6990                         if (ac->name && *ac->name) {
6991                                 gchar *buf;
6992                                 QUOTE_IF_REQUIRED_NORMAL(buf, ac->name, return NULL);
6993                                 from = g_strdup_printf("%s <%s>",
6994                                                        buf, ac->address);
6995                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
6996                         } else {
6997                                 from = g_strdup_printf("%s",
6998                                                        ac->address);
6999                                 gtk_entry_set_text(GTK_ENTRY(from_name), from);
7000                         }
7001                 }
7002                 COMBOBOX_ADD(menu, name, ac->account_id);
7003                 g_free(name);
7004                 g_free(from);
7005         }
7006
7007         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), def_menu);
7008
7009         g_signal_connect(G_OBJECT(optmenu), "changed",
7010                         G_CALLBACK(account_activated),
7011                         compose);
7012         g_signal_connect(G_OBJECT(from_name), "populate-popup",
7013                          G_CALLBACK(compose_entry_popup_extend),
7014                          NULL);
7015
7016         gtk_box_pack_start(GTK_BOX(hbox), optmenubox, FALSE, FALSE, 0);
7017         gtk_box_pack_start(GTK_BOX(hbox), from_name, TRUE, TRUE, 0);
7018         
7019         gtk_tooltips_set_tip(compose->tooltips, optmenubox,
7020                 _("Account to use for this email"), NULL);
7021         gtk_tooltips_set_tip(compose->tooltips, from_name,
7022                 _("Sender address to be used"), NULL);
7023
7024         compose->from_name = from_name;
7025         
7026         return hbox;
7027 }
7028
7029 static void compose_set_priority_cb(gpointer data,
7030                                     guint action,
7031                                     GtkWidget *widget)
7032 {
7033         Compose *compose = (Compose *) data;
7034         compose->priority = action;
7035 }
7036
7037 static void compose_reply_change_mode(gpointer data,
7038                                     ComposeMode action,
7039                                     GtkWidget *widget)
7040 {
7041         Compose *compose = (Compose *) data;
7042         gboolean was_modified = compose->modified;
7043
7044         gboolean all = FALSE, ml = FALSE, sender = FALSE, followup = FALSE;
7045         
7046         g_return_if_fail(compose->replyinfo != NULL);
7047         
7048         if (action == COMPOSE_REPLY && prefs_common.default_reply_list)
7049                 ml = TRUE;
7050         if (action == COMPOSE_REPLY && compose->rmode == COMPOSE_FOLLOWUP_AND_REPLY_TO)
7051                 followup = TRUE;
7052         if (action == COMPOSE_REPLY_TO_ALL)
7053                 all = TRUE;
7054         if (action == COMPOSE_REPLY_TO_SENDER)
7055                 sender = TRUE;
7056         if (action == COMPOSE_REPLY_TO_LIST)
7057                 ml = TRUE;
7058
7059         compose_remove_header_entries(compose);
7060         compose_reply_set_entry(compose, compose->replyinfo, all, ml, sender, followup);
7061         if (compose->account->set_autocc && compose->account->auto_cc)
7062                 compose_entry_append(compose, compose->account->auto_cc, COMPOSE_CC);
7063
7064         if (compose->account->set_autobcc && compose->account->auto_bcc) 
7065                 compose_entry_append(compose, compose->account->auto_bcc, COMPOSE_BCC);
7066         
7067         if (compose->account->set_autoreplyto && compose->account->auto_replyto)
7068                 compose_entry_append(compose, compose->account->auto_replyto, COMPOSE_REPLYTO);
7069         compose_show_first_last_header(compose, TRUE);
7070         compose->modified = was_modified;
7071         compose_set_title(compose);
7072 }
7073
7074 static void compose_update_priority_menu_item(Compose * compose)
7075 {
7076         GtkItemFactory *ifactory;
7077         GtkWidget *menuitem = NULL;
7078
7079         ifactory = gtk_item_factory_from_widget(compose->menubar);
7080         
7081         switch (compose->priority) {
7082                 case PRIORITY_HIGHEST:
7083                         menuitem = gtk_item_factory_get_item
7084                                 (ifactory, "/Options/Priority/Highest");
7085                         break;
7086                 case PRIORITY_HIGH:
7087                         menuitem = gtk_item_factory_get_item
7088                                 (ifactory, "/Options/Priority/High");
7089                         break;
7090                 case PRIORITY_NORMAL:
7091                         menuitem = gtk_item_factory_get_item
7092                                 (ifactory, "/Options/Priority/Normal");
7093                         break;
7094                 case PRIORITY_LOW:
7095                         menuitem = gtk_item_factory_get_item
7096                                 (ifactory, "/Options/Priority/Low");
7097                         break;
7098                 case PRIORITY_LOWEST:
7099                         menuitem = gtk_item_factory_get_item
7100                                 (ifactory, "/Options/Priority/Lowest");
7101                         break;
7102         }
7103         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7104 }       
7105
7106 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data)
7107 {
7108         Compose *compose = (Compose *) data;
7109         gchar *systemid;
7110         GtkItemFactory *ifactory;
7111         gboolean can_sign = FALSE, can_encrypt = FALSE;
7112
7113         g_return_if_fail(GTK_IS_CHECK_MENU_ITEM(widget));
7114
7115         if (!GTK_CHECK_MENU_ITEM(widget)->active)
7116                 return;
7117
7118         systemid = g_object_get_data(G_OBJECT(widget), "privacy_system");
7119         g_free(compose->privacy_system);
7120         compose->privacy_system = NULL;
7121         if (systemid != NULL) {
7122                 compose->privacy_system = g_strdup(systemid);
7123
7124                 can_sign = privacy_system_can_sign(systemid);
7125                 can_encrypt = privacy_system_can_encrypt(systemid);
7126         }
7127
7128         debug_print("activated privacy system: %s\n", systemid != NULL ? systemid : "None");
7129
7130         ifactory = gtk_item_factory_from_widget(compose->menubar);
7131         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7132         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7133 }
7134
7135 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn)
7136 {
7137         static gchar *branch_path = "/Options/Privacy System";
7138         GtkItemFactory *ifactory;
7139         GtkWidget *menuitem = NULL;
7140         GList *amenu;
7141         gboolean can_sign = FALSE, can_encrypt = FALSE;
7142         gboolean found = FALSE;
7143
7144         ifactory = gtk_item_factory_from_widget(compose->menubar);
7145
7146         if (compose->privacy_system != NULL) {
7147                 gchar *systemid;
7148
7149                 menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7150                 g_return_if_fail(menuitem != NULL);
7151
7152                 amenu = GTK_MENU_SHELL(menuitem)->children;
7153                 menuitem = NULL;
7154                 while (amenu != NULL) {
7155                         GList *alist = amenu->next;
7156
7157                         systemid = g_object_get_data(G_OBJECT(amenu->data), "privacy_system");
7158                         if (systemid != NULL) {
7159                                 if (strcmp(systemid, compose->privacy_system) == 0) {
7160                                         menuitem = GTK_WIDGET(amenu->data);
7161
7162                                         can_sign = privacy_system_can_sign(systemid);
7163                                         can_encrypt = privacy_system_can_encrypt(systemid);
7164                                         found = TRUE;
7165                                         break;
7166                                 } 
7167                         } else if (strlen(compose->privacy_system) == 0) {
7168                                         menuitem = GTK_WIDGET(amenu->data);
7169
7170                                         can_sign = FALSE;
7171                                         can_encrypt = FALSE;
7172                                         found = TRUE;
7173                                         break;
7174                         }
7175
7176                         amenu = alist;
7177                 }
7178                 if (menuitem != NULL)
7179                         gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem), TRUE);
7180                 
7181                 if (warn && !found && strlen(compose->privacy_system)) {
7182                         alertpanel_warning(_("The privacy system '%s' cannot be loaded. You "
7183                                   "will not be able to sign or encrypt this message."),
7184                                   compose->privacy_system);
7185                 }
7186         } 
7187
7188         menu_set_sensitive(ifactory, "/Options/Sign", can_sign);
7189         menu_set_sensitive(ifactory, "/Options/Encrypt", can_encrypt);
7190 }       
7191  
7192 static void compose_set_out_encoding(Compose *compose)
7193 {
7194         GtkItemFactoryEntry *entry;
7195         GtkItemFactory *ifactory;
7196         CharSet out_encoding;
7197         gchar *path, *p, *q;
7198         GtkWidget *item;
7199
7200         out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
7201         ifactory = gtk_item_factory_from_widget(compose->menubar);
7202
7203         for (entry = compose_entries; entry->callback != compose_address_cb;
7204              entry++) {
7205                 if (entry->callback == compose_set_encoding_cb &&
7206                     (CharSet)entry->callback_action == out_encoding) {
7207                         p = q = path = g_strdup(entry->path);
7208                         while (*p) {
7209                                 if (*p == '_') {
7210                                         if (p[1] == '_') {
7211                                                 p++;
7212                                                 *q++ = '_';
7213                                         }
7214                                 } else
7215                                         *q++ = *p;
7216                                 p++;
7217                         }
7218                         *q = '\0';
7219                         item = gtk_item_factory_get_item(ifactory, path);
7220                         gtk_widget_activate(item);
7221                         g_free(path);
7222                         break;
7223                 }
7224         }
7225 }
7226
7227 static void compose_set_template_menu(Compose *compose)
7228 {
7229         GSList *tmpl_list, *cur;
7230         GtkWidget *menu;
7231         GtkWidget *item;
7232
7233         tmpl_list = template_get_config();
7234
7235         menu = gtk_menu_new();
7236
7237         for (cur = tmpl_list; cur != NULL; cur = cur->next) {
7238                 Template *tmpl = (Template *)cur->data;
7239
7240                 item = gtk_menu_item_new_with_label(tmpl->name);
7241                 gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
7242                 g_signal_connect(G_OBJECT(item), "activate",
7243                                  G_CALLBACK(compose_template_activate_cb),
7244                                  compose);
7245                 g_object_set_data(G_OBJECT(item), "template", tmpl);
7246                 gtk_widget_show(item);
7247         }
7248
7249         gtk_widget_show(menu);
7250         gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
7251 }
7252
7253 void compose_update_actions_menu(Compose *compose)
7254 {
7255         GtkItemFactory *ifactory;
7256
7257         ifactory = gtk_item_factory_from_widget(compose->menubar);
7258         action_update_compose_menu(ifactory, "/Tools/Actions", compose);
7259 }
7260
7261 static void compose_update_privacy_systems_menu(Compose *compose)
7262 {
7263         static gchar *branch_path = "/Options/Privacy System";
7264         GtkItemFactory *ifactory;
7265         GtkWidget *menuitem;
7266         GSList *systems, *cur;
7267         GList *amenu;
7268         GtkWidget *widget;
7269         GtkWidget *system_none;
7270         GSList *group;
7271
7272         ifactory = gtk_item_factory_from_widget(compose->menubar);
7273
7274         /* remove old entries */
7275         menuitem = gtk_item_factory_get_widget(ifactory, branch_path);
7276         g_return_if_fail(menuitem != NULL);
7277
7278         amenu = GTK_MENU_SHELL(menuitem)->children->next;
7279         while (amenu != NULL) {
7280                 GList *alist = amenu->next;
7281                 gtk_widget_destroy(GTK_WIDGET(amenu->data));
7282                 amenu = alist;
7283         }
7284
7285         system_none = gtk_item_factory_get_widget(ifactory,
7286                 "/Options/Privacy System/None");
7287
7288         g_signal_connect(G_OBJECT(system_none), "activate",
7289                 G_CALLBACK(compose_set_privacy_system_cb), compose);
7290
7291         systems = privacy_get_system_ids();
7292         for (cur = systems; cur != NULL; cur = g_slist_next(cur)) {
7293                 gchar *systemid = cur->data;
7294
7295                 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(system_none));
7296                 widget = gtk_radio_menu_item_new_with_label(group,
7297                         privacy_system_get_name(systemid));
7298                 g_object_set_data_full(G_OBJECT(widget), "privacy_system",
7299                                        g_strdup(systemid), g_free);
7300                 g_signal_connect(G_OBJECT(widget), "activate",
7301                         G_CALLBACK(compose_set_privacy_system_cb), compose);
7302
7303                 gtk_menu_append(GTK_MENU(system_none->parent), widget);
7304                 gtk_widget_show(widget);
7305                 g_free(systemid);
7306         }
7307         g_slist_free(systems);
7308 }
7309
7310 void compose_reflect_prefs_all(void)
7311 {
7312         GList *cur;
7313         Compose *compose;
7314
7315         for (cur = compose_list; cur != NULL; cur = cur->next) {
7316                 compose = (Compose *)cur->data;
7317                 compose_set_template_menu(compose);
7318         }
7319 }
7320
7321 void compose_reflect_prefs_pixmap_theme(void)
7322 {
7323         GList *cur;
7324         Compose *compose;
7325
7326         for (cur = compose_list; cur != NULL; cur = cur->next) {
7327                 compose = (Compose *)cur->data;
7328                 toolbar_update(TOOLBAR_COMPOSE, compose);
7329         }
7330 }
7331
7332 static const gchar *compose_quote_char_from_context(Compose *compose)
7333 {
7334         const gchar *qmark = NULL;
7335
7336         g_return_val_if_fail(compose != NULL, NULL);
7337
7338         switch (compose->mode) {
7339                 /* use forward-specific quote char */
7340                 case COMPOSE_FORWARD:
7341                 case COMPOSE_FORWARD_AS_ATTACH:
7342                 case COMPOSE_FORWARD_INLINE:
7343                         if (compose->folder && compose->folder->prefs &&
7344                                         compose->folder->prefs->forward_with_format)
7345                                 qmark = compose->folder->prefs->forward_quotemark;
7346                         else if (compose->account->forward_with_format)
7347                                 qmark = compose->account->forward_quotemark;
7348                         else
7349                                 qmark = prefs_common.fw_quotemark;
7350                         break;
7351
7352                 /* use reply-specific quote char in all other modes */
7353                 default:
7354                         if (compose->folder && compose->folder->prefs &&
7355                                         compose->folder->prefs->reply_with_format)
7356                                 qmark = compose->folder->prefs->reply_quotemark;
7357                         else if (compose->account->reply_with_format)
7358                                 qmark = compose->account->reply_quotemark;
7359                         else
7360                                 qmark = prefs_common.quotemark;
7361                         break;
7362         }
7363
7364         if (qmark == NULL || *qmark == '\0')
7365                 qmark = "> ";
7366
7367         return qmark;
7368 }
7369
7370 static void compose_template_apply(Compose *compose, Template *tmpl,
7371                                    gboolean replace)
7372 {
7373         GtkTextView *text;
7374         GtkTextBuffer *buffer;
7375         GtkTextMark *mark;
7376         GtkTextIter iter;
7377         const gchar *qmark;
7378         gchar *parsed_str = NULL;
7379         gint cursor_pos = 0;
7380         const gchar *err_msg = _("Template body format error at line %d.");
7381         if (!tmpl) return;
7382
7383         /* process the body */
7384
7385         text = GTK_TEXT_VIEW(compose->text);
7386         buffer = gtk_text_view_get_buffer(text);
7387
7388         if (tmpl->value) {
7389                 qmark = compose_quote_char_from_context(compose);
7390
7391                 if (compose->replyinfo != NULL) {
7392
7393                         if (replace)
7394                                 gtk_text_buffer_set_text(buffer, "", -1);
7395                         mark = gtk_text_buffer_get_insert(buffer);
7396                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7397
7398                         parsed_str = compose_quote_fmt(compose, compose->replyinfo,
7399                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7400
7401                 } else if (compose->fwdinfo != NULL) {
7402
7403                         if (replace)
7404                                 gtk_text_buffer_set_text(buffer, "", -1);
7405                         mark = gtk_text_buffer_get_insert(buffer);
7406                         gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7407
7408                         parsed_str = compose_quote_fmt(compose, compose->fwdinfo,
7409                                                    tmpl->value, qmark, NULL, FALSE, FALSE, err_msg);
7410
7411                 } else {
7412                         MsgInfo* dummyinfo = compose_msginfo_new_from_compose(compose);
7413
7414                         GtkTextIter start, end;
7415                         gchar *tmp = NULL;
7416
7417                         gtk_text_buffer_get_start_iter(buffer, &start);
7418                         gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
7419                         tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
7420
7421                         /* clear the buffer now */
7422                         if (replace)
7423                                 gtk_text_buffer_set_text(buffer, "", -1);
7424
7425                         parsed_str = compose_quote_fmt(compose, dummyinfo,
7426                                                            tmpl->value, qmark, tmp, FALSE, FALSE, err_msg);
7427                         procmsg_msginfo_free( dummyinfo );
7428
7429                         g_free( tmp );
7430                 } 
7431         } else {
7432                 if (replace)
7433                         gtk_text_buffer_set_text(buffer, "", -1);
7434                 mark = gtk_text_buffer_get_insert(buffer);
7435                 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
7436         }       
7437
7438         if (replace && parsed_str && compose->account->auto_sig)
7439                 compose_insert_sig(compose, FALSE);
7440
7441         if (replace && parsed_str) {
7442                 gtk_text_buffer_get_start_iter(buffer, &iter);
7443                 gtk_text_buffer_place_cursor(buffer, &iter);
7444         }
7445         
7446         if (parsed_str) {
7447                 cursor_pos = quote_fmt_get_cursor_pos();
7448                 compose->set_cursor_pos = cursor_pos;
7449                 if (cursor_pos == -1)
7450                         cursor_pos = 0;
7451                 gtk_text_buffer_get_start_iter(buffer, &iter);
7452                 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
7453                 gtk_text_buffer_place_cursor(buffer, &iter);
7454         }
7455
7456         /* process the other fields */
7457
7458         compose_template_apply_fields(compose, tmpl);
7459         compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
7460         quote_fmt_reset_vartable();
7461         compose_changed_cb(NULL, compose);
7462 }
7463
7464 static void compose_template_apply_fields(Compose *compose, Template *tmpl)
7465 {
7466         MsgInfo* dummyinfo = NULL;
7467         MsgInfo *msginfo = NULL;
7468         gchar *buf = NULL;
7469
7470         if (compose->replyinfo != NULL)
7471                 msginfo = compose->replyinfo;
7472         else if (compose->fwdinfo != NULL)
7473                 msginfo = compose->fwdinfo;
7474         else {
7475                 dummyinfo = compose_msginfo_new_from_compose(compose);
7476                 msginfo = dummyinfo;
7477         }
7478
7479         if (tmpl->to && *tmpl->to != '\0') {
7480 #ifdef USE_ASPELL
7481                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7482                                 compose->gtkaspell);
7483 #else
7484                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7485 #endif
7486                 quote_fmt_scan_string(tmpl->to);
7487                 quote_fmt_parse();
7488
7489                 buf = quote_fmt_get_buffer();
7490                 if (buf == NULL) {
7491                         alertpanel_error(_("Template To format error."));
7492                 } else {
7493                         compose_entry_append(compose, buf, COMPOSE_TO);
7494                 }
7495         }
7496
7497         if (tmpl->cc && *tmpl->cc != '\0') {
7498 #ifdef USE_ASPELL
7499                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7500                                 compose->gtkaspell);
7501 #else
7502                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7503 #endif
7504                 quote_fmt_scan_string(tmpl->cc);
7505                 quote_fmt_parse();
7506
7507                 buf = quote_fmt_get_buffer();
7508                 if (buf == NULL) {
7509                         alertpanel_error(_("Template Cc format error."));
7510                 } else {
7511                         compose_entry_append(compose, buf, COMPOSE_CC);
7512                 }
7513         }
7514
7515         if (tmpl->bcc && *tmpl->bcc != '\0') {
7516 #ifdef USE_ASPELL
7517                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7518                                 compose->gtkaspell);
7519 #else
7520                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7521 #endif
7522                 quote_fmt_scan_string(tmpl->bcc);
7523                 quote_fmt_parse();
7524
7525                 buf = quote_fmt_get_buffer();
7526                 if (buf == NULL) {
7527                         alertpanel_error(_("Template Bcc format error."));
7528                 } else {
7529                         compose_entry_append(compose, buf, COMPOSE_BCC);
7530                 }
7531         }
7532
7533         /* process the subject */
7534         if (tmpl->subject && *tmpl->subject != '\0') {
7535 #ifdef USE_ASPELL
7536                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account,
7537                                 compose->gtkaspell);
7538 #else
7539                 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account);
7540 #endif
7541                 quote_fmt_scan_string(tmpl->subject);
7542                 quote_fmt_parse();
7543
7544                 buf = quote_fmt_get_buffer();
7545                 if (buf == NULL) {
7546                         alertpanel_error(_("Template subject format error."));
7547                 } else {
7548                         gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
7549                 }
7550         }
7551
7552         procmsg_msginfo_free( dummyinfo );
7553 }
7554
7555 static void compose_destroy(Compose *compose)
7556 {
7557         GtkTextBuffer *buffer;
7558         GtkClipboard *clipboard;
7559
7560         compose_list = g_list_remove(compose_list, compose);
7561
7562         if (compose->updating) {
7563                 debug_print("danger, not destroying anything now\n");
7564                 compose->deferred_destroy = TRUE;
7565                 return;
7566         }
7567         /* NOTE: address_completion_end() does nothing with the window
7568          * however this may change. */
7569         address_completion_end(compose->window);
7570
7571         slist_free_strings(compose->to_list);
7572         g_slist_free(compose->to_list);
7573         slist_free_strings(compose->newsgroup_list);
7574         g_slist_free(compose->newsgroup_list);
7575         slist_free_strings(compose->header_list);
7576         g_slist_free(compose->header_list);
7577
7578         procmsg_msginfo_free(compose->targetinfo);
7579         procmsg_msginfo_free(compose->replyinfo);
7580         procmsg_msginfo_free(compose->fwdinfo);
7581
7582         g_free(compose->replyto);
7583         g_free(compose->cc);
7584         g_free(compose->bcc);
7585         g_free(compose->newsgroups);
7586         g_free(compose->followup_to);
7587
7588         g_free(compose->ml_post);
7589
7590         g_free(compose->inreplyto);
7591         g_free(compose->references);
7592         g_free(compose->msgid);
7593         g_free(compose->boundary);
7594
7595         g_free(compose->redirect_filename);
7596         if (compose->undostruct)
7597                 undo_destroy(compose->undostruct);
7598
7599         g_free(compose->sig_str);
7600
7601         g_free(compose->exteditor_file);
7602
7603         g_free(compose->orig_charset);
7604
7605         g_free(compose->privacy_system);
7606
7607         if (addressbook_get_target_compose() == compose)
7608                 addressbook_set_target_compose(NULL);
7609
7610 #if USE_ASPELL
7611         if (compose->gtkaspell) {
7612                 gtkaspell_delete(compose->gtkaspell);
7613                 compose->gtkaspell = NULL;
7614         }
7615 #endif
7616
7617         prefs_common.compose_width = compose->scrolledwin->allocation.width;
7618         prefs_common.compose_height = compose->window->allocation.height;
7619
7620         if (!gtk_widget_get_parent(compose->paned))
7621                 gtk_widget_destroy(compose->paned);
7622         gtk_widget_destroy(compose->popupmenu);
7623
7624         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
7625         clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
7626         gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
7627
7628         gtk_widget_destroy(compose->window);
7629         toolbar_destroy(compose->toolbar);
7630         g_free(compose->toolbar);
7631         g_mutex_free(compose->mutex);
7632         g_free(compose);
7633 }
7634
7635 static void compose_attach_info_free(AttachInfo *ainfo)
7636 {
7637         g_free(ainfo->file);
7638         g_free(ainfo->content_type);
7639         g_free(ainfo->name);
7640         g_free(ainfo);
7641 }
7642
7643 static void compose_attach_update_label(Compose *compose)
7644 {
7645         GtkTreeIter iter;
7646         gint i = 1;
7647         gchar *text;
7648         GtkTreeModel *model;
7649         
7650         if(compose == NULL)
7651                 return;
7652                 
7653         model = gtk_tree_view_get_model(GTK_TREE_VIEW(compose->attach_clist));
7654         if(!gtk_tree_model_get_iter_first(model, &iter)) {
7655                 gtk_label_set_text(GTK_LABEL(compose->attach_label), "");       
7656                 return;
7657         }
7658         
7659         while(gtk_tree_model_iter_next(model, &iter))
7660                 i++;
7661         
7662         text = g_strdup_printf("(%d)", i);
7663         gtk_label_set_text(GTK_LABEL(compose->attach_label), text);
7664         g_free(text);
7665 }
7666
7667 static void compose_attach_remove_selected(Compose *compose)
7668 {
7669         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7670         GtkTreeSelection *selection;
7671         GList *sel, *cur;
7672         GtkTreeModel *model;
7673
7674         selection = gtk_tree_view_get_selection(tree_view);
7675         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7676
7677         if (!sel) 
7678                 return;
7679
7680         for (cur = sel; cur != NULL; cur = cur->next) {
7681                 GtkTreePath *path = cur->data;
7682                 GtkTreeRowReference *ref = gtk_tree_row_reference_new
7683                                                 (model, cur->data);
7684                 cur->data = ref;
7685                 gtk_tree_path_free(path);
7686         }
7687
7688         for (cur = sel; cur != NULL; cur = cur->next) {
7689                 GtkTreeRowReference *ref = cur->data;
7690                 GtkTreePath *path = gtk_tree_row_reference_get_path(ref);
7691                 GtkTreeIter iter;
7692
7693                 if (gtk_tree_model_get_iter(model, &iter, path))
7694                         gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
7695                 
7696                 gtk_tree_path_free(path);
7697                 gtk_tree_row_reference_free(ref);
7698         }
7699
7700         g_list_free(sel);
7701         compose_attach_update_label(compose);
7702 }
7703
7704 static struct _AttachProperty
7705 {
7706         GtkWidget *window;
7707         GtkWidget *mimetype_entry;
7708         GtkWidget *encoding_optmenu;
7709         GtkWidget *path_entry;
7710         GtkWidget *filename_entry;
7711         GtkWidget *ok_btn;
7712         GtkWidget *cancel_btn;
7713 } attach_prop;
7714
7715 static void gtk_tree_path_free_(gpointer ptr, gpointer data)
7716 {       
7717         gtk_tree_path_free((GtkTreePath *)ptr);
7718 }
7719
7720 static void compose_attach_property(Compose *compose)
7721 {
7722         GtkTreeView *tree_view = GTK_TREE_VIEW(compose->attach_clist);
7723         AttachInfo *ainfo;
7724         GtkComboBox *optmenu;
7725         GtkTreeSelection *selection;
7726         GList *sel;
7727         GtkTreeModel *model;
7728         GtkTreeIter iter;
7729         GtkTreePath *path;
7730         static gboolean cancelled;
7731
7732         /* only if one selected */
7733         selection = gtk_tree_view_get_selection(tree_view);
7734         if (gtk_tree_selection_count_selected_rows(selection) != 1) 
7735                 return;
7736
7737         sel = gtk_tree_selection_get_selected_rows(selection, &model);
7738         if (!sel)
7739                 return;
7740
7741         path = (GtkTreePath *) sel->data;
7742         gtk_tree_model_get_iter(model, &iter, path);
7743         gtk_tree_model_get(model, &iter, COL_DATA, &ainfo, -1); 
7744         
7745         if (!ainfo) {
7746                 g_list_foreach(sel, gtk_tree_path_free_, NULL);
7747                 g_list_free(sel);
7748                 return;
7749         }               
7750         g_list_free(sel);
7751
7752         if (!attach_prop.window)
7753                 compose_attach_property_create(&cancelled);
7754         gtk_widget_grab_focus(attach_prop.ok_btn);
7755         gtk_widget_show(attach_prop.window);
7756         manage_window_set_transient(GTK_WINDOW(attach_prop.window));
7757
7758         optmenu = GTK_COMBO_BOX(attach_prop.encoding_optmenu);
7759         if (ainfo->encoding == ENC_UNKNOWN)
7760                 combobox_select_by_data(optmenu, ENC_BASE64);
7761         else
7762                 combobox_select_by_data(optmenu, ainfo->encoding);
7763
7764         gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
7765                            ainfo->content_type ? ainfo->content_type : "");
7766         gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry),
7767                            ainfo->file ? ainfo->file : "");
7768         gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
7769                            ainfo->name ? ainfo->name : "");
7770
7771         for (;;) {
7772                 const gchar *entry_text;
7773                 gchar *text;
7774                 gchar *cnttype = NULL;
7775                 gchar *file = NULL;
7776                 off_t size = 0;
7777
7778                 cancelled = FALSE;
7779                 gtk_main();
7780
7781                 gtk_widget_hide(attach_prop.window);
7782                 
7783                 if (cancelled) 
7784                         break;
7785
7786                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.mimetype_entry));
7787                 if (*entry_text != '\0') {
7788                         gchar *p;
7789
7790                         text = g_strstrip(g_strdup(entry_text));
7791                         if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
7792                                 cnttype = g_strdup(text);
7793                                 g_free(text);
7794                         } else {
7795                                 alertpanel_error(_("Invalid MIME type."));
7796                                 g_free(text);
7797                                 continue;
7798                         }
7799                 }
7800
7801                 ainfo->encoding = combobox_get_active_data(optmenu);
7802
7803                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.path_entry));
7804                 if (*entry_text != '\0') {
7805                         if (is_file_exist(entry_text) &&
7806                             (size = get_file_size(entry_text)) > 0)
7807                                 file = g_strdup(entry_text);
7808                         else {
7809                                 alertpanel_error
7810                                         (_("File doesn't exist or is empty."));
7811                                 g_free(cnttype);
7812                                 continue;
7813                         }
7814                 }
7815
7816                 entry_text = gtk_entry_get_text(GTK_ENTRY(attach_prop.filename_entry));
7817                 if (*entry_text != '\0') {
7818                         g_free(ainfo->name);
7819                         ainfo->name = g_strdup(entry_text);
7820                 }
7821
7822                 if (cnttype) {
7823                         g_free(ainfo->content_type);
7824                         ainfo->content_type = cnttype;
7825                 }
7826                 if (file) {
7827                         g_free(ainfo->file);
7828                         ainfo->file = file;
7829                 }
7830                 if (size)
7831                         ainfo->size = size;
7832
7833                 /* update tree store */
7834                 text = to_human_readable(ainfo->size);
7835                 gtk_tree_model_get_iter(model, &iter, path);
7836                 gtk_list_store_set(GTK_LIST_STORE(model), &iter,
7837                                    COL_MIMETYPE, ainfo->content_type,
7838                                    COL_SIZE, text,
7839                                    COL_NAME, ainfo->name,
7840                                    -1);
7841                 
7842                 break;
7843         }
7844
7845         gtk_tree_path_free(path);
7846 }
7847
7848 #define SET_LABEL_AND_ENTRY(str, entry, top) \
7849 { \
7850         label = gtk_label_new(str); \
7851         gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
7852                          GTK_FILL, 0, 0, 0); \
7853         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
7854  \
7855         entry = gtk_entry_new(); \
7856         gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
7857                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
7858 }
7859
7860 static void compose_attach_property_create(gboolean *cancelled)
7861 {
7862         GtkWidget *window;
7863         GtkWidget *vbox;
7864         GtkWidget *table;
7865         GtkWidget *label;
7866         GtkWidget *mimetype_entry;
7867         GtkWidget *hbox;
7868         GtkWidget *optmenu;
7869         GtkListStore *optmenu_menu;
7870         GtkWidget *path_entry;
7871         GtkWidget *filename_entry;
7872         GtkWidget *hbbox;
7873         GtkWidget *ok_btn;
7874         GtkWidget *cancel_btn;
7875         GList     *mime_type_list, *strlist;
7876         GtkTreeIter iter;
7877
7878         debug_print("Creating attach_property window...\n");
7879
7880         window = gtkut_window_new(GTK_WINDOW_TOPLEVEL, "compose_attach_property");
7881         gtk_widget_set_size_request(window, 480, -1);
7882         gtk_container_set_border_width(GTK_CONTAINER(window), 8);
7883         gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
7884         gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
7885         gtk_window_set_modal(GTK_WINDOW(window), TRUE);
7886         g_signal_connect(G_OBJECT(window), "delete_event",
7887                          G_CALLBACK(attach_property_delete_event),
7888                          cancelled);
7889         g_signal_connect(G_OBJECT(window), "key_press_event",
7890                          G_CALLBACK(attach_property_key_pressed),
7891                          cancelled);
7892
7893         vbox = gtk_vbox_new(FALSE, 8);
7894         gtk_container_add(GTK_CONTAINER(window), vbox);
7895
7896         table = gtk_table_new(4, 2, FALSE);
7897         gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
7898         gtk_table_set_row_spacings(GTK_TABLE(table), 8);
7899         gtk_table_set_col_spacings(GTK_TABLE(table), 8);
7900
7901         label = gtk_label_new(_("MIME type")); 
7902         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, (0 + 1), 
7903                          GTK_FILL, 0, 0, 0); 
7904         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); 
7905         mimetype_entry = gtk_combo_box_entry_new_text(); 
7906         gtk_table_attach(GTK_TABLE(table), mimetype_entry, 1, 2, 0, (0 + 1), 
7907                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7908                          
7909         /* stuff with list */
7910         mime_type_list = procmime_get_mime_type_list();
7911         strlist = NULL;
7912         for (; mime_type_list != NULL; mime_type_list = mime_type_list->next) {
7913                 MimeType *type = (MimeType *) mime_type_list->data;
7914                 gchar *tmp;
7915
7916                 tmp = g_strdup_printf("%s/%s", type->type, type->sub_type);
7917
7918                 if (g_list_find_custom(strlist, tmp, (GCompareFunc)strcmp2))
7919                         g_free(tmp);
7920                 else
7921                         strlist = g_list_insert_sorted(strlist, (gpointer)tmp,
7922                                         (GCompareFunc)strcmp2);
7923         }
7924
7925         for (mime_type_list = strlist; mime_type_list != NULL; 
7926                 mime_type_list = mime_type_list->next) {
7927                 gtk_combo_box_append_text(GTK_COMBO_BOX(mimetype_entry), mime_type_list->data);
7928                 g_free(mime_type_list->data);
7929         }
7930         g_list_free(strlist);
7931         gtk_combo_box_set_active(GTK_COMBO_BOX(mimetype_entry), 0);              
7932         mimetype_entry = GTK_BIN(mimetype_entry)->child;                         
7933
7934         label = gtk_label_new(_("Encoding"));
7935         gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
7936                          GTK_FILL, 0, 0, 0);
7937         gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
7938
7939         hbox = gtk_hbox_new(FALSE, 0);
7940         gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
7941                          GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
7942
7943         optmenu = gtkut_sc_combobox_create(NULL, TRUE);
7944         optmenu_menu = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(optmenu)));
7945
7946         COMBOBOX_ADD(optmenu_menu, "7bit", ENC_7BIT);
7947         COMBOBOX_ADD(optmenu_menu, "8bit", ENC_8BIT);
7948         COMBOBOX_ADD(optmenu_menu, "quoted-printable",  ENC_QUOTED_PRINTABLE);
7949         COMBOBOX_ADD(optmenu_menu, "base64", ENC_BASE64);
7950         gtk_combo_box_set_active(GTK_COMBO_BOX(optmenu), 0);
7951
7952         gtk_box_pack_start(GTK_BOX(hbox), optmenu, TRUE, TRUE, 0);
7953
7954         SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
7955         SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
7956
7957         gtkut_stock_button_set_create(&hbbox, &cancel_btn, GTK_STOCK_CANCEL,
7958                                       &ok_btn, GTK_STOCK_OK,
7959                                       NULL, NULL);
7960         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
7961         gtk_widget_grab_default(ok_btn);
7962
7963         g_signal_connect(G_OBJECT(ok_btn), "clicked",
7964                          G_CALLBACK(attach_property_ok),
7965                          cancelled);
7966         g_signal_connect(G_OBJECT(cancel_btn), "clicked",
7967                          G_CALLBACK(attach_property_cancel),
7968                          cancelled);
7969
7970         gtk_widget_show_all(vbox);
7971
7972         attach_prop.window           = window;
7973         attach_prop.mimetype_entry   = mimetype_entry;
7974         attach_prop.encoding_optmenu = optmenu;
7975         attach_prop.path_entry       = path_entry;
7976         attach_prop.filename_entry   = filename_entry;
7977         attach_prop.ok_btn           = ok_btn;
7978         attach_prop.cancel_btn       = cancel_btn;
7979 }
7980
7981 #undef SET_LABEL_AND_ENTRY
7982
7983 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
7984 {
7985         *cancelled = FALSE;
7986         gtk_main_quit();
7987 }
7988
7989 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
7990 {
7991         *cancelled = TRUE;
7992         gtk_main_quit();
7993 }
7994
7995 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
7996                                          gboolean *cancelled)
7997 {
7998         *cancelled = TRUE;
7999         gtk_main_quit();
8000
8001         return TRUE;
8002 }
8003
8004 static gboolean attach_property_key_pressed(GtkWidget *widget,
8005                                             GdkEventKey *event,
8006                                             gboolean *cancelled)
8007 {
8008         if (event && event->keyval == GDK_Escape) {
8009                 *cancelled = TRUE;
8010                 gtk_main_quit();
8011         }
8012         return FALSE;
8013 }
8014
8015 static void compose_exec_ext_editor(Compose *compose)
8016 {
8017 #ifdef G_OS_UNIX
8018         gchar *tmp;
8019         pid_t pid;
8020         gint pipe_fds[2];
8021
8022         tmp = g_strdup_printf("%s%ctmpmsg.%p", get_tmp_dir(),
8023                               G_DIR_SEPARATOR, compose);
8024
8025         if (pipe(pipe_fds) < 0) {
8026                 perror("pipe");
8027                 g_free(tmp);
8028                 return;
8029         }
8030
8031         if ((pid = fork()) < 0) {
8032                 perror("fork");
8033                 g_free(tmp);
8034                 return;
8035         }
8036
8037         if (pid != 0) {
8038                 /* close the write side of the pipe */
8039                 close(pipe_fds[1]);
8040
8041                 compose->exteditor_file    = g_strdup(tmp);
8042                 compose->exteditor_pid     = pid;
8043
8044                 compose_set_ext_editor_sensitive(compose, FALSE);
8045
8046                 compose->exteditor_ch = g_io_channel_unix_new(pipe_fds[0]);
8047                 compose->exteditor_tag = g_io_add_watch(compose->exteditor_ch,
8048                                                         G_IO_IN,
8049                                                         compose_input_cb,
8050                                                         compose);
8051         } else {        /* process-monitoring process */
8052                 pid_t pid_ed;
8053
8054                 if (setpgid(0, 0))
8055                         perror("setpgid");
8056
8057                 /* close the read side of the pipe */
8058                 close(pipe_fds[0]);
8059
8060                 if (compose_write_body_to_file(compose, tmp) < 0) {
8061                         fd_write_all(pipe_fds[1], "2\n", 2);
8062                         _exit(1);
8063                 }
8064
8065                 pid_ed = compose_exec_ext_editor_real(tmp);
8066                 if (pid_ed < 0) {
8067                         fd_write_all(pipe_fds[1], "1\n", 2);
8068                         _exit(1);
8069                 }
8070
8071                 /* wait until editor is terminated */
8072                 waitpid(pid_ed, NULL, 0);
8073
8074                 fd_write_all(pipe_fds[1], "0\n", 2);
8075
8076                 close(pipe_fds[1]);
8077                 _exit(0);
8078         }
8079
8080         g_free(tmp);
8081 #endif /* G_OS_UNIX */
8082 }
8083
8084 #ifdef G_OS_UNIX
8085 static gint compose_exec_ext_editor_real(const gchar *file)
8086 {
8087         gchar buf[1024];
8088         gchar *p;
8089         gchar **cmdline;
8090         pid_t pid;
8091
8092         g_return_val_if_fail(file != NULL, -1);
8093
8094         if ((pid = fork()) < 0) {
8095                 perror("fork");
8096                 return -1;
8097         }
8098
8099         if (pid != 0) return pid;
8100
8101         /* grandchild process */
8102
8103         if (setpgid(0, getppid()))
8104                 perror("setpgid");
8105
8106         if (prefs_common_get_ext_editor_cmd() &&
8107             (p = strchr(prefs_common_get_ext_editor_cmd(), '%')) &&
8108             *(p + 1) == 's' && !strchr(p + 2, '%')) {
8109                 g_snprintf(buf, sizeof(buf), prefs_common_get_ext_editor_cmd(), file);
8110         } else {
8111                 if (prefs_common_get_ext_editor_cmd())
8112                         g_warning("External editor command line is invalid: '%s'\n",
8113                                   prefs_common_get_ext_editor_cmd());
8114                 g_snprintf(buf, sizeof(buf), DEFAULT_EDITOR_CMD, file);
8115         }
8116
8117         cmdline = strsplit_with_quote(buf, " ", 1024);
8118         execvp(cmdline[0], cmdline);
8119
8120         perror("execvp");
8121         g_strfreev(cmdline);
8122
8123         _exit(1);
8124 }
8125
8126 static gboolean compose_ext_editor_kill(Compose *compose)
8127 {
8128         pid_t pgid = compose->exteditor_pid * -1;
8129         gint ret;
8130
8131         ret = kill(pgid, 0);
8132
8133         if (ret == 0 || (ret == -1 && EPERM == errno)) {
8134                 AlertValue val;
8135                 gchar *msg;
8136
8137                 msg = g_strdup_printf
8138                         (_("The external editor is still working.\n"
8139                            "Force terminating the process?\n"
8140                            "process group id: %d"), -pgid);
8141                 val = alertpanel_full(_("Notice"), msg, GTK_STOCK_NO, GTK_STOCK_YES,
8142                                       NULL, FALSE, NULL, ALERT_WARNING, G_ALERTDEFAULT);
8143                         
8144                 g_free(msg);
8145
8146                 if (val == G_ALERTALTERNATE) {
8147                         g_source_remove(compose->exteditor_tag);
8148                         g_io_channel_shutdown(compose->exteditor_ch,
8149                                               FALSE, NULL);
8150                         g_io_channel_unref(compose->exteditor_ch);
8151
8152                         if (kill(pgid, SIGTERM) < 0) perror("kill");
8153                         waitpid(compose->exteditor_pid, NULL, 0);
8154
8155                         g_warning("Terminated process group id: %d", -pgid);
8156                         g_warning("Temporary file: %s",
8157                                   compose->exteditor_file);
8158
8159                         compose_set_ext_editor_sensitive(compose, TRUE);
8160
8161                         g_free(compose->exteditor_file);
8162                         compose->exteditor_file    = NULL;
8163                         compose->exteditor_pid     = -1;
8164                         compose->exteditor_ch      = NULL;
8165                         compose->exteditor_tag     = -1;
8166                 } else
8167                         return FALSE;
8168         }
8169
8170         return TRUE;
8171 }
8172
8173 static gboolean compose_input_cb(GIOChannel *source, GIOCondition condition,
8174                                  gpointer data)
8175 {
8176         gchar buf[3] = "3";
8177         Compose *compose = (Compose *)data;
8178         gsize bytes_read;
8179
8180         debug_print(_("Compose: input from monitoring process\n"));
8181
8182         g_io_channel_read_chars(source, buf, sizeof(buf), &bytes_read, NULL);
8183
8184         g_io_channel_shutdown(source, FALSE, NULL);
8185         g_io_channel_unref(source);
8186
8187         waitpid(compose->exteditor_pid, NULL, 0);
8188
8189         if (buf[0] == '0') {            /* success */
8190                 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8191                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
8192
8193                 gtk_text_buffer_set_text(buffer, "", -1);
8194                 compose_insert_file(compose, compose->exteditor_file);
8195                 compose_changed_cb(NULL, compose);
8196
8197                 if (g_unlink(compose->exteditor_file) < 0)
8198                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8199         } else if (buf[0] == '1') {     /* failed */
8200                 g_warning("Couldn't exec external editor\n");
8201                 if (g_unlink(compose->exteditor_file) < 0)
8202                         FILE_OP_ERROR(compose->exteditor_file, "unlink");
8203         } else if (buf[0] == '2') {
8204                 g_warning("Couldn't write to file\n");
8205         } else if (buf[0] == '3') {
8206                 g_warning("Pipe read failed\n");
8207         }
8208
8209         compose_set_ext_editor_sensitive(compose, TRUE);
8210
8211         g_free(compose->exteditor_file);
8212         compose->exteditor_file    = NULL;
8213         compose->exteditor_pid     = -1;
8214         compose->exteditor_ch      = NULL;
8215         compose->exteditor_tag     = -1;
8216
8217         return FALSE;
8218 }
8219
8220 static void compose_set_ext_editor_sensitive(Compose *compose,
8221                                              gboolean sensitive)
8222 {
8223         GtkItemFactory *ifactory;
8224
8225         ifactory = gtk_item_factory_from_widget(compose->menubar);
8226
8227         menu_set_sensitive(ifactory, "/Message/Send", sensitive);
8228         menu_set_sensitive(ifactory, "/Message/Send later", sensitive);
8229         menu_set_sensitive(ifactory, "/Message/Insert file", sensitive);
8230         menu_set_sensitive(ifactory, "/Message/Insert signature", sensitive);
8231         menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
8232         menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
8233         menu_set_sensitive(ifactory, "/Edit/Edit with external editor",
8234                            sensitive);
8235
8236         gtk_widget_set_sensitive(compose->text,                       sensitive);
8237         if (compose->toolbar->send_btn)
8238                 gtk_widget_set_sensitive(compose->toolbar->send_btn,      sensitive);
8239         if (compose->toolbar->sendl_btn)
8240                 gtk_widget_set_sensitive(compose->toolbar->sendl_btn,     sensitive);
8241         if (compose->toolbar->draft_btn)
8242                 gtk_widget_set_sensitive(compose->toolbar->draft_btn,     sensitive);
8243         if (compose->toolbar->insert_btn)
8244                 gtk_widget_set_sensitive(compose->toolbar->insert_btn,    sensitive);
8245         if (compose->toolbar->sig_btn)
8246                 gtk_widget_set_sensitive(compose->toolbar->sig_btn,       sensitive);
8247         if (compose->toolbar->exteditor_btn)
8248                 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, sensitive);
8249         if (compose->toolbar->linewrap_current_btn)
8250                 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, sensitive);
8251         if (compose->toolbar->linewrap_all_btn)
8252                 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, sensitive);
8253 }
8254 #endif /* G_OS_UNIX */
8255
8256 /**
8257  * compose_undo_state_changed:
8258  *
8259  * Change the sensivity of the menuentries undo and redo
8260  **/
8261 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
8262                                        gint redo_state, gpointer data)
8263 {
8264         GtkWidget *widget = GTK_WIDGET(data);
8265         GtkItemFactory *ifactory;
8266
8267         g_return_if_fail(widget != NULL);
8268
8269         ifactory = gtk_item_factory_from_widget(widget);
8270
8271         switch (undo_state) {
8272         case UNDO_STATE_TRUE:
8273                 if (!undostruct->undo_state) {
8274                         undostruct->undo_state = TRUE;
8275                         menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
8276                 }
8277                 break;
8278         case UNDO_STATE_FALSE:
8279                 if (undostruct->undo_state) {
8280                         undostruct->undo_state = FALSE;
8281                         menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
8282                 }
8283                 break;
8284         case UNDO_STATE_UNCHANGED:
8285                 break;
8286         case UNDO_STATE_REFRESH:
8287                 menu_set_sensitive(ifactory, "/Edit/Undo",
8288                                    undostruct->undo_state);
8289                 break;
8290         default:
8291                 g_warning("Undo state not recognized");
8292                 break;
8293         }
8294
8295         switch (redo_state) {
8296         case UNDO_STATE_TRUE:
8297                 if (!undostruct->redo_state) {
8298                         undostruct->redo_state = TRUE;
8299                         menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
8300                 }
8301                 break;
8302         case UNDO_STATE_FALSE:
8303                 if (undostruct->redo_state) {
8304                         undostruct->redo_state = FALSE;
8305                         menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
8306                 }
8307                 break;
8308         case UNDO_STATE_UNCHANGED:
8309                 break;
8310         case UNDO_STATE_REFRESH:
8311                 menu_set_sensitive(ifactory, "/Edit/Redo",
8312                                    undostruct->redo_state);
8313                 break;
8314         default:
8315                 g_warning("Redo state not recognized");
8316                 break;
8317         }
8318 }
8319
8320 /* callback functions */
8321
8322 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
8323  * includes "non-client" (windows-izm) in calculation, so this calculation
8324  * may not be accurate.
8325  */
8326 static gboolean compose_edit_size_alloc(GtkEditable *widget,
8327                                         GtkAllocation *allocation,
8328                                         GtkSHRuler *shruler)
8329 {
8330         if (prefs_common.show_ruler) {
8331                 gint char_width = 0, char_height = 0;
8332                 gint line_width_in_chars;
8333
8334                 gtkut_get_font_size(GTK_WIDGET(widget),
8335                                     &char_width, &char_height);
8336                 line_width_in_chars =
8337                         (allocation->width - allocation->x) / char_width;
8338
8339                 /* got the maximum */
8340                 gtk_ruler_set_range(GTK_RULER(shruler),
8341                                     0.0, line_width_in_chars, 0,
8342                                     /*line_width_in_chars*/ char_width);
8343         }
8344
8345         return TRUE;
8346 }
8347
8348 static void account_activated(GtkComboBox *optmenu, gpointer data)
8349 {
8350         Compose *compose = (Compose *)data;
8351
8352         PrefsAccount *ac;
8353         gchar *folderidentifier;
8354         gint account_id = 0;
8355         GtkTreeModel *menu;
8356         GtkTreeIter iter;
8357
8358         /* Get ID of active account in the combo box */
8359         menu = gtk_combo_box_get_model(optmenu);
8360         gtk_combo_box_get_active_iter(optmenu, &iter);
8361         gtk_tree_model_get(menu, &iter, 1, &account_id, -1);
8362
8363         ac = account_find_from_id(account_id);
8364         g_return_if_fail(ac != NULL);
8365
8366         if (ac != compose->account)
8367                 compose_select_account(compose, ac, FALSE);
8368
8369         /* Set message save folder */
8370         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8371                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
8372         }
8373         g_signal_connect(G_OBJECT(compose->savemsg_checkbtn), "toggled",
8374                          G_CALLBACK(compose_savemsg_checkbtn_cb), compose);
8375                            
8376         gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8377         if (account_get_special_folder(compose->account, F_OUTBOX)) {
8378                 folderidentifier = folder_item_get_identifier(account_get_special_folder
8379                                   (compose->account, F_OUTBOX));
8380                 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
8381                 g_free(folderidentifier);
8382         }
8383 }
8384
8385 static void attach_selected(GtkTreeView *tree_view, GtkTreePath *tree_path,
8386                             GtkTreeViewColumn *column, Compose *compose)
8387 {
8388         compose_attach_property(compose);
8389 }
8390
8391 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
8392                                       gpointer data)
8393 {
8394         Compose *compose = (Compose *)data;
8395         GtkTreeSelection *attach_selection;
8396         gint attach_nr_selected;
8397         GtkItemFactory *ifactory;
8398         
8399         if (!event) return FALSE;
8400
8401         if (event->button == 3) {
8402                 attach_selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));
8403                 attach_nr_selected = gtk_tree_selection_count_selected_rows(attach_selection);
8404                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
8405                         
8406                 if (attach_nr_selected > 0)
8407                 {
8408                         menu_set_sensitive(ifactory, "/Remove", TRUE);
8409                         menu_set_sensitive(ifactory, "/Properties...", TRUE);
8410                 } else {
8411                         menu_set_sensitive(ifactory, "/Remove", FALSE);
8412                         menu_set_sensitive(ifactory, "/Properties...", FALSE);
8413                 }
8414                         
8415                 gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
8416                                NULL, NULL, event->button, event->time);
8417                 return TRUE;                           
8418         }
8419
8420         return FALSE;
8421 }
8422
8423 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
8424                                    gpointer data)
8425 {
8426         Compose *compose = (Compose *)data;
8427
8428         if (!event) return FALSE;
8429
8430         switch (event->keyval) {
8431         case GDK_Delete:
8432                 compose_attach_remove_selected(compose);
8433                 break;
8434         }
8435         return FALSE;
8436 }
8437
8438 static void compose_allow_user_actions (Compose *compose, gboolean allow)
8439 {
8440         GtkItemFactory *ifactory = gtk_item_factory_from_widget(compose->menubar);
8441         toolbar_comp_set_sensitive(compose, allow);
8442         menu_set_sensitive(ifactory, "/Message", allow);
8443         menu_set_sensitive(ifactory, "/Edit", allow);
8444 #if USE_ASPELL
8445         menu_set_sensitive(ifactory, "/Spelling", allow);
8446 #endif  
8447         menu_set_sensitive(ifactory, "/Options", allow);
8448         menu_set_sensitive(ifactory, "/Tools", allow);
8449         menu_set_sensitive(ifactory, "/Help", allow);
8450         
8451         gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), allow);
8452
8453 }
8454
8455 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
8456 {
8457         Compose *compose = (Compose *)data;
8458         
8459         if (prefs_common.work_offline && 
8460             !inc_offline_should_override(TRUE,
8461                 _("Claws Mail needs network access in order "
8462                   "to send this email.")))
8463                 return;
8464         
8465         if (compose->draft_timeout_tag >= 0) { /* CLAWS: disable draft timeout */
8466                 g_source_remove(compose->draft_timeout_tag);
8467                 compose->draft_timeout_tag = -1;
8468         }
8469
8470         compose_send(compose);
8471 }
8472
8473 static void compose_send_later_cb(gpointer data, guint action,
8474                                   GtkWidget *widget)
8475 {
8476         Compose *compose = (Compose *)data;
8477         gint val;
8478
8479         inc_lock();
8480         val = compose_queue_sub(compose, NULL, NULL, NULL, TRUE, TRUE);
8481         inc_unlock();
8482
8483         if (!val) {
8484                 compose_close(compose);
8485         } else if (val == -1) {
8486                 alertpanel_error(_("Could not queue message."));
8487         } else if (val == -2) {
8488                 alertpanel_error(_("Could not queue message:\n\n%s."), strerror(errno));
8489         } else if (val == -3) {
8490                 if (privacy_peek_error())
8491                 alertpanel_error(_("Could not queue message for sending:\n\n"
8492                                    "Signature failed: %s"), privacy_get_error());
8493         } else if (val == -4) {
8494                 alertpanel_error(_("Could not queue message for sending:\n\n"
8495                                    "Charset conversion failed."));
8496         } else if (val == -5) {
8497                 alertpanel_error(_("Could not queue message for sending:\n\n"
8498                                    "Couldn't get recipient encryption key."));
8499         } else if (val == -6) {
8500                 /* silent error */
8501         }
8502         toolbar_main_set_sensitive(mainwindow_get_mainwindow());
8503 }
8504
8505 #define DRAFTED_AT_EXIT "drafted_at_exit"
8506 static void compose_register_draft(MsgInfo *info)
8507 {
8508         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8509                                       DRAFTED_AT_EXIT, NULL);
8510         FILE *fp = fopen(filepath, "ab");
8511         
8512         if (fp) {
8513                 fprintf(fp, "%s\t%d\n", folder_item_get_identifier(info->folder), 
8514                                 info->msgnum);
8515                 fclose(fp);
8516         }
8517                 
8518         g_free(filepath);       
8519 }
8520
8521 gboolean compose_draft (gpointer data, guint action) 
8522 {
8523         Compose *compose = (Compose *)data;
8524         FolderItem *draft;
8525         gchar *tmp;
8526         gint msgnum;
8527         MsgFlags flag = {0, 0};
8528         static gboolean lock = FALSE;
8529         MsgInfo *newmsginfo;
8530         FILE *fp;
8531         gboolean target_locked = FALSE;
8532         gboolean err = FALSE;
8533
8534         if (lock) return FALSE;
8535
8536         if (compose->sending)
8537                 return TRUE;
8538
8539         draft = account_get_special_folder(compose->account, F_DRAFT);
8540         g_return_val_if_fail(draft != NULL, FALSE);
8541         
8542         if (!g_mutex_trylock(compose->mutex)) {
8543                 /* we don't want to lock the mutex once it's available,
8544                  * because as the only other part of compose.c locking
8545                  * it is compose_close - which means once unlocked,
8546                  * the compose struct will be freed */
8547                 debug_print("couldn't lock mutex, probably sending\n");
8548                 return FALSE;
8549         }
8550         
8551         lock = TRUE;
8552
8553         tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
8554                               G_DIR_SEPARATOR, compose);
8555         if ((fp = g_fopen(tmp, "wb")) == NULL) {
8556                 FILE_OP_ERROR(tmp, "fopen");
8557                 goto warn_err;
8558         }
8559
8560         /* chmod for security */
8561         if (change_file_mode_rw(fp, tmp) < 0) {
8562                 FILE_OP_ERROR(tmp, "chmod");
8563                 g_warning("can't change file mode\n");
8564         }
8565
8566         /* Save draft infos */
8567         err |= (fprintf(fp, "X-Claws-Account-Id:%d\n", compose->account->account_id) < 0);
8568         err |= (fprintf(fp, "S:%s\n", compose->account->address) < 0);
8569
8570         if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn))) {
8571                 gchar *savefolderid;
8572
8573                 savefolderid = gtk_editable_get_chars(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
8574                 err |= (fprintf(fp, "SCF:%s\n", savefolderid) < 0);
8575                 g_free(savefolderid);
8576         }
8577         if (compose->return_receipt) {
8578                 err |= (fprintf(fp, "RRCPT:1\n") < 0);
8579         }
8580         if (compose->privacy_system) {
8581                 err |= (fprintf(fp, "X-Claws-Sign:%d\n", compose->use_signing) < 0);
8582                 err |= (fprintf(fp, "X-Claws-Encrypt:%d\n", compose->use_encryption) < 0);
8583                 err |= (fprintf(fp, "X-Claws-Privacy-System:%s\n", compose->privacy_system) < 0);
8584         }
8585
8586         /* Message-ID of message replying to */
8587         if ((compose->replyinfo != NULL) && (compose->replyinfo->msgid != NULL)) {
8588                 gchar *folderid;
8589                 
8590                 folderid = folder_item_get_identifier(compose->replyinfo->folder);
8591                 err |= (fprintf(fp, "RMID:%s\t%d\t%s\n", folderid, compose->replyinfo->msgnum, compose->replyinfo->msgid) < 0);
8592                 g_free(folderid);
8593         }
8594         /* Message-ID of message forwarding to */
8595         if ((compose->fwdinfo != NULL) && (compose->fwdinfo->msgid != NULL)) {
8596                 gchar *folderid;
8597                 
8598                 folderid = folder_item_get_identifier(compose->fwdinfo->folder);
8599                 err |= (fprintf(fp, "FMID:%s\t%d\t%s\n", folderid, compose->fwdinfo->msgnum, compose->fwdinfo->msgid) < 0);
8600                 g_free(folderid);
8601         }
8602
8603         /* end of headers */
8604         err |= (fprintf(fp, "X-Claws-End-Special-Headers: 1\n") < 0);
8605
8606         if (err) {
8607                 fclose(fp);
8608                 goto warn_err;
8609         }
8610
8611         if (compose_write_to_file(compose, fp, COMPOSE_WRITE_FOR_STORE, action != COMPOSE_AUTO_SAVE) < 0) {
8612                 fclose(fp);
8613                 goto warn_err;
8614         }
8615         if (fclose(fp) == EOF) {
8616                 goto warn_err;
8617         }
8618         
8619         if (compose->targetinfo) {
8620                 target_locked = MSG_IS_LOCKED(compose->targetinfo->flags);
8621                 flag.perm_flags = target_locked?MSG_LOCKED:0;
8622         }
8623         flag.tmp_flags = MSG_DRAFT;
8624
8625         folder_item_scan(draft);
8626         if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
8627                 MsgInfo *tmpinfo = NULL;
8628                 debug_print("didn't get msgnum after adding draft [%s]\n", compose->msgid?compose->msgid:"no msgid");
8629                 if (compose->msgid) {
8630                         tmpinfo = folder_item_get_msginfo_by_msgid(draft, compose->msgid);
8631                 }
8632                 if (tmpinfo) {
8633                         msgnum = tmpinfo->msgnum;
8634                         procmsg_msginfo_free(tmpinfo);
8635                         debug_print("got draft msgnum %d from scanning\n", msgnum);
8636                 } else {
8637                         debug_print("didn't get draft msgnum after scanning\n");
8638                 }
8639         } else {
8640                 debug_print("got draft msgnum %d from adding\n", msgnum);
8641         }
8642         if (msgnum < 0) {
8643 warn_err:
8644                 g_unlink(tmp);
8645                 g_free(tmp);
8646                 if (action != COMPOSE_AUTO_SAVE) {
8647                         if (action != COMPOSE_DRAFT_FOR_EXIT)
8648                                 alertpanel_error(_("Could not save draft."));
8649                         else {
8650                                 AlertValue val;
8651                                 gtkut_window_popup(compose->window);
8652                                 val = alertpanel_full(_("Could not save draft"),
8653                                         _("Could not save draft.\n"
8654                                         "Do you want to cancel exit or discard this email?"),
8655                                           _("_Cancel exit"), _("_Discard email"), NULL,
8656                                           FALSE, NULL, ALERT_QUESTION, G_ALERTDEFAULT);
8657                                 if (val == G_ALERTALTERNATE) {
8658                                         lock = FALSE;
8659                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8660                                         compose_close(compose);
8661                                         return TRUE;
8662                                 } else {
8663                                         lock = FALSE;
8664                                         g_mutex_unlock(compose->mutex); /* must be done before closing */
8665                                         return FALSE;
8666                                 }
8667                         }
8668                 }
8669                 goto unlock;
8670         }
8671         g_free(tmp);
8672
8673         if (compose->mode == COMPOSE_REEDIT) {
8674                 compose_remove_reedit_target(compose, TRUE);
8675         }
8676
8677         newmsginfo = folder_item_get_msginfo(draft, msgnum);
8678
8679         if (newmsginfo) {
8680                 procmsg_msginfo_unset_flags(newmsginfo, ~0, ~0);
8681                 if (target_locked)
8682                         procmsg_msginfo_set_flags(newmsginfo, MSG_LOCKED, MSG_DRAFT);
8683                 else
8684                         procmsg_msginfo_set_flags(newmsginfo, 0, MSG_DRAFT);
8685                 if (compose_use_attach(compose) && action != COMPOSE_AUTO_SAVE)
8686                         procmsg_msginfo_set_flags(newmsginfo, 0,
8687                                                   MSG_HAS_ATTACHMENT);
8688
8689                 if (action == COMPOSE_DRAFT_FOR_EXIT) {
8690                         compose_register_draft(newmsginfo);
8691                 }
8692                 procmsg_msginfo_free(newmsginfo);
8693         }
8694         
8695         folder_item_scan(draft);
8696         
8697         if (action == COMPOSE_QUIT_EDITING || action == COMPOSE_DRAFT_FOR_EXIT) {
8698                 lock = FALSE;
8699                 g_mutex_unlock(compose->mutex); /* must be done before closing */
8700                 compose_close(compose);
8701                 return TRUE;
8702         } else {
8703                 struct stat s;
8704                 gchar *path;
8705
8706                 path = folder_item_fetch_msg(draft, msgnum);
8707                 if (path == NULL) {
8708                         debug_print("can't fetch %s:%d\n", draft->path, msgnum);
8709                         goto unlock;
8710                 }
8711                 if (g_stat(path, &s) < 0) {
8712                         FILE_OP_ERROR(path, "stat");
8713                         g_free(path);
8714                         goto unlock;
8715                 }
8716                 g_free(path);
8717
8718                 procmsg_msginfo_free(compose->targetinfo);
8719                 compose->targetinfo = procmsg_msginfo_new();
8720                 compose->targetinfo->msgnum = msgnum;
8721                 compose->targetinfo->size = s.st_size;
8722                 compose->targetinfo->mtime = s.st_mtime;
8723                 compose->targetinfo->folder = draft;
8724                 if (target_locked)
8725                         procmsg_msginfo_set_flags(compose->targetinfo, MSG_LOCKED, 0);
8726                 compose->mode = COMPOSE_REEDIT;
8727                 
8728                 if (action == COMPOSE_AUTO_SAVE) {
8729                         compose->autosaved_draft = compose->targetinfo;
8730                 }
8731                 compose->modified = FALSE;
8732                 compose_set_title(compose);
8733         }
8734 unlock:
8735         lock = FALSE;
8736         g_mutex_unlock(compose->mutex);
8737         return TRUE;
8738 }
8739
8740 void compose_clear_exit_drafts(void)
8741 {
8742         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8743                                       DRAFTED_AT_EXIT, NULL);
8744         if (is_file_exist(filepath))
8745                 g_unlink(filepath);
8746         
8747         g_free(filepath);
8748 }
8749
8750 void compose_reopen_exit_drafts(void)
8751 {
8752         gchar *filepath = g_strconcat(get_rc_dir(), G_DIR_SEPARATOR_S,
8753                                       DRAFTED_AT_EXIT, NULL);
8754         FILE *fp = fopen(filepath, "rb");
8755         gchar buf[1024];
8756         
8757         if (fp) {
8758                 while (fgets(buf, sizeof(buf), fp)) {
8759                         gchar **parts = g_strsplit(buf, "\t", 2);
8760                         const gchar *folder = parts[0];
8761                         int msgnum = parts[1] ? atoi(parts[1]):-1;
8762                         
8763                         if (folder && *folder && msgnum > -1) {
8764                                 FolderItem *item = folder_find_item_from_identifier(folder);
8765                                 MsgInfo *info = folder_item_get_msginfo(item, msgnum);
8766                                 if (info)
8767                                         compose_reedit(info, FALSE);
8768                         }
8769                         g_strfreev(parts);
8770                 }       
8771                 fclose(fp);
8772         }       
8773         g_free(filepath);
8774         compose_clear_exit_drafts();
8775 }
8776
8777 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
8778 {
8779         compose_draft(data, action);
8780 }
8781
8782 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data)
8783 {
8784         if (compose && file_list) {
8785                 GList *tmp;
8786
8787                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8788                         gchar *file = (gchar *) tmp->data;
8789                         gchar *utf8_filename = conv_filename_to_utf8(file);
8790                         compose_attach_append(compose, file, utf8_filename, NULL);
8791                         compose_changed_cb(NULL, compose);
8792                         if (free_data) {
8793                         g_free(file);
8794                                 tmp->data = NULL;
8795                         }
8796                         g_free(utf8_filename);
8797                 }
8798         }
8799 }
8800
8801 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
8802 {
8803         Compose *compose = (Compose *)data;
8804         GList *file_list;
8805
8806         if (compose->redirect_filename != NULL)
8807                 return;
8808
8809         file_list = filesel_select_multiple_files_open(_("Select file"));
8810
8811         if (file_list) {
8812                 compose_attach_from_list(compose, file_list, TRUE);
8813                 g_list_free(file_list);
8814         }
8815 }
8816
8817 static void compose_insert_file_cb(gpointer data, guint action,
8818                                    GtkWidget *widget)
8819 {
8820         Compose *compose = (Compose *)data;
8821         GList *file_list;
8822
8823         file_list = filesel_select_multiple_files_open(_("Select file"));
8824
8825         if (file_list) {
8826                 GList *tmp;
8827
8828                 for ( tmp = file_list; tmp; tmp = tmp->next) {
8829                         gchar *file = (gchar *) tmp->data;
8830                         gchar *filedup = g_strdup(file);
8831                         gchar *shortfile = g_path_get_basename(filedup);
8832                         ComposeInsertResult res;
8833
8834                         res = compose_insert_file(compose, file);
8835                         if (res == COMPOSE_INSERT_READ_ERROR) {
8836                                 alertpanel_error(_("File '%s' could not be read."), shortfile);
8837                         } else if (res == COMPOSE_INSERT_INVALID_CHARACTER) {
8838                                 alertpanel_error(_("File '%s' contained invalid characters\n"
8839                                                    "for the current encoding, insertion may be incorrect."), shortfile);
8840                         }
8841                         g_free(shortfile);
8842                         g_free(filedup);
8843                         g_free(file);
8844                 }
8845                 g_list_free(file_list);
8846         }
8847 }
8848
8849 static void compose_insert_sig_cb(gpointer data, guint action,
8850                                   GtkWidget *widget)
8851 {
8852         Compose *compose = (Compose *)data;
8853
8854         compose_insert_sig(compose, FALSE);
8855 }
8856
8857 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
8858                               gpointer data)
8859 {
8860         gint x, y;
8861         Compose *compose = (Compose *)data;
8862
8863         gtkut_widget_get_uposition(widget, &x, &y);
8864         prefs_common.compose_x = x;
8865         prefs_common.compose_y = y;
8866
8867         if (compose->sending || compose->updating)
8868                 return TRUE;
8869         compose_close_cb(compose, 0, NULL);
8870         return TRUE;
8871 }
8872
8873 void compose_close_toolbar(Compose *compose)
8874 {
8875         compose_close_cb(compose, 0, NULL);
8876 }
8877
8878 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
8879 {
8880         Compose *compose = (Compose *)data;
8881         AlertValue val;
8882
8883 #ifdef G_OS_UNIX
8884         if (compose->exteditor_tag != -1) {
8885                 if (!compose_ext_editor_kill(compose))
8886                         return;
8887         }
8888 #endif
8889
8890         if (compose->modified) {
8891                 val = alertpanel(_("Discard message"),
8892                                  _("This message has been modified. Discard it?"),
8893                                  _("_Discard"), _("_Save to Drafts"), GTK_STOCK_CANCEL);
8894
8895                 switch (val) {
8896                 case G_ALERTDEFAULT:
8897                         if (prefs_common.autosave)
8898                                 compose_remove_draft(compose);                  
8899                         break;
8900                 case G_ALERTALTERNATE:
8901                         compose_draft_cb(data, COMPOSE_QUIT_EDITING, NULL);
8902                         return;
8903                 default:
8904                         return;
8905                 }
8906         }
8907
8908         compose_close(compose);
8909 }
8910
8911 static void compose_set_encoding_cb(gpointer data, guint action,
8912                                     GtkWidget *widget)
8913 {
8914         Compose *compose = (Compose *)data;
8915
8916         if (GTK_CHECK_MENU_ITEM(widget)->active)
8917                 compose->out_encoding = (CharSet)action;
8918 }
8919
8920 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
8921 {
8922         Compose *compose = (Compose *)data;
8923
8924         addressbook_open(compose);
8925 }
8926
8927 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
8928 {
8929         Compose *compose = (Compose *)data;
8930         Template *tmpl;
8931         gchar *msg;
8932         AlertValue val;
8933
8934         tmpl = g_object_get_data(G_OBJECT(widget), "template");
8935         g_return_if_fail(tmpl != NULL);
8936
8937         msg = g_strdup_printf(_("Do you want to apply the template '%s' ?"),
8938                               tmpl->name);
8939         val = alertpanel(_("Apply template"), msg,
8940                          _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
8941         g_free(msg);
8942
8943         if (val == G_ALERTDEFAULT)
8944                 compose_template_apply(compose, tmpl, TRUE);
8945         else if (val == G_ALERTALTERNATE)
8946                 compose_template_apply(compose, tmpl, FALSE);
8947 }
8948
8949 static void compose_ext_editor_cb(gpointer data, guint action,
8950                                   GtkWidget *widget)
8951 {
8952         Compose *compose = (Compose *)data;
8953
8954         compose_exec_ext_editor(compose);
8955 }
8956
8957 static void compose_undo_cb(Compose *compose)
8958 {
8959         gboolean prev_autowrap = compose->autowrap;
8960
8961         compose->autowrap = FALSE;
8962         undo_undo(compose->undostruct);
8963         compose->autowrap = prev_autowrap;
8964 }
8965
8966 static void compose_redo_cb(Compose *compose)
8967 {
8968         gboolean prev_autowrap = compose->autowrap;
8969         
8970         compose->autowrap = FALSE;
8971         undo_redo(compose->undostruct);
8972         compose->autowrap = prev_autowrap;
8973 }
8974
8975 static void entry_cut_clipboard(GtkWidget *entry)
8976 {
8977         if (GTK_IS_EDITABLE(entry))
8978                 gtk_editable_cut_clipboard (GTK_EDITABLE(entry));
8979         else if (GTK_IS_TEXT_VIEW(entry))
8980                 gtk_text_buffer_cut_clipboard(
8981                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8982                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
8983                         TRUE);
8984 }
8985
8986 static void entry_copy_clipboard(GtkWidget *entry)
8987 {
8988         if (GTK_IS_EDITABLE(entry))
8989                 gtk_editable_copy_clipboard (GTK_EDITABLE(entry));
8990         else if (GTK_IS_TEXT_VIEW(entry))
8991                 gtk_text_buffer_copy_clipboard(
8992                         gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry)),
8993                         gtk_clipboard_get(GDK_SELECTION_CLIPBOARD));
8994 }
8995
8996 static void entry_paste_clipboard(Compose *compose, GtkWidget *entry, 
8997                                   gboolean wrap, GdkAtom clip, GtkTextIter *insert_place)
8998 {
8999         if (GTK_IS_TEXT_VIEW(entry)) {
9000                 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9001                 GtkTextMark *mark_start = gtk_text_buffer_get_insert(buffer);
9002                 GtkTextIter start_iter, end_iter;
9003                 gint start, end;
9004                 gchar *contents = gtk_clipboard_wait_for_text(gtk_clipboard_get(clip));
9005
9006                 if (contents == NULL)
9007                         return;
9008
9009                 undo_paste_clipboard(GTK_TEXT_VIEW(compose->text), compose->undostruct);
9010
9011                 /* we shouldn't delete the selection when middle-click-pasting, or we
9012                  * can't mid-click-paste our own selection */
9013                 if (clip != GDK_SELECTION_PRIMARY) {
9014                         gtk_text_buffer_delete_selection(buffer, FALSE, TRUE);
9015                 }
9016                 
9017                 if (insert_place == NULL) {
9018                         /* if insert_place isn't specified, insert at the cursor.
9019                          * used for Ctrl-V pasting */
9020                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9021                         start = gtk_text_iter_get_offset(&start_iter);
9022                         gtk_text_buffer_insert(buffer, &start_iter, contents, strlen(contents));
9023                 } else {
9024                         /* if insert_place is specified, paste here.
9025                          * used for mid-click-pasting */
9026                         start = gtk_text_iter_get_offset(insert_place);
9027                         gtk_text_buffer_insert(buffer, insert_place, contents, strlen(contents));
9028                 }
9029                 
9030                 if (!wrap) {
9031                         /* paste unwrapped: mark the paste so it's not wrapped later */
9032                         end = start + strlen(contents);
9033                         gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, start);
9034                         gtk_text_buffer_get_iter_at_offset(buffer, &end_iter, end);
9035                         gtk_text_buffer_apply_tag_by_name(buffer, "no_wrap", &start_iter, &end_iter);
9036                 } else if (wrap && clip == GDK_SELECTION_PRIMARY) {
9037                         /* rewrap paragraph now (after a mid-click-paste) */
9038                         mark_start = gtk_text_buffer_get_insert(buffer);
9039                         gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark_start);
9040                         gtk_text_iter_backward_char(&start_iter);
9041                         compose_beautify_paragraph(compose, &start_iter, TRUE);
9042                 }
9043         } else if (GTK_IS_EDITABLE(entry))
9044                 gtk_editable_paste_clipboard (GTK_EDITABLE(entry));
9045         
9046 }
9047
9048 static void entry_allsel(GtkWidget *entry)
9049 {
9050         if (GTK_IS_EDITABLE(entry))
9051                 gtk_editable_select_region(GTK_EDITABLE(entry), 0, -1);
9052         else if (GTK_IS_TEXT_VIEW(entry)) {
9053                 GtkTextIter startiter, enditer;
9054                 GtkTextBuffer *textbuf;
9055
9056                 textbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(entry));
9057                 gtk_text_buffer_get_start_iter(textbuf, &startiter);
9058                 gtk_text_buffer_get_end_iter(textbuf, &enditer);
9059
9060                 gtk_text_buffer_move_mark_by_name(textbuf, 
9061                         "selection_bound", &startiter);
9062                 gtk_text_buffer_move_mark_by_name(textbuf, 
9063                         "insert", &enditer);
9064         }
9065 }
9066
9067 static void compose_cut_cb(Compose *compose)
9068 {
9069         if (compose->focused_editable 
9070 #ifndef MAEMO
9071             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9072 #endif
9073             )
9074                 entry_cut_clipboard(compose->focused_editable);
9075 }
9076
9077 static void compose_copy_cb(Compose *compose)
9078 {
9079         if (compose->focused_editable 
9080 #ifndef MAEMO
9081             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9082 #endif
9083             )
9084                 entry_copy_clipboard(compose->focused_editable);
9085 }
9086
9087 static void compose_paste_cb(Compose *compose)
9088 {
9089         gint prev_autowrap;
9090         GtkTextBuffer *buffer;
9091         BLOCK_WRAP();
9092         if (compose->focused_editable &&
9093             GTK_WIDGET_HAS_FOCUS(compose->focused_editable))
9094                 entry_paste_clipboard(compose, compose->focused_editable, 
9095                                 prefs_common.linewrap_pastes,
9096                                 GDK_SELECTION_CLIPBOARD, NULL);
9097         UNBLOCK_WRAP();
9098 }
9099
9100 static void compose_paste_as_quote_cb(Compose *compose)
9101 {
9102         gint wrap_quote = prefs_common.linewrap_quote;
9103         if (compose->focused_editable 
9104 #ifndef MAEMO
9105             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9106 #endif
9107             ) {
9108                 /* let text_insert() (called directly or at a later time
9109                  * after the gtk_editable_paste_clipboard) know that 
9110                  * text is to be inserted as a quotation. implemented
9111                  * by using a simple refcount... */
9112                 gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data(
9113                                                 G_OBJECT(compose->focused_editable),
9114                                                 "paste_as_quotation"));
9115                 g_object_set_data(G_OBJECT(compose->focused_editable),
9116                                     "paste_as_quotation",
9117                                     GINT_TO_POINTER(paste_as_quotation + 1));
9118                 prefs_common.linewrap_quote = prefs_common.linewrap_pastes;
9119                 entry_paste_clipboard(compose, compose->focused_editable, 
9120                                 prefs_common.linewrap_pastes,
9121                                 GDK_SELECTION_CLIPBOARD, NULL);
9122                 prefs_common.linewrap_quote = wrap_quote;
9123         }
9124 }
9125
9126 static void compose_paste_no_wrap_cb(Compose *compose)
9127 {
9128         gint prev_autowrap;
9129         GtkTextBuffer *buffer;
9130         BLOCK_WRAP();
9131         if (compose->focused_editable 
9132 #ifndef MAEMO
9133             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9134 #endif
9135             )
9136                 entry_paste_clipboard(compose, compose->focused_editable, FALSE,
9137                         GDK_SELECTION_CLIPBOARD, NULL);
9138         UNBLOCK_WRAP();
9139 }
9140
9141 static void compose_paste_wrap_cb(Compose *compose)
9142 {
9143         gint prev_autowrap;
9144         GtkTextBuffer *buffer;
9145         BLOCK_WRAP();
9146         if (compose->focused_editable 
9147 #ifndef MAEMO
9148             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9149 #endif
9150             )
9151                 entry_paste_clipboard(compose, compose->focused_editable, TRUE,
9152                         GDK_SELECTION_CLIPBOARD, NULL);
9153         UNBLOCK_WRAP();
9154 }
9155
9156 static void compose_allsel_cb(Compose *compose)
9157 {
9158         if (compose->focused_editable 
9159 #ifndef MAEMO
9160             && GTK_WIDGET_HAS_FOCUS(compose->focused_editable)
9161 #endif
9162             )
9163                 entry_allsel(compose->focused_editable);
9164 }
9165
9166 static void textview_move_beginning_of_line (GtkTextView *text)
9167 {
9168         GtkTextBuffer *buffer;
9169         GtkTextMark *mark;
9170         GtkTextIter ins;
9171
9172         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9173
9174         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9175         mark = gtk_text_buffer_get_insert(buffer);
9176         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9177         gtk_text_iter_set_line_offset(&ins, 0);
9178         gtk_text_buffer_place_cursor(buffer, &ins);
9179 }
9180
9181 static void textview_move_forward_character (GtkTextView *text)
9182 {
9183         GtkTextBuffer *buffer;
9184         GtkTextMark *mark;
9185         GtkTextIter ins;
9186
9187         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9188
9189         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9190         mark = gtk_text_buffer_get_insert(buffer);
9191         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9192         if (gtk_text_iter_forward_cursor_position(&ins))
9193                 gtk_text_buffer_place_cursor(buffer, &ins);
9194 }
9195
9196 static void textview_move_backward_character (GtkTextView *text)
9197 {
9198         GtkTextBuffer *buffer;
9199         GtkTextMark *mark;
9200         GtkTextIter ins;
9201
9202         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9203
9204         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9205         mark = gtk_text_buffer_get_insert(buffer);
9206         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9207         if (gtk_text_iter_backward_cursor_position(&ins))
9208                 gtk_text_buffer_place_cursor(buffer, &ins);
9209 }
9210
9211 static void textview_move_forward_word (GtkTextView *text)
9212 {
9213         GtkTextBuffer *buffer;
9214         GtkTextMark *mark;
9215         GtkTextIter ins;
9216         gint count;
9217
9218         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9219
9220         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9221         mark = gtk_text_buffer_get_insert(buffer);
9222         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9223         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9224         if (gtk_text_iter_forward_word_ends(&ins, count)) {
9225                 gtk_text_iter_backward_word_start(&ins);
9226                 gtk_text_buffer_place_cursor(buffer, &ins);
9227         }
9228 }
9229
9230 static void textview_move_backward_word (GtkTextView *text)
9231 {
9232         GtkTextBuffer *buffer;
9233         GtkTextMark *mark;
9234         GtkTextIter ins;
9235         gint count;
9236
9237         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9238
9239         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9240         mark = gtk_text_buffer_get_insert(buffer);
9241         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9242         count = gtk_text_iter_inside_word (&ins) ? 2 : 1;
9243         if (gtk_text_iter_backward_word_starts(&ins, 1))
9244                 gtk_text_buffer_place_cursor(buffer, &ins);
9245 }
9246
9247 static void textview_move_end_of_line (GtkTextView *text)
9248 {
9249         GtkTextBuffer *buffer;
9250         GtkTextMark *mark;
9251         GtkTextIter ins;
9252
9253         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9254
9255         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9256         mark = gtk_text_buffer_get_insert(buffer);
9257         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9258         if (gtk_text_iter_forward_to_line_end(&ins))
9259                 gtk_text_buffer_place_cursor(buffer, &ins);
9260 }
9261
9262 static void textview_move_next_line (GtkTextView *text)
9263 {
9264         GtkTextBuffer *buffer;
9265         GtkTextMark *mark;
9266         GtkTextIter ins;
9267         gint offset;
9268
9269         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9270
9271         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9272         mark = gtk_text_buffer_get_insert(buffer);
9273         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9274         offset = gtk_text_iter_get_line_offset(&ins);
9275         if (gtk_text_iter_forward_line(&ins)) {
9276                 gtk_text_iter_set_line_offset(&ins, offset);
9277                 gtk_text_buffer_place_cursor(buffer, &ins);
9278         }
9279 }
9280
9281 static void textview_move_previous_line (GtkTextView *text)
9282 {
9283         GtkTextBuffer *buffer;
9284         GtkTextMark *mark;
9285         GtkTextIter ins;
9286         gint offset;
9287
9288         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9289
9290         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9291         mark = gtk_text_buffer_get_insert(buffer);
9292         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9293         offset = gtk_text_iter_get_line_offset(&ins);
9294         if (gtk_text_iter_backward_line(&ins)) {
9295                 gtk_text_iter_set_line_offset(&ins, offset);
9296                 gtk_text_buffer_place_cursor(buffer, &ins);
9297         }
9298 }
9299
9300 static void textview_delete_forward_character (GtkTextView *text)
9301 {
9302         GtkTextBuffer *buffer;
9303         GtkTextMark *mark;
9304         GtkTextIter ins, end_iter;
9305
9306         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9307
9308         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9309         mark = gtk_text_buffer_get_insert(buffer);
9310         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9311         end_iter = ins;
9312         if (gtk_text_iter_forward_char(&end_iter)) {
9313                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9314         }
9315 }
9316
9317 static void textview_delete_backward_character (GtkTextView *text)
9318 {
9319         GtkTextBuffer *buffer;
9320         GtkTextMark *mark;
9321         GtkTextIter ins, end_iter;
9322
9323         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9324
9325         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9326         mark = gtk_text_buffer_get_insert(buffer);
9327         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9328         end_iter = ins;
9329         if (gtk_text_iter_backward_char(&end_iter)) {
9330                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9331         }
9332 }
9333
9334 static void textview_delete_forward_word (GtkTextView *text)
9335 {
9336         GtkTextBuffer *buffer;
9337         GtkTextMark *mark;
9338         GtkTextIter ins, end_iter;
9339
9340         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9341
9342         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9343         mark = gtk_text_buffer_get_insert(buffer);
9344         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9345         end_iter = ins;
9346         if (gtk_text_iter_forward_word_end(&end_iter)) {
9347                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9348         }
9349 }
9350
9351 static void textview_delete_backward_word (GtkTextView *text)
9352 {
9353         GtkTextBuffer *buffer;
9354         GtkTextMark *mark;
9355         GtkTextIter ins, end_iter;
9356
9357         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9358
9359         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9360         mark = gtk_text_buffer_get_insert(buffer);
9361         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9362         end_iter = ins;
9363         if (gtk_text_iter_backward_word_start(&end_iter)) {
9364                 gtk_text_buffer_delete(buffer, &end_iter, &ins);
9365         }
9366 }
9367
9368 static void textview_delete_line (GtkTextView *text)
9369 {
9370         GtkTextBuffer *buffer;
9371         GtkTextMark *mark;
9372         GtkTextIter ins, start_iter, end_iter;
9373         gboolean found;
9374
9375         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9376
9377         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9378         mark = gtk_text_buffer_get_insert(buffer);
9379         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9380
9381         start_iter = ins;
9382         gtk_text_iter_set_line_offset(&start_iter, 0);
9383
9384         end_iter = ins;
9385         if (gtk_text_iter_ends_line(&end_iter))
9386                 found = gtk_text_iter_forward_char(&end_iter);
9387         else
9388                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9389
9390         if (found)
9391                 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
9392 }
9393
9394 static void textview_delete_to_line_end (GtkTextView *text)
9395 {
9396         GtkTextBuffer *buffer;
9397         GtkTextMark *mark;
9398         GtkTextIter ins, end_iter;
9399         gboolean found;
9400
9401         g_return_if_fail(GTK_IS_TEXT_VIEW(text));
9402
9403         buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
9404         mark = gtk_text_buffer_get_insert(buffer);
9405         gtk_text_buffer_get_iter_at_mark(buffer, &ins, mark);
9406         end_iter = ins;
9407         if (gtk_text_iter_ends_line(&end_iter))
9408                 found = gtk_text_iter_forward_char(&end_iter);
9409         else
9410                 found = gtk_text_iter_forward_to_line_end(&end_iter);
9411         if (found)
9412                 gtk_text_buffer_delete(buffer, &ins, &end_iter);
9413 }
9414
9415 static void compose_advanced_action_cb(Compose *compose,
9416                                         ComposeCallAdvancedAction action)
9417 {
9418         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
9419         static struct {
9420                 void (*do_action) (GtkTextView *text);
9421         } action_table[] = {
9422                 {textview_move_beginning_of_line},
9423                 {textview_move_forward_character},
9424                 {textview_move_backward_character},
9425                 {textview_move_forward_word},
9426                 {textview_move_backward_word},
9427                 {textview_move_end_of_line},
9428                 {textview_move_next_line},
9429                 {textview_move_previous_line},
9430                 {textview_delete_forward_character},
9431                 {textview_delete_backward_character},
9432                 {textview_delete_forward_word},
9433                 {textview_delete_backward_word},
9434                 {textview_delete_line},
9435                 {NULL}, /* gtk_stext_delete_line_n */
9436                 {textview_delete_to_line_end}
9437         };
9438
9439         if (!GTK_WIDGET_HAS_FOCUS(text)) return;
9440
9441         if (action >= COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE &&
9442             action <= COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END) {
9443                 if (action_table[action].do_action)
9444                         action_table[action].do_action(text);
9445                 else
9446                         g_warning("Not implemented yet.");
9447         }
9448 }
9449
9450 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
9451 {
9452         gchar *str = NULL;
9453         
9454         if (GTK_IS_EDITABLE(widget)) {
9455                 str = gtk_editable_get_chars(GTK_EDITABLE(widget), 0, -1);
9456                 gtk_editable_set_position(GTK_EDITABLE(widget), 
9457                         strlen(str));
9458                 g_free(str);
9459                 if (widget->parent && widget->parent->parent
9460                  && widget->parent->parent->parent) {
9461                         if (GTK_IS_SCROLLED_WINDOW(widget->parent->parent->parent)) {
9462                                 gint y = widget->allocation.y;
9463                                 gint height = widget->allocation.height;
9464                                 GtkAdjustment *shown = gtk_scrolled_window_get_vadjustment
9465                                         (GTK_SCROLLED_WINDOW(widget->parent->parent->parent));
9466
9467                                 if (y < (int)shown->value) {
9468                                         gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), y - 1);
9469                                 }
9470                                 if (y + height > (int)shown->value + (int)shown->page_size) {
9471                                         if (y - height - 1 < (int)shown->upper - (int)shown->page_size) {
9472                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9473                                                         y + height - (int)shown->page_size - 1);
9474                                         } else {
9475                                                 gtk_adjustment_set_value(GTK_ADJUSTMENT(shown), 
9476                                                         (int)shown->upper - (int)shown->page_size - 1);
9477                                         }
9478                                 }
9479                         }
9480                 }
9481         }
9482
9483         if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
9484                 compose->focused_editable = widget;
9485         
9486 #ifdef MAEMO
9487         if (GTK_IS_TEXT_VIEW(widget) 
9488             && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->edit_vbox) {
9489                 gtk_widget_ref(compose->notebook);
9490                 gtk_widget_ref(compose->edit_vbox);
9491                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9492                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9493                 gtk_paned_add1(GTK_PANED(compose->paned), compose->edit_vbox);
9494                 gtk_paned_add2(GTK_PANED(compose->paned), compose->notebook);
9495                 gtk_widget_unref(compose->notebook);
9496                 gtk_widget_unref(compose->edit_vbox);
9497                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9498                                         G_CALLBACK(compose_grab_focus_cb),
9499                                         compose);
9500                 gtk_widget_grab_focus(widget);
9501                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9502                                         G_CALLBACK(compose_grab_focus_cb),
9503                                         compose);
9504         } else if (!GTK_IS_TEXT_VIEW(widget) 
9505                    && gtk_paned_get_child1(GTK_PANED(compose->paned)) != compose->notebook) {
9506                 gtk_widget_ref(compose->notebook);
9507                 gtk_widget_ref(compose->edit_vbox);
9508                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->notebook);
9509                 gtk_container_remove(GTK_CONTAINER(compose->paned), compose->edit_vbox);
9510                 gtk_paned_add1(GTK_PANED(compose->paned), compose->notebook);
9511                 gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
9512                 gtk_widget_unref(compose->notebook);
9513                 gtk_widget_unref(compose->edit_vbox);
9514                 g_signal_handlers_block_by_func(G_OBJECT(widget),
9515                                         G_CALLBACK(compose_grab_focus_cb),
9516                                         compose);
9517                 gtk_widget_grab_focus(widget);
9518                 g_signal_handlers_unblock_by_func(G_OBJECT(widget),
9519                                         G_CALLBACK(compose_grab_focus_cb),
9520                                         compose);
9521         }
9522 #endif
9523 }
9524
9525 static void compose_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
9526 {
9527         compose->modified = TRUE;
9528 #ifndef MAEMO
9529         compose_set_title(compose);
9530 #endif
9531 }
9532
9533 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
9534 {
9535         Compose *compose = (Compose *)data;
9536
9537         if (action == 1)
9538                 compose_wrap_all_full(compose, TRUE);
9539         else
9540                 compose_beautify_paragraph(compose, NULL, TRUE);
9541 }
9542
9543 static void compose_find_cb(gpointer data, guint action, GtkWidget *widget)
9544 {
9545         Compose *compose = (Compose *)data;
9546
9547         message_search_compose(compose);
9548 }
9549
9550 static void compose_toggle_autowrap_cb(gpointer data, guint action,
9551                                        GtkWidget *widget)
9552 {
9553         Compose *compose = (Compose *)data;
9554         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9555         if (compose->autowrap)
9556                 compose_wrap_all_full(compose, TRUE);
9557         compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
9558 }
9559
9560 static void compose_toggle_sign_cb(gpointer data, guint action,
9561                                    GtkWidget *widget)
9562 {
9563         Compose *compose = (Compose *)data;
9564
9565         if (GTK_CHECK_MENU_ITEM(widget)->active)
9566                 compose->use_signing = TRUE;
9567         else
9568                 compose->use_signing = FALSE;
9569 }
9570
9571 static void compose_toggle_encrypt_cb(gpointer data, guint action,
9572                                       GtkWidget *widget)
9573 {
9574         Compose *compose = (Compose *)data;
9575
9576         if (GTK_CHECK_MENU_ITEM(widget)->active)
9577                 compose->use_encryption = TRUE;
9578         else
9579                 compose->use_encryption = FALSE;
9580 }
9581
9582 static void activate_privacy_system(Compose *compose, PrefsAccount *account, gboolean warn) 
9583 {
9584         g_free(compose->privacy_system);
9585
9586         compose->privacy_system = g_strdup(account->default_privacy_system);
9587         compose_update_privacy_system_menu_item(compose, warn);
9588 }
9589
9590 static void compose_toggle_ruler_cb(gpointer data, guint action,
9591                                     GtkWidget *widget)
9592 {
9593         Compose *compose = (Compose *)data;
9594
9595         if (GTK_CHECK_MENU_ITEM(widget)->active) {
9596                 gtk_widget_show(compose->ruler_hbox);
9597                 prefs_common.show_ruler = TRUE;
9598         } else {
9599                 gtk_widget_hide(compose->ruler_hbox);
9600                 gtk_widget_queue_resize(compose->edit_vbox);
9601                 prefs_common.show_ruler = FALSE;
9602         }
9603 }
9604
9605 static void compose_attach_drag_received_cb (GtkWidget          *widget,
9606                                              GdkDragContext     *context,
9607                                              gint                x,
9608                                              gint                y,
9609                                              GtkSelectionData   *data,
9610                                              guint               info,
9611                                              guint               time,
9612                                              gpointer            user_data)
9613 {
9614         Compose *compose = (Compose *)user_data;
9615         GList *list, *tmp;
9616
9617         if (gdk_atom_name(data->type) && 
9618             !strcmp(gdk_atom_name(data->type), "text/uri-list")
9619             && gtk_drag_get_source_widget(context) != 
9620                 summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9621                 list = uri_list_extract_filenames((const gchar *)data->data);
9622                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9623                         gchar *utf8_filename = conv_filename_to_utf8((const gchar *)tmp->data);
9624                         compose_attach_append
9625                                 (compose, (const gchar *)tmp->data,
9626                                  utf8_filename, NULL);
9627                         g_free(utf8_filename);
9628                 }
9629                 if (list) compose_changed_cb(NULL, compose);
9630                 list_free_strings(list);
9631                 g_list_free(list);
9632         } else if (gtk_drag_get_source_widget(context) 
9633                    == summary_get_main_widget(mainwindow_get_mainwindow()->summaryview)) {
9634                 /* comes from our summaryview */
9635                 SummaryView * summaryview = NULL;
9636                 GSList * list = NULL, *cur = NULL;
9637                 
9638                 if (mainwindow_get_mainwindow())
9639                         summaryview = mainwindow_get_mainwindow()->summaryview;
9640                 
9641                 if (summaryview)
9642                         list = summary_get_selected_msg_list(summaryview);
9643                 
9644                 for (cur = list; cur; cur = cur->next) {
9645                         MsgInfo *msginfo = (MsgInfo *)cur->data;
9646                         gchar *file = NULL;
9647                         if (msginfo)
9648                                 file = procmsg_get_message_file_full(msginfo, 
9649                                         TRUE, TRUE);
9650                         if (file) {
9651                                 compose_attach_append(compose, (const gchar *)file, 
9652                                         (const gchar *)file, "message/rfc822");
9653                                 g_free(file);
9654                         }
9655                 }
9656                 g_slist_free(list);
9657         }
9658 }
9659
9660 static gboolean compose_drag_drop(GtkWidget *widget,
9661                                   GdkDragContext *drag_context,
9662                                   gint x, gint y,
9663                                   guint time, gpointer user_data)
9664 {
9665         /* not handling this signal makes compose_insert_drag_received_cb
9666          * called twice */
9667         return TRUE;                                     
9668 }
9669
9670 static void compose_insert_drag_received_cb (GtkWidget          *widget,
9671                                              GdkDragContext     *drag_context,
9672                                              gint                x,
9673                                              gint                y,
9674                                              GtkSelectionData   *data,
9675                                              guint               info,
9676                                              guint               time,
9677                                              gpointer            user_data)
9678 {
9679         Compose *compose = (Compose *)user_data;
9680         GList *list, *tmp;
9681
9682         /* strangely, testing data->type == gdk_atom_intern("text/uri-list", TRUE)
9683          * does not work */
9684         if (gdk_atom_name(data->type) && !strcmp(gdk_atom_name(data->type), "text/uri-list")) {
9685                 AlertValue val = G_ALERTDEFAULT;
9686
9687                 list = uri_list_extract_filenames((const gchar *)data->data);
9688
9689                 if (list == NULL && strstr((gchar *)(data->data), "://")) {
9690                         /* Assume a list of no files, and data has ://, is a remote link */
9691                         gchar *tmpdata = g_strstrip(g_strdup((const gchar *)data->data));
9692                         gchar *tmpfile = get_tmp_file();
9693                         str_write_to_file(tmpdata, tmpfile);
9694                         g_free(tmpdata);  
9695                         compose_insert_file(compose, tmpfile);
9696                         g_unlink(tmpfile);
9697                         g_free(tmpfile);
9698                         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9699                         compose_beautify_paragraph(compose, NULL, TRUE);
9700                         return;
9701                 }
9702                 switch (prefs_common.compose_dnd_mode) {
9703                         case COMPOSE_DND_ASK:
9704                                 val = alertpanel_full(_("Insert or attach?"),
9705                                          _("Do you want to insert the contents of the file(s) "
9706                                            "into the message body, or attach it to the email?"),
9707                                           GTK_STOCK_CANCEL, _("+_Insert"), _("_Attach"),
9708                                           TRUE, NULL, ALERT_QUESTION, G_ALERTALTERNATE);
9709                                 break;
9710                         case COMPOSE_DND_INSERT:
9711                                 val = G_ALERTALTERNATE;
9712                                 break;
9713                         case COMPOSE_DND_ATTACH:
9714                                 val = G_ALERTOTHER;
9715                                 break;
9716                         default:
9717                                 /* unexpected case */
9718                                 g_warning("error: unexpected compose_dnd_mode option value in compose_insert_drag_received_cb()");
9719                 }
9720
9721                 if (val & G_ALERTDISABLE) {
9722                         val &= ~G_ALERTDISABLE;
9723                         /* remember what action to perform by default, only if we don't click Cancel */
9724                         if (val == G_ALERTALTERNATE)
9725                                 prefs_common.compose_dnd_mode = COMPOSE_DND_INSERT;
9726                         else if (val == G_ALERTOTHER)
9727                                         prefs_common.compose_dnd_mode = COMPOSE_DND_ATTACH;
9728                 }
9729
9730                 if (val == G_ALERTDEFAULT || val == G_ALERTCANCEL) {
9731                         gtk_drag_finish(drag_context, FALSE, FALSE, time);
9732                         list_free_strings(list);
9733                         g_list_free(list);
9734                         return;
9735                 } else if (val == G_ALERTOTHER) {
9736                         compose_attach_drag_received_cb(widget, drag_context, x, y, data, info, time, user_data);
9737                         list_free_strings(list);
9738                         g_list_free(list);
9739                         return;
9740                 } 
9741
9742                 for (tmp = list; tmp != NULL; tmp = tmp->next) {
9743                         compose_insert_file(compose, (const gchar *)tmp->data);
9744                 }
9745                 list_free_strings(list);
9746                 g_list_free(list);
9747                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9748                 return;
9749         } else {
9750 #if GTK_CHECK_VERSION(2, 8, 0)
9751                 /* do nothing, handled by GTK */
9752 #else
9753                 gchar *tmpfile = get_tmp_file();
9754                 str_write_to_file((const gchar *)data->data, tmpfile);
9755                 compose_insert_file(compose, tmpfile);
9756                 g_unlink(tmpfile);
9757                 g_free(tmpfile);
9758                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9759 #endif
9760                 return;
9761         }
9762         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9763 }
9764
9765 static void compose_header_drag_received_cb (GtkWidget          *widget,
9766                                              GdkDragContext     *drag_context,
9767                                              gint                x,
9768                                              gint                y,
9769                                              GtkSelectionData   *data,
9770                                              guint               info,
9771                                              guint               time,
9772                                              gpointer            user_data)
9773 {
9774         GtkEditable *entry = (GtkEditable *)user_data;
9775         gchar *email = (gchar *)data->data;
9776
9777         /* strangely, testing data->type == gdk_atom_intern("text/plain", TRUE)
9778          * does not work */
9779
9780         if (!strncmp(email, "mailto:", strlen("mailto:"))) {
9781                 gchar *decoded=g_new(gchar, strlen(email));
9782                 int start = 0;
9783
9784                 email += strlen("mailto:");
9785                 decode_uri(decoded, email); /* will fit */
9786                 gtk_editable_delete_text(entry, 0, -1);
9787                 gtk_editable_insert_text(entry, decoded, strlen(decoded), &start);
9788                 gtk_drag_finish(drag_context, TRUE, FALSE, time);
9789                 g_free(decoded);
9790                 return;
9791         }
9792         gtk_drag_finish(drag_context, TRUE, FALSE, time);
9793 }
9794
9795 static void compose_toggle_return_receipt_cb(gpointer data, guint action,
9796                                              GtkWidget *widget)
9797 {
9798         Compose *compose = (Compose *)data;
9799
9800         if (GTK_CHECK_MENU_ITEM(widget)->active)
9801                 compose->return_receipt = TRUE;
9802         else
9803                 compose->return_receipt = FALSE;
9804 }
9805
9806 static void compose_toggle_remove_refs_cb(gpointer data, guint action,
9807                                              GtkWidget *widget)
9808 {
9809         Compose *compose = (Compose *)data;
9810
9811         if (GTK_CHECK_MENU_ITEM(widget)->active)
9812                 compose->remove_references = TRUE;
9813         else
9814                 compose->remove_references = FALSE;
9815 }
9816
9817 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
9818                                             GdkEventKey *event,
9819                                             ComposeHeaderEntry *headerentry)
9820 {
9821         if ((g_slist_length(headerentry->compose->header_list) > 0) &&
9822             ((headerentry->headernum + 1) != headerentry->compose->header_nextrow) &&
9823             !(event->state & GDK_MODIFIER_MASK) &&
9824             (event->keyval == GDK_BackSpace) &&
9825             (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) == 0)) {
9826                 gtk_container_remove
9827                         (GTK_CONTAINER(headerentry->compose->header_table),
9828                          headerentry->combo);
9829                 gtk_container_remove
9830                         (GTK_CONTAINER(headerentry->compose->header_table),
9831                          headerentry->entry);
9832                 headerentry->compose->header_list =
9833                         g_slist_remove(headerentry->compose->header_list,
9834                                        headerentry);
9835                 g_free(headerentry);
9836         } else  if (event->keyval == GDK_Tab) {
9837                 if (headerentry->compose->header_last == headerentry) {
9838                         /* Override default next focus, and give it to subject_entry
9839                          * instead of notebook tabs
9840                          */
9841                         g_signal_stop_emission_by_name(G_OBJECT(entry), "key-press-event"); 
9842                         gtk_widget_grab_focus(headerentry->compose->subject_entry);
9843                         return TRUE;
9844                 }
9845         }
9846         return FALSE;
9847 }
9848
9849 static gboolean compose_headerentry_changed_cb(GtkWidget *entry,
9850                                     ComposeHeaderEntry *headerentry)
9851 {
9852         if (strlen(gtk_entry_get_text(GTK_ENTRY(entry))) != 0) {
9853                 compose_create_header_entry(headerentry->compose);
9854                 g_signal_handlers_disconnect_matched
9855                         (G_OBJECT(entry), G_SIGNAL_MATCH_DATA,
9856                          0, 0, NULL, NULL, headerentry);
9857                 
9858                 /* Automatically scroll down */
9859                 compose_show_first_last_header(headerentry->compose, FALSE);
9860                 
9861         }
9862         return FALSE;
9863 }
9864
9865 static void compose_show_first_last_header(Compose *compose, gboolean show_first)
9866 {
9867         GtkAdjustment *vadj;
9868
9869         g_return_if_fail(compose);
9870         g_return_if_fail(GTK_IS_WIDGET(compose->header_table));
9871         g_return_if_fail(GTK_IS_VIEWPORT(compose->header_table->parent));
9872
9873         vadj = gtk_viewport_get_vadjustment(GTK_VIEWPORT(compose->header_table->parent));
9874         gtk_adjustment_set_value(vadj, (show_first ? vadj->lower : vadj->upper));
9875         gtk_adjustment_changed(vadj);
9876 }
9877
9878 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
9879                           const gchar *text, gint len, Compose *compose)
9880 {
9881         gint paste_as_quotation = GPOINTER_TO_INT(g_object_get_data
9882                                 (G_OBJECT(compose->text), "paste_as_quotation"));
9883         GtkTextMark *mark;
9884
9885         g_return_if_fail(text != NULL);
9886
9887         g_signal_handlers_block_by_func(G_OBJECT(buffer),
9888                                         G_CALLBACK(text_inserted),
9889                                         compose);
9890         if (paste_as_quotation) {
9891                 gchar *new_text;
9892                 const gchar *qmark;
9893                 guint pos = 0;
9894                 GtkTextIter start_iter;
9895
9896                 if (len < 0)
9897                         len = strlen(text);
9898
9899                 new_text = g_strndup(text, len);
9900
9901                 qmark = compose_quote_char_from_context(compose);
9902
9903                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9904                 gtk_text_buffer_place_cursor(buffer, iter);
9905
9906                 pos = gtk_text_iter_get_offset(iter);
9907
9908                 compose_quote_fmt(compose, NULL, "%Q", qmark, new_text, TRUE, FALSE,
9909                                                   _("Quote format error at line %d."));
9910                 quote_fmt_reset_vartable();
9911                 g_free(new_text);
9912                 g_object_set_data(G_OBJECT(compose->text), "paste_as_quotation",
9913                                   GINT_TO_POINTER(paste_as_quotation - 1));
9914                                   
9915                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9916                 gtk_text_buffer_place_cursor(buffer, iter);
9917                 gtk_text_buffer_delete_mark(buffer, mark);
9918
9919                 gtk_text_buffer_get_iter_at_offset(buffer, &start_iter, pos);
9920                 mark = gtk_text_buffer_create_mark(buffer, NULL, &start_iter, FALSE);
9921                 compose_beautify_paragraph(compose, &start_iter, FALSE);
9922                 gtk_text_buffer_get_iter_at_mark(buffer, &start_iter, mark);
9923                 gtk_text_buffer_delete_mark(buffer, mark);
9924         } else {
9925                 if (strcmp(text, "\n") || compose->automatic_break
9926                 || gtk_text_iter_starts_line(iter))
9927                         gtk_text_buffer_insert(buffer, iter, text, len);
9928                 else {
9929                         debug_print("insert nowrap \\n\n");
9930                         gtk_text_buffer_insert_with_tags_by_name(buffer, 
9931                                 iter, text, len, "no_join", NULL);
9932                 }
9933         }
9934         
9935         if (!paste_as_quotation) {
9936                 mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
9937                 compose_beautify_paragraph(compose, iter, FALSE);
9938                 gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
9939                 gtk_text_buffer_delete_mark(buffer, mark);
9940         }
9941
9942         g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
9943                                           G_CALLBACK(text_inserted),
9944                                           compose);
9945         g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
9946
9947         if (prefs_common.autosave && 
9948             gtk_text_buffer_get_char_count(buffer) % prefs_common.autosave_length == 0 &&
9949             compose->draft_timeout_tag != -2 /* disabled while loading */)
9950                 compose->draft_timeout_tag = g_timeout_add
9951                         (500, (GtkFunction) compose_defer_auto_save_draft, compose);
9952 }
9953 static gint compose_defer_auto_save_draft(Compose *compose)
9954 {
9955         compose->draft_timeout_tag = -1;
9956         compose_draft_cb((gpointer)compose, COMPOSE_AUTO_SAVE, NULL);
9957         return FALSE;
9958 }
9959
9960 #if USE_ASPELL
9961 static void compose_check_all(Compose *compose)
9962 {
9963         if (compose->gtkaspell)
9964                 gtkaspell_check_all(compose->gtkaspell);
9965 }
9966
9967 static void compose_highlight_all(Compose *compose)
9968 {
9969         if (compose->gtkaspell)
9970                 gtkaspell_highlight_all(compose->gtkaspell);
9971 }
9972
9973 static void compose_check_backwards(Compose *compose)
9974 {
9975         if (compose->gtkaspell) 
9976                 gtkaspell_check_backwards(compose->gtkaspell);
9977         else {
9978                 GtkItemFactory *ifactory;
9979                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9980                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9981                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9982         }
9983 }
9984
9985 static void compose_check_forwards_go(Compose *compose)
9986 {
9987         if (compose->gtkaspell) 
9988                 gtkaspell_check_forwards_go(compose->gtkaspell);
9989         else {
9990                 GtkItemFactory *ifactory;
9991                 ifactory = gtk_item_factory_from_widget(compose->popupmenu);
9992                 menu_set_sensitive(ifactory, "/Edit/Check backwards misspelled word", FALSE);
9993                 menu_set_sensitive(ifactory, "/Edit/Forward to next misspelled word", FALSE);
9994         }
9995 }
9996 #endif
9997
9998 /*!
9999  *\brief        Guess originating forward account from MsgInfo and several 
10000  *              "common preference" settings. Return NULL if no guess. 
10001  */
10002 static PrefsAccount *compose_guess_forward_account_from_msginfo(MsgInfo *msginfo)
10003 {
10004         PrefsAccount *account = NULL;
10005         
10006         g_return_val_if_fail(msginfo, NULL);
10007         g_return_val_if_fail(msginfo->folder, NULL);
10008         g_return_val_if_fail(msginfo->folder->prefs, NULL);
10009
10010         if (msginfo->folder->prefs->enable_default_account)
10011                 account = account_find_from_id(msginfo->folder->prefs->default_account);
10012                 
10013         if (!account) 
10014                 account = msginfo->folder->folder->account;
10015                 
10016         if (!account && msginfo->to && prefs_common.forward_account_autosel) {
10017                 gchar *to;
10018                 Xstrdup_a(to, msginfo->to, return NULL);
10019                 extract_address(to);
10020                 account = account_find_from_address(to);
10021         }
10022
10023         if (!account && prefs_common.forward_account_autosel) {
10024                 gchar cc[BUFFSIZE];
10025                 if (!procheader_get_header_from_msginfo
10026                         (msginfo, cc,sizeof cc , "Cc:")) { 
10027                         gchar *buf = cc + strlen("Cc:");
10028                         extract_address(buf);
10029                         account = account_find_from_address(buf);
10030                 }
10031         }
10032         
10033         if (!account && prefs_common.forward_account_autosel) {
10034                 gchar deliveredto[BUFFSIZE];
10035                 if (!procheader_get_header_from_msginfo
10036                         (msginfo, deliveredto,sizeof deliveredto , "Delivered-To:")) { 
10037                         gchar *buf = deliveredto + strlen("Delivered-To:");
10038                         extract_address(buf);
10039                         account = account_find_from_address(buf);
10040                 }
10041         }
10042         
10043         return account;
10044 }
10045
10046 gboolean compose_close(Compose *compose)
10047 {
10048         gint x, y;
10049
10050         if (!g_mutex_trylock(compose->mutex)) {
10051                 /* we have to wait for the (possibly deferred by auto-save)
10052                  * drafting to be done, before destroying the compose under
10053                  * it. */
10054                 debug_print("waiting for drafting to finish...\n");
10055                 compose_allow_user_actions(compose, FALSE);
10056                 g_timeout_add (500, (GSourceFunc) compose_close, compose);
10057                 return FALSE;
10058         }
10059         g_return_val_if_fail(compose, FALSE);
10060         gtkut_widget_get_uposition(compose->window, &x, &y);
10061         prefs_common.compose_x = x;
10062         prefs_common.compose_y = y;
10063         g_mutex_unlock(compose->mutex);
10064         compose_destroy(compose);
10065         return FALSE;
10066 }
10067
10068 /**
10069  * Add entry field for each address in list.
10070  * \param compose     E-Mail composition object.
10071  * \param listAddress List of (formatted) E-Mail addresses.
10072  */
10073 static void compose_add_field_list( Compose *compose, GList *listAddress ) {
10074         GList *node;
10075         gchar *addr;
10076         node = listAddress;
10077         while( node ) {
10078                 addr = ( gchar * ) node->data;
10079                 compose_entry_append( compose, addr, COMPOSE_TO );
10080                 node = g_list_next( node );
10081         }
10082 }
10083
10084 static void compose_reply_from_messageview_real(MessageView *msgview, GSList *msginfo_list, 
10085                                     guint action, gboolean opening_multiple)
10086 {
10087         gchar *body = NULL;
10088         GSList *new_msglist = NULL;
10089         MsgInfo *tmp_msginfo = NULL;
10090         gboolean originally_enc = FALSE;
10091         Compose *compose = NULL;
10092
10093         g_return_if_fail(msgview != NULL);
10094
10095         g_return_if_fail(msginfo_list != NULL);
10096
10097         if (g_slist_length(msginfo_list) == 1 && !opening_multiple) {
10098                 MimeInfo *mimeinfo = messageview_get_selected_mime_part(msgview);
10099                 MsgInfo *orig_msginfo = (MsgInfo *)msginfo_list->data;
10100
10101                 if (mimeinfo != NULL && mimeinfo->type == MIMETYPE_MESSAGE && 
10102                     !g_ascii_strcasecmp(mimeinfo->subtype, "rfc822")) {
10103                         tmp_msginfo = procmsg_msginfo_new_from_mimeinfo(
10104                                                 orig_msginfo, mimeinfo);
10105                         if (tmp_msginfo != NULL) {
10106                                 new_msglist = g_slist_append(NULL, tmp_msginfo);
10107
10108                                 originally_enc = MSG_IS_ENCRYPTED(orig_msginfo->flags);
10109                                 tmp_msginfo->folder = orig_msginfo->folder;
10110                                 tmp_msginfo->msgnum = orig_msginfo->msgnum; 
10111                                 if (orig_msginfo->tags)
10112                                         tmp_msginfo->tags = g_slist_copy(orig_msginfo->tags);
10113                         }
10114                 }
10115         }
10116
10117         if (!opening_multiple)
10118                 body = messageview_get_selection(msgview);
10119
10120         if (new_msglist) {
10121                 compose = compose_reply_mode((ComposeMode)action, new_msglist, body);
10122                 procmsg_msginfo_free(tmp_msginfo);
10123                 g_slist_free(new_msglist);
10124         } else
10125                 compose = compose_reply_mode((ComposeMode)action, msginfo_list, body);
10126
10127         if (compose && originally_enc) {
10128                 compose_force_encryption(compose, compose->account, FALSE);
10129         }
10130
10131         g_free(body);
10132 }
10133
10134 void compose_reply_from_messageview(MessageView *msgview, GSList *msginfo_list, 
10135                                     guint action)
10136 {
10137         if ((!prefs_common.forward_as_attachment || action != COMPOSE_FORWARD) 
10138         &&  action != COMPOSE_FORWARD_AS_ATTACH && g_slist_length(msginfo_list) > 1) {
10139                 GSList *cur = msginfo_list;
10140                 gchar *msg = g_strdup_printf(_("You are about to reply to %d "
10141                                                "messages. Opening the windows "
10142                                                "could take some time. Do you "
10143                                                "want to continue?"), 
10144                                                g_slist_length(msginfo_list));
10145                 if (g_slist_length(msginfo_list) > 9
10146                 &&  alertpanel(_("Warning"), msg, GTK_STOCK_CANCEL, "+" GTK_STOCK_YES, NULL)
10147                     != G_ALERTALTERNATE) {
10148                         g_free(msg);
10149                         return;
10150                 }
10151                 g_free(msg);
10152                 /* We'll open multiple compose windows */
10153                 /* let the WM place the next windows */
10154                 compose_force_window_origin = FALSE;
10155                 for (; cur; cur = cur->next) {
10156                         GSList tmplist;
10157                         tmplist.data = cur->data;
10158                         tmplist.next = NULL;
10159                         compose_reply_from_messageview_real(msgview, &tmplist, action, TRUE);
10160                 }
10161                 compose_force_window_origin = TRUE;
10162         } else {
10163                 /* forwarding multiple mails as attachments is done via a
10164                  * single compose window */
10165                 compose_reply_from_messageview_real(msgview, msginfo_list, action, FALSE);
10166         }
10167 }
10168
10169 void compose_set_position(Compose *compose, gint pos)
10170 {
10171         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10172
10173         gtkut_text_view_set_position(text, pos);
10174 }
10175
10176 gboolean compose_search_string(Compose *compose,
10177                                 const gchar *str, gboolean case_sens)
10178 {
10179         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10180
10181         return gtkut_text_view_search_string(text, str, case_sens);
10182 }
10183
10184 gboolean compose_search_string_backward(Compose *compose,
10185                                 const gchar *str, gboolean case_sens)
10186 {
10187         GtkTextView *text = GTK_TEXT_VIEW(compose->text);
10188
10189         return gtkut_text_view_search_string_backward(text, str, case_sens);
10190 }
10191
10192 /* allocate a msginfo structure and populate its data from a compose data structure */
10193 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose)
10194 {
10195         MsgInfo *newmsginfo;
10196         GSList *list;
10197         gchar buf[BUFFSIZE];
10198
10199         g_return_val_if_fail( compose != NULL, NULL );
10200
10201         newmsginfo = procmsg_msginfo_new();
10202
10203         /* date is now */
10204         get_rfc822_date(buf, sizeof(buf));
10205         newmsginfo->date = g_strdup(buf);
10206
10207         /* from */
10208         if (compose->from_name) {
10209                 newmsginfo->from = gtk_editable_get_chars(GTK_EDITABLE(compose->from_name), 0, -1);
10210                 newmsginfo->fromname = procheader_get_fromname(newmsginfo->from);
10211         }
10212
10213         /* subject */
10214         if (compose->subject_entry)
10215                 newmsginfo->subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
10216
10217         /* to, cc, reply-to, newsgroups */
10218         for (list = compose->header_list; list; list = list->next) {
10219                 gchar *header = gtk_editable_get_chars(
10220                                                                 GTK_EDITABLE(
10221                                                                 GTK_BIN(((ComposeHeaderEntry *)list->data)->combo)->child), 0, -1);
10222                 gchar *entry = gtk_editable_get_chars(
10223                                                                 GTK_EDITABLE(((ComposeHeaderEntry *)list->data)->entry), 0, -1);
10224
10225                 if ( strcasecmp(header, prefs_common_translated_header_name("To:")) == 0 ) {
10226                         if ( newmsginfo->to == NULL ) {
10227                                 newmsginfo->to = g_strdup(entry);
10228                         } else if (entry && *entry) {
10229                                 gchar *tmp = g_strconcat(newmsginfo->to, ", ", entry, NULL);
10230                                 g_free(newmsginfo->to);
10231                                 newmsginfo->to = tmp;
10232                         }
10233                 } else
10234                 if ( strcasecmp(header, prefs_common_translated_header_name("Cc:")) == 0 ) {
10235                         if ( newmsginfo->cc == NULL ) {
10236                                 newmsginfo->cc = g_strdup(entry);
10237                         } else if (entry && *entry) {
10238                                 gchar *tmp = g_strconcat(newmsginfo->cc, ", ", entry, NULL);
10239                                 g_free(newmsginfo->cc);
10240                                 newmsginfo->cc = tmp;
10241                         }
10242                 } else
10243                 if ( strcasecmp(header,
10244                                                 prefs_common_translated_header_name("Newsgroups:")) == 0 ) {
10245                         if ( newmsginfo->newsgroups == NULL ) {
10246                                 newmsginfo->newsgroups = g_strdup(entry);
10247                         } else if (entry && *entry) {
10248                                 gchar *tmp = g_strconcat(newmsginfo->newsgroups, ", ", entry, NULL);
10249                                 g_free(newmsginfo->newsgroups);
10250                                 newmsginfo->newsgroups = tmp;
10251                         }
10252                 }
10253
10254                 g_free(header);
10255                 g_free(entry);  
10256         }
10257
10258         /* other data is unset */
10259
10260         return newmsginfo;
10261 }
10262
10263 #ifdef USE_ASPELL
10264 /* update compose's dictionaries from folder dict settings */
10265 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
10266                                                 FolderItem *folder_item)
10267 {
10268         g_return_if_fail(compose != NULL);
10269
10270         if (compose->gtkaspell && folder_item && folder_item->prefs) {
10271                 FolderItemPrefs *prefs = folder_item->prefs;
10272
10273                 if (prefs->enable_default_dictionary)
10274                         gtkaspell_change_dict(compose->gtkaspell,
10275                                         prefs->default_dictionary, FALSE);
10276                 if (folder_item->prefs->enable_default_alt_dictionary)
10277                         gtkaspell_change_alt_dict(compose->gtkaspell,
10278                                         prefs->default_alt_dictionary);
10279                 if (prefs->enable_default_dictionary
10280                         || prefs->enable_default_alt_dictionary)
10281                         compose_spell_menu_changed(compose);
10282         }
10283 }
10284 #endif
10285
10286 /*
10287  * End of Source.
10288  */