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