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