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