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