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