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