talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

compose.c (292487B)


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