1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2017 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 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include "config.h"
22 #endif
23 
24 #include "defs.h"
25 
26 #ifndef PANGO_ENABLE_ENGINE
27 #  define PANGO_ENABLE_ENGINE
28 #endif
29 
30 #include <glib.h>
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
33 #include <gtk/gtkmain.h>
34 #include <gtk/gtkmenu.h>
35 #include <gtk/gtkmenuitem.h>
36 #include <gtk/gtkitemfactory.h>
37 #include <gtk/gtkcheckmenuitem.h>
38 #include <gtk/gtkoptionmenu.h>
39 #include <gtk/gtkwidget.h>
40 #include <gtk/gtkliststore.h>
41 #include <gtk/gtktreeview.h>
42 #include <gtk/gtktreeselection.h>
43 #include <gtk/gtkcellrenderertext.h>
44 #include <gtk/gtkvpaned.h>
45 #include <gtk/gtkentry.h>
46 #include <gtk/gtkeditable.h>
47 #include <gtk/gtktextview.h>
48 #include <gtk/gtkwindow.h>
49 #include <gtk/gtksignal.h>
50 #include <gtk/gtkvbox.h>
51 #include <gtk/gtkcontainer.h>
52 #include <gtk/gtktoolbar.h>
53 #include <gtk/gtktoolitem.h>
54 #include <gtk/gtktoolbutton.h>
55 #include <gtk/gtkseparatortoolitem.h>
56 #include <gtk/gtktable.h>
57 #include <gtk/gtkhbox.h>
58 #include <gtk/gtklabel.h>
59 #include <gtk/gtkcheckbutton.h>
60 #include <gtk/gtkscrolledwindow.h>
61 #include <gtk/gtktreeview.h>
62 #include <gtk/gtkdnd.h>
63 #include <gtk/gtkclipboard.h>
64 #include <gtk/gtkstock.h>
65 #include <gtk/gtkdialog.h>
66 #include <gtk/gtkimage.h>
67 #include <gtk/gtktooltips.h>
68 #include <pango/pango-break.h>
69 
70 #if USE_GTKSPELL
71 #  include <gtk/gtkradiomenuitem.h>
72 #  include <gtkspell/gtkspell.h>
73 #if USE_ENCHANT
74 #  include <enchant/enchant.h>
75 #else
76 #  include <aspell.h>
77 #endif
78 #endif
79 
80 #include <stdio.h>
81 #include <stdlib.h>
82 #include <string.h>
83 #include <ctype.h>
84 #include <sys/types.h>
85 #include <sys/stat.h>
86 #include <unistd.h>
87 #include <time.h>
88 #include <stdlib.h>
89 #if HAVE_SYS_WAIT_H
90 #  include <sys/wait.h>
91 #endif
92 #include <signal.h>
93 #include <errno.h>
94 #ifdef G_OS_WIN32
95 #  include <windows.h>
96 #endif
97 
98 #include "main.h"
99 #include "mainwindow.h"
100 #include "compose.h"
101 #include "addressbook.h"
102 #include "folderview.h"
103 #include "procmsg.h"
104 #include "menu.h"
105 #include "stock_pixmap.h"
106 #include "send_message.h"
107 #include "imap.h"
108 #include "news.h"
109 #include "customheader.h"
110 #include "prefs_common.h"
111 #include "prefs_common_dialog.h"
112 #include "prefs_account.h"
113 #include "prefs_toolbar.h"
114 #include "action.h"
115 #include "account.h"
116 #include "account_dialog.h"
117 #include "filesel.h"
118 #include "procheader.h"
119 #include "procmime.h"
120 #include "statusbar.h"
121 #include "about.h"
122 #include "base64.h"
123 #include "quoted-printable.h"
124 #include "codeconv.h"
125 #include "utils.h"
126 #include "gtkutils.h"
127 #include "socket.h"
128 #include "alertpanel.h"
129 #include "manage_window.h"
130 #include "gtkshruler.h"
131 #include "folder.h"
132 #include "filter.h"
133 #include "addr_compl.h"
134 #include "quote_fmt.h"
135 #include "template.h"
136 #include "undo.h"
137 #include "plugin.h"
138 #include "md5.h"
139 #include "inc.h"
140 
141 #if USE_GPGME
142 #  include "rfc2015.h"
143 #endif
144 
145 enum
146 {
147 	COL_MIMETYPE,
148 	COL_SIZE,
149 	COL_NAME,
150 	COL_ATTACH_INFO,
151 	N_ATTACH_COLS
152 };
153 
154 typedef enum
155 {
156 	COMPOSE_ACTION_MOVE_BEGINNING_OF_LINE,
157 	COMPOSE_ACTION_MOVE_FORWARD_CHARACTER,
158 	COMPOSE_ACTION_MOVE_BACKWARD_CHARACTER,
159 	COMPOSE_ACTION_MOVE_FORWARD_WORD,
160 	COMPOSE_ACTION_MOVE_BACKWARD_WORD,
161 	COMPOSE_ACTION_MOVE_END_OF_LINE,
162 	COMPOSE_ACTION_MOVE_NEXT_LINE,
163 	COMPOSE_ACTION_MOVE_PREVIOUS_LINE,
164 	COMPOSE_ACTION_DELETE_FORWARD_CHARACTER,
165 	COMPOSE_ACTION_DELETE_BACKWARD_CHARACTER,
166 	COMPOSE_ACTION_DELETE_FORWARD_WORD,
167 	COMPOSE_ACTION_DELETE_BACKWARD_WORD,
168 	COMPOSE_ACTION_DELETE_LINE,
169 	COMPOSE_ACTION_DELETE_LINE_N,
170 	COMPOSE_ACTION_DELETE_TO_LINE_END
171 } ComposeAction;
172 
173 #define B64_LINE_SIZE		57
174 #define B64_BUFFSIZE		77
175 
176 #define MAX_REFERENCES_LEN	999
177 
178 #define TEXTVIEW_MARGIN		6
179 
180 static GdkColor quote_color = {0, 0, 0, 0xbfff};
181 
182 static GList *compose_list = NULL;
183 
184 static Compose *compose_create			(PrefsAccount	*account,
185 						 ComposeMode	 mode);
186 static Compose *compose_find_window_by_target	(MsgInfo	*msginfo);
187 static gboolean compose_window_exist		(gint		 x,
188 						 gint		 y);
189 static void compose_connect_changed_callbacks	(Compose	*compose);
190 
191 static GtkWidget *compose_toolbar_create	(Compose	*compose);
192 static GtkWidget *compose_toolbar_create_from_list
193 						(Compose	*compose,
194 						 GList		*item_list);
195 static void compose_set_toolbar_button_visibility
196 						(Compose	*compose);
197 
198 static GtkWidget *compose_account_option_menu_create
199 						(Compose	*compose,
200 						 GtkWidget	*hbox);
201 static GtkWidget *compose_signature_menu_create(Compose		*compose,
202 						GtkWidget	*hbox);
203 static void compose_set_out_encoding		(Compose	*compose);
204 static void compose_set_template_menu		(Compose	*compose);
205 static void compose_template_apply		(Compose	*compose,
206 						 Template	*tmpl,
207 						 gboolean	 replace);
208 static void compose_destroy			(Compose	*compose);
209 
210 static void compose_entry_show			(Compose	*compose,
211 						 ComposeEntryType type);
212 static GtkEntry *compose_get_entry		(Compose	*compose,
213 						 ComposeEntryType type);
214 static void compose_entries_set			(Compose	*compose,
215 						 const gchar	*mailto);
216 static void compose_entries_set_from_item	(Compose	*compose,
217 						 FolderItem	*item,
218 						 ComposeMode	 mode);
219 static gint compose_parse_header		(Compose	*compose,
220 						 MsgInfo	*msginfo);
221 static gchar *compose_parse_references		(const gchar	*ref,
222 						 const gchar	*msgid);
223 static gint compose_parse_source_msg		(Compose	*compose,
224 						 MsgInfo	*msginfo);
225 
226 static gchar *compose_quote_fmt			(Compose	*compose,
227 						 MsgInfo	*msginfo,
228 						 const gchar	*fmt,
229 						 const gchar	*qmark,
230 						 const gchar	*body);
231 
232 static void compose_reply_set_entry		(Compose	*compose,
233 						 MsgInfo	*msginfo,
234 						 ComposeMode	 mode);
235 static void compose_reedit_set_entry		(Compose	*compose,
236 						 MsgInfo	*msginfo);
237 
238 static void compose_insert_sig			(Compose	*compose,
239 						 gboolean	 append,
240 						 gboolean	 replace,
241 						 gboolean	 scroll);
242 static void compose_enable_sig			(Compose	*compose);
243 static gchar *compose_get_signature_str		(Compose	*compose);
244 
245 static void compose_insert_file			(Compose	*compose,
246 						 const gchar	*file,
247 						 gboolean	 scroll);
248 
249 static void compose_attach_parts		(Compose	*compose,
250 						 MsgInfo	*msginfo);
251 
252 static void compose_wrap_paragraph		(Compose	*compose,
253 						 GtkTextIter	*par_iter);
254 static void compose_wrap_all			(Compose	*compose);
255 static void compose_wrap_all_full		(Compose	*compose,
256 						 gboolean	 autowrap);
257 
258 static void compose_set_title			(Compose	*compose);
259 static void compose_select_account		(Compose	*compose,
260 						 PrefsAccount	*account,
261 						 gboolean	 init);
262 static void compose_update_signature_menu	(Compose	*compose);
263 
264 static gboolean compose_check_for_valid_recipient
265 						(Compose	*compose);
266 static gboolean compose_check_entries		(Compose	*compose);
267 static gboolean compose_check_attachments	(Compose	*compose);
268 static gboolean compose_check_recipients	(Compose	*compose);
269 static gboolean compose_check_activities	(Compose	*compose);
270 
271 static void compose_add_new_recipients_to_addressbook
272 						(Compose	*compose);
273 
274 static gint compose_send_real			(Compose	*compose);
275 static gint compose_write_to_file		(Compose	*compose,
276 						 const gchar	*file,
277 						 gboolean	 is_draft);
278 static gint compose_write_body_to_file		(Compose	*compose,
279 						 const gchar	*file);
280 static gint compose_redirect_write_to_file	(Compose	*compose,
281 						 const gchar	*file);
282 static gint compose_remove_reedit_target	(Compose	*compose);
283 static gint compose_queue			(Compose	*compose,
284 						 const gchar	*file);
285 static gint compose_write_attach		(Compose	*compose,
286 						 FILE		*fp,
287 						 const gchar	*charset);
288 static gint compose_write_headers		(Compose	*compose,
289 						 FILE		*fp,
290 						 const gchar	*charset,
291 						 const gchar	*body_charset,
292 						 EncodingType	 encoding,
293 						 gboolean	 is_draft);
294 static gint compose_redirect_write_headers	(Compose	*compose,
295 						 FILE		*fp);
296 
297 static void compose_convert_header		(Compose	*compose,
298 						 gchar		*dest,
299 						 gint		 len,
300 						 const gchar	*src,
301 						 gint		 header_len,
302 						 gboolean	 addr_field,
303 						 const gchar	*encoding);
304 static gchar *compose_convert_filename		(Compose	*compose,
305 						 const gchar	*src,
306 						 const gchar	*param_name,
307 						 const gchar	*encoding);
308 static void compose_generate_msgid		(Compose	*compose,
309 						 gchar		*buf,
310 						 gint		 len);
311 
312 static void compose_attach_info_free		(AttachInfo	*ainfo);
313 static void compose_attach_remove_selected	(Compose	*compose);
314 
315 static void compose_attach_property		(Compose	*compose);
316 static void compose_attach_property_create	(gboolean	*cancelled);
317 static void attach_property_ok			(GtkWidget	*widget,
318 						 gboolean	*cancelled);
319 static void attach_property_cancel		(GtkWidget	*widget,
320 						 gboolean	*cancelled);
321 static gint attach_property_delete_event	(GtkWidget	*widget,
322 						 GdkEventAny	*event,
323 						 gboolean	*cancelled);
324 static gboolean attach_property_key_pressed	(GtkWidget	*widget,
325 						 GdkEventKey	*event,
326 						 gboolean	*cancelled);
327 
328 static void compose_attach_open			(Compose	*compose);
329 
330 static void compose_exec_ext_editor		(Compose	*compose);
331 static gboolean compose_ext_editor_kill		(Compose	*compose);
332 static void compose_ext_editor_child_exit	(GPid		 pid,
333 						 gint		 status,
334 						 gpointer	 data);
335 static void compose_set_ext_editor_sensitive	(Compose	*compose,
336 						 gboolean	 sensitive);
337 
338 static void compose_undo_state_changed		(UndoMain	*undostruct,
339 						 gint		 undo_state,
340 						 gint		 redo_state,
341 						 gpointer	 data);
342 
343 static gint calc_cursor_xpos	(GtkTextView	*text,
344 				 gint		 extra,
345 				 gint		 char_width);
346 
347 /* callback functions */
348 
349 static gboolean compose_edit_size_alloc (GtkEditable	*widget,
350 					 GtkAllocation	*allocation,
351 					 GtkSHRuler	*shruler);
352 
353 static void toolbar_send_cb		(GtkWidget	*widget,
354 					 gpointer	 data);
355 static void toolbar_send_later_cb	(GtkWidget	*widget,
356 					 gpointer	 data);
357 static void toolbar_draft_cb		(GtkWidget	*widget,
358 					 gpointer	 data);
359 static void toolbar_insert_cb		(GtkWidget	*widget,
360 					 gpointer	 data);
361 static void toolbar_attach_cb		(GtkWidget	*widget,
362 					 gpointer	 data);
363 static void toolbar_sig_cb		(GtkWidget	*widget,
364 					 gpointer	 data);
365 static void toolbar_ext_editor_cb	(GtkWidget	*widget,
366 					 gpointer	 data);
367 static void toolbar_linewrap_cb		(GtkWidget	*widget,
368 					 gpointer	 data);
369 static void toolbar_address_cb		(GtkWidget	*widget,
370 					 gpointer	 data);
371 static void toolbar_prefs_common_cb	(GtkWidget	*widget,
372 					 gpointer	 data);
373 static void toolbar_prefs_account_cb	(GtkWidget	*widget,
374 					 gpointer	 data);
375 
376 static gboolean toolbar_button_pressed	(GtkWidget	*widget,
377 					 GdkEventButton	*event,
378 					 gpointer	 data);
379 
380 static void account_activated		(GtkMenuItem	*menuitem,
381 					 gpointer	 data);
382 static void sig_combo_changed		(GtkComboBox	*combo,
383 					 gpointer	 data);
384 
385 static void attach_selection_changed	(GtkTreeSelection	*selection,
386 					 gpointer		 data);
387 
388 static gboolean attach_button_pressed	(GtkWidget	*widget,
389 					 GdkEventButton	*event,
390 					 gpointer	 data);
391 static gboolean attach_key_pressed	(GtkWidget	*widget,
392 					 GdkEventKey	*event,
393 					 gpointer	 data);
394 
395 static void compose_send_cb		(gpointer	 data,
396 					 guint		 action,
397 					 GtkWidget	*widget);
398 static void compose_send_later_cb	(gpointer	 data,
399 					 guint		 action,
400 					 GtkWidget	*widget);
401 
402 static void compose_draft_cb		(gpointer	 data,
403 					 guint		 action,
404 					 GtkWidget	*widget);
405 
406 static void compose_attach_open_cb	(gpointer	 data,
407 					 guint		 action,
408 					 GtkWidget	*widget);
409 static void compose_attach_cb		(gpointer	 data,
410 					 guint		 action,
411 					 GtkWidget	*widget);
412 static void compose_insert_file_cb	(gpointer	 data,
413 					 guint		 action,
414 					 GtkWidget	*widget);
415 static void compose_insert_sig_cb	(gpointer	 data,
416 					 guint		 action,
417 					 GtkWidget	*widget);
418 
419 static void compose_close_cb		(gpointer	 data,
420 					 guint		 action,
421 					 GtkWidget	*widget);
422 
423 static void compose_set_encoding_cb	(gpointer	 data,
424 					 guint		 action,
425 					 GtkWidget	*widget);
426 
427 static void compose_address_cb		(gpointer	 data,
428 					 guint		 action,
429 					 GtkWidget	*widget);
430 static void compose_template_activate_cb(GtkWidget	*widget,
431 					 gpointer	 data);
432 
433 static void compose_ext_editor_cb	(gpointer	 data,
434 					 guint		 action,
435 					 GtkWidget	*widget);
436 
437 static gint compose_delete_cb		(GtkWidget	*widget,
438 					 GdkEventAny	*event,
439 					 gpointer	 data);
440 
441 static gint compose_window_state_cb	(GtkWidget		*widget,
442 					 GdkEventWindowState	*event,
443 					 gpointer		 data);
444 
445 static void compose_undo_cb		(Compose	*compose);
446 static void compose_redo_cb		(Compose	*compose);
447 static void compose_cut_cb		(Compose	*compose);
448 static void compose_copy_cb		(Compose	*compose);
449 static void compose_paste_cb		(Compose	*compose);
450 static void compose_paste_as_quote_cb	(Compose	*compose);
451 static void compose_allsel_cb		(Compose	*compose);
452 
453 static void compose_grab_focus_cb	(GtkWidget	*widget,
454 					 Compose	*compose);
455 
456 #if USE_GPGME
457 static void compose_signing_toggled	(GtkWidget	*widget,
458 					 Compose	*compose);
459 static void compose_encrypt_toggled	(GtkWidget	*widget,
460 					 Compose	*compose);
461 #endif
462 
463 #if 0
464 static void compose_attach_toggled	(GtkWidget	*widget,
465 					 Compose	*compose);
466 #endif
467 
468 static void compose_buffer_changed_cb	(GtkTextBuffer	*textbuf,
469 					 Compose	*compose);
470 static void compose_changed_cb		(GtkEditable	*editable,
471 					 Compose	*compose);
472 
473 static void compose_wrap_cb		(gpointer	 data,
474 					 guint		 action,
475 					 GtkWidget	*widget);
476 static void compose_toggle_autowrap_cb	(gpointer	 data,
477 					 guint		 action,
478 					 GtkWidget	*widget);
479 
480 static void compose_toggle_to_cb	(gpointer	 data,
481 					 guint		 action,
482 					 GtkWidget	*widget);
483 static void compose_toggle_cc_cb	(gpointer	 data,
484 					 guint		 action,
485 					 GtkWidget	*widget);
486 static void compose_toggle_bcc_cb	(gpointer	 data,
487 					 guint		 action,
488 					 GtkWidget	*widget);
489 static void compose_toggle_replyto_cb	(gpointer	 data,
490 					 guint		 action,
491 					 GtkWidget	*widget);
492 static void compose_toggle_followupto_cb(gpointer	 data,
493 					 guint		 action,
494 					 GtkWidget	*widget);
495 static void compose_toggle_ruler_cb	(gpointer	 data,
496 					 guint		 action,
497 					 GtkWidget	*widget);
498 static void compose_toggle_attach_cb	(gpointer	 data,
499 					 guint		 action,
500 					 GtkWidget	*widget);
501 static void compose_customize_toolbar_cb(gpointer	 data,
502 					 guint		 action,
503 					 GtkWidget	*widget);
504 
505 static void compose_toggle_mdn_cb	(gpointer	 data,
506 					 guint		 action,
507 					 GtkWidget	*widget);
508 
509 #if USE_GPGME
510 static void compose_toggle_sign_cb	(gpointer	 data,
511 					 guint		 action,
512 					 GtkWidget	*widget);
513 static void compose_toggle_encrypt_cb	(gpointer	 data,
514 					 guint		 action,
515 					 GtkWidget	*widget);
516 #endif
517 
518 #if USE_GTKSPELL
519 static void compose_set_spell_lang_menu (Compose	*compose);
520 static void compose_change_spell_lang_menu
521 					(Compose	*compose,
522 					 const gchar	*lang);
523 static void compose_toggle_spell_cb	(gpointer	 data,
524 					 guint		 action,
525 					 GtkWidget	*widget);
526 static void compose_set_spell_lang_cb	(GtkWidget	*widget,
527 					 gpointer	 data);
528 #endif
529 
530 static void compose_attach_drag_received_cb (GtkWidget		*widget,
531 					     GdkDragContext	*drag_context,
532 					     gint		 x,
533 					     gint		 y,
534 					     GtkSelectionData	*data,
535 					     guint		 info,
536 					     guint		 time,
537 					     gpointer		 user_data);
538 static void compose_insert_drag_received_cb (GtkWidget		*widget,
539 					     GdkDragContext	*drag_context,
540 					     gint		 x,
541 					     gint		 y,
542 					     GtkSelectionData	*data,
543 					     guint		 info,
544 					     guint		 time,
545 					     gpointer		 user_data);
546 
547 static void to_activated		(GtkWidget	*widget,
548 					 Compose	*compose);
549 static void newsgroups_activated	(GtkWidget	*widget,
550 					 Compose	*compose);
551 static void cc_activated		(GtkWidget	*widget,
552 					 Compose	*compose);
553 static void bcc_activated		(GtkWidget	*widget,
554 					 Compose	*compose);
555 static void replyto_activated		(GtkWidget	*widget,
556 					 Compose	*compose);
557 static void followupto_activated	(GtkWidget	*widget,
558 					 Compose	*compose);
559 static void subject_activated		(GtkWidget	*widget,
560 					 Compose	*compose);
561 
562 static void text_inserted		(GtkTextBuffer	*buffer,
563 					 GtkTextIter	*iter,
564 					 const gchar	*text,
565 					 gint		 len,
566 					 Compose	*compose);
567 
568 static gboolean autosave_timeout	(gpointer	 data);
569 
570 
571 static GtkItemFactoryEntry compose_popup_entries[] =
572 {
573 	{N_("/_Open"),		NULL, compose_attach_open_cb, 0, NULL},
574 	{N_("/---"),		NULL, NULL, 0, "<Separator>"},
575 	{N_("/_Add..."),	NULL, compose_attach_cb, 0, NULL},
576 	{N_("/_Remove"),	NULL, compose_attach_remove_selected, 0, NULL},
577 	{N_("/---"),		NULL, NULL, 0, "<Separator>"},
578 	{N_("/_Properties..."),	NULL, compose_attach_property, 0, NULL}
579 };
580 
581 static GtkItemFactoryEntry compose_entries[] =
582 {
583 	{N_("/_File"),				NULL, NULL, 0, "<Branch>"},
584 	{N_("/_File/_Send"),			"<shift><control>E",
585 						compose_send_cb, 0, NULL},
586 	{N_("/_File/Send _later"),		"<shift><control>S",
587 						compose_send_later_cb,  0, NULL},
588 	{N_("/_File/---"),			NULL, NULL, 0, "<Separator>"},
589 	{N_("/_File/Save to _draft folder"),
590 						"<shift><control>D", compose_draft_cb, 0, NULL},
591 	{N_("/_File/Save and _keep editing"),
592 						"<control>S", compose_draft_cb, 1, NULL},
593 	{N_("/_File/---"),			NULL, NULL, 0, "<Separator>"},
594 	{N_("/_File/_Attach file"),		"<control>M", compose_attach_cb,      0, NULL},
595 	{N_("/_File/_Insert file"),		"<control>I", compose_insert_file_cb, 0, NULL},
596 	{N_("/_File/---"),			NULL, NULL, 0, "<Separator>"},
597 	{N_("/_File/Insert si_gnature"),	"<control>G", compose_insert_sig_cb,  0, NULL},
598 	{N_("/_File/A_ppend signature"),	"<shift><control>G", compose_insert_sig_cb,  1, NULL},
599 	{N_("/_File/---"),			NULL, NULL, 0, "<Separator>"},
600 	{N_("/_File/_Close"),			"<control>W", compose_close_cb, 0, NULL},
601 
602 	{N_("/_Edit"),			NULL, NULL, 0, "<Branch>"},
603 	{N_("/_Edit/_Undo"),		"<control>Z", compose_undo_cb, 0, NULL},
604 	{N_("/_Edit/_Redo"),		"<control>Y", compose_redo_cb, 0, NULL},
605 	{N_("/_Edit/---"),		NULL, NULL, 0, "<Separator>"},
606 	{N_("/_Edit/Cu_t"),		"<control>X", compose_cut_cb,    0, NULL},
607 	{N_("/_Edit/_Copy"),		"<control>C", compose_copy_cb,   0, NULL},
608 	{N_("/_Edit/_Paste"),		"<control>V", compose_paste_cb,  0, NULL},
609 	{N_("/_Edit/Paste as _quotation"),
610 					NULL, compose_paste_as_quote_cb, 0, NULL},
611 	{N_("/_Edit/Select _all"),	"<control>A", compose_allsel_cb, 0, NULL},
612 	{N_("/_Edit/---"),		NULL, NULL, 0, "<Separator>"},
613 	{N_("/_Edit/_Wrap current paragraph"),
614 					"<control>L", compose_wrap_cb, 0, NULL},
615 	{N_("/_Edit/Wrap all long _lines"),
616 					"<control><alt>L", compose_wrap_cb, 1, NULL},
617 	{N_("/_Edit/Aut_o wrapping"),	"<shift><control>L", compose_toggle_autowrap_cb, 0, "<ToggleItem>"},
618 	{N_("/_View"),			NULL, NULL, 0, "<Branch>"},
619 	{N_("/_View/_To"),		NULL, compose_toggle_to_cb     , 0, "<ToggleItem>"},
620 	{N_("/_View/_Cc"),		NULL, compose_toggle_cc_cb     , 0, "<ToggleItem>"},
621 	{N_("/_View/_Bcc"),		NULL, compose_toggle_bcc_cb    , 0, "<ToggleItem>"},
622 	{N_("/_View/_Reply-To"),	NULL, compose_toggle_replyto_cb, 0, "<ToggleItem>"},
623 	{N_("/_View/---"),		NULL, NULL, 0, "<Separator>"},
624 	{N_("/_View/_Followup-To"),	NULL, compose_toggle_followupto_cb, 0, "<ToggleItem>"},
625 	{N_("/_View/---"),		NULL, NULL, 0, "<Separator>"},
626 	{N_("/_View/R_uler"),		NULL, compose_toggle_ruler_cb, 0, "<ToggleItem>"},
627 	{N_("/_View/---"),		NULL, NULL, 0, "<Separator>"},
628 	{N_("/_View/_Attachment"),	NULL, compose_toggle_attach_cb, 0, "<ToggleItem>"},
629 	{N_("/_View/---"),		NULL, NULL, 0, "<Separator>"},
630 	{N_("/_View/Cu_stomize toolbar..."),
631 					NULL, compose_customize_toolbar_cb, 0, NULL},
632 	{N_("/_View/---"),		NULL, NULL, 0, "<Separator>"},
633 
634 #define ENC_ACTION(action) \
635 	NULL, compose_set_encoding_cb, action, \
636 	"/View/Character encoding/Automatic"
637 
638 	{N_("/_View/Character _encoding"), NULL, NULL, 0, "<Branch>"},
639 	{N_("/_View/Character _encoding/_Automatic"),
640 			NULL, compose_set_encoding_cb, C_AUTO, "<RadioItem>"},
641 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
642 
643 	{N_("/_View/Character _encoding/7bit ascii (US-ASC_II)"),
644 	 ENC_ACTION(C_US_ASCII)},
645 	{N_("/_View/Character _encoding/Unicode (_UTF-8)"),
646 	 ENC_ACTION(C_UTF_8)},
647 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
648 
649 	{N_("/_View/Character _encoding/Western European (ISO-8859-_1)"),
650 	 ENC_ACTION(C_ISO_8859_1)},
651 	{N_("/_View/Character _encoding/Western European (ISO-8859-15)"),
652 	 ENC_ACTION(C_ISO_8859_15)},
653 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
654 
655 	{N_("/_View/Character _encoding/Central European (ISO-8859-_2)"),
656 	 ENC_ACTION(C_ISO_8859_2)},
657 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
658 
659 	{N_("/_View/Character _encoding/_Baltic (ISO-8859-13)"),
660 	 ENC_ACTION(C_ISO_8859_13)},
661 	{N_("/_View/Character _encoding/Baltic (ISO-8859-_4)"),
662 	 ENC_ACTION(C_ISO_8859_4)},
663 	{N_("/_View/Character _encoding/Baltic (Windows-1257)"),
664 	 ENC_ACTION(C_WINDOWS_1257)},
665 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
666 
667 	{N_("/_View/Character _encoding/Greek (ISO-8859-_7)"),
668 	 ENC_ACTION(C_ISO_8859_7)},
669 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
670 
671 	{N_("/_View/Character _encoding/Arabic (ISO-8859-_6)"),
672 	 ENC_ACTION(C_ISO_8859_6)},
673 	{N_("/_View/Character _encoding/Arabic (Windows-1256)"),
674 	 ENC_ACTION(C_WINDOWS_1256)},
675 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
676 
677 	{N_("/_View/Character _encoding/Hebrew (ISO-8859-_8)"),
678 	 ENC_ACTION(C_ISO_8859_8)},
679 	{N_("/_View/Character _encoding/Hebrew (Windows-1255)"),
680 	 ENC_ACTION(C_WINDOWS_1255)},
681 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
682 
683 	{N_("/_View/Character _encoding/Turkish (ISO-8859-_9)"),
684 	 ENC_ACTION(C_ISO_8859_9)},
685 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
686 
687 	{N_("/_View/Character _encoding/Cyrillic (ISO-8859-_5)"),
688 	 ENC_ACTION(C_ISO_8859_5)},
689 	{N_("/_View/Character _encoding/Cyrillic (KOI8-_R)"),
690 	 ENC_ACTION(C_KOI8_R)},
691 	{N_("/_View/Character _encoding/Cyrillic (KOI8-U)"),
692 	 ENC_ACTION(C_KOI8_U)},
693 	{N_("/_View/Character _encoding/Cyrillic (Windows-1251)"),
694 	 ENC_ACTION(C_WINDOWS_1251)},
695 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
696 
697 	{N_("/_View/Character _encoding/Japanese (ISO-2022-_JP)"),
698 	 ENC_ACTION(C_ISO_2022_JP)},
699 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
700 
701 	{N_("/_View/Character _encoding/Simplified Chinese (_GB2312)"),
702 	 ENC_ACTION(C_GB2312)},
703 	{N_("/_View/Character _encoding/Simplified Chinese (GBK)"),
704 	 ENC_ACTION(C_GBK)},
705 	{N_("/_View/Character _encoding/Traditional Chinese (_Big5)"),
706 	 ENC_ACTION(C_BIG5)},
707 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
708 
709 	{N_("/_View/Character _encoding/Korean (EUC-_KR)"),
710 	 ENC_ACTION(C_EUC_KR)},
711 	{N_("/_View/Character _encoding/---"), NULL, NULL, 0, "<Separator>"},
712 
713 	{N_("/_View/Character _encoding/Thai (TIS-620)"),
714 	 ENC_ACTION(C_TIS_620)},
715 	{N_("/_View/Character _encoding/Thai (Windows-874)"),
716 	 ENC_ACTION(C_WINDOWS_874)},
717 
718 	{N_("/_Tools"),			NULL, NULL, 0, "<Branch>"},
719 	{N_("/_Tools/_Address book"),	"<shift><control>A", compose_address_cb , 0, NULL},
720 	{N_("/_Tools/_Template"),	NULL, NULL, 0, "<Branch>"},
721 #ifndef G_OS_WIN32
722 	{N_("/_Tools/Actio_ns"),	NULL, NULL, 0, "<Branch>"},
723 #endif
724 	{N_("/_Tools/---"),		NULL, NULL, 0, "<Separator>"},
725 	{N_("/_Tools/Edit with e_xternal editor"),
726 					"<shift><control>X", compose_ext_editor_cb, 0, NULL},
727 	{N_("/_Tools/---"),		NULL, NULL, 0, "<Separator>"},
728 	{N_("/_Tools/Request _disposition notification"),   	NULL, compose_toggle_mdn_cb   , 0, "<ToggleItem>"},
729 
730 #if USE_GPGME
731 	{N_("/_Tools/---"),		NULL, NULL, 0, "<Separator>"},
732 	{N_("/_Tools/PGP Si_gn"),   	NULL, compose_toggle_sign_cb   , 0, "<ToggleItem>"},
733 	{N_("/_Tools/PGP _Encrypt"),	NULL, compose_toggle_encrypt_cb, 0, "<ToggleItem>"},
734 #endif /* USE_GPGME */
735 
736 #if USE_GTKSPELL
737 	{N_("/_Tools/---"),			NULL, NULL, 0, "<Separator>"},
738 	{N_("/_Tools/_Check spell"),		NULL, compose_toggle_spell_cb, 0, "<ToggleItem>"},
739 	{N_("/_Tools/_Set spell language"),	NULL, NULL, 0, "<Branch>"},
740 #endif /* USE_GTKSPELL */
741 
742 	{N_("/_Help"),			NULL, NULL, 0, "<Branch>"},
743 	{N_("/_Help/_About"),		NULL, about_show, 0, NULL}
744 };
745 
746 enum
747 {
748 	DRAG_TYPE_RFC822,
749 	DRAG_TYPE_URI_LIST,
750 
751 	N_DRAG_TYPES
752 };
753 
754 static GtkTargetEntry compose_drag_types[] =
755 {
756 	{"message/rfc822", GTK_TARGET_SAME_APP, DRAG_TYPE_RFC822},
757 	{"text/uri-list", 0, DRAG_TYPE_URI_LIST}
758 };
759 
760 
compose_new(PrefsAccount * account,FolderItem * item,const gchar * mailto,GPtrArray * attach_files)761 Compose *compose_new(PrefsAccount *account, FolderItem *item,
762 		     const gchar *mailto, GPtrArray *attach_files)
763 {
764 	Compose *compose;
765 	GtkTextView *text;
766 	GtkTextBuffer *buffer;
767 	GtkTextIter iter;
768 
769 	if (!account) account = cur_account;
770 	g_return_val_if_fail(account != NULL, NULL);
771 
772 	compose = compose_create(account, COMPOSE_NEW);
773 
774 	undo_block(compose->undostruct);
775 
776 	if (prefs_common.auto_sig)
777 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
778 
779 	text = GTK_TEXT_VIEW(compose->text);
780 	buffer = gtk_text_view_get_buffer(text);
781 	gtk_text_buffer_get_start_iter(buffer, &iter);
782 	gtk_text_buffer_place_cursor(buffer, &iter);
783 
784 	if (account->protocol != A_NNTP) {
785 		if (mailto && *mailto != '\0') {
786 			compose_entries_set(compose, mailto);
787 			gtk_widget_grab_focus(compose->subject_entry);
788 		} else if (item) {
789 			compose_entries_set_from_item
790 				(compose, item, COMPOSE_NEW);
791 			if (item->auto_to)
792 				gtk_widget_grab_focus(compose->subject_entry);
793 			else
794 				gtk_widget_grab_focus(compose->to_entry);
795 		} else
796 			gtk_widget_grab_focus(compose->to_entry);
797 	} else {
798 		if (mailto && *mailto != '\0') {
799 			compose_entry_append(compose, mailto,
800 					     COMPOSE_ENTRY_NEWSGROUPS);
801 			gtk_widget_grab_focus(compose->subject_entry);
802 		} else
803 			gtk_widget_grab_focus(compose->newsgroups_entry);
804 	}
805 
806 	if (attach_files) {
807 		gint i;
808 		gchar *file;
809 
810 		for (i = 0; i < attach_files->len; i++) {
811 			gchar *utf8file;
812 
813 			file = g_ptr_array_index(attach_files, i);
814 			utf8file = conv_filename_to_utf8(file);
815 			compose_attach_append(compose, file, utf8file, NULL);
816 			g_free(utf8file);
817 		}
818 	}
819 
820 	undo_unblock(compose->undostruct);
821 
822 	compose_connect_changed_callbacks(compose);
823 	compose_set_title(compose);
824 
825 	syl_plugin_signal_emit("compose-created", compose);
826 
827 	if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
828 		compose->autosave_tag =
829 			g_timeout_add_full(G_PRIORITY_LOW,
830 					   prefs_common.autosave_itv * 60 * 1000,
831 					   autosave_timeout, compose, NULL);
832 	if (prefs_common.auto_exteditor)
833 		compose_exec_ext_editor(compose);
834 
835 	return compose;
836 }
837 
compose_reply(MsgInfo * msginfo,FolderItem * item,ComposeMode mode,const gchar * body)838 Compose *compose_reply(MsgInfo *msginfo, FolderItem *item, ComposeMode mode,
839 		       const gchar *body)
840 {
841 	Compose *compose;
842 	PrefsAccount *account = NULL;
843 	MsgInfo *replyinfo;
844 	GtkTextBuffer *buffer;
845 	GtkTextIter iter;
846 	gboolean quote = FALSE;
847 
848 	g_return_val_if_fail(msginfo != NULL, NULL);
849 
850 	if (COMPOSE_QUOTE_MODE(mode) == COMPOSE_WITH_QUOTE)
851 		quote = TRUE;
852 
853 	if (msginfo->folder)
854 		account = account_find_from_item_property(msginfo->folder);
855 	if (!account && msginfo->to && prefs_common.reply_account_autosel) {
856 		gchar *to;
857 		to = g_strdup(msginfo->to);
858 		extract_address(to);
859 		account = account_find_from_address(to);
860 		g_free(to);
861 	}
862 	if (!account && msginfo->folder && msginfo->folder->folder)
863 		account = msginfo->folder->folder->account;
864 	if (!account)
865 		account = cur_account;
866 	g_return_val_if_fail(account != NULL, NULL);
867 
868 	compose = compose_create(account, COMPOSE_REPLY);
869 
870 	replyinfo = procmsg_msginfo_get_full_info(msginfo);
871 	if (!replyinfo)
872 		replyinfo = procmsg_msginfo_copy(msginfo);
873 	if (msginfo->folder) {
874 		gchar *id;
875 		id = folder_item_get_identifier(msginfo->folder);
876 		if (id) {
877 			compose->reply_target = g_strdup_printf
878 				("%s/%u", id, msginfo->msgnum);
879 			g_free(id);
880 		}
881 	}
882 
883 	if (compose_parse_header(compose, msginfo) < 0)
884 		return compose;
885 
886 	undo_block(compose->undostruct);
887 
888 	compose_reply_set_entry(compose, msginfo, mode);
889 	if (item)
890 		compose_entries_set_from_item(compose, item, COMPOSE_REPLY);
891 
892 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
893 
894 	if (account->sig_before_quote && prefs_common.auto_sig) {
895 		GtkTextMark *mark;
896 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
897 		mark = gtk_text_buffer_get_insert(buffer);
898 		gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
899 		gtk_text_buffer_insert(buffer, &iter, "\n", 1);
900 	}
901 
902 	if (quote) {
903 		compose_quote_fmt(compose, replyinfo,
904 				  prefs_common.quotefmt,
905 				  prefs_common.quotemark, body);
906 	}
907 
908 	if (!account->sig_before_quote && prefs_common.auto_sig)
909 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
910 
911 	if (quote && prefs_common.linewrap_quote)
912 		compose_wrap_all(compose);
913 
914 	gtk_text_buffer_get_start_iter(buffer, &iter);
915 	gtk_text_buffer_place_cursor(buffer, &iter);
916 
917 	gtk_widget_grab_focus(compose->text);
918 
919 	undo_unblock(compose->undostruct);
920 
921 	compose_connect_changed_callbacks(compose);
922 	compose_set_title(compose);
923 
924 #if USE_GPGME
925 	if (rfc2015_is_available() && account->encrypt_reply &&
926 	    MSG_IS_ENCRYPTED(replyinfo->flags)) {
927 		GtkItemFactory *ifactory;
928 
929 		ifactory = gtk_item_factory_from_widget(compose->menubar);
930 		menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
931 	}
932 #endif
933 
934 	procmsg_msginfo_free(replyinfo);
935 
936 	syl_plugin_signal_emit("compose-created", compose);
937 
938 	if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
939 		compose->autosave_tag =
940 			g_timeout_add_full(G_PRIORITY_LOW,
941 					   prefs_common.autosave_itv * 60 * 1000,
942 					   autosave_timeout, compose, NULL);
943 	if (prefs_common.auto_exteditor)
944 		compose_exec_ext_editor(compose);
945 
946 	return compose;
947 }
948 
compose_forward(GSList * mlist,FolderItem * item,gboolean as_attach,const gchar * body)949 Compose *compose_forward(GSList *mlist, FolderItem *item, gboolean as_attach,
950 			 const gchar *body)
951 {
952 	Compose *compose;
953 	PrefsAccount *account = NULL;
954 	GtkTextView *text;
955 	GtkTextBuffer *buffer;
956 	GtkTextIter iter;
957 	GSList *cur;
958 	MsgInfo *msginfo;
959 	GString *forward_targets;
960 
961 	g_return_val_if_fail(mlist != NULL, NULL);
962 
963 	msginfo = (MsgInfo *)mlist->data;
964 
965 	if (msginfo->folder)
966 		account = account_find_from_item(msginfo->folder);
967 	if (!account) account = cur_account;
968 	g_return_val_if_fail(account != NULL, NULL);
969 
970 	compose = compose_create(account, COMPOSE_FORWARD);
971 
972 	forward_targets = g_string_new("");
973 	for (cur = mlist; cur != NULL; cur = cur->next) {
974 		msginfo = (MsgInfo *)cur->data;
975 		if (msginfo->folder) {
976 			gchar *id;
977 			id = folder_item_get_identifier(msginfo->folder);
978 			if (id) {
979 				if (forward_targets->len > 0)
980 					g_string_append(forward_targets,
981 							"\n ");
982 				g_string_append_printf(forward_targets, "%s/%u",
983 						       id, msginfo->msgnum);
984 				g_free(id);
985 			}
986 		}
987 	}
988 	if (forward_targets->len > 0)
989 		compose->forward_targets = g_string_free(forward_targets,
990 							 FALSE);
991 	else
992 		g_string_free(forward_targets, TRUE);
993 
994 	undo_block(compose->undostruct);
995 
996 	compose_entry_set(compose, "Fw: ", COMPOSE_ENTRY_SUBJECT);
997 	if (mlist->next == NULL && msginfo->subject && *msginfo->subject)
998 		compose_entry_append(compose, msginfo->subject,
999 				     COMPOSE_ENTRY_SUBJECT);
1000 	if (item)
1001 		compose_entries_set_from_item(compose, item, COMPOSE_FORWARD);
1002 
1003 	text = GTK_TEXT_VIEW(compose->text);
1004 	buffer = gtk_text_view_get_buffer(text);
1005 
1006 	if (account->sig_before_quote && prefs_common.auto_sig) {
1007 		GtkTextMark *mark;
1008 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
1009 		mark = gtk_text_buffer_get_insert(buffer);
1010 		gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1011 		gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1012 	}
1013 
1014 	for (cur = mlist; cur != NULL; cur = cur->next) {
1015 		msginfo = (MsgInfo *)cur->data;
1016 
1017 		if (as_attach) {
1018 			gchar *msgfile;
1019 
1020 			msgfile = procmsg_get_message_file_path(msginfo);
1021 			if (!is_file_exist(msgfile))
1022 				g_warning(_("%s: file not exist\n"), msgfile);
1023 			else
1024 				compose_attach_append(compose, msgfile, msgfile,
1025 						      "message/rfc822");
1026 
1027 			g_free(msgfile);
1028 		} else {
1029 			MsgInfo *full_msginfo;
1030 
1031 			full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1032 			if (!full_msginfo)
1033 				full_msginfo = procmsg_msginfo_copy(msginfo);
1034 
1035 			if (cur != mlist) {
1036 				GtkTextMark *mark;
1037 				mark = gtk_text_buffer_get_insert(buffer);
1038 				gtk_text_buffer_get_iter_at_mark
1039 					(buffer, &iter, mark);
1040 				gtk_text_buffer_insert
1041 					(buffer, &iter, "\n\n", 2);
1042 			}
1043 
1044 			compose_quote_fmt(compose, full_msginfo,
1045 					  prefs_common.fw_quotefmt,
1046 					  prefs_common.fw_quotemark, body);
1047 			compose_attach_parts(compose, msginfo);
1048 
1049 			procmsg_msginfo_free(full_msginfo);
1050 
1051 			if (body) break;
1052 		}
1053 	}
1054 
1055 	if (!account->sig_before_quote && prefs_common.auto_sig)
1056 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
1057 
1058 	if (prefs_common.linewrap_quote)
1059 		compose_wrap_all(compose);
1060 
1061 	gtk_text_buffer_get_start_iter(buffer, &iter);
1062 	gtk_text_buffer_place_cursor(buffer, &iter);
1063 
1064 	undo_unblock(compose->undostruct);
1065 
1066 	compose_connect_changed_callbacks(compose);
1067 	compose_set_title(compose);
1068 
1069 	if (account->protocol != A_NNTP)
1070 		gtk_widget_grab_focus(compose->to_entry);
1071 	else
1072 		gtk_widget_grab_focus(compose->newsgroups_entry);
1073 
1074 	syl_plugin_signal_emit("compose-created", compose);
1075 
1076 	if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1077 		compose->autosave_tag =
1078 			g_timeout_add_full(G_PRIORITY_LOW,
1079 					   prefs_common.autosave_itv * 60 * 1000,
1080 					   autosave_timeout, compose, NULL);
1081 	if (prefs_common.auto_exteditor)
1082 		compose_exec_ext_editor(compose);
1083 
1084 	return compose;
1085 }
1086 
compose_redirect(MsgInfo * msginfo,FolderItem * item)1087 Compose *compose_redirect(MsgInfo *msginfo, FolderItem *item)
1088 {
1089 	Compose *compose;
1090 	PrefsAccount *account;
1091 	GtkTextView *text;
1092 	GtkTextBuffer *buffer;
1093 	GtkTextMark *mark;
1094 	GtkTextIter iter;
1095 	FILE *fp;
1096 	gchar buf[BUFFSIZE];
1097 
1098 	g_return_val_if_fail(msginfo != NULL, NULL);
1099 	g_return_val_if_fail(msginfo->folder != NULL, NULL);
1100 
1101 	account = account_find_from_item(msginfo->folder);
1102 	if (!account) account = cur_account;
1103 	g_return_val_if_fail(account != NULL, NULL);
1104 
1105 	compose = compose_create(account, COMPOSE_REDIRECT);
1106 	compose->targetinfo = procmsg_msginfo_copy(msginfo);
1107 
1108 	if (compose_parse_header(compose, msginfo) < 0)
1109 		return compose;
1110 
1111 	undo_block(compose->undostruct);
1112 
1113 	if (msginfo->subject)
1114 		compose_entry_set(compose, msginfo->subject,
1115 				  COMPOSE_ENTRY_SUBJECT);
1116 	compose_entries_set_from_item(compose, item, COMPOSE_REDIRECT);
1117 
1118 	text = GTK_TEXT_VIEW(compose->text);
1119 	buffer = gtk_text_view_get_buffer(text);
1120 	mark = gtk_text_buffer_get_insert(buffer);
1121 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1122 
1123 	if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1124 		g_warning(_("Can't get text part\n"));
1125 	else {
1126 		gboolean prev_autowrap = compose->autowrap;
1127 
1128 		compose->autowrap = FALSE;
1129 		while (fgets(buf, sizeof(buf), fp) != NULL) {
1130 			strcrchomp(buf);
1131 			gtk_text_buffer_insert(buffer, &iter, buf, -1);
1132 		}
1133 		compose->autowrap = prev_autowrap;
1134 		fclose(fp);
1135 	}
1136 	compose_attach_parts(compose, msginfo);
1137 
1138 	if (account->protocol != A_NNTP)
1139 		gtk_widget_grab_focus(compose->to_entry);
1140 	else
1141 		gtk_widget_grab_focus(compose->newsgroups_entry);
1142 
1143 	gtk_text_view_set_editable(text, FALSE);
1144 
1145 	undo_unblock(compose->undostruct);
1146 
1147 	compose_connect_changed_callbacks(compose);
1148 	compose_set_title(compose);
1149 
1150 	syl_plugin_signal_emit("compose-created", compose);
1151 
1152 	return compose;
1153 }
1154 
compose_reedit(MsgInfo * msginfo)1155 Compose *compose_reedit(MsgInfo *msginfo)
1156 {
1157 	Compose *compose;
1158 	PrefsAccount *account;
1159 	GtkTextView *text;
1160 	GtkTextBuffer *buffer;
1161 	GtkTextMark *mark;
1162 	GtkTextIter iter;
1163 	FILE *fp;
1164 	gchar buf[BUFFSIZE];
1165 	const gchar *str;
1166 	GtkWidget *focus_widget = NULL;
1167 	GtkItemFactory *ifactory;
1168 
1169 	g_return_val_if_fail(msginfo != NULL, NULL);
1170 	g_return_val_if_fail(msginfo->folder != NULL, NULL);
1171 
1172 	account = account_find_from_msginfo(msginfo);
1173 	if (!account) account = cur_account;
1174 	g_return_val_if_fail(account != NULL, NULL);
1175 
1176 	if (msginfo->folder->stype == F_DRAFT ||
1177 	    msginfo->folder->stype == F_QUEUE) {
1178 		compose = compose_find_window_by_target(msginfo);
1179 		if (compose) {
1180 			debug_print
1181 				("compose_reedit(): existing window found.\n");
1182 			gtk_window_present(GTK_WINDOW(compose->window));
1183 			return compose;
1184 		}
1185 	}
1186 
1187 	compose = compose_create(account, COMPOSE_REEDIT);
1188 	compose->targetinfo = procmsg_msginfo_copy(msginfo);
1189 
1190 	if (compose_parse_header(compose, msginfo) < 0)
1191 		return compose;
1192 	compose_parse_source_msg(compose, msginfo);
1193 
1194 	undo_block(compose->undostruct);
1195 
1196 	compose_reedit_set_entry(compose, msginfo);
1197 
1198 	text = GTK_TEXT_VIEW(compose->text);
1199 	buffer = gtk_text_view_get_buffer(text);
1200 	mark = gtk_text_buffer_get_insert(buffer);
1201 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1202 
1203 	if ((fp = procmime_get_first_text_content(msginfo, NULL)) == NULL)
1204 		g_warning(_("Can't get text part\n"));
1205 	else {
1206 		gboolean prev_autowrap = compose->autowrap;
1207 
1208 		compose->autowrap = FALSE;
1209 		while (fgets(buf, sizeof(buf), fp) != NULL) {
1210 			strcrchomp(buf);
1211 			gtk_text_buffer_insert(buffer, &iter, buf, -1);
1212 		}
1213 		compose_enable_sig(compose);
1214 		compose->autowrap = prev_autowrap;
1215 		fclose(fp);
1216 	}
1217 	compose_attach_parts(compose, msginfo);
1218 
1219 	gtk_text_buffer_get_start_iter(buffer, &iter);
1220 	gtk_text_buffer_place_cursor(buffer, &iter);
1221 
1222 	if (account->protocol != A_NNTP) {
1223 		str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
1224 		if (!str || *str == '\0')
1225 			focus_widget = compose->to_entry;
1226 	} else {
1227 		str = gtk_entry_get_text(GTK_ENTRY(compose->newsgroups_entry));
1228 		if (!str || *str == '\0')
1229 			focus_widget = compose->newsgroups_entry;
1230 	}
1231 	if (!focus_widget) {
1232 		str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
1233 		if (!str || *str == '\0')
1234 			focus_widget = compose->subject_entry;
1235 	}
1236 	if (focus_widget)
1237 		gtk_widget_grab_focus(focus_widget);
1238 	else
1239 		gtk_widget_grab_focus(compose->text);
1240 
1241 	undo_unblock(compose->undostruct);
1242 
1243 	compose_connect_changed_callbacks(compose);
1244 	compose_set_title(compose);
1245 
1246 	ifactory = gtk_item_factory_from_widget(compose->menubar);
1247 	if (compose->use_mdn) {
1248 		menu_set_active(ifactory, "/Tools/Request disposition notification", TRUE);
1249 	}
1250 	menu_set_active(ifactory, "/Edit/Auto wrapping", compose->autowrap);
1251 #if USE_GTKSPELL
1252 	menu_set_active(ifactory, "/Tools/Check spell", compose->check_spell);
1253 	compose_change_spell_lang_menu(compose, compose->spell_lang);
1254 #endif
1255 #if USE_GPGME
1256 	menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing);
1257 	menu_set_active(ifactory, "/Tools/PGP Encrypt", compose->use_encryption);
1258 #endif
1259 
1260 	syl_plugin_signal_emit("compose-created", compose);
1261 
1262 	if (prefs_common.enable_autosave && prefs_common.autosave_itv > 0)
1263 		compose->autosave_tag =
1264 			g_timeout_add_full(G_PRIORITY_LOW,
1265 					   prefs_common.autosave_itv * 60 * 1000,
1266 					   autosave_timeout, compose, NULL);
1267 	if (prefs_common.auto_exteditor)
1268 		compose_exec_ext_editor(compose);
1269 
1270 	return compose;
1271 }
1272 
compose_get_compose_list(void)1273 GList *compose_get_compose_list(void)
1274 {
1275 	return compose_list;
1276 }
1277 
compose_entry_show(Compose * compose,ComposeEntryType type)1278 static void compose_entry_show(Compose *compose, ComposeEntryType type)
1279 {
1280 	GtkItemFactory *ifactory;
1281 
1282 	ifactory = gtk_item_factory_from_widget(compose->menubar);
1283 
1284 	switch (type) {
1285 	case COMPOSE_ENTRY_CC:
1286 		menu_set_active(ifactory, "/View/Cc", TRUE);
1287 		break;
1288 	case COMPOSE_ENTRY_BCC:
1289 		menu_set_active(ifactory, "/View/Bcc", TRUE);
1290 		break;
1291 	case COMPOSE_ENTRY_REPLY_TO:
1292 		menu_set_active(ifactory, "/View/Reply-To", TRUE);
1293 		break;
1294 	case COMPOSE_ENTRY_FOLLOWUP_TO:
1295 		menu_set_active(ifactory, "/View/Followup-To", TRUE);
1296 		break;
1297 	case COMPOSE_ENTRY_TO:
1298 		menu_set_active(ifactory, "/View/To", TRUE);
1299 		break;
1300 	default:
1301 		break;
1302 	}
1303 }
1304 
compose_get_entry(Compose * compose,ComposeEntryType type)1305 static GtkEntry *compose_get_entry(Compose *compose, ComposeEntryType type)
1306 {
1307 	GtkEntry *entry;
1308 
1309 	switch (type) {
1310 	case COMPOSE_ENTRY_CC:
1311 		entry = GTK_ENTRY(compose->cc_entry);
1312 		break;
1313 	case COMPOSE_ENTRY_BCC:
1314 		entry = GTK_ENTRY(compose->bcc_entry);
1315 		break;
1316 	case COMPOSE_ENTRY_REPLY_TO:
1317 		entry = GTK_ENTRY(compose->reply_entry);
1318 		break;
1319 	case COMPOSE_ENTRY_SUBJECT:
1320 		entry = GTK_ENTRY(compose->subject_entry);
1321 		break;
1322 	case COMPOSE_ENTRY_NEWSGROUPS:
1323 		entry = GTK_ENTRY(compose->newsgroups_entry);
1324 		break;
1325 	case COMPOSE_ENTRY_FOLLOWUP_TO:
1326 		entry = GTK_ENTRY(compose->followup_entry);
1327 		break;
1328 	case COMPOSE_ENTRY_TO:
1329 	default:
1330 		entry = GTK_ENTRY(compose->to_entry);
1331 		break;
1332 	}
1333 
1334 	return entry;
1335 }
1336 
compose_entry_set(Compose * compose,const gchar * text,ComposeEntryType type)1337 void compose_entry_set(Compose *compose, const gchar *text,
1338 		       ComposeEntryType type)
1339 {
1340 	GtkEntry *entry;
1341 
1342 	if (!text) return;
1343 
1344 	compose_entry_show(compose, type);
1345 	entry = compose_get_entry(compose, type);
1346 
1347 	gtk_entry_set_text(entry, text);
1348 }
1349 
compose_entry_append(Compose * compose,const gchar * text,ComposeEntryType type)1350 void compose_entry_append(Compose *compose, const gchar *text,
1351 			  ComposeEntryType type)
1352 {
1353 	GtkEntry *entry;
1354 	const gchar *str;
1355 	gint pos;
1356 
1357 	if (!text || *text == '\0') return;
1358 
1359 	compose_entry_show(compose, type);
1360 	entry = compose_get_entry(compose, type);
1361 
1362 	if (type != COMPOSE_ENTRY_SUBJECT) {
1363 		str = gtk_entry_get_text(entry);
1364 		if (*str != '\0') {
1365 			pos = entry->text_length;
1366 			gtk_editable_insert_text(GTK_EDITABLE(entry),
1367 						 ", ", -1, &pos);
1368 		}
1369 	}
1370 
1371 	pos = entry->text_length;
1372 	gtk_editable_insert_text(GTK_EDITABLE(entry), text, -1, &pos);
1373 }
1374 
compose_entry_get_text(Compose * compose,ComposeEntryType type)1375 gchar *compose_entry_get_text(Compose *compose, ComposeEntryType type)
1376 {
1377 	GtkEntry *entry;
1378 
1379 	entry = compose_get_entry(compose, type);
1380 	return gtk_editable_get_chars(GTK_EDITABLE(entry), 0, -1);
1381 }
1382 
compose_entries_set(Compose * compose,const gchar * mailto)1383 static void compose_entries_set(Compose *compose, const gchar *mailto)
1384 {
1385 	gchar *to = NULL;
1386 	gchar *cc = NULL;
1387 	gchar *subject = NULL;
1388 	gchar *inreplyto = NULL;
1389 	gchar *body = NULL;
1390 
1391 	scan_mailto_url(mailto, &to, &cc, NULL, &subject, &inreplyto, &body);
1392 
1393 	if (to)
1394 		compose_entry_set(compose, to, COMPOSE_ENTRY_TO);
1395 	if (cc)
1396 		compose_entry_set(compose, cc, COMPOSE_ENTRY_CC);
1397 	if (subject)
1398 		compose_entry_set(compose, subject, COMPOSE_ENTRY_SUBJECT);
1399 	if (inreplyto) {
1400 		if (strchr(inreplyto, '<'))
1401 			extract_parenthesis(inreplyto, '<', '>');
1402 		remove_space(inreplyto);
1403 		compose->inreplyto = g_strdup(inreplyto);
1404 	}
1405 	if (body) {
1406 		GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1407 		GtkTextBuffer *buffer;
1408 		GtkTextMark *mark;
1409 		GtkTextIter iter;
1410 		gboolean prev_autowrap = compose->autowrap;
1411 
1412 		compose->autowrap = FALSE;
1413 
1414 		buffer = gtk_text_view_get_buffer(text);
1415 		mark = gtk_text_buffer_get_insert(buffer);
1416 		gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1417 
1418 		gtk_text_buffer_insert(buffer, &iter, body, -1);
1419 		gtk_text_buffer_insert(buffer, &iter, "\n", 1);
1420 
1421 		compose->autowrap = prev_autowrap;
1422 		if (compose->autowrap)
1423 			compose_wrap_all(compose);
1424 	}
1425 
1426 	g_free(to);
1427 	g_free(cc);
1428 	g_free(subject);
1429 	g_free(inreplyto);
1430 	g_free(body);
1431 }
1432 
compose_entries_set_from_item(Compose * compose,FolderItem * item,ComposeMode mode)1433 static void compose_entries_set_from_item(Compose *compose, FolderItem *item,
1434 					  ComposeMode mode)
1435 {
1436 	g_return_if_fail(item != NULL);
1437 
1438 	if (item->auto_to) {
1439 		if (mode != COMPOSE_REPLY || item->use_auto_to_on_reply)
1440 			compose_entry_set(compose, item->auto_to,
1441 					  COMPOSE_ENTRY_TO);
1442 	}
1443 	if (item->auto_cc)
1444 		compose_entry_set(compose, item->auto_cc, COMPOSE_ENTRY_CC);
1445 	if (item->auto_bcc)
1446 		compose_entry_set(compose, item->auto_bcc, COMPOSE_ENTRY_BCC);
1447 	if (item->auto_replyto)
1448 		compose_entry_set(compose, item->auto_replyto,
1449 				  COMPOSE_ENTRY_REPLY_TO);
1450 }
1451 
1452 #undef ACTIVATE_MENU
1453 
compose_parse_header(Compose * compose,MsgInfo * msginfo)1454 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
1455 {
1456 	static HeaderEntry hentry[] = {{"Reply-To:",	NULL, TRUE},
1457 				       {"Cc:",		NULL, TRUE},
1458 				       {"References:",	NULL, FALSE},
1459 				       {"Bcc:",		NULL, TRUE},
1460 				       {"Newsgroups:",  NULL, TRUE},
1461 				       {"Followup-To:", NULL, TRUE},
1462 				       {"List-Post:",   NULL, FALSE},
1463 				       {"Content-Type:",NULL, FALSE},
1464 				       {NULL,		NULL, FALSE}};
1465 
1466 	enum
1467 	{
1468 		H_REPLY_TO	= 0,
1469 		H_CC		= 1,
1470 		H_REFERENCES	= 2,
1471 		H_BCC		= 3,
1472 		H_NEWSGROUPS    = 4,
1473 		H_FOLLOWUP_TO	= 5,
1474 		H_LIST_POST     = 6,
1475 		H_CONTENT_TYPE  = 7
1476 	};
1477 
1478 	FILE *fp;
1479 	gchar *charset = NULL;
1480 
1481 	g_return_val_if_fail(msginfo != NULL, -1);
1482 
1483 	if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
1484 	procheader_get_header_fields(fp, hentry);
1485 	fclose(fp);
1486 
1487 	if (hentry[H_CONTENT_TYPE].body != NULL) {
1488 		procmime_scan_content_type_str(hentry[H_CONTENT_TYPE].body,
1489 					       NULL, &charset, NULL, NULL);
1490 		g_free(hentry[H_CONTENT_TYPE].body);
1491 		hentry[H_CONTENT_TYPE].body = NULL;
1492 	}
1493 	if (hentry[H_REPLY_TO].body != NULL) {
1494 		if (hentry[H_REPLY_TO].body[0] != '\0') {
1495 			compose->replyto =
1496 				conv_unmime_header(hentry[H_REPLY_TO].body,
1497 						   charset);
1498 		}
1499 		g_free(hentry[H_REPLY_TO].body);
1500 		hentry[H_REPLY_TO].body = NULL;
1501 	}
1502 	if (hentry[H_CC].body != NULL) {
1503 		compose->cc = conv_unmime_header(hentry[H_CC].body, charset);
1504 		g_free(hentry[H_CC].body);
1505 		hentry[H_CC].body = NULL;
1506 	}
1507 	if (hentry[H_REFERENCES].body != NULL) {
1508 		if (compose->mode == COMPOSE_REEDIT)
1509 			compose->references = hentry[H_REFERENCES].body;
1510 		else {
1511 			compose->references = compose_parse_references
1512 				(hentry[H_REFERENCES].body, msginfo->msgid);
1513 			g_free(hentry[H_REFERENCES].body);
1514 		}
1515 		hentry[H_REFERENCES].body = NULL;
1516 	}
1517 	if (hentry[H_BCC].body != NULL) {
1518 		if (compose->mode == COMPOSE_REEDIT)
1519 			compose->bcc =
1520 				conv_unmime_header(hentry[H_BCC].body, charset);
1521 		g_free(hentry[H_BCC].body);
1522 		hentry[H_BCC].body = NULL;
1523 	}
1524 	if (hentry[H_NEWSGROUPS].body != NULL) {
1525 		compose->newsgroups = hentry[H_NEWSGROUPS].body;
1526 		hentry[H_NEWSGROUPS].body = NULL;
1527 	}
1528 	if (hentry[H_FOLLOWUP_TO].body != NULL) {
1529 		if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
1530 			compose->followup_to =
1531 				conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
1532 						   charset);
1533 		}
1534 		g_free(hentry[H_FOLLOWUP_TO].body);
1535 		hentry[H_FOLLOWUP_TO].body = NULL;
1536 	}
1537 	if (hentry[H_LIST_POST].body != NULL) {
1538 		gchar *to = NULL;
1539 
1540 		extract_address(hentry[H_LIST_POST].body);
1541 		if (hentry[H_LIST_POST].body[0] != '\0') {
1542 			scan_mailto_url(hentry[H_LIST_POST].body,
1543 					&to, NULL, NULL, NULL, NULL, NULL);
1544 			if (to) {
1545 				g_free(compose->ml_post);
1546 				compose->ml_post = to;
1547 			}
1548 		}
1549 		g_free(hentry[H_LIST_POST].body);
1550 		hentry[H_LIST_POST].body = NULL;
1551 	}
1552 
1553 	g_free(charset);
1554 
1555 	if (compose->mode == COMPOSE_REEDIT) {
1556 		if (msginfo->inreplyto && *msginfo->inreplyto)
1557 			compose->inreplyto = g_strdup(msginfo->inreplyto);
1558 		return 0;
1559 	}
1560 
1561 	if (msginfo->msgid && *msginfo->msgid)
1562 		compose->inreplyto = g_strdup(msginfo->msgid);
1563 
1564 	if (!compose->references) {
1565 		if (msginfo->msgid && *msginfo->msgid) {
1566 			if (msginfo->inreplyto && *msginfo->inreplyto)
1567 				compose->references =
1568 					g_strdup_printf("<%s>\n\t<%s>",
1569 							msginfo->inreplyto,
1570 							msginfo->msgid);
1571 			else
1572 				compose->references =
1573 					g_strconcat("<", msginfo->msgid, ">",
1574 						    NULL);
1575 		} else if (msginfo->inreplyto && *msginfo->inreplyto) {
1576 			compose->references =
1577 				g_strconcat("<", msginfo->inreplyto, ">",
1578 					    NULL);
1579 		}
1580 	}
1581 
1582 	return 0;
1583 }
1584 
compose_parse_references(const gchar * ref,const gchar * msgid)1585 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
1586 {
1587 	GSList *ref_id_list, *cur;
1588 	GString *new_ref;
1589 	gchar *new_ref_str;
1590 
1591 	ref_id_list = references_list_append(NULL, ref);
1592 	if (!ref_id_list) return NULL;
1593 	if (msgid && *msgid)
1594 		ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
1595 
1596 	for (;;) {
1597 		gint len = 0;
1598 
1599 		for (cur = ref_id_list; cur != NULL; cur = cur->next)
1600 			/* "<" + Message-ID + ">" + CR+LF+TAB */
1601 			len += strlen((gchar *)cur->data) + 5;
1602 
1603 		if (len > MAX_REFERENCES_LEN) {
1604 			/* remove second message-ID */
1605 			if (ref_id_list && ref_id_list->next &&
1606 			    ref_id_list->next->next) {
1607 				g_free(ref_id_list->next->data);
1608 				ref_id_list = g_slist_remove
1609 					(ref_id_list, ref_id_list->next->data);
1610 			} else {
1611 				slist_free_strings(ref_id_list);
1612 				g_slist_free(ref_id_list);
1613 				return NULL;
1614 			}
1615 		} else
1616 			break;
1617 	}
1618 
1619 	new_ref = g_string_new("");
1620 	for (cur = ref_id_list; cur != NULL; cur = cur->next) {
1621 		if (new_ref->len > 0)
1622 			g_string_append(new_ref, "\n\t");
1623 		g_string_sprintfa(new_ref, "<%s>", (gchar *)cur->data);
1624 	}
1625 
1626 	slist_free_strings(ref_id_list);
1627 	g_slist_free(ref_id_list);
1628 
1629 	new_ref_str = new_ref->str;
1630 	g_string_free(new_ref, FALSE);
1631 
1632 	return new_ref_str;
1633 }
1634 
compose_parse_source_msg(Compose * compose,MsgInfo * msginfo)1635 static gint compose_parse_source_msg(Compose *compose, MsgInfo *msginfo)
1636 {
1637 	static HeaderEntry hentry[] = {{"X-Sylpheed-Reply:", NULL, FALSE},
1638 				       {"X-Sylpheed-Forward:", NULL, FALSE},
1639 				       {"REP:", NULL, FALSE},
1640 				       {"FWD:", NULL, FALSE},
1641 				       {"Disposition-Notification-To:", NULL, FALSE},
1642 				       {"X-Sylpheed-Compose-AutoWrap:", NULL, FALSE},
1643 				       {"X-Sylpheed-Compose-CheckSpell:", NULL, FALSE},
1644 				       {"X-Sylpheed-Compose-SpellLang:", NULL, FALSE},
1645 				       {"X-Sylpheed-Compose-UseSigning:", NULL, FALSE},
1646 				       {"X-Sylpheed-Compose-UseEncryption:", NULL, FALSE},
1647 				       {NULL, NULL, FALSE}};
1648 
1649 	enum
1650 	{
1651 		H_X_SYLPHEED_REPLY = 0,
1652 		H_X_SYLPHEED_FORWARD = 1,
1653 		H_REP = 2,
1654 		H_FWD = 3,
1655 		H_MDN = 4,
1656 		H_X_SYLPHEED_COMPOSE_AUTOWRAP = 5,
1657 		H_X_SYLPHEED_COMPOSE_CHECKSPELL = 6,
1658 		H_X_SYLPHEED_COMPOSE_SPELLLANG = 7,
1659 		H_X_SYLPHEED_COMPOSE_USESIGNING = 8,
1660 		H_X_SYLPHEED_COMPOSE_USEENCRYPTION = 9
1661 	};
1662 
1663 	gchar *file;
1664 	FILE *fp;
1665 	gchar *str;
1666 	gchar buf[BUFFSIZE];
1667 	gint hnum;
1668 
1669 	g_return_val_if_fail(msginfo != NULL, -1);
1670 
1671 	file = procmsg_get_message_file(msginfo);
1672 
1673 	if ((fp = g_fopen(file, "rb")) == NULL) {
1674 		FILE_OP_ERROR(file, "fopen");
1675 		return -1;
1676 	}
1677 
1678 	while ((hnum = procheader_get_one_field(buf, sizeof(buf), fp, hentry))
1679 	       != -1) {
1680 		str = buf + strlen(hentry[hnum].name);
1681 		while (g_ascii_isspace(*str))
1682 			++str;
1683 		if ((hnum == H_X_SYLPHEED_REPLY || hnum == H_REP) &&
1684 		    !compose->reply_target) {
1685 			compose->reply_target = g_strdup(str);
1686 		} else if ((hnum == H_X_SYLPHEED_FORWARD || hnum == H_FWD) &&
1687 			   !compose->forward_targets) {
1688 			compose->forward_targets = g_strdup(str);
1689 		} else if (hnum == H_MDN) {
1690 			compose->use_mdn = TRUE;
1691 		} else if (hnum == H_X_SYLPHEED_COMPOSE_AUTOWRAP) {
1692 			if (g_ascii_strcasecmp(str, "TRUE") == 0)
1693 				compose->autowrap = TRUE;
1694 			else
1695 				compose->autowrap = FALSE;
1696 #if USE_GTKSPELL
1697 		} else if (hnum == H_X_SYLPHEED_COMPOSE_CHECKSPELL) {
1698 			if (g_ascii_strcasecmp(str, "TRUE") == 0)
1699 				compose->check_spell = TRUE;
1700 			else
1701 				compose->check_spell = FALSE;
1702 		} else if (hnum == H_X_SYLPHEED_COMPOSE_SPELLLANG) {
1703 			g_free(compose->spell_lang);
1704 			compose->spell_lang = g_strdup(str);
1705 #endif
1706 #if USE_GPGME
1707 		} else if (hnum == H_X_SYLPHEED_COMPOSE_USESIGNING) {
1708 			if (g_ascii_strcasecmp(str, "TRUE") == 0)
1709 				compose->use_signing = TRUE;
1710 			else
1711 				compose->use_signing = FALSE;
1712 		} else if (hnum == H_X_SYLPHEED_COMPOSE_USEENCRYPTION) {
1713 			if (g_ascii_strcasecmp(str, "TRUE") == 0)
1714 				compose->use_encryption = TRUE;
1715 			else
1716 				compose->use_encryption = FALSE;
1717 #endif
1718 		}
1719 	}
1720 
1721 	fclose(fp);
1722 	g_free(file);
1723 
1724 	return 0;
1725 }
1726 
compose_quote_fmt(Compose * compose,MsgInfo * msginfo,const gchar * fmt,const gchar * qmark,const gchar * body)1727 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
1728 				const gchar *fmt, const gchar *qmark,
1729 				const gchar *body)
1730 {
1731 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
1732 	GtkTextBuffer *buffer;
1733 	GtkTextMark *mark;
1734 	GtkTextIter iter;
1735 	static MsgInfo dummyinfo;
1736 	gchar *quote_str = NULL;
1737 	gchar *buf;
1738 	gchar *p, *lastp;
1739 	gint len;
1740 	gboolean prev_autowrap;
1741 
1742 	if (!msginfo)
1743 		msginfo = &dummyinfo;
1744 
1745 	if (qmark == NULL || *qmark == '\0')
1746 		qmark = "> ";
1747 
1748 	quote_fmt_init(msginfo, NULL, NULL);
1749 	quote_fmt_scan_string(qmark);
1750 	quote_fmt_parse();
1751 
1752 	buf = quote_fmt_get_buffer();
1753 	if (buf == NULL)
1754 		alertpanel_error(_("Quote mark format error."));
1755 	else
1756 		quote_str = g_strdup(buf);
1757 
1758 	if (fmt && *fmt != '\0') {
1759 		quote_fmt_init(msginfo, quote_str, body);
1760 		quote_fmt_scan_string(fmt);
1761 		quote_fmt_parse();
1762 
1763 		buf = quote_fmt_get_buffer();
1764 		if (buf == NULL) {
1765 			alertpanel_error(_("Message reply/forward format error."));
1766 			g_free(quote_str);
1767 			return NULL;
1768 		}
1769 	} else
1770 		buf = "";
1771 
1772 	g_free(quote_str);
1773 
1774 	prev_autowrap = compose->autowrap;
1775 	compose->autowrap = FALSE;
1776 
1777 	buffer = gtk_text_view_get_buffer(text);
1778 	mark = gtk_text_buffer_get_insert(buffer);
1779 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
1780 
1781 	for (p = buf; *p != '\0'; ) {
1782 		lastp = strchr(p, '\n');
1783 		len = lastp ? lastp - p + 1 : -1;
1784 		gtk_text_buffer_insert(buffer, &iter, p, len);
1785 		if (lastp)
1786 			p = lastp + 1;
1787 		else
1788 			break;
1789 	}
1790 
1791 	compose->autowrap = prev_autowrap;
1792 	if (compose->autowrap)
1793 		compose_wrap_all(compose);
1794 
1795 	return buf;
1796 }
1797 
compose_reply_set_entry(Compose * compose,MsgInfo * msginfo,ComposeMode mode)1798 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
1799 				    ComposeMode mode)
1800 {
1801 	GSList *cc_list = NULL;
1802 	GSList *cur;
1803 	GHashTable *to_table;
1804 	gboolean to_all = FALSE, to_ml = FALSE, ignore_replyto = FALSE;
1805 	gchar *from_str = NULL, *to_str = NULL, *cc_str = NULL,
1806 	      *replyto_str = NULL;
1807 	gboolean address_only = prefs_common.reply_address_only;
1808 
1809 	g_return_if_fail(compose->account != NULL);
1810 	g_return_if_fail(msginfo != NULL);
1811 
1812 	switch (COMPOSE_MODE(mode)) {
1813 	case COMPOSE_REPLY_TO_SENDER:
1814 		ignore_replyto = TRUE;
1815 		break;
1816 	case COMPOSE_REPLY_TO_ALL:
1817 		to_all = TRUE;
1818 		break;
1819 	case COMPOSE_REPLY_TO_LIST:
1820 		to_ml = TRUE;
1821 		break;
1822 	default:
1823 		break;
1824 	}
1825 
1826 	if (address_only) {
1827 		from_str = extract_addresses(msginfo->from);
1828 		to_str = extract_addresses(msginfo->to);
1829 		cc_str = extract_addresses(compose->cc);
1830 		replyto_str = extract_addresses(compose->replyto);
1831 	} else {
1832 		from_str = g_strdup(msginfo->from);
1833 		to_str = g_strdup(msginfo->to);
1834 		cc_str = g_strdup(compose->cc);
1835 		replyto_str = g_strdup(compose->replyto);
1836 	}
1837 
1838 	if (compose->account->protocol != A_NNTP) {
1839 		if (to_ml && compose->ml_post) {
1840 			/* don't reply to list for confirmation request etc. */
1841 			if ((!to_str ||
1842 			     !strcasestr_with_skip_quote(to_str,
1843 							 compose->ml_post)) &&
1844 			    (!cc_str ||
1845 			     !strcasestr_with_skip_quote(cc_str,
1846 							 compose->ml_post)))
1847 				to_ml = FALSE;
1848 		}
1849 		if (to_ml && compose->ml_post) {
1850 			compose_entry_set(compose, compose->ml_post,
1851 					  COMPOSE_ENTRY_TO);
1852 			if (replyto_str &&
1853 			    !address_equal(replyto_str, compose->ml_post))
1854 				compose_entry_set(compose, replyto_str,
1855 						  COMPOSE_ENTRY_CC);
1856 		} else if (prefs_common.inherit_recipient_on_self_reply &&
1857 			   address_equal(compose->account->address, from_str)) {
1858 			compose_entry_set(compose, to_str, COMPOSE_ENTRY_TO);
1859 			if (to_all) {
1860 				compose_entry_set(compose, cc_str,
1861 						  COMPOSE_ENTRY_CC);
1862 				to_all = FALSE;
1863 			}
1864 		} else {
1865 			compose_entry_set(compose,
1866 					  (replyto_str && !ignore_replyto)
1867 					  ? replyto_str
1868 					  : from_str ? from_str : "",
1869 					  COMPOSE_ENTRY_TO);
1870 		}
1871 	} else {
1872 		if (ignore_replyto) {
1873 			compose_entry_set(compose, from_str ? from_str : "",
1874 					  COMPOSE_ENTRY_TO);
1875 		} else {
1876 			if (to_all) {
1877 				compose_entry_set
1878 					(compose,
1879 					 (replyto_str && !ignore_replyto)
1880 					 ? replyto_str
1881 					 : from_str ? from_str : "",
1882 					 COMPOSE_ENTRY_TO);
1883 			}
1884 			compose_entry_set(compose,
1885 					  compose->followup_to ? compose->followup_to
1886 					  : compose->newsgroups ? compose->newsgroups
1887 					  : "",
1888 					  COMPOSE_ENTRY_NEWSGROUPS);
1889 		}
1890 	}
1891 
1892 	if (msginfo->subject && *msginfo->subject) {
1893 		gchar *buf;
1894 		gchar *p;
1895 
1896 		buf = g_strdup(msginfo->subject);
1897 
1898 		if (msginfo->folder && msginfo->folder->trim_compose_subject)
1899 			trim_subject(buf);
1900 
1901 		while (!g_ascii_strncasecmp(buf, "Re:", 3)) {
1902 			p = buf + 3;
1903 			while (g_ascii_isspace(*p)) p++;
1904 			memmove(buf, p, strlen(p) + 1);
1905 		}
1906 
1907 		compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1908 		compose_entry_append(compose, buf, COMPOSE_ENTRY_SUBJECT);
1909 
1910 		g_free(buf);
1911 	} else
1912 		compose_entry_set(compose, "Re: ", COMPOSE_ENTRY_SUBJECT);
1913 
1914 	if (!compose->replyto && to_ml && compose->ml_post)
1915 		goto done;
1916 	if (!to_all)
1917 		goto done;
1918 
1919 	if (replyto_str && from_str)
1920 		cc_list = address_list_append_orig(cc_list, from_str);
1921 	cc_list = address_list_append_orig(cc_list, to_str);
1922 	cc_list = address_list_append_orig(cc_list, cc_str);
1923 
1924 	to_table = g_hash_table_new(g_str_hash, g_str_equal);
1925 	if (replyto_str) {
1926 		gchar *replyto;
1927 
1928 		replyto = g_strdup(replyto_str);
1929 		extract_address(replyto);
1930 		g_hash_table_insert(to_table, replyto, GINT_TO_POINTER(1));
1931 	} else if (from_str) {
1932 		gchar *from;
1933 
1934 		from = g_strdup(from_str);
1935 		extract_address(from);
1936 		g_hash_table_insert(to_table, from, GINT_TO_POINTER(1));
1937 	}
1938 	if (compose->account->address)
1939 		g_hash_table_insert(to_table,
1940 				    g_strdup(compose->account->address),
1941 				    GINT_TO_POINTER(1));
1942 
1943 	/* remove duplicate addresses */
1944 	for (cur = cc_list; cur != NULL; ) {
1945 		gchar *addr = (gchar *)cur->data;
1946 		GSList *next = cur->next;
1947 		gchar *addr_;
1948 
1949 		addr_ = g_strdup(addr);
1950 		extract_address(addr_);
1951 		if (g_hash_table_lookup(to_table, addr_) != NULL) {
1952 			cc_list = g_slist_remove(cc_list, addr);
1953 			g_free(addr_);
1954 			g_free(addr);
1955 		} else
1956 			g_hash_table_insert(to_table, addr_, cur);
1957 
1958 		cur = next;
1959 	}
1960 	hash_free_strings(to_table);
1961 	g_hash_table_destroy(to_table);
1962 
1963 	if (cc_list) {
1964 		for (cur = cc_list; cur != NULL; cur = cur->next)
1965 			compose_entry_append(compose, (gchar *)cur->data,
1966 					     COMPOSE_ENTRY_CC);
1967 		slist_free_strings(cc_list);
1968 		g_slist_free(cc_list);
1969 	}
1970 
1971 done:
1972 	g_free(from_str);
1973 	g_free(to_str);
1974 	g_free(cc_str);
1975 	g_free(replyto_str);
1976 }
1977 
compose_reedit_set_entry(Compose * compose,MsgInfo * msginfo)1978 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
1979 {
1980 	g_return_if_fail(msginfo != NULL);
1981 	g_return_if_fail(compose->account != NULL);
1982 
1983 	compose_entry_set(compose, msginfo->to     , COMPOSE_ENTRY_TO);
1984 	compose_entry_set(compose, compose->cc     , COMPOSE_ENTRY_CC);
1985 	compose_entry_set(compose, compose->bcc    , COMPOSE_ENTRY_BCC);
1986 	compose_entry_set(compose, compose->replyto, COMPOSE_ENTRY_REPLY_TO);
1987 	if (compose->account->protocol == A_NNTP) {
1988 		compose_entry_set(compose, compose->newsgroups,
1989 				  COMPOSE_ENTRY_NEWSGROUPS);
1990 		compose_entry_set(compose, compose->followup_to,
1991 				  COMPOSE_ENTRY_FOLLOWUP_TO);
1992 	}
1993 	compose_entry_set(compose, msginfo->subject, COMPOSE_ENTRY_SUBJECT);
1994 }
1995 
compose_insert_sig(Compose * compose,gboolean append,gboolean replace,gboolean scroll)1996 static void compose_insert_sig(Compose *compose, gboolean append,
1997 			       gboolean replace, gboolean scroll)
1998 {
1999 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2000 	GtkTextBuffer *buffer;
2001 	GtkTextMark *mark;
2002 	GtkTextIter iter;
2003 	gchar *sig_str;
2004 	gboolean prev_autowrap;
2005 
2006 	debug_print("compose_insert_sig: append:%d replace:%d scroll:%d\n",
2007 		    append, replace, scroll);
2008 
2009 	g_return_if_fail(compose->account != NULL);
2010 
2011 	prev_autowrap = compose->autowrap;
2012 	compose->autowrap = FALSE;
2013 
2014 	buffer = gtk_text_view_get_buffer(text);
2015 	mark = gtk_text_buffer_get_insert(buffer);
2016 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2017 
2018 	if (replace) {
2019 		GtkTextIter start_iter, end_iter;
2020 
2021 		gtk_text_buffer_get_start_iter(buffer, &start_iter);
2022 		gtk_text_buffer_get_end_iter(buffer, &iter);
2023 
2024 		while (gtk_text_iter_begins_tag
2025 			(&start_iter, compose->sig_tag) ||
2026 		       gtk_text_iter_forward_to_tag_toggle
2027 			(&start_iter, compose->sig_tag)) {
2028 			end_iter = start_iter;
2029 			if (gtk_text_iter_forward_to_tag_toggle
2030 				(&end_iter, compose->sig_tag)) {
2031 				gtk_text_buffer_delete
2032 					(buffer, &start_iter, &end_iter);
2033 				iter = start_iter;
2034 			}
2035 		}
2036 	}
2037 
2038 	if (scroll) {
2039 		if (append)
2040 			gtk_text_buffer_get_end_iter(buffer, &iter);
2041 		else
2042 			gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2043 	}
2044 
2045 	sig_str = compose_get_signature_str(compose);
2046 	if (sig_str) {
2047 		if (!replace)
2048 			gtk_text_buffer_insert(buffer, &iter, "\n\n", 2);
2049 		else if (!gtk_text_iter_starts_line(&iter))
2050 			gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2051 		gtk_text_buffer_insert_with_tags
2052 			(buffer, &iter, sig_str, -1, compose->sig_tag, NULL);
2053 		g_free(sig_str);
2054 		if (scroll)
2055 			gtk_text_buffer_place_cursor(buffer, &iter);
2056 	}
2057 
2058 	compose->autowrap = prev_autowrap;
2059 	if (compose->autowrap)
2060 		compose_wrap_all(compose);
2061 
2062 	if (scroll)
2063 		gtk_text_view_scroll_mark_onscreen(text, mark);
2064 }
2065 
compose_enable_sig(Compose * compose)2066 static void compose_enable_sig(Compose *compose)
2067 {
2068 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2069 	GtkTextBuffer *buffer;
2070 	GtkTextIter iter, start, end;
2071 	gchar *sig_str;
2072 
2073 	g_return_if_fail(compose->account != NULL);
2074 
2075 	buffer = gtk_text_view_get_buffer(text);
2076 	gtk_text_buffer_get_start_iter(buffer, &iter);
2077 
2078 	sig_str = compose_get_signature_str(compose);
2079 	if (!sig_str)
2080 		return;
2081 
2082 	if (gtkut_text_buffer_find(buffer, &iter, sig_str, TRUE, &start)) {
2083 		end = start;
2084 		gtk_text_iter_forward_chars(&end, g_utf8_strlen(sig_str, -1));
2085 		gtk_text_buffer_apply_tag(buffer, compose->sig_tag,
2086 					  &start, &end);
2087 	}
2088 
2089 	g_free(sig_str);
2090 }
2091 
compose_get_signature_str(Compose * compose)2092 static gchar *compose_get_signature_str(Compose *compose)
2093 {
2094 	gchar *sig_path;
2095 	gchar *sig_body = NULL;
2096 	gchar *utf8_sig_body = NULL;
2097 	gchar *utf8_sig_str = NULL;
2098 
2099 	g_return_val_if_fail(compose->account != NULL, NULL);
2100 
2101 	if (compose->account->sig_type == SIG_DIRECT) {
2102 		gchar *sig_text;
2103 		gchar *p, *sp;
2104 		gint sig_index;
2105 
2106 		sig_index = gtk_combo_box_get_active(GTK_COMBO_BOX(compose->sig_combo));
2107 		if (sig_index > 0)
2108 			sig_text = compose->account->sig_texts[sig_index];
2109 		else
2110 			sig_text = compose->account->sig_text;
2111 		if (!sig_text)
2112 			return NULL;
2113 
2114 		sp = sig_text;
2115 		p = sig_body = g_malloc(strlen(sig_text) + 1);
2116 		while (*sp) {
2117 			if (*sp == '\\' && *(sp + 1) == 'n') {
2118 				*p++ = '\n';
2119 				sp += 2;
2120 			} else
2121 				*p++ = *sp++;
2122 		}
2123 		*p = '\0';
2124 
2125 		if (prefs_common.sig_sep) {
2126 			utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2127 						   sig_body, NULL);
2128 			g_free(sig_body);
2129 		} else
2130 			utf8_sig_str = sig_body;
2131 
2132 		return utf8_sig_str;
2133 	}
2134 
2135 	if (!compose->account->sig_path)
2136 		return NULL;
2137 
2138 	if (g_path_is_absolute(compose->account->sig_path) ||
2139 	    compose->account->sig_type == SIG_COMMAND)
2140 		sig_path = g_strdup(compose->account->sig_path);
2141 	else {
2142 #ifdef G_OS_WIN32
2143 		sig_path = g_strconcat(get_rc_dir(),
2144 #else
2145 		sig_path = g_strconcat(get_home_dir(),
2146 #endif
2147 				       G_DIR_SEPARATOR_S,
2148 				       compose->account->sig_path, NULL);
2149 	}
2150 
2151 	if (compose->account->sig_type == SIG_FILE) {
2152 		if (!is_file_or_fifo_exist(sig_path)) {
2153 			debug_print("can't open signature file: %s\n",
2154 				    sig_path);
2155 			g_free(sig_path);
2156 			return NULL;
2157 		}
2158 	}
2159 
2160 	if (compose->account->sig_type == SIG_COMMAND)
2161 		sig_body = get_command_output(sig_path);
2162 	else {
2163 		gchar *tmp;
2164 
2165 		tmp = file_read_to_str(sig_path);
2166 		if (!tmp)
2167 			return NULL;
2168 		sig_body = normalize_newlines(tmp);
2169 		g_free(tmp);
2170 	}
2171 	g_free(sig_path);
2172 
2173 	if (sig_body) {
2174 		gint error = 0;
2175 
2176 		utf8_sig_body = conv_codeset_strdup_full
2177 			(sig_body, conv_get_locale_charset_str(),
2178 			 CS_INTERNAL, &error);
2179 		if (!utf8_sig_body || error != 0) {
2180 			if (g_utf8_validate(sig_body, -1, NULL) == TRUE) {
2181 				g_free(utf8_sig_body);
2182 				utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2183 			}
2184 		} else {
2185 			g_free(sig_body);
2186 			sig_body = utf8_sig_body;
2187 			utf8_sig_body = conv_utf8todisp(sig_body, NULL);
2188 		}
2189 		g_free(sig_body);
2190 	}
2191 
2192 	if (prefs_common.sig_sep) {
2193 		utf8_sig_str = g_strconcat(prefs_common.sig_sep, "\n",
2194 					   utf8_sig_body, NULL);
2195 		g_free(utf8_sig_body);
2196 	} else
2197 		utf8_sig_str = utf8_sig_body;
2198 
2199 	return utf8_sig_str;
2200 }
2201 
compose_insert_file(Compose * compose,const gchar * file,gboolean scroll)2202 static void compose_insert_file(Compose *compose, const gchar *file,
2203 				gboolean scroll)
2204 {
2205 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2206 	GtkTextBuffer *buffer;
2207 	GtkTextMark *mark;
2208 	GtkTextIter iter;
2209 	const gchar *cur_encoding;
2210 	gchar buf[BUFFSIZE];
2211 	gint len;
2212 	FILE *fp;
2213 	gboolean prev_autowrap;
2214 	CharSet enc;
2215 	gchar *tmp_file = NULL;
2216 
2217 	g_return_if_fail(file != NULL);
2218 
2219 	enc = conv_check_file_encoding(file);
2220 	if (enc == C_UTF_16 || enc == C_UTF_16BE || enc == C_UTF_16LE) {
2221 		tmp_file = get_tmp_file();
2222 		if (conv_copy_file(file, tmp_file, conv_get_charset_str(enc)) < 0) {
2223 			g_warning("compose_insert_file: Cannot convert UTF-16 file %s to UTF-8\n", file);
2224 			g_free(tmp_file);
2225 			tmp_file = NULL;
2226 		}
2227 	}
2228 
2229 	if (tmp_file) {
2230 		if ((fp = g_fopen(tmp_file, "rb")) == NULL) {
2231 			FILE_OP_ERROR(tmp_file, "fopen");
2232 			g_unlink(tmp_file);
2233 			g_free(tmp_file);
2234 			return;
2235 		}
2236 	} else {
2237 		if ((fp = g_fopen(file, "rb")) == NULL) {
2238 			FILE_OP_ERROR(file, "fopen");
2239 			return;
2240 		}
2241 	}
2242 
2243 	prev_autowrap = compose->autowrap;
2244 	compose->autowrap = FALSE;
2245 
2246 	buffer = gtk_text_view_get_buffer(text);
2247 	mark = gtk_text_buffer_get_insert(buffer);
2248 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2249 
2250 	cur_encoding = conv_get_locale_charset_str();
2251 
2252 	while (fgets(buf, sizeof(buf), fp) != NULL) {
2253 		gchar *str;
2254 		gint error = 0;
2255 
2256 		if (enc == C_UTF_8) {
2257 			str = conv_utf8todisp(buf, NULL);
2258 		} else {
2259 			str = conv_codeset_strdup_full(buf, cur_encoding,
2260 						       CS_INTERNAL, &error);
2261 			if (!str || error != 0) {
2262 				if (g_utf8_validate(buf, -1, NULL) == TRUE) {
2263 					g_free(str);
2264 					str = g_strdup(buf);
2265 				}
2266 			}
2267 			if (!str) continue;
2268 		}
2269 
2270 		/* strip <CR> if DOS/Windows file,
2271 		   replace <CR> with <LF> if Macintosh file. */
2272 		strcrchomp(str);
2273 		len = strlen(str);
2274 		if (len > 0 && str[len - 1] != '\n') {
2275 			while (--len >= 0)
2276 				if (str[len] == '\r') str[len] = '\n';
2277 		}
2278 
2279 		gtk_text_buffer_insert(buffer, &iter, str, -1);
2280 		g_free(str);
2281 	}
2282 
2283 	fclose(fp);
2284 	if (tmp_file) {
2285 		g_unlink(tmp_file);
2286 		g_free(tmp_file);
2287 	}
2288 
2289 	compose->autowrap = prev_autowrap;
2290 	if (compose->autowrap)
2291 		compose_wrap_all(compose);
2292 
2293 	if (scroll)
2294 		gtk_text_view_scroll_mark_onscreen(text, mark);
2295 }
2296 
compose_attach_append(Compose * compose,const gchar * file,const gchar * filename,const gchar * content_type)2297 void compose_attach_append(Compose *compose, const gchar *file,
2298 			   const gchar *filename,
2299 			   const gchar *content_type)
2300 {
2301 	GtkTreeIter iter;
2302 	AttachInfo *ainfo;
2303 	FILE *fp;
2304 	off_t size;
2305 
2306 	g_return_if_fail(file != NULL);
2307 	g_return_if_fail(*file != '\0');
2308 
2309 	if (!is_file_exist(file)) {
2310 		g_warning(_("File %s doesn't exist\n"), file);
2311 		return;
2312 	}
2313 	if ((size = get_file_size(file)) < 0) {
2314 		g_warning(_("Can't get file size of %s\n"), file);
2315 		return;
2316 	}
2317 	if (size == 0) {
2318 		manage_window_focus_in(compose->window, NULL, NULL);
2319 		alertpanel_notice(_("File %s is empty."), file);
2320 		return;
2321 	}
2322 	if ((fp = g_fopen(file, "rb")) == NULL) {
2323 		manage_window_focus_in(compose->window, NULL, NULL);
2324 		alertpanel_error(_("Can't read %s."), file);
2325 		return;
2326 	}
2327 	fclose(fp);
2328 
2329 	if (!compose->use_attach) {
2330 		GtkItemFactory *ifactory;
2331 
2332 		ifactory = gtk_item_factory_from_widget(compose->menubar);
2333 		menu_set_active(ifactory, "/View/Attachment", TRUE);
2334 	}
2335 
2336 	ainfo = g_new0(AttachInfo, 1);
2337 	ainfo->file = g_strdup(file);
2338 
2339 	if (content_type) {
2340 		ainfo->content_type = g_strdup(content_type);
2341 		if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
2342 			MsgInfo *msginfo;
2343 			MsgFlags flags = {0, 0};
2344 			const gchar *name;
2345 
2346 			if (procmime_get_encoding_for_text_file(file) == ENC_7BIT)
2347 				ainfo->encoding = ENC_7BIT;
2348 			else
2349 				ainfo->encoding = ENC_8BIT;
2350 
2351 			msginfo = procheader_parse_file(file, flags, FALSE);
2352 			if (msginfo && msginfo->subject)
2353 				name = msginfo->subject;
2354 			else
2355 				name = g_basename(filename ? filename : file);
2356 
2357 			ainfo->name = g_strdup_printf(_("Message: %s"), name);
2358 
2359 			procmsg_msginfo_free(msginfo);
2360 		} else {
2361 			if (!g_ascii_strncasecmp(content_type, "text", 4))
2362 				ainfo->encoding = procmime_get_encoding_for_text_file(file);
2363 			else
2364 				ainfo->encoding = ENC_BASE64;
2365 			ainfo->name = g_strdup
2366 				(g_basename(filename ? filename : file));
2367 		}
2368 	} else {
2369 		ainfo->content_type = procmime_get_mime_type(file);
2370 		if (!ainfo->content_type) {
2371 			ainfo->content_type =
2372 				g_strdup("application/octet-stream");
2373 			ainfo->encoding = ENC_BASE64;
2374 		} else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
2375 			ainfo->encoding =
2376 				procmime_get_encoding_for_text_file(file);
2377 		else
2378 			ainfo->encoding = ENC_BASE64;
2379 		ainfo->name = g_strdup(g_basename(filename ? filename : file));
2380 	}
2381 	ainfo->size = size;
2382 
2383 	gtk_list_store_append(compose->attach_store, &iter);
2384 	gtk_list_store_set(compose->attach_store, &iter,
2385 			   COL_MIMETYPE, ainfo->content_type,
2386 			   COL_SIZE, to_human_readable(ainfo->size),
2387 			   COL_NAME, ainfo->name,
2388 			   COL_ATTACH_INFO, ainfo,
2389 			   -1);
2390 
2391 	syl_plugin_signal_emit("compose-attach-changed", compose);
2392 }
2393 
compose_attach_parts(Compose * compose,MsgInfo * msginfo)2394 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
2395 {
2396 	MimeInfo *mimeinfo;
2397 	MimeInfo *child;
2398 	gchar *infile;
2399 	gchar *outfile;
2400 
2401 	mimeinfo = procmime_scan_message(msginfo);
2402 	if (!mimeinfo) return;
2403 
2404 	infile = procmsg_get_message_file_path(msginfo);
2405 
2406 	child = mimeinfo;
2407 	while (child != NULL) {
2408 		if (child->children || child->mime_type == MIME_MULTIPART)
2409 			goto next;
2410 		if (child->mime_type != MIME_MESSAGE_RFC822 &&
2411 		    child->mime_type != MIME_IMAGE &&
2412 		    child->mime_type != MIME_AUDIO &&
2413 		    child->mime_type != MIME_VIDEO &&
2414 		    !child->filename && !child->name)
2415 			goto next;
2416 
2417 		outfile = procmime_get_tmp_file_name(child);
2418 		if (procmime_get_part(outfile, infile, child) < 0) {
2419 			g_warning(_("Can't get the part of multipart message."));
2420 			g_free(outfile);
2421 			goto next;
2422 		}
2423 
2424 		compose_attach_append
2425 			(compose, outfile,
2426 			 child->filename ? child->filename : child->name,
2427 			 child->content_type);
2428 
2429 		g_free(outfile);
2430 
2431 next:
2432 		if (child->mime_type == MIME_MESSAGE_RFC822)
2433 			child = child->next;
2434 		else
2435 			child = procmime_mimeinfo_next(child);
2436 	}
2437 
2438 	g_free(infile);
2439 	procmime_mimeinfo_free_all(mimeinfo);
2440 }
2441 
2442 #define INDENT_CHARS	">|#"
2443 
2444 typedef enum {
2445 	WAIT_FOR_INDENT_CHAR,
2446 	WAIT_FOR_INDENT_CHAR_OR_SPACE,
2447 } IndentState;
2448 
2449 /* return indent length, we allow:
2450    indent characters followed by indent characters or spaces/tabs,
2451    alphabets and numbers immediately followed by indent characters,
2452    and the repeating sequences of the above
2453    If quote ends with multiple spaces, only the first one is included. */
compose_get_quote_str(GtkTextBuffer * buffer,const GtkTextIter * start,gint * len)2454 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
2455 				    const GtkTextIter *start, gint *len)
2456 {
2457 	GtkTextIter iter = *start;
2458 	gunichar wc;
2459 	gchar ch[6];
2460 	gint clen;
2461 	IndentState state = WAIT_FOR_INDENT_CHAR;
2462 	gboolean is_space;
2463 	gboolean is_indent;
2464 	gint alnum_count = 0;
2465 	gint space_count = 0;
2466 	gint quote_len = 0;
2467 
2468 	while (!gtk_text_iter_ends_line(&iter)) {
2469 		wc = gtk_text_iter_get_char(&iter);
2470 		if (g_unichar_iswide(wc))
2471 			break;
2472 		clen = g_unichar_to_utf8(wc, ch);
2473 		if (clen != 1)
2474 			break;
2475 
2476 		is_indent = strchr(INDENT_CHARS, ch[0]) ? TRUE : FALSE;
2477 		is_space = g_unichar_isspace(wc);
2478 
2479 		if (state == WAIT_FOR_INDENT_CHAR) {
2480 			if (!is_indent && !g_unichar_isalnum(wc))
2481 				break;
2482 			if (is_indent) {
2483 				quote_len += alnum_count + space_count + 1;
2484 				alnum_count = space_count = 0;
2485 				state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
2486 			} else
2487 				alnum_count++;
2488 		} else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
2489 			if (!is_indent && !is_space && !g_unichar_isalnum(wc))
2490 				break;
2491 			if (is_space)
2492 				space_count++;
2493 			else if (is_indent) {
2494 				quote_len += alnum_count + space_count + 1;
2495 				alnum_count = space_count = 0;
2496 			} else {
2497 				alnum_count++;
2498 				state = WAIT_FOR_INDENT_CHAR;
2499 			}
2500 		}
2501 
2502 		gtk_text_iter_forward_char(&iter);
2503 	}
2504 
2505 	if (quote_len > 0 && space_count > 0)
2506 		quote_len++;
2507 
2508 	if (len)
2509 		*len = quote_len;
2510 
2511 	if (quote_len > 0) {
2512 		iter = *start;
2513 		gtk_text_iter_forward_chars(&iter, quote_len);
2514 		return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
2515 	}
2516 
2517 	return NULL;
2518 }
2519 
2520 /* return TRUE if the line is itemized */
compose_is_itemized(GtkTextBuffer * buffer,const GtkTextIter * start)2521 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
2522 				    const GtkTextIter *start)
2523 {
2524 	GtkTextIter iter = *start;
2525 	gunichar wc;
2526 	gchar ch[6];
2527 	gint clen;
2528 
2529 	if (gtk_text_iter_ends_line(&iter))
2530 		return FALSE;
2531 
2532 	while (1) {
2533 		wc = gtk_text_iter_get_char(&iter);
2534 		if (!g_unichar_isspace(wc))
2535 			break;
2536 		gtk_text_iter_forward_char(&iter);
2537 		if (gtk_text_iter_ends_line(&iter))
2538 			return FALSE;
2539 	}
2540 
2541 	clen = g_unichar_to_utf8(wc, ch);
2542 
2543 	/* (1), 2), 3. etc. */
2544 	if ((clen == 1 && ch[0] == '(') || g_unichar_isdigit(wc)) {
2545 		gboolean digit_appeared = FALSE;
2546 
2547 		if (ch[0] == '(')
2548 			gtk_text_iter_forward_char(&iter);
2549 
2550 		while (1) {
2551 			wc = gtk_text_iter_get_char(&iter);
2552 			clen = g_unichar_to_utf8(wc, ch);
2553 			if (g_unichar_isdigit(wc)) {
2554 				digit_appeared = TRUE;
2555 				gtk_text_iter_forward_char(&iter);
2556 				if (gtk_text_iter_ends_line(&iter))
2557 					return FALSE;
2558 			} else if (clen == 1 &&
2559 				   (ch[0] == ')' || ch[0] == '.')) {
2560 				if (!digit_appeared)
2561 					return FALSE;
2562 				gtk_text_iter_forward_char(&iter);
2563 				if (gtk_text_iter_ends_line(&iter))
2564 					return TRUE;
2565 				wc = gtk_text_iter_get_char(&iter);
2566 				if (g_unichar_isspace(wc))
2567 					return TRUE;
2568 				else
2569 					return FALSE;
2570 			} else
2571 				return FALSE;
2572 		}
2573 	}
2574 
2575 	if (clen != 1)
2576 		return FALSE;
2577 	if (!strchr("*-+", ch[0]))
2578 		return FALSE;
2579 
2580 	gtk_text_iter_forward_char(&iter);
2581 	if (gtk_text_iter_ends_line(&iter))
2582 		return FALSE;
2583 	wc = gtk_text_iter_get_char(&iter);
2584 	if (g_unichar_isspace(wc))
2585 		return TRUE;
2586 	else if (ch[0] == '-') {
2587 		/* -- */
2588 		clen = g_unichar_to_utf8(wc, ch);
2589 		if (clen == 1 && ch[0] == '-')
2590 			return TRUE;
2591 	}
2592 
2593 	return FALSE;
2594 }
2595 
compose_get_line_break_pos(GtkTextBuffer * buffer,const GtkTextIter * start,GtkTextIter * break_pos,gint max_col,gint quote_len)2596 static gboolean compose_get_line_break_pos(GtkTextBuffer *buffer,
2597 					   const GtkTextIter *start,
2598 					   GtkTextIter *break_pos,
2599 					   gint max_col,
2600 					   gint quote_len)
2601 {
2602 	GtkTextIter iter = *start, line_end = *start;
2603 	PangoLogAttr *attrs;
2604 	gchar *str;
2605 	gchar *p;
2606 	gint len;
2607 	gint i;
2608 	gint col = 0;
2609 	gint pos = 0;
2610 	gboolean can_break = FALSE;
2611 	gboolean do_break = FALSE;
2612 	gboolean prev_dont_break = FALSE;
2613 
2614 	gtk_text_iter_forward_to_line_end(&line_end);
2615 	str = gtk_text_buffer_get_text(buffer, &iter, &line_end, FALSE);
2616 	len = g_utf8_strlen(str, -1);
2617 	/* g_print("breaking line: %d: %s (len = %d)\n",
2618 		gtk_text_iter_get_line(&iter), str, len); */
2619 	attrs = g_new(PangoLogAttr, len + 1);
2620 
2621 	pango_default_break(str, -1, NULL, attrs, len + 1);
2622 
2623 	p = str;
2624 
2625 	/* skip quote and leading spaces */
2626 	for (i = 0; *p != '\0' && i < len; i++) {
2627 		gunichar wc;
2628 
2629 		wc = g_utf8_get_char(p);
2630 		if (i >= quote_len && !g_unichar_isspace(wc))
2631 			break;
2632 		if (g_unichar_iswide(wc))
2633 			col += 2;
2634 		else if (*p == '\t')
2635 			col += 8;
2636 		else
2637 			col++;
2638 		p = g_utf8_next_char(p);
2639 	}
2640 
2641 	for (; *p != '\0' && i < len; i++) {
2642 		PangoLogAttr *attr = attrs + i;
2643 		gunichar wc;
2644 		gint uri_len;
2645 
2646 		if (attr->is_line_break && can_break && !prev_dont_break)
2647 			pos = i;
2648 
2649 		/* don't wrap URI */
2650 		if ((uri_len = get_uri_len(p)) > 0) {
2651 			col += uri_len;
2652 			if (pos > 0 && col > max_col) {
2653 				do_break = TRUE;
2654 				break;
2655 			}
2656 			i += uri_len - 1;
2657 			p += uri_len;
2658 			can_break = TRUE;
2659 			continue;
2660 		}
2661 
2662 		wc = g_utf8_get_char(p);
2663 		if (g_unichar_iswide(wc)) {
2664 			col += 2;
2665 			if (prev_dont_break && can_break && attr->is_line_break)
2666 				pos = i;
2667 		} else if (*p == '\t')
2668 			col += 8;
2669 		else
2670 			col++;
2671 		if (pos > 0 && col > max_col) {
2672 			do_break = TRUE;
2673 			break;
2674 		}
2675 
2676 		if (*p == '-' || *p == '/')
2677 			prev_dont_break = TRUE;
2678 		else
2679 			prev_dont_break = FALSE;
2680 
2681 		p = g_utf8_next_char(p);
2682 		can_break = TRUE;
2683 	}
2684 
2685 	debug_print("compose_get_line_break_pos(): do_break = %d, pos = %d, col = %d\n", do_break, pos, col);
2686 
2687 	g_free(attrs);
2688 	g_free(str);
2689 
2690 	*break_pos = *start;
2691 	gtk_text_iter_set_line_offset(break_pos, pos);
2692 
2693 	return do_break;
2694 }
2695 
compose_join_next_line(GtkTextBuffer * buffer,GtkTextIter * iter,const gchar * quote_str)2696 static gboolean compose_join_next_line(GtkTextBuffer *buffer,
2697 				       GtkTextIter *iter,
2698 				       const gchar *quote_str)
2699 {
2700 	GtkTextIter iter_ = *iter, cur, prev, next, end;
2701 	PangoLogAttr attrs[3];
2702 	gchar *str;
2703 	gchar *next_quote_str;
2704 	gunichar wc1, wc2;
2705 	gint quote_len;
2706 	gboolean keep_cursor = FALSE;
2707 
2708 	if (!gtk_text_iter_forward_line(&iter_) ||
2709 	    gtk_text_iter_ends_line(&iter_))
2710 		return FALSE;
2711 
2712 	next_quote_str = compose_get_quote_str(buffer, &iter_, &quote_len);
2713 
2714 	if ((quote_str || next_quote_str) &&
2715 	    strcmp2(quote_str, next_quote_str) != 0) {
2716 		g_free(next_quote_str);
2717 		return FALSE;
2718 	}
2719 	g_free(next_quote_str);
2720 
2721 	end = iter_;
2722 	if (quote_len > 0) {
2723 		gtk_text_iter_forward_chars(&end, quote_len);
2724 		if (gtk_text_iter_ends_line(&end))
2725 			return FALSE;
2726 	}
2727 
2728 	/* don't join itemized lines */
2729 	if (compose_is_itemized(buffer, &end))
2730 		return FALSE;
2731 
2732 	/* delete quote str */
2733 	if (quote_len > 0)
2734 		gtk_text_buffer_delete(buffer, &iter_, &end);
2735 
2736 	/* delete linebreak and extra spaces */
2737 	prev = cur = iter_;
2738 	while (gtk_text_iter_backward_char(&cur)) {
2739 		wc1 = gtk_text_iter_get_char(&cur);
2740 		if (!g_unichar_isspace(wc1))
2741 			break;
2742 		prev = cur;
2743 	}
2744 	next = cur = iter_;
2745 	while (!gtk_text_iter_ends_line(&cur)) {
2746 		wc1 = gtk_text_iter_get_char(&cur);
2747 		if (!g_unichar_isspace(wc1))
2748 			break;
2749 		gtk_text_iter_forward_char(&cur);
2750 		next = cur;
2751 	}
2752 	if (!gtk_text_iter_equal(&prev, &next)) {
2753 		GtkTextMark *mark;
2754 
2755 		mark = gtk_text_buffer_get_insert(buffer);
2756 		gtk_text_buffer_get_iter_at_mark(buffer, &cur, mark);
2757 		if (gtk_text_iter_equal(&prev, &cur))
2758 			keep_cursor = TRUE;
2759 		gtk_text_buffer_delete(buffer, &prev, &next);
2760 	}
2761 	iter_ = prev;
2762 
2763 	/* insert space if required */
2764 	gtk_text_iter_backward_char(&prev);
2765 	wc1 = gtk_text_iter_get_char(&prev);
2766 	wc2 = gtk_text_iter_get_char(&next);
2767 	gtk_text_iter_forward_char(&next);
2768 	str = gtk_text_buffer_get_text(buffer, &prev, &next, FALSE);
2769 	pango_default_break(str, -1, NULL, attrs, 3);
2770 	if (!attrs[1].is_line_break ||
2771 	    (!g_unichar_iswide(wc1) || !g_unichar_iswide(wc2))) {
2772 		gtk_text_buffer_insert(buffer, &iter_, " ", 1);
2773 		if (keep_cursor) {
2774 			gtk_text_iter_backward_char(&iter_);
2775 			gtk_text_buffer_place_cursor(buffer, &iter_);
2776 		}
2777 	}
2778 	g_free(str);
2779 
2780 	*iter = iter_;
2781 	return TRUE;
2782 }
2783 
compose_wrap_paragraph(Compose * compose,GtkTextIter * par_iter)2784 static void compose_wrap_paragraph(Compose *compose, GtkTextIter *par_iter)
2785 {
2786 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2787 	GtkTextBuffer *buffer;
2788 	GtkTextIter iter, break_pos;
2789 	gchar *quote_str = NULL;
2790 	gint quote_len;
2791 	gboolean wrap_quote = prefs_common.linewrap_quote;
2792 	gboolean prev_autowrap = compose->autowrap;
2793 
2794 	compose->autowrap = FALSE;
2795 
2796 	buffer = gtk_text_view_get_buffer(text);
2797 
2798 	if (par_iter) {
2799 		iter = *par_iter;
2800 	} else {
2801 		GtkTextMark *mark;
2802 		mark = gtk_text_buffer_get_insert(buffer);
2803 		gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2804 	}
2805 
2806 	/* move to paragraph start */
2807 	gtk_text_iter_set_line_offset(&iter, 0);
2808 	if (gtk_text_iter_ends_line(&iter)) {
2809 		while (gtk_text_iter_ends_line(&iter) &&
2810 		       gtk_text_iter_forward_line(&iter))
2811 			;
2812 	} else {
2813 		while (gtk_text_iter_backward_line(&iter)) {
2814 			if (gtk_text_iter_ends_line(&iter)) {
2815 				gtk_text_iter_forward_line(&iter);
2816 				break;
2817 			}
2818 		}
2819 	}
2820 
2821 	/* go until paragraph end (empty line) */
2822 	while (!gtk_text_iter_ends_line(&iter)) {
2823 		quote_str = compose_get_quote_str(buffer, &iter, &quote_len);
2824 		if (quote_str) {
2825 			if (!wrap_quote) {
2826 				gtk_text_iter_forward_line(&iter);
2827 				g_free(quote_str);
2828 				continue;
2829 			}
2830 			debug_print("compose_wrap_paragraph(): quote_str = '%s'\n", quote_str);
2831 		}
2832 
2833 		if (compose_get_line_break_pos(buffer, &iter, &break_pos,
2834 					       prefs_common.linewrap_len,
2835 					       quote_len)) {
2836 			GtkTextIter prev, next, cur;
2837 
2838 			gtk_text_buffer_insert(buffer, &break_pos, "\n", 1);
2839 
2840 			/* remove trailing spaces */
2841 			cur = break_pos;
2842 			gtk_text_iter_backward_char(&cur);
2843 			prev = next = cur;
2844 			while (!gtk_text_iter_starts_line(&cur)) {
2845 				gunichar wc;
2846 
2847 				gtk_text_iter_backward_char(&cur);
2848 				wc = gtk_text_iter_get_char(&cur);
2849 				if (!g_unichar_isspace(wc))
2850 					break;
2851 				prev = cur;
2852 			}
2853 			if (!gtk_text_iter_equal(&prev, &next)) {
2854 				gtk_text_buffer_delete(buffer, &prev, &next);
2855 				break_pos = next;
2856 				gtk_text_iter_forward_char(&break_pos);
2857 			}
2858 
2859 			if (quote_str)
2860 				gtk_text_buffer_insert(buffer, &break_pos,
2861 						       quote_str, -1);
2862 
2863 			iter = break_pos;
2864 			compose_join_next_line(buffer, &iter, quote_str);
2865 
2866 			/* move iter to current line start */
2867 			gtk_text_iter_set_line_offset(&iter, 0);
2868 		} else {
2869 			/* move iter to next line start */
2870 			iter = break_pos;
2871 			gtk_text_iter_forward_line(&iter);
2872 		}
2873 
2874 		g_free(quote_str);
2875 	}
2876 
2877 	if (par_iter)
2878 		*par_iter = iter;
2879 
2880 	compose->autowrap = prev_autowrap;
2881 }
2882 
compose_wrap_all(Compose * compose)2883 static void compose_wrap_all(Compose *compose)
2884 {
2885 	compose_wrap_all_full(compose, FALSE);
2886 }
2887 
compose_wrap_all_full(Compose * compose,gboolean autowrap)2888 static void compose_wrap_all_full(Compose *compose, gboolean autowrap)
2889 {
2890 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2891 	GtkTextBuffer *buffer;
2892 	GtkTextIter iter;
2893 
2894 	buffer = gtk_text_view_get_buffer(text);
2895 
2896 	gtk_text_buffer_get_start_iter(buffer, &iter);
2897 	while (!gtk_text_iter_is_end(&iter))
2898 		compose_wrap_paragraph(compose, &iter);
2899 }
2900 
compose_set_title(Compose * compose)2901 static void compose_set_title(Compose *compose)
2902 {
2903 	gchar *str;
2904 	gchar *edited;
2905 	const gchar *subject;
2906 
2907 	subject = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
2908 	if (!subject || subject[0] == '\0')
2909 		subject = _("(No Subject)");
2910 
2911 	edited = compose->modified ? " *" : "";
2912 	str = g_strdup_printf(_("%s - Compose%s"), subject, edited);
2913 	gtk_window_set_title(GTK_WINDOW(compose->window), str);
2914 	g_free(str);
2915 }
2916 
compose_select_account(Compose * compose,PrefsAccount * account,gboolean init)2917 static void compose_select_account(Compose *compose, PrefsAccount *account,
2918 				   gboolean init)
2919 {
2920 	GtkItemFactory *ifactory;
2921 	PrefsAccount *prev_account;
2922 
2923 	g_return_if_fail(account != NULL);
2924 
2925 	prev_account = compose->account;
2926 	compose->account = account;
2927 
2928 	compose_set_title(compose);
2929 
2930 	ifactory = gtk_item_factory_from_widget(compose->menubar);
2931 
2932 	if (account->protocol == A_NNTP &&
2933 	    (init || prev_account->protocol != A_NNTP)) {
2934 		gtk_widget_show(compose->newsgroups_hbox);
2935 		gtk_widget_show(compose->newsgroups_entry);
2936 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 4);
2937 		compose->use_newsgroups = TRUE;
2938 
2939 		menu_set_active(ifactory, "/View/To", FALSE);
2940 		menu_set_sensitive(ifactory, "/View/To", TRUE);
2941 		menu_set_active(ifactory, "/View/Cc", FALSE);
2942 		menu_set_sensitive(ifactory, "/View/Followup-To", TRUE);
2943 	} else if (account->protocol != A_NNTP &&
2944 		   (init || prev_account->protocol == A_NNTP)) {
2945 		gtk_widget_hide(compose->newsgroups_hbox);
2946 		gtk_widget_hide(compose->newsgroups_entry);
2947 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 2, 0);
2948 		gtk_widget_queue_resize(compose->table_vbox);
2949 		compose->use_newsgroups = FALSE;
2950 
2951 		menu_set_active(ifactory, "/View/To", TRUE);
2952 		menu_set_sensitive(ifactory, "/View/To", FALSE);
2953 		menu_set_active(ifactory, "/View/Cc", TRUE);
2954 		menu_set_active(ifactory, "/View/Followup-To", FALSE);
2955 		menu_set_sensitive(ifactory, "/View/Followup-To", FALSE);
2956 	}
2957 
2958 	if (account->set_autocc) {
2959 		compose_entry_show(compose, COMPOSE_ENTRY_CC);
2960 		if (account->auto_cc && compose->mode != COMPOSE_REEDIT)
2961 			compose_entry_set(compose, account->auto_cc,
2962 					  COMPOSE_ENTRY_CC);
2963 	}
2964 	if (account->set_autobcc) {
2965 		compose_entry_show(compose, COMPOSE_ENTRY_BCC);
2966 		if (account->auto_bcc && compose->mode != COMPOSE_REEDIT)
2967 			compose_entry_set(compose, account->auto_bcc,
2968 					  COMPOSE_ENTRY_BCC);
2969 	}
2970 	if (account->set_autoreplyto) {
2971 		compose_entry_show(compose, COMPOSE_ENTRY_REPLY_TO);
2972 		if (account->auto_replyto && compose->mode != COMPOSE_REEDIT)
2973 			compose_entry_set(compose, account->auto_replyto,
2974 					  COMPOSE_ENTRY_REPLY_TO);
2975 	}
2976 
2977 #if USE_GPGME
2978 	if (rfc2015_is_available()) {
2979 		if (account->default_sign)
2980 			menu_set_active(ifactory, "/Tools/PGP Sign", TRUE);
2981 		if (account->default_encrypt)
2982 			menu_set_active(ifactory, "/Tools/PGP Encrypt", TRUE);
2983 	}
2984 #endif /* USE_GPGME */
2985 
2986 	compose_update_signature_menu(compose);
2987 
2988 	if (!init && compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
2989 		compose_insert_sig(compose, TRUE, TRUE, FALSE);
2990 }
2991 
compose_update_signature_menu(Compose * compose)2992 static void compose_update_signature_menu(Compose *compose)
2993 {
2994 	GtkTreeModel *model;
2995 	GtkListStore *store;
2996 	GtkTreeIter iter;
2997 	gboolean valid;
2998 	gint i;
2999 	gchar *name;
3000 
3001 	if (!compose->account)
3002 		return;
3003 
3004 	model = gtk_combo_box_get_model(GTK_COMBO_BOX(compose->sig_combo));
3005 	store = GTK_LIST_STORE(model);
3006 	valid = gtk_tree_model_get_iter_first(model, &iter);
3007 
3008 	for (i = 0; valid && i < sizeof(compose->account->sig_names) /
3009 		sizeof(compose->account->sig_names[0]); i++) {
3010 		if (compose->account->sig_names[i] &&
3011 		    compose->account->sig_names[i][0] != '\0') {
3012 			name = g_strdup_printf
3013 				("%s", compose->account->sig_names[i]);
3014 		} else {
3015 			name = g_strdup_printf(_("Signature %d"), i + 1);
3016 		}
3017 		gtk_list_store_set(store, &iter, 0, name, -1);
3018 		g_free(name);
3019 		valid = gtk_tree_model_iter_next(model, &iter);
3020 	}
3021 
3022 	g_signal_handlers_block_by_func(G_OBJECT(compose->sig_combo),
3023 					G_CALLBACK(sig_combo_changed), compose);
3024 	gtk_combo_box_set_active(GTK_COMBO_BOX(compose->sig_combo), 0);
3025 	g_signal_handlers_unblock_by_func(G_OBJECT(compose->sig_combo),
3026 					  G_CALLBACK(sig_combo_changed),
3027 					  compose);
3028 
3029 	if (compose->account->sig_type != SIG_DIRECT) {
3030 		gtk_widget_set_sensitive(compose->sig_combo, FALSE);
3031 	} else {
3032 		gtk_widget_set_sensitive(compose->sig_combo, TRUE);
3033 	}
3034 }
3035 
compose_check_for_valid_recipient(Compose * compose)3036 static gboolean compose_check_for_valid_recipient(Compose *compose)
3037 {
3038 	const gchar *to_raw = "", *cc_raw = "", *bcc_raw = "";
3039 	const gchar *newsgroups_raw = "";
3040 	gchar *to, *cc, *bcc;
3041 	gchar *newsgroups;
3042 	gboolean valid;
3043 
3044 	if (compose->use_to)
3045 		to_raw = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3046 	if (compose->use_cc)
3047 		cc_raw = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3048 	if (compose->use_bcc)
3049 		bcc_raw = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3050 	if (compose->use_newsgroups)
3051 		newsgroups_raw = gtk_entry_get_text
3052 			(GTK_ENTRY(compose->newsgroups_entry));
3053 
3054 	if (*to_raw == '\0' && *cc_raw == '\0' && *bcc_raw == '\0' &&
3055 	    *newsgroups_raw == '\0')
3056 		return FALSE;
3057 
3058 	to = g_strstrip(g_strdup(to_raw));
3059 	cc = g_strstrip(g_strdup(cc_raw));
3060 	bcc = g_strstrip(g_strdup(bcc_raw));
3061 	newsgroups = g_strstrip(g_strdup(newsgroups_raw));
3062 
3063 	if (*to == '\0' && *cc == '\0' && *bcc == '\0' && *newsgroups == '\0')
3064 		valid = FALSE;
3065 	else
3066 		valid = TRUE;
3067 
3068 	g_free(newsgroups);
3069 	g_free(bcc);
3070 	g_free(cc);
3071 	g_free(to);
3072 
3073 	return valid;
3074 }
3075 
compose_check_entries(Compose * compose)3076 static gboolean compose_check_entries(Compose *compose)
3077 {
3078 	const gchar *str;
3079 
3080 	if (compose_check_for_valid_recipient(compose) == FALSE) {
3081 		alertpanel_error(_("Recipient is not specified."));
3082 		return FALSE;
3083 	}
3084 
3085 	str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3086 	if (*str == '\0') {
3087 		AlertValue aval;
3088 
3089 		aval = alertpanel(_("Empty subject"),
3090 				  _("Subject is empty. Send it anyway?"),
3091 				  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3092 		if (aval != G_ALERTDEFAULT)
3093 			return FALSE;
3094 	}
3095 
3096 	return TRUE;
3097 }
3098 
compose_check_attachments(Compose * compose)3099 static gboolean compose_check_attachments(Compose *compose)
3100 {
3101 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3102 	GtkTextBuffer *buffer;
3103 	GtkTextIter iter, line_end;
3104 	gchar *line;
3105 	gchar **strv;
3106 	gint i;
3107 	gboolean attach_found = FALSE;
3108 	gboolean valid = TRUE;
3109 
3110 	if (!prefs_common.check_attach)
3111 		return TRUE;
3112 	if (!prefs_common.check_attach_str)
3113 		return TRUE;
3114 
3115 	if (compose->use_attach &&
3116 	    gtk_tree_model_iter_n_children
3117 		(GTK_TREE_MODEL(compose->attach_store), NULL) > 0)
3118 		return TRUE;
3119 
3120 	buffer = gtk_text_view_get_buffer(text);
3121 	gtk_text_buffer_get_start_iter(buffer, &iter);
3122 	line_end = iter;
3123 
3124 	strv = g_strsplit(prefs_common.check_attach_str, ",", -1);
3125 	for (i = 0; strv[i] != NULL; i++)
3126 		g_strstrip(strv[i]);
3127 
3128 	while (valid) {
3129 		valid = gtk_text_iter_forward_to_line_end(&line_end);
3130 		line = gtk_text_buffer_get_text(buffer, &iter, &line_end,
3131 						FALSE);
3132 		iter = line_end;
3133 		if (get_quote_level(line) != -1)
3134 			continue;
3135 
3136 		for (i = 0; strv[i] != NULL; i++) {
3137 			if (strv[i][0] == '\0')
3138 				continue;
3139 			if (strcasestr(line, strv[i])) {
3140 				attach_found = TRUE;
3141 				valid = FALSE;
3142 				break;
3143 			}
3144 		}
3145 
3146 		g_free(line);
3147 	}
3148 
3149 	g_strfreev(strv);
3150 
3151 	if (attach_found) {
3152 		AlertValue aval;
3153 
3154 		aval = alertpanel(_("Attachment is missing"),
3155 				  _("There is no attachment. Send it without attachments?"),
3156 				  GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3157 		if (aval != G_ALERTDEFAULT)
3158 			return FALSE;
3159 	}
3160 
3161 	return TRUE;
3162 }
3163 
check_recp_delete_event(GtkWidget * widget,GdkEventAny * event,gint * state)3164 static gint check_recp_delete_event(GtkWidget *widget, GdkEventAny *event,
3165 				    gint *state)
3166 {
3167 	*state = GTK_RESPONSE_CANCEL;
3168 	return TRUE;
3169 }
3170 
check_recp_key_pressed(GtkWidget * widget,GdkEventKey * event,gint * state)3171 static gboolean check_recp_key_pressed(GtkWidget *widget, GdkEventKey *event,
3172 				       gint *state)
3173 {
3174 	if (event && event->keyval == GDK_Escape) {
3175 		*state = GTK_RESPONSE_CANCEL;
3176 		return TRUE;
3177 	}
3178 	return FALSE;
3179 }
3180 
check_recp_ok(GtkWidget * widget,gint * state)3181 static void check_recp_ok(GtkWidget *widget, gint *state)
3182 {
3183 	*state = GTK_RESPONSE_OK;
3184 }
3185 
check_recp_cancel(GtkWidget * widget,gint * state)3186 static void check_recp_cancel(GtkWidget *widget, gint *state)
3187 {
3188 	*state = GTK_RESPONSE_CANCEL;
3189 }
3190 
compose_check_recipients(Compose * compose)3191 static gboolean compose_check_recipients(Compose *compose)
3192 {
3193 	GtkWidget *window;
3194 	GtkWidget *vbox;
3195 	GtkWidget *hbox;
3196 	GtkWidget *image;
3197 	GtkWidget *vbox2;
3198 	GtkWidget *label;
3199 	GtkWidget *table;
3200 	GtkWidget *entry;
3201 	gchar buf[1024];
3202 	const gchar *text;
3203 	GtkWidget *scrwin;
3204 	GtkWidget *treeview;
3205 	GtkTreeStore *store;
3206 	GtkTreeViewColumn *column;
3207 	GtkCellRenderer *renderer;
3208 	GtkTreeIter iter, parent;
3209 	GtkWidget *hbbox;
3210 	GtkWidget *ok_btn;
3211 	GtkWidget *cancel_btn;
3212 	static PangoFontDescription *font_desc;
3213 	GtkStyle *style;
3214 
3215 	GSList *cur, *to_list = NULL;
3216 	gboolean check_recp = FALSE;
3217 	gint state = 0;
3218 
3219 	g_return_val_if_fail(compose->account != NULL, FALSE);
3220 	g_return_val_if_fail(compose->account->address != NULL, FALSE);
3221 
3222 	if (!prefs_common.check_recipients)
3223 		return TRUE;
3224 
3225 	if (prefs_common.check_recp_exclude) {
3226 		gchar **strv;
3227 		gint i;
3228 
3229 		strv = g_strsplit(prefs_common.check_recp_exclude, ",", -1);
3230 		for (i = 0; strv[i] != NULL; i++)
3231 			g_strstrip(strv[i]);
3232 
3233 		if (compose->use_to) {
3234 			text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3235 			to_list = address_list_append_orig(NULL, text);
3236 		}
3237 		if (compose->use_cc) {
3238 			text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3239 			to_list = address_list_append_orig(to_list, text);
3240 		}
3241 		if (compose->use_bcc) {
3242 			text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3243 			to_list = address_list_append_orig(to_list, text);
3244 		}
3245 
3246 		for (cur = to_list; cur != NULL; cur = cur->next) {
3247 			for (i = 0; strv[i] != NULL; i++) {
3248 				if (strv[i][0] == '\0')
3249 					continue;
3250 				if (strcasestr((gchar *)cur->data, strv[i]))
3251 					break;
3252 			}
3253 			if (!strv[i]) {
3254 				/* not found in exclude list */
3255 				check_recp = TRUE;
3256 				break;
3257 			}
3258 		}
3259 
3260 		slist_free_strings(to_list);
3261 		g_slist_free(to_list);
3262 		to_list = NULL;
3263 		g_strfreev(strv);
3264 	} else
3265 		check_recp = TRUE;
3266 
3267 	if (!check_recp)
3268 		return TRUE;
3269 
3270 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
3271 	gtk_container_set_border_width(GTK_CONTAINER(window), 8);
3272 	gtk_window_set_title(GTK_WINDOW(window), _("Check recipients"));
3273 	gtk_window_set_position(GTK_WINDOW(window),
3274 				GTK_WIN_POS_CENTER_ON_PARENT);
3275 	gtk_window_set_modal(GTK_WINDOW(window), TRUE);
3276 	gtk_widget_set_size_request(window, 480 * gtkut_get_dpi_multiplier(), -1);
3277 	gtk_widget_realize(window);
3278 	g_signal_connect(G_OBJECT(window), "delete_event",
3279 			 G_CALLBACK(check_recp_delete_event), &state);
3280 	g_signal_connect(G_OBJECT(window), "key_press_event",
3281 			 G_CALLBACK(check_recp_key_pressed), &state);
3282 
3283 	vbox = gtk_vbox_new(FALSE, 8);
3284 	gtk_container_add(GTK_CONTAINER(window), vbox);
3285 
3286 	hbox = gtk_hbox_new(FALSE, 12);
3287 	gtk_container_set_border_width(GTK_CONTAINER(hbox), 12);
3288 	gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
3289 
3290 	image = gtk_image_new_from_stock
3291 		(GTK_STOCK_DIALOG_QUESTION, GTK_ICON_SIZE_DIALOG);
3292 	gtk_misc_set_alignment(GTK_MISC(image), 0.5, 0.0);
3293 	gtk_box_pack_start(GTK_BOX(hbox), image, FALSE, FALSE, 0);
3294 
3295 	vbox2 = gtk_vbox_new(FALSE, 12);
3296 	gtk_box_pack_start(GTK_BOX(hbox), vbox2, TRUE, TRUE, 0);
3297 
3298 	label = gtk_label_new(_("Check recipients"));
3299 	gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3300 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3301         gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3302 
3303 	if (!font_desc) {
3304 		gint size;
3305 
3306 		size = pango_font_description_get_size
3307 			(label->style->font_desc);
3308 		font_desc = pango_font_description_new();
3309 		pango_font_description_set_weight
3310 			(font_desc, PANGO_WEIGHT_BOLD);
3311 		pango_font_description_set_size
3312 			(font_desc, size * PANGO_SCALE_LARGE);
3313 	}
3314 	if (font_desc)
3315 		gtk_widget_modify_font(label, font_desc);
3316 
3317 	label = gtk_label_new
3318 		(_("Really send this mail to the following addresses?"));
3319 	gtk_box_pack_start(GTK_BOX(vbox2), label, TRUE, TRUE, 0);
3320 	gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.0);
3321 	gtk_label_set_line_wrap(GTK_LABEL(label), TRUE);
3322 	gtk_label_set_selectable(GTK_LABEL(label), TRUE);
3323 	GTK_WIDGET_UNSET_FLAGS(label, GTK_CAN_FOCUS);
3324 
3325 	table = gtk_table_new(2, 2, FALSE);
3326 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
3327 	gtk_table_set_row_spacings(GTK_TABLE(table), 4);
3328 	gtk_table_set_col_spacings(GTK_TABLE(table), 4);
3329 
3330 	hbox = gtk_hbox_new(FALSE, 0);
3331 	label = gtk_label_new(prefs_common.trans_hdr ? _("From:")
3332 			      : "From:");
3333 	gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3334 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 0, 1,
3335                          GTK_FILL, 0, 2, 0);
3336 	entry = gtk_entry_new();
3337 	gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3338 	gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3339 	style = gtk_widget_get_style(window);
3340 	gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3341 			       &style->bg[GTK_STATE_NORMAL]);
3342 	gtk_table_attach_defaults
3343                 (GTK_TABLE(table), entry, 1, 2, 0, 1);
3344 
3345 	if (compose->account->name && *compose->account->name) {
3346 		g_snprintf(buf, sizeof(buf), "%s <%s>",
3347 			   compose->account->name, compose->account->address);
3348 		gtk_entry_set_text(GTK_ENTRY(entry), buf);
3349 	} else
3350 		gtk_entry_set_text(GTK_ENTRY(entry), compose->account->address);
3351 
3352 	hbox = gtk_hbox_new(FALSE, 0);
3353 	label = gtk_label_new(prefs_common.trans_hdr ? _("Subject:")
3354 			      : "Subject:");
3355 	gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
3356 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, 1, 2,
3357                          GTK_FILL, 0, 2, 0);
3358 	entry = gtk_entry_new();
3359 	gtk_entry_set_max_length(GTK_ENTRY(entry), MAX_ENTRY_LENGTH);
3360 	gtk_editable_set_editable(GTK_EDITABLE(entry), FALSE);
3361 	style = gtk_widget_get_style(window);
3362 	gtk_widget_modify_base(entry, GTK_STATE_NORMAL,
3363 			       &style->bg[GTK_STATE_NORMAL]);
3364 	gtk_table_attach_defaults
3365                 (GTK_TABLE(table), entry, 1, 2, 1, 2);
3366 
3367 	text = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
3368 	gtk_entry_set_text(GTK_ENTRY(entry), text);
3369 
3370 	scrwin = gtk_scrolled_window_new(NULL, NULL);
3371 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrwin),
3372 				       GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
3373 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrwin),
3374 					    GTK_SHADOW_IN);
3375 	gtk_widget_set_size_request(scrwin, -1, 180 * gtkut_get_dpi_multiplier());
3376 	gtk_box_pack_start(GTK_BOX(vbox), scrwin, TRUE, TRUE, 0);
3377 
3378 	store = gtk_tree_store_new(1, G_TYPE_STRING);
3379 	if (compose->use_to) {
3380 		text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3381 		to_list = address_list_append_orig(NULL, text);
3382 		if (to_list) {
3383 			gtk_tree_store_append(store, &parent, NULL);
3384 			gtk_tree_store_set(store, &parent, 0,
3385 					   prefs_common.trans_hdr ?
3386 					   _("To:") : "To:", -1);
3387 			for (cur = to_list; cur != NULL; cur = cur->next) {
3388 				gtk_tree_store_append(store, &iter, &parent);
3389 				gtk_tree_store_set(store, &iter, 0,
3390 						   (gchar *)cur->data, -1);
3391 			}
3392 			slist_free_strings(to_list);
3393 			g_slist_free(to_list);
3394 		}
3395 	}
3396 	if (compose->use_cc) {
3397 		text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3398 		to_list = address_list_append_orig(NULL, text);
3399 		if (to_list) {
3400 			gtk_tree_store_append(store, &parent, NULL);
3401 			gtk_tree_store_set(store, &parent, 0,
3402 					   prefs_common.trans_hdr ?
3403 					   _("Cc:") : "Cc:", -1);
3404 			for (cur = to_list; cur != NULL; cur = cur->next) {
3405 				gtk_tree_store_append(store, &iter, &parent);
3406 				gtk_tree_store_set(store, &iter, 0,
3407 						   (gchar *)cur->data, -1);
3408 			}
3409 			slist_free_strings(to_list);
3410 			g_slist_free(to_list);
3411 		}
3412 	}
3413 	if (compose->use_bcc) {
3414 		text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3415 		to_list = address_list_append_orig(NULL, text);
3416 		if (to_list) {
3417 			gtk_tree_store_append(store, &parent, NULL);
3418 			gtk_tree_store_set(store, &parent, 0,
3419 					   prefs_common.trans_hdr ?
3420 					   _("Bcc:") : "Bcc:", -1);
3421 			for (cur = to_list; cur != NULL; cur = cur->next) {
3422 				gtk_tree_store_append(store, &iter, &parent);
3423 				gtk_tree_store_set(store, &iter, 0,
3424 						   (gchar *)cur->data, -1);
3425 			}
3426 			slist_free_strings(to_list);
3427 			g_slist_free(to_list);
3428 		}
3429 	}
3430 
3431 	treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
3432 	g_object_unref(G_OBJECT(store));
3433 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(treeview), TRUE);
3434 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(treeview), TRUE);
3435 
3436 	gtk_container_add(GTK_CONTAINER(scrwin), treeview);
3437 
3438 	renderer = gtk_cell_renderer_text_new();
3439 	g_object_set(renderer, "ypad", 0, NULL);
3440 	column = gtk_tree_view_column_new_with_attributes
3441 		(_("Address"), renderer, "text", 0, NULL);
3442 	gtk_tree_view_append_column(GTK_TREE_VIEW(treeview), column);
3443 
3444 	gtk_tree_view_expand_all(GTK_TREE_VIEW(treeview));
3445 
3446 	gtkut_stock_button_set_create(&hbbox, &ok_btn, _("_Send"),
3447 				      &cancel_btn, GTK_STOCK_CANCEL,
3448 				      NULL, NULL);
3449         gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
3450         gtk_widget_grab_default(ok_btn);
3451 	gtk_widget_grab_focus(ok_btn);
3452 
3453 	g_signal_connect(G_OBJECT(ok_btn), "clicked",
3454 			 G_CALLBACK(check_recp_ok), &state);
3455 	g_signal_connect(G_OBJECT(cancel_btn), "clicked",
3456 			 G_CALLBACK(check_recp_cancel), &state);
3457 
3458 	manage_window_set_transient(GTK_WINDOW(window));
3459 
3460 	gtk_widget_show_all(window);
3461 
3462 	while (state == 0)
3463 		gtk_main_iteration();
3464 
3465 	gtk_widget_destroy(window);
3466 
3467 	if (state == GTK_RESPONSE_OK)
3468 		return TRUE;
3469 
3470 	return FALSE;
3471 }
3472 
compose_check_activities(Compose * compose)3473 static gboolean compose_check_activities(Compose *compose)
3474 {
3475 	if (inc_is_active()) {
3476 		alertpanel_notice(_("Checking for new messages is currently running.\n"
3477 				    "Please try again later."));
3478 		return FALSE;
3479 	}
3480 
3481 	return TRUE;
3482 }
3483 
compose_add_new_recipients_to_addressbook(Compose * compose)3484 static void compose_add_new_recipients_to_addressbook(Compose *compose)
3485 {
3486 	GSList *to_list = NULL, *cur;
3487 	const gchar *text;
3488 
3489 	if (compose->use_to) {
3490 		text = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
3491 		to_list = address_list_append_orig(NULL, text);
3492 	}
3493 	if (compose->use_cc) {
3494 		text = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
3495 		to_list = address_list_append_orig(to_list, text);
3496 	}
3497 	if (compose->use_bcc) {
3498 		text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
3499 		to_list = address_list_append_orig(to_list, text);
3500 	}
3501 
3502 	for (cur = to_list; cur != NULL; cur = cur->next) {
3503 		gchar *orig_addr = cur->data;
3504 		gchar *name, *addr;
3505 
3506 		name = procheader_get_fromname(orig_addr);
3507 		addr = g_strdup(orig_addr);
3508 		extract_address(addr);
3509 		if (!g_ascii_strcasecmp(name, addr)) {
3510 			g_free(name);
3511 			name = NULL;
3512 		}
3513 
3514 		if (addressbook_has_address(addr))
3515 			debug_print("compose_add_new_recipients_to_addressbook: address <%s> already registered.\n", addr);
3516 		else
3517 			addressbook_add_contact_autoreg(name, addr, NULL);
3518 
3519 		g_free(addr);
3520 		g_free(name);
3521 	}
3522 
3523 	slist_free_strings(to_list);
3524 	g_slist_free(to_list);
3525 }
3526 
compose_lock(Compose * compose)3527 void compose_lock(Compose *compose)
3528 {
3529 	compose->lock_count++;
3530 }
3531 
compose_unlock(Compose * compose)3532 void compose_unlock(Compose *compose)
3533 {
3534 	if (compose->lock_count > 0)
3535 		compose->lock_count--;
3536 }
3537 
compose_block_modified(Compose * compose)3538 void compose_block_modified(Compose *compose)
3539 {
3540 	compose->block_modified = TRUE;
3541 }
3542 
compose_unblock_modified(Compose * compose)3543 void compose_unblock_modified(Compose *compose)
3544 {
3545 	compose->block_modified = FALSE;
3546 }
3547 
3548 #define C_LOCK()			\
3549 {					\
3550 	inc_lock();			\
3551 	compose_lock(compose);		\
3552 }
3553 
3554 #define C_UNLOCK()			\
3555 {					\
3556 	compose_unlock(compose);	\
3557 	inc_unlock();			\
3558 }
3559 
compose_send_real(Compose * compose)3560 static gint compose_send_real(Compose *compose)
3561 {
3562 	gchar tmp[MAXPATHLEN + 1];
3563 	gint ok = 0;
3564 	gboolean cancel = FALSE;
3565 
3566 	if (compose->lock_count > 0)
3567 		return 1;
3568 
3569 	g_return_val_if_fail(compose->account != NULL, -1);
3570 
3571 	C_LOCK();
3572 
3573 	if (compose_check_entries(compose) == FALSE) {
3574 		C_UNLOCK();
3575 		return 1;
3576 	}
3577 	if (compose_check_attachments(compose) == FALSE) {
3578 		C_UNLOCK();
3579 		return 1;
3580 	}
3581 	if (compose_check_recipients(compose) == FALSE) {
3582 		C_UNLOCK();
3583 		return 1;
3584 	}
3585 	if (compose_check_activities(compose) == FALSE) {
3586 		C_UNLOCK();
3587 		return 1;
3588 	}
3589 
3590 	if (!main_window_toggle_online_if_offline(main_window_get())) {
3591 		C_UNLOCK();
3592 		return 1;
3593 	}
3594 
3595 	/* write to temporary file */
3596 	g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
3597 		   get_tmp_dir(), G_DIR_SEPARATOR, compose);
3598 
3599 	if (compose->mode == COMPOSE_REDIRECT) {
3600 		if (compose_redirect_write_to_file(compose, tmp) < 0) {
3601 			C_UNLOCK();
3602 			return -1;
3603 		}
3604 	} else {
3605 		if (compose_write_to_file(compose, tmp, FALSE) < 0) {
3606 			C_UNLOCK();
3607 			return -1;
3608 		}
3609 	}
3610 
3611 	if (!compose->to_list && !compose->newsgroup_list) {
3612 		g_warning(_("can't get recipient list."));
3613 		g_unlink(tmp);
3614 		C_UNLOCK();
3615 		return 1;
3616 	}
3617 
3618 	syl_plugin_signal_emit("compose-send", compose, compose->mode, 0,
3619 			       tmp, compose->to_list, &cancel);
3620 	if (cancel) {
3621 		g_unlink(tmp);
3622 		C_UNLOCK();
3623 		return -1;
3624 	}
3625 
3626 	if (compose->to_list) {
3627 		PrefsAccount *ac;
3628 
3629 		if (compose->account->protocol != A_NNTP)
3630 			ac = compose->account;
3631 		else {
3632 			ac = account_find_from_address(compose->account->address);
3633 			if (!ac) {
3634 				if (cur_account && cur_account->protocol != A_NNTP)
3635 					ac = cur_account;
3636 				else
3637 					ac = account_get_default();
3638 			}
3639 			if (!ac || ac->protocol == A_NNTP) {
3640 				alertpanel_error(_("Account for sending mail is not specified.\n"
3641 						   "Please select a mail account before sending."));
3642 				g_unlink(tmp);
3643 				C_UNLOCK();
3644 				return -1;
3645 			}
3646 		}
3647 
3648 		/* POP before SMTP requires inc to be unlocked.
3649 		   send_message() also locks inc internally. */
3650 		inc_unlock();
3651 		ok = send_message(tmp, ac, compose->to_list);
3652 		inc_lock();
3653 
3654 		statusbar_pop_all();
3655 	}
3656 
3657 	if (ok == 0 && compose->newsgroup_list) {
3658 		ok = news_post(FOLDER(compose->account->folder), tmp);
3659 		if (ok < 0) {
3660 			alertpanel_error(_("Error occurred while posting the message to %s ."),
3661 					 compose->account->nntp_server);
3662 			g_unlink(tmp);
3663 			C_UNLOCK();
3664 			return -1;
3665 		}
3666 	}
3667 
3668 	if (ok == 0) {
3669 		if (compose->mode == COMPOSE_REEDIT) {
3670 			compose_remove_reedit_target(compose);
3671 			if (compose->targetinfo)
3672 				folderview_update_item
3673 					(compose->targetinfo->folder, TRUE);
3674 		}
3675 
3676 		if (compose->reply_target)
3677 			send_message_set_reply_flag(compose->reply_target,
3678 						    compose->inreplyto);
3679 		else if (compose->forward_targets)
3680 			send_message_set_forward_flags
3681 				(compose->forward_targets);
3682 
3683 		/* save message to outbox */
3684 		if (prefs_common.savemsg) {
3685 			FolderItem *outbox;
3686 			gboolean drop_done = FALSE;
3687 
3688 			/* filter sent message */
3689 			if (prefs_common.filter_sent) {
3690 				FilterInfo *fltinfo;
3691 
3692 				fltinfo = filter_info_new();
3693 				fltinfo->account = compose->account;
3694 				fltinfo->flags.perm_flags = 0;
3695 				fltinfo->flags.tmp_flags = MSG_RECEIVED;
3696 
3697 				filter_apply(prefs_common.fltlist, tmp,
3698 					     fltinfo);
3699 
3700 				drop_done = fltinfo->drop_done;
3701 				folderview_update_all_updated(TRUE);
3702 				filter_info_free(fltinfo);
3703 			}
3704 
3705 			if (!drop_done) {
3706 				outbox = account_get_special_folder
3707 					(compose->account, F_OUTBOX);
3708 				if (procmsg_save_to_outbox(outbox, tmp) < 0) {
3709 					alertpanel_error
3710 						(_("Sending of message was completed, but the message could not be saved to outbox."));
3711 					ok = -2;
3712 				} else
3713 					folderview_update_item(outbox, TRUE);
3714 			}
3715 		}
3716 
3717 		/* Add recipients to addressbook automatically */
3718 		if (prefs_common.recipients_autoreg) {
3719 			compose_add_new_recipients_to_addressbook(compose);
3720 		}
3721 	}
3722 
3723 	g_unlink(tmp);
3724 	C_UNLOCK();
3725 
3726 	return ok;
3727 }
3728 
3729 #if USE_GPGME
compose_get_self_key_id(Compose * compose)3730 static const gchar *compose_get_self_key_id(Compose *compose)
3731 {
3732 	const gchar *key_id = NULL;
3733 
3734 	switch (compose->account->sign_key) {
3735 	case SIGN_KEY_DEFAULT:
3736 		break;
3737 	case SIGN_KEY_BY_FROM:
3738 		key_id = compose->account->address;
3739 		break;
3740 	case SIGN_KEY_CUSTOM:
3741 		key_id = compose->account->sign_key_id;
3742 		break;
3743 	default:
3744 		break;
3745 	}
3746 
3747 	return key_id;
3748 }
3749 
3750 /* interfaces to rfc2015 to keep out the prefs stuff there.
3751  * returns 0 on success and -1 on error. */
compose_create_signers_list(Compose * compose,GSList ** pkey_list)3752 static gint compose_create_signers_list(Compose *compose, GSList **pkey_list)
3753 {
3754 	const gchar *key_id = NULL;
3755 	GSList *key_list;
3756 
3757 	key_id = compose_get_self_key_id(compose);
3758 	if (!key_id) {
3759 		*pkey_list = NULL;
3760 		return 0;
3761 	}
3762 
3763 	key_list = rfc2015_create_signers_list(key_id);
3764 	if (!key_list) {
3765 		alertpanel_error(_("Could not find any key associated with "
3766 				   "currently selected key id `%s'."), key_id);
3767 		return -1;
3768 	}
3769 
3770 	*pkey_list = key_list;
3771 	return 0;
3772 }
3773 
compose_create_encrypt_recipients_list(Compose * compose)3774 static GSList *compose_create_encrypt_recipients_list(Compose *compose)
3775 {
3776 	GSList *recp_list = NULL;
3777 	const gchar *key_id = NULL;
3778 
3779 	g_return_val_if_fail(compose->to_list != NULL, NULL);
3780 
3781 	recp_list = g_slist_copy(compose->to_list);
3782 	if (compose->account->encrypt_to_self) {
3783 		key_id = compose_get_self_key_id(compose);
3784 		recp_list = g_slist_append(recp_list, (gpointer)key_id);
3785 	}
3786 
3787 	return recp_list;
3788 }
3789 
3790 /* clearsign message body text */
compose_clearsign_text(Compose * compose,gchar ** text)3791 static gint compose_clearsign_text(Compose *compose, gchar **text)
3792 {
3793 	GSList *key_list;
3794 	gchar *tmp_file;
3795 
3796 	tmp_file = get_tmp_file();
3797 	if (str_write_to_file(*text, tmp_file) < 0) {
3798 		g_free(tmp_file);
3799 		return -1;
3800 	}
3801 
3802 	if (compose_create_signers_list(compose, &key_list) < 0) {
3803 		g_unlink(tmp_file);
3804 		g_free(tmp_file);
3805 		return -1;
3806 	}
3807 	if (rfc2015_clearsign(tmp_file, key_list) < 0) {
3808 		alertpanel_error(_("Can't sign the message."));
3809 		g_unlink(tmp_file);
3810 		g_free(tmp_file);
3811 		return -1;
3812 	}
3813 
3814 	g_free(*text);
3815 	*text = file_read_to_str(tmp_file);
3816 	g_unlink(tmp_file);
3817 	g_free(tmp_file);
3818 	if (*text == NULL)
3819 		return -1;
3820 
3821 	return 0;
3822 }
3823 
compose_encrypt_armored(Compose * compose,gchar ** text)3824 static gint compose_encrypt_armored(Compose *compose, gchar **text)
3825 {
3826 	gchar *tmp_file;
3827 	GSList *recp_list;
3828 
3829 	tmp_file = get_tmp_file();
3830 	if (str_write_to_file(*text, tmp_file) < 0) {
3831 		g_free(tmp_file);
3832 		return -1;
3833 	}
3834 
3835 	recp_list = compose_create_encrypt_recipients_list(compose);
3836 
3837 	if (rfc2015_encrypt_armored(tmp_file, recp_list) < 0) {
3838 		alertpanel_error(_("Can't encrypt the message."));
3839 		g_slist_free(recp_list);
3840 		g_unlink(tmp_file);
3841 		g_free(tmp_file);
3842 		return -1;
3843 	}
3844 
3845 	g_slist_free(recp_list);
3846 	g_free(*text);
3847 	*text = file_read_to_str(tmp_file);
3848 	g_unlink(tmp_file);
3849 	g_free(tmp_file);
3850 	if (*text == NULL)
3851 		return -1;
3852 
3853 	return 0;
3854 }
3855 
compose_encrypt_sign_armored(Compose * compose,gchar ** text)3856 static gint compose_encrypt_sign_armored(Compose *compose, gchar **text)
3857 {
3858 	GSList *key_list;
3859 	GSList *recp_list;
3860 	gchar *tmp_file;
3861 
3862 	tmp_file = get_tmp_file();
3863 	if (str_write_to_file(*text, tmp_file) < 0) {
3864 		g_free(tmp_file);
3865 		return -1;
3866 	}
3867 
3868 	if (compose_create_signers_list(compose, &key_list) < 0) {
3869 		g_unlink(tmp_file);
3870 		g_free(tmp_file);
3871 		return -1;
3872 	}
3873 
3874 	recp_list = compose_create_encrypt_recipients_list(compose);
3875 
3876 	if (rfc2015_encrypt_sign_armored(tmp_file, recp_list, key_list) < 0) {
3877 		alertpanel_error(_("Can't encrypt or sign the message."));
3878 		g_slist_free(recp_list);
3879 		g_unlink(tmp_file);
3880 		g_free(tmp_file);
3881 		return -1;
3882 	}
3883 
3884 	g_slist_free(recp_list);
3885 	g_free(*text);
3886 	*text = file_read_to_str(tmp_file);
3887 	g_unlink(tmp_file);
3888 	g_free(tmp_file);
3889 	if (*text == NULL)
3890 		return -1;
3891 
3892 	return 0;
3893 }
3894 #endif /* USE_GPGME */
3895 
compose_write_to_file(Compose * compose,const gchar * file,gboolean is_draft)3896 static gint compose_write_to_file(Compose *compose, const gchar *file,
3897 				  gboolean is_draft)
3898 {
3899 	GtkTextBuffer *buffer;
3900 	GtkTextIter start, end;
3901 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
3902 	FILE *fp;
3903 	size_t len;
3904 	gchar *chars;
3905 	gchar *buf;
3906 	gchar *canon_buf;
3907 	const gchar *out_charset;
3908 	const gchar *body_charset;
3909 	const gchar *src_charset = CS_INTERNAL;
3910 	EncodingType encoding;
3911 	gint line;
3912 #if USE_GPGME
3913 	gboolean use_pgpmime_encryption = FALSE;
3914 	gboolean use_pgpmime_signing = FALSE;
3915 #endif
3916 
3917 	if ((fp = g_fopen(file, "wb")) == NULL) {
3918 		FILE_OP_ERROR(file, "fopen");
3919 		return -1;
3920 	}
3921 
3922 	/* chmod for security */
3923 	if (change_file_mode_rw(fp, file) < 0) {
3924 		FILE_OP_ERROR(file, "chmod");
3925 		g_warning(_("can't change file mode\n"));
3926 	}
3927 
3928 	/* get outgoing charset */
3929 	out_charset = conv_get_charset_str(compose->out_encoding);
3930 	if (!out_charset)
3931 		out_charset = conv_get_outgoing_charset_str();
3932 	if (!g_ascii_strcasecmp(out_charset, CS_US_ASCII))
3933 		out_charset = CS_ISO_8859_1;
3934 	body_charset = out_charset;
3935 
3936 	/* get all composed text */
3937 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
3938 	gtk_text_buffer_get_start_iter(buffer, &start);
3939 	gtk_text_buffer_get_end_iter(buffer, &end);
3940 	chars = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
3941 	if (is_ascii_str(chars)) {
3942 		buf = chars;
3943 		chars = NULL;
3944 		body_charset = CS_US_ASCII;
3945 		encoding = ENC_7BIT;
3946 	} else {
3947 		gint error = 0;
3948 
3949 		buf = conv_codeset_strdup_full
3950 			(chars, src_charset, body_charset, &error);
3951 		if (!buf || error != 0) {
3952 			AlertValue aval = G_ALERTDEFAULT;
3953 			gchar *msg;
3954 
3955 			g_free(buf);
3956 
3957 			if (!is_draft) {
3958 				msg = g_strdup_printf(_("Can't convert the character encoding of the message body from %s to %s.\n"
3959 							"\n"
3960 							"Send it as %s anyway?"),
3961 						      src_charset, body_charset,
3962 						      src_charset);
3963 				aval = alertpanel_full
3964 					(_("Code conversion error"), msg, ALERT_ERROR,
3965 					 G_ALERTALTERNATE,
3966 					 FALSE, GTK_STOCK_YES, GTK_STOCK_NO, NULL);
3967 				g_free(msg);
3968 			}
3969 
3970 			if (aval != G_ALERTDEFAULT) {
3971 				g_free(chars);
3972 				fclose(fp);
3973 				g_unlink(file);
3974 				return -1;
3975 			} else {
3976 				buf = chars;
3977 				out_charset = body_charset = src_charset;
3978 				chars = NULL;
3979 			}
3980 		}
3981 
3982 		if (prefs_common.encoding_method == CTE_BASE64)
3983 			encoding = ENC_BASE64;
3984 		else if (prefs_common.encoding_method == CTE_QUOTED_PRINTABLE)
3985 			encoding = ENC_QUOTED_PRINTABLE;
3986 		else if (prefs_common.encoding_method == CTE_8BIT)
3987 			encoding = ENC_8BIT;
3988 		else
3989 			encoding = procmime_get_encoding_for_charset
3990 				(body_charset);
3991 	}
3992 	g_free(chars);
3993 
3994 	canon_buf = canonicalize_str(buf);
3995 	g_free(buf);
3996 	buf = canon_buf;
3997 
3998 #if USE_GPGME
3999 	if (compose->use_signing && !compose->account->clearsign)
4000 		use_pgpmime_signing = TRUE;
4001 	if (compose->use_encryption && compose->account->ascii_armored) {
4002 		use_pgpmime_encryption = FALSE;
4003 		use_pgpmime_signing = FALSE;
4004 	}
4005 	if (compose->use_encryption && !compose->account->ascii_armored)
4006 		use_pgpmime_encryption = TRUE;
4007 
4008 	/* protect trailing spaces */
4009 	if (rfc2015_is_available() && !is_draft && use_pgpmime_signing) {
4010 		if (encoding == ENC_7BIT) {
4011 			if (!g_ascii_strcasecmp(body_charset, CS_ISO_2022_JP)) {
4012 				gchar *tmp;
4013 				tmp = strchomp_all(buf);
4014 				g_free(buf);
4015 				buf = tmp;
4016 			} else
4017 				encoding = ENC_QUOTED_PRINTABLE;
4018 		} else if (encoding == ENC_8BIT) {
4019 			encoding = procmime_get_encoding_for_str(buf);
4020 			if (encoding == ENC_7BIT)
4021 				encoding = ENC_QUOTED_PRINTABLE;
4022 		}
4023 	}
4024 
4025 	if (rfc2015_is_available() && !is_draft) {
4026 		if ((compose->use_encryption &&
4027 		     compose->account->ascii_armored) ||
4028 		    (compose->use_signing && compose->account->clearsign)) {
4029 			/* MIME encoding doesn't fit with cleartext signature */
4030 			if (encoding == ENC_QUOTED_PRINTABLE || encoding == ENC_BASE64)
4031 				encoding = ENC_8BIT;
4032 
4033 		}
4034 	}
4035 #endif
4036 
4037 	debug_print("src encoding = %s, out encoding = %s, "
4038 		    "body encoding = %s, transfer encoding = %s\n",
4039 		    src_charset, out_charset, body_charset,
4040 		    procmime_get_encoding_str(encoding));
4041 
4042 	/* check for line length limit */
4043 	if (!is_draft &&
4044 	    encoding != ENC_QUOTED_PRINTABLE && encoding != ENC_BASE64 &&
4045 	    check_line_length(buf, 1000, &line) < 0) {
4046 		AlertValue aval;
4047 		gchar *msg;
4048 
4049 		msg = g_strdup_printf
4050 			(_("Line %d exceeds the line length limit (998 bytes).\n"
4051 			   "The contents of the message might be broken on the way to the delivery.\n"
4052 			   "\n"
4053 			   "Send it anyway?"), line + 1);
4054 		aval = alertpanel_full(_("Line length limit"),
4055 				       msg, ALERT_WARNING,
4056 				       G_ALERTALTERNATE, FALSE,
4057 				       GTK_STOCK_YES, GTK_STOCK_NO, NULL);
4058 		if (aval != G_ALERTDEFAULT) {
4059 			g_free(msg);
4060 			fclose(fp);
4061 			g_unlink(file);
4062 			g_free(buf);
4063 			return -1;
4064 		}
4065 	}
4066 
4067 	/* write headers */
4068 	if (compose_write_headers(compose, fp, out_charset,
4069 				  body_charset, encoding, is_draft) < 0) {
4070 		g_warning("can't write headers\n");
4071 		fclose(fp);
4072 		g_unlink(file);
4073 		g_free(buf);
4074 		return -1;
4075 	}
4076 
4077 #if USE_GPGME
4078 	/* do ascii-armor encryption and/or clearsign */
4079 	if (rfc2015_is_available() && !is_draft) {
4080 		gint ret;
4081 
4082 		if (compose->use_encryption && compose->account->ascii_armored) {
4083 			if (compose->use_signing)
4084 				ret = compose_encrypt_sign_armored(compose, &buf);
4085 			else
4086 				ret = compose_encrypt_armored(compose, &buf);
4087 			if (ret < 0) {
4088 				g_warning("ascii-armored encryption failed\n");
4089 				fclose(fp);
4090 				g_unlink(file);
4091 				g_free(buf);
4092 				return -1;
4093 			}
4094 		} else if (compose->use_signing && compose->account->clearsign) {
4095 			if (compose_clearsign_text(compose, &buf) < 0) {
4096 				g_warning("clearsign failed\n");
4097 				fclose(fp);
4098 				g_unlink(file);
4099 				g_free(buf);
4100 				return -1;
4101 			}
4102 		}
4103 	}
4104 #endif
4105 
4106 	if (compose->use_attach &&
4107 	    gtk_tree_model_iter_n_children(model, NULL) > 0) {
4108 #if USE_GPGME
4109             /* This prolog message is ignored by mime software and
4110              * because it would make our signing/encryption task
4111              * tougher, we don't emit it in that case */
4112             if (!rfc2015_is_available() ||
4113 		(!compose->use_signing && !compose->use_encryption))
4114 #endif
4115 		fputs("This is a multi-part message in MIME format.\n", fp);
4116 
4117 		fprintf(fp, "\n--%s\n", compose->boundary);
4118 		fprintf(fp, "Content-Type: text/plain; charset=%s\n",
4119 			body_charset);
4120 #if USE_GPGME
4121 		if (rfc2015_is_available() && use_pgpmime_signing)
4122 			fprintf(fp, "Content-Disposition: inline\n");
4123 #endif
4124 		fprintf(fp, "Content-Transfer-Encoding: %s\n",
4125 			procmime_get_encoding_str(encoding));
4126 		fputc('\n', fp);
4127 	}
4128 
4129 	/* write body */
4130 	len = strlen(buf);
4131 	if (encoding == ENC_BASE64) {
4132 		gchar outbuf[B64_BUFFSIZE];
4133 		gint i, l;
4134 
4135 		for (i = 0; i < len; i += B64_LINE_SIZE) {
4136 			l = MIN(B64_LINE_SIZE, len - i);
4137 			base64_encode(outbuf, (guchar *)buf + i, l);
4138 			fputs(outbuf, fp);
4139 			fputc('\n', fp);
4140 		}
4141 	} else if (encoding == ENC_QUOTED_PRINTABLE) {
4142 		gchar *outbuf;
4143 		size_t outlen;
4144 
4145 		outbuf = g_malloc(len * 4);
4146 		qp_encode_line(outbuf, (guchar *)buf);
4147 		outlen = strlen(outbuf);
4148 		if (fwrite(outbuf, sizeof(gchar), outlen, fp) != outlen) {
4149 			FILE_OP_ERROR(file, "fwrite");
4150 			fclose(fp);
4151 			g_unlink(file);
4152 			g_free(outbuf);
4153 			g_free(buf);
4154 			return -1;
4155 		}
4156 		g_free(outbuf);
4157 	} else if (fwrite(buf, sizeof(gchar), len, fp) != len) {
4158 		FILE_OP_ERROR(file, "fwrite");
4159 		fclose(fp);
4160 		g_unlink(file);
4161 		g_free(buf);
4162 		return -1;
4163 	}
4164 	g_free(buf);
4165 
4166 	if (compose->use_attach &&
4167 	    gtk_tree_model_iter_n_children(model, NULL) > 0) {
4168 		if (compose_write_attach(compose, fp, out_charset) < 0) {
4169 			fclose(fp);
4170 			g_unlink(file);
4171 			return -1;
4172 		}
4173 	}
4174 
4175 	if (fclose(fp) == EOF) {
4176 		FILE_OP_ERROR(file, "fclose");
4177 		g_unlink(file);
4178 		return -1;
4179 	}
4180 
4181 #if USE_GPGME
4182 	if (!rfc2015_is_available() || is_draft) {
4183 		uncanonicalize_file_replace(file);
4184 		return 0;
4185 	}
4186 
4187 	if (use_pgpmime_signing || use_pgpmime_encryption) {
4188 		if (canonicalize_file_replace(file) < 0) {
4189 			g_unlink(file);
4190 			return -1;
4191 		}
4192 	}
4193 
4194 	if (use_pgpmime_signing && !use_pgpmime_encryption) {
4195 		GSList *key_list;
4196 
4197 		if (compose_create_signers_list(compose, &key_list) < 0) {
4198 			g_unlink(file);
4199 			return -1;
4200 		}
4201 		if (rfc2015_sign(file, key_list) < 0) {
4202 			alertpanel_error(_("Can't sign the message."));
4203 			g_unlink(file);
4204 			return -1;
4205 		}
4206 	} else if (use_pgpmime_encryption) {
4207 		GSList *key_list;
4208 		GSList *recp_list;
4209 
4210 		if (compose->use_bcc) {
4211 			const gchar *text;
4212 			gchar *bcc;
4213 			AlertValue aval;
4214 
4215 			text = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4216 			if (*text != '\0') {
4217 				bcc = g_strdup(text);
4218 				g_strstrip(bcc);
4219 				if (*bcc != '\0') {
4220 					aval = alertpanel_full
4221 						(_("Encrypting with Bcc"),
4222 						 _("This message has Bcc recipients. If this message is encrypted, all Bcc recipients will be visible by examing the encryption key list, leading to loss of confidentiality.\n"
4223 						   "\n"
4224 						   "Send it anyway?"),
4225 						 ALERT_WARNING, G_ALERTDEFAULT, FALSE,
4226 						 GTK_STOCK_YES, GTK_STOCK_NO, NULL);
4227 					if (aval != G_ALERTDEFAULT) {
4228 						g_free(bcc);
4229 						g_unlink(file);
4230 						return -1;
4231 					}
4232 				}
4233 				g_free(bcc);
4234 			}
4235 		}
4236 
4237 		recp_list = compose_create_encrypt_recipients_list(compose);
4238 
4239 		if (use_pgpmime_signing) {
4240 			if (compose_create_signers_list
4241 				(compose, &key_list) < 0) {
4242 				g_unlink(file);
4243 				return -1;
4244 			}
4245 
4246 			if (rfc2015_encrypt_sign(file, recp_list, key_list) < 0) {
4247 				alertpanel_error(_("Can't encrypt or sign the message."));
4248 				g_slist_free(recp_list);
4249 				g_unlink(file);
4250 				return -1;
4251 			}
4252 		} else if (rfc2015_encrypt(file, recp_list) < 0) {
4253 			alertpanel_error(_("Can't encrypt the message."));
4254 			g_slist_free(recp_list);
4255 			g_unlink(file);
4256 			return -1;
4257 		}
4258 
4259 		g_slist_free(recp_list);
4260 	}
4261 #endif /* USE_GPGME */
4262 
4263 	uncanonicalize_file_replace(file);
4264 
4265 	return 0;
4266 }
4267 
compose_write_body_to_file(Compose * compose,const gchar * file)4268 static gint compose_write_body_to_file(Compose *compose, const gchar *file)
4269 {
4270 	GtkTextBuffer *buffer;
4271 	GtkTextIter start, end;
4272 	FILE *fp;
4273 	size_t len;
4274 	gchar *chars, *tmp;
4275 
4276 	if ((fp = g_fopen(file, "wb")) == NULL) {
4277 		FILE_OP_ERROR(file, "fopen");
4278 		return -1;
4279 	}
4280 
4281 	/* chmod for security */
4282 	if (change_file_mode_rw(fp, file) < 0) {
4283 		FILE_OP_ERROR(file, "chmod");
4284 		g_warning(_("can't change file mode\n"));
4285 	}
4286 
4287 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
4288 	gtk_text_buffer_get_start_iter(buffer, &start);
4289 	gtk_text_buffer_get_end_iter(buffer, &end);
4290 	tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
4291 
4292 	chars = conv_codeset_strdup
4293 		(tmp, CS_INTERNAL, conv_get_locale_charset_str());
4294 
4295 	g_free(tmp);
4296 
4297 	if (!chars) {
4298 		fclose(fp);
4299 		g_unlink(file);
4300 		return -1;
4301 	}
4302 
4303 	/* write body */
4304 	len = strlen(chars);
4305 	if (fwrite(chars, sizeof(gchar), len, fp) != len) {
4306 		FILE_OP_ERROR(file, "fwrite");
4307 		g_free(chars);
4308 		fclose(fp);
4309 		g_unlink(file);
4310 		return -1;
4311 	}
4312 
4313 	g_free(chars);
4314 
4315 	if (fclose(fp) == EOF) {
4316 		FILE_OP_ERROR(file, "fclose");
4317 		g_unlink(file);
4318 		return -1;
4319 	}
4320 	return 0;
4321 }
4322 
compose_redirect_write_to_file(Compose * compose,const gchar * file)4323 static gint compose_redirect_write_to_file(Compose *compose, const gchar *file)
4324 {
4325 	FILE *fp;
4326 	FILE *fdest;
4327 	size_t len;
4328 	gchar buf[BUFFSIZE];
4329 
4330 	g_return_val_if_fail(file != NULL, -1);
4331 	g_return_val_if_fail(compose->account != NULL, -1);
4332 	g_return_val_if_fail(compose->account->address != NULL, -1);
4333 	g_return_val_if_fail(compose->mode == COMPOSE_REDIRECT, -1);
4334 	g_return_val_if_fail(compose->targetinfo != NULL, -1);
4335 
4336 	if ((fp = procmsg_open_message(compose->targetinfo)) == NULL)
4337 		return -1;
4338 
4339 	if ((fdest = g_fopen(file, "wb")) == NULL) {
4340 		FILE_OP_ERROR(file, "fopen");
4341 		fclose(fp);
4342 		return -1;
4343 	}
4344 
4345 	if (change_file_mode_rw(fdest, file) < 0) {
4346 		FILE_OP_ERROR(file, "chmod");
4347 		g_warning(_("can't change file mode\n"));
4348 	}
4349 
4350 	while (procheader_get_one_field(buf, sizeof(buf), fp, NULL) == 0) {
4351 		if (g_ascii_strncasecmp(buf, "Return-Path:",
4352 					strlen("Return-Path:")) == 0 ||
4353 		    g_ascii_strncasecmp(buf, "Delivered-To:",
4354 					strlen("Delivered-To:")) == 0 ||
4355 		    g_ascii_strncasecmp(buf, "Received:",
4356 					strlen("Received:")) == 0 ||
4357 		    g_ascii_strncasecmp(buf, "Subject:",
4358 					strlen("Subject:")) == 0 ||
4359 		    g_ascii_strncasecmp(buf, "X-UIDL:",
4360 					strlen("X-UIDL:")) == 0)
4361 			continue;
4362 
4363 		if (fputs(buf, fdest) == EOF)
4364 			goto error;
4365 
4366 #if 0
4367 		if (g_ascii_strncasecmp(buf, "From:", strlen("From:")) == 0) {
4368 			fputs("\n (by way of ", fdest);
4369 			if (compose->account->name) {
4370 				compose_convert_header(compose,
4371 						       buf, sizeof(buf),
4372 						       compose->account->name,
4373 						       strlen(" (by way of "),
4374 						       FALSE, NULL);
4375 				fprintf(fdest, "%s <%s>", buf,
4376 					compose->account->address);
4377 			} else
4378 				fputs(compose->account->address, fdest);
4379 			fputs(")", fdest);
4380 		}
4381 #endif
4382 
4383 		if (fputs("\n", fdest) == EOF)
4384 			goto error;
4385 	}
4386 
4387 	compose_redirect_write_headers(compose, fdest);
4388 
4389 	while ((len = fread(buf, sizeof(gchar), sizeof(buf), fp)) > 0) {
4390 		if (fwrite(buf, sizeof(gchar), len, fdest) != len) {
4391 			FILE_OP_ERROR(file, "fwrite");
4392 			goto error;
4393 		}
4394 	}
4395 
4396 	fclose(fp);
4397 	if (fclose(fdest) == EOF) {
4398 		FILE_OP_ERROR(file, "fclose");
4399 		g_unlink(file);
4400 		return -1;
4401 	}
4402 
4403 	return 0;
4404 error:
4405 	fclose(fp);
4406 	fclose(fdest);
4407 	g_unlink(file);
4408 
4409 	return -1;
4410 }
4411 
compose_remove_reedit_target(Compose * compose)4412 static gint compose_remove_reedit_target(Compose *compose)
4413 {
4414 	FolderItem *item;
4415 	MsgInfo *msginfo = compose->targetinfo;
4416 
4417 	g_return_val_if_fail(compose->mode == COMPOSE_REEDIT, -1);
4418 	if (!msginfo) return -1;
4419 
4420 	item = msginfo->folder;
4421 	g_return_val_if_fail(item != NULL, -1);
4422 
4423 	folder_item_scan(item);
4424 	if (procmsg_msg_exist(msginfo) &&
4425 	    (item->stype == F_DRAFT || item->stype == F_QUEUE)) {
4426 		if (folder_item_remove_msg(item, msginfo) < 0) {
4427 			g_warning(_("can't remove the old message\n"));
4428 			return -1;
4429 		}
4430 	}
4431 
4432 	return 0;
4433 }
4434 
compose_queue(Compose * compose,const gchar * file)4435 static gint compose_queue(Compose *compose, const gchar *file)
4436 {
4437 	FolderItem *queue;
4438 	gchar *tmp;
4439 	FILE *fp, *src_fp;
4440 	GSList *cur;
4441 	gchar buf[BUFFSIZE];
4442 	gint num;
4443 	MsgFlags flag = {0, MSG_QUEUED};
4444 
4445 	debug_print(_("queueing message...\n"));
4446 	g_return_val_if_fail(compose->to_list != NULL ||
4447 			     compose->newsgroup_list != NULL,
4448 			     -1);
4449 	g_return_val_if_fail(compose->account != NULL, -1);
4450 
4451 	tmp = g_strdup_printf("%s%cqueue.%p", get_tmp_dir(),
4452 			      G_DIR_SEPARATOR, compose);
4453 	if ((fp = g_fopen(tmp, "wb")) == NULL) {
4454 		FILE_OP_ERROR(tmp, "fopen");
4455 		g_free(tmp);
4456 		return -1;
4457 	}
4458 	if ((src_fp = g_fopen(file, "rb")) == NULL) {
4459 		FILE_OP_ERROR(file, "fopen");
4460 		fclose(fp);
4461 		g_unlink(tmp);
4462 		g_free(tmp);
4463 		return -1;
4464 	}
4465 	if (change_file_mode_rw(fp, tmp) < 0) {
4466 		FILE_OP_ERROR(tmp, "chmod");
4467 		g_warning(_("can't change file mode\n"));
4468 	}
4469 
4470 	/* queueing variables */
4471 	fprintf(fp, "AF:\n");
4472 	fprintf(fp, "NF:0\n");
4473 	fprintf(fp, "PS:10\n");
4474 	fprintf(fp, "SRH:1\n");
4475 	fprintf(fp, "SFN:\n");
4476 	fprintf(fp, "DSR:\n");
4477 	if (compose->msgid)
4478 		fprintf(fp, "MID:<%s>\n", compose->msgid);
4479 	else
4480 		fprintf(fp, "MID:\n");
4481 	fprintf(fp, "CFG:\n");
4482 	fprintf(fp, "PT:0\n");
4483 	fprintf(fp, "S:%s\n", compose->account->address);
4484 	fprintf(fp, "RQ:\n");
4485 	if (compose->account->smtp_server)
4486 		fprintf(fp, "SSV:%s\n", compose->account->smtp_server);
4487 	else
4488 		fprintf(fp, "SSV:\n");
4489 	if (compose->account->nntp_server)
4490 		fprintf(fp, "NSV:%s\n", compose->account->nntp_server);
4491 	else
4492 		fprintf(fp, "NSV:\n");
4493 	fprintf(fp, "SSH:\n");
4494 	if (compose->to_list) {
4495 		fprintf(fp, "R:<%s>", (gchar *)compose->to_list->data);
4496 		for (cur = compose->to_list->next; cur != NULL;
4497 		     cur = cur->next)
4498 			fprintf(fp, ",<%s>", (gchar *)cur->data);
4499 		fprintf(fp, "\n");
4500 	} else
4501 		fprintf(fp, "R:\n");
4502 	/* Sylpheed account ID */
4503 	fprintf(fp, "AID:%d\n", compose->account->account_id);
4504 	/* Reply target */
4505 	if (compose->reply_target)
4506 		fprintf(fp, "REP:%s\n", compose->reply_target);
4507 	/* Forward target */
4508 	if (compose->forward_targets)
4509 		fprintf(fp, "FWD:%s\n", compose->forward_targets);
4510 	fprintf(fp, "\n");
4511 
4512 	while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4513 		if (fputs(buf, fp) == EOF) {
4514 			FILE_OP_ERROR(tmp, "fputs");
4515 			fclose(fp);
4516 			fclose(src_fp);
4517 			g_unlink(tmp);
4518 			g_free(tmp);
4519 			return -1;
4520 		}
4521 	}
4522 
4523 	fclose(src_fp);
4524 	if (fclose(fp) == EOF) {
4525 		FILE_OP_ERROR(tmp, "fclose");
4526 		g_unlink(tmp);
4527 		g_free(tmp);
4528 		return -1;
4529 	}
4530 
4531 	queue = account_get_special_folder(compose->account, F_QUEUE);
4532 	if (!queue) {
4533 		g_warning(_("can't find queue folder\n"));
4534 		g_unlink(tmp);
4535 		g_free(tmp);
4536 		return -1;
4537 	}
4538 	folder_item_scan(queue);
4539 	if ((num = folder_item_add_msg(queue, tmp, &flag, TRUE)) < 0) {
4540 		g_warning(_("can't queue the message\n"));
4541 		g_unlink(tmp);
4542 		g_free(tmp);
4543 		return -1;
4544 	}
4545 	g_free(tmp);
4546 
4547 	if (compose->mode == COMPOSE_REEDIT) {
4548 		compose_remove_reedit_target(compose);
4549 		if (compose->targetinfo &&
4550 		    compose->targetinfo->folder != queue)
4551 			folderview_update_item
4552 				(compose->targetinfo->folder, TRUE);
4553 	}
4554 
4555 	folder_item_scan(queue);
4556 	folderview_update_item(queue, TRUE);
4557 
4558 	/* Add recipients to addressbook automatically */
4559 	if (prefs_common.recipients_autoreg) {
4560 		compose_add_new_recipients_to_addressbook(compose);
4561 	}
4562 
4563 	main_window_set_menu_sensitive(main_window_get());
4564 	main_window_set_toolbar_sensitive(main_window_get());
4565 
4566 	return 0;
4567 }
4568 
compose_write_attach(Compose * compose,FILE * fp,const gchar * charset)4569 static gint compose_write_attach(Compose *compose, FILE *fp,
4570 				 const gchar *charset)
4571 {
4572 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
4573 	GtkTreeIter iter;
4574 	gboolean valid;
4575 	AttachInfo *ainfo;
4576 	FILE *attach_fp;
4577 	gint len;
4578 	EncodingType encoding;
4579 	ContentType content_type;
4580 	gchar *tmp_file = NULL;
4581 	const gchar *src_file;
4582 	FILE *src_fp;
4583 	FILE *tmp_fp = NULL;
4584 
4585 	for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
4586 	     valid = gtk_tree_model_iter_next(model, &iter)) {
4587 		gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
4588 
4589 		if (!is_file_exist(ainfo->file)) {
4590 			alertpanel_error(_("File %s doesn't exist."),
4591 					 ainfo->file);
4592 			return -1;
4593 		}
4594 		if (get_file_size(ainfo->file) <= 0) {
4595 			alertpanel_error(_("File %s is empty."), ainfo->file);
4596 			return -1;
4597 		}
4598 		if ((attach_fp = g_fopen(ainfo->file, "rb")) == NULL) {
4599 			alertpanel_error(_("Can't open file %s."), ainfo->file);
4600 			return -1;
4601 		}
4602 
4603 		fprintf(fp, "\n--%s\n", compose->boundary);
4604 
4605 		encoding = ainfo->encoding;
4606 
4607 		if (!g_ascii_strncasecmp(ainfo->content_type, "message/", 8)) {
4608 			fprintf(fp, "Content-Type: %s\n", ainfo->content_type);
4609 			fprintf(fp, "Content-Disposition: inline\n");
4610 
4611 			/* message/... shouldn't be encoded */
4612 			if (encoding == ENC_QUOTED_PRINTABLE ||
4613 			    encoding == ENC_BASE64)
4614 				encoding = ENC_8BIT;
4615 		} else {
4616 			if (prefs_common.mime_fencoding_method ==
4617 			    FENC_RFC2231) {
4618 				gchar *param;
4619 
4620 				param = compose_convert_filename
4621 					(compose, ainfo->name, "name", charset);
4622 				fprintf(fp, "Content-Type: %s;\n"
4623 					    "%s\n",
4624 					ainfo->content_type, param);
4625 				g_free(param);
4626 				param = compose_convert_filename
4627 					(compose, ainfo->name, "filename",
4628 					 charset);
4629 				fprintf(fp, "Content-Disposition: attachment;\n"
4630 					    "%s\n", param);
4631 				g_free(param);
4632 			} else {
4633 				gchar filename[BUFFSIZE];
4634 
4635 				compose_convert_header(compose, filename,
4636 						       sizeof(filename),
4637 						       ainfo->name, 12, FALSE,
4638 						       charset);
4639 				fprintf(fp, "Content-Type: %s;\n"
4640 					    " name=\"%s\"\n",
4641 					ainfo->content_type, filename);
4642 				fprintf(fp, "Content-Disposition: attachment;\n"
4643 					    " filename=\"%s\"\n", filename);
4644 			}
4645 
4646 #if USE_GPGME
4647 			/* force encoding to protect trailing spaces */
4648 			if (rfc2015_is_available() && compose->use_signing &&
4649 			    !compose->account->clearsign) {
4650 				if (encoding == ENC_7BIT)
4651 					encoding = ENC_QUOTED_PRINTABLE;
4652 				else if (encoding == ENC_8BIT)
4653 					encoding = ENC_BASE64;
4654 			}
4655 #endif
4656 		}
4657 
4658 		fprintf(fp, "Content-Transfer-Encoding: %s\n\n",
4659 			procmime_get_encoding_str(encoding));
4660 
4661 		content_type = procmime_scan_mime_type(ainfo->content_type);
4662 
4663 		if (content_type == MIME_TEXT || content_type == MIME_TEXT_HTML) {
4664 			CharSet enc;
4665 
4666 			enc = conv_check_file_encoding(ainfo->file);
4667 			if (enc == C_UTF_16 || enc == C_UTF_16BE || enc == C_UTF_16LE) {
4668 				tmp_file = get_tmp_file();
4669 				if (conv_copy_file(ainfo->file, tmp_file, conv_get_charset_str(enc)) < 0) {
4670 					g_warning("compose_write_attach: Cannot convert UTF-16 file %s to UTF-8", ainfo->file);
4671 					g_free(tmp_file);
4672 					tmp_file = NULL;
4673 				}
4674 			}
4675 		}
4676 
4677 		if (tmp_file) {
4678 			src_file = tmp_file;
4679 		} else {
4680 			src_file = ainfo->file;
4681 		}
4682 
4683 		if (encoding == ENC_BASE64) {
4684 			gchar inbuf[B64_LINE_SIZE], outbuf[B64_BUFFSIZE];
4685 			gchar *canon_file = NULL;
4686 
4687 			if (content_type == MIME_TEXT ||
4688 			    content_type == MIME_TEXT_HTML ||
4689 			    content_type == MIME_MESSAGE_RFC822) {
4690 				canon_file = get_tmp_file();
4691 				if (canonicalize_file(src_file, canon_file) < 0) {
4692 					g_free(canon_file);
4693 					if (tmp_file) {
4694 						g_unlink(tmp_file);
4695 						g_free(tmp_file);
4696 					}
4697 					fclose(attach_fp);
4698 					return -1;
4699 				}
4700 				if ((tmp_fp = g_fopen(canon_file, "rb")) == NULL) {
4701 					FILE_OP_ERROR(canon_file, "fopen");
4702 					g_unlink(canon_file);
4703 					g_free(canon_file);
4704 					if (tmp_file) {
4705 						g_unlink(tmp_file);
4706 						g_free(tmp_file);
4707 					}
4708 					fclose(attach_fp);
4709 					return -1;
4710 				}
4711 			}
4712 
4713 			if (tmp_fp) {
4714 				src_fp = tmp_fp;
4715 			} else {
4716 				src_fp = attach_fp;
4717 			}
4718 
4719 			while ((len = fread(inbuf, sizeof(gchar),
4720 					    B64_LINE_SIZE, src_fp))
4721 			       == B64_LINE_SIZE) {
4722 				base64_encode(outbuf, (guchar *)inbuf,
4723 					      B64_LINE_SIZE);
4724 				fputs(outbuf, fp);
4725 				fputc('\n', fp);
4726 			}
4727 			if (len > 0 && feof(src_fp)) {
4728 				base64_encode(outbuf, (guchar *)inbuf, len);
4729 				fputs(outbuf, fp);
4730 				fputc('\n', fp);
4731 			}
4732 
4733 			if (tmp_fp) {
4734 				fclose(tmp_fp);
4735 				tmp_fp = NULL;
4736 			}
4737 			if (canon_file) {
4738 				g_unlink(canon_file);
4739 				g_free(canon_file);
4740 			}
4741 		} else {
4742 			if (tmp_file) {
4743 				if ((tmp_fp = g_fopen(tmp_file, "rb")) == NULL) {
4744 					FILE_OP_ERROR(tmp_file, "fopen");
4745 					g_unlink(tmp_file);
4746 					g_free(tmp_file);
4747 					fclose(attach_fp);
4748 					return -1;
4749 				}
4750 				src_fp = tmp_fp;
4751 			} else {
4752 				src_fp = attach_fp;
4753 			}
4754 
4755 			if (encoding == ENC_QUOTED_PRINTABLE) {
4756 				gchar inbuf[BUFFSIZE], outbuf[BUFFSIZE * 4];
4757 
4758 				while (fgets(inbuf, sizeof(inbuf), src_fp) != NULL) {
4759 					qp_encode_line(outbuf, (guchar *)inbuf);
4760 					fputs(outbuf, fp);
4761 				}
4762 			} else {
4763 				gchar buf[BUFFSIZE];
4764 
4765 				while (fgets(buf, sizeof(buf), src_fp) != NULL) {
4766 					strcrchomp(buf);
4767 					fputs(buf, fp);
4768 				}
4769 			}
4770 
4771 			if (tmp_fp) {
4772 				fclose(tmp_fp);
4773 				tmp_fp = NULL;
4774 			}
4775 		}
4776 
4777 		if (tmp_file) {
4778 			g_unlink(tmp_file);
4779 			g_free(tmp_file);
4780 			tmp_file = NULL;
4781 		}
4782 		fclose(attach_fp);
4783 	}
4784 
4785 	fprintf(fp, "\n--%s--\n", compose->boundary);
4786 	return 0;
4787 }
4788 
4789 #define QUOTE_REQUIRED(str) \
4790 	(*str != '"' && strpbrk(str, ",.[]<>") != NULL)
4791 
4792 #define PUT_RECIPIENT_HEADER(header, str)				     \
4793 {									     \
4794 	if (*str != '\0') {						     \
4795 		gchar *dest;						     \
4796 									     \
4797 		dest = g_strdup(str);					     \
4798 		g_strstrip(dest);					     \
4799 		if (*dest != '\0') {					     \
4800 			compose->to_list = address_list_append		     \
4801 				(compose->to_list, dest);		     \
4802 			compose_convert_header				     \
4803 				(compose, buf, sizeof(buf), dest,	     \
4804 				 strlen(header) + 2, TRUE, charset);	     \
4805 			fprintf(fp, "%s: %s\n", header, buf);		     \
4806 		}							     \
4807 		g_free(dest);						     \
4808 	}								     \
4809 }
4810 
4811 #define IS_IN_CUSTOM_HEADER(header) \
4812 	(compose->account->add_customhdr && \
4813 	 custom_header_find(compose->account->customhdr_list, header) != NULL)
4814 
compose_write_headers(Compose * compose,FILE * fp,const gchar * charset,const gchar * body_charset,EncodingType encoding,gboolean is_draft)4815 static gint compose_write_headers(Compose *compose, FILE *fp,
4816 				  const gchar *charset,
4817 				  const gchar *body_charset,
4818 				  EncodingType encoding, gboolean is_draft)
4819 {
4820 	gchar buf[BUFFSIZE];
4821 	const gchar *entry_str;
4822 	gchar *str;
4823 
4824 	g_return_val_if_fail(fp != NULL, -1);
4825 	g_return_val_if_fail(charset != NULL, -1);
4826 	g_return_val_if_fail(compose->account != NULL, -1);
4827 	g_return_val_if_fail(compose->account->address != NULL, -1);
4828 
4829 	/* Date */
4830 	if (compose->account->add_date) {
4831 		get_rfc822_date(buf, sizeof(buf));
4832 		fprintf(fp, "Date: %s\n", buf);
4833 	}
4834 
4835 	/* From */
4836 	if (compose->account->name && *compose->account->name) {
4837 		compose_convert_header
4838 			(compose, buf, sizeof(buf), compose->account->name,
4839 			 strlen("From: "), TRUE, charset);
4840 		if (QUOTE_REQUIRED(buf))
4841 			fprintf(fp, "From: \"%s\" <%s>\n",
4842 				buf, compose->account->address);
4843 		else
4844 			fprintf(fp, "From: %s <%s>\n",
4845 				buf, compose->account->address);
4846 	} else
4847 		fprintf(fp, "From: %s\n", compose->account->address);
4848 
4849 	slist_free_strings(compose->to_list);
4850 	g_slist_free(compose->to_list);
4851 	compose->to_list = NULL;
4852 
4853 	/* To */
4854 	if (compose->use_to) {
4855 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
4856 		PUT_RECIPIENT_HEADER("To", entry_str);
4857 	}
4858 
4859 	slist_free_strings(compose->newsgroup_list);
4860 	g_slist_free(compose->newsgroup_list);
4861 	compose->newsgroup_list = NULL;
4862 
4863 	/* Newsgroups */
4864 	if (compose->use_newsgroups) {
4865 		entry_str = gtk_entry_get_text
4866 			(GTK_ENTRY(compose->newsgroups_entry));
4867 		if (*entry_str != '\0') {
4868 			str = g_strdup(entry_str);
4869 			g_strstrip(str);
4870 			remove_space(str);
4871 			if (*str != '\0') {
4872 				compose->newsgroup_list =
4873 					newsgroup_list_append
4874 						(compose->newsgroup_list, str);
4875 				compose_convert_header(compose,
4876 						       buf, sizeof(buf), str,
4877 						       strlen("Newsgroups: "),
4878 						       FALSE, charset);
4879 				fprintf(fp, "Newsgroups: %s\n", buf);
4880 			}
4881 			g_free(str);
4882 		}
4883 	}
4884 
4885 	/* Cc */
4886 	if (compose->use_cc) {
4887 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
4888 		PUT_RECIPIENT_HEADER("Cc", entry_str);
4889 	}
4890 
4891 	/* Bcc */
4892 	if (compose->use_bcc) {
4893 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
4894 		PUT_RECIPIENT_HEADER("Bcc", entry_str);
4895 	}
4896 
4897 	if (!is_draft && !compose->to_list && !compose->newsgroup_list)
4898 		return -1;
4899 
4900 	/* Subject */
4901 	entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
4902 	if (*entry_str != '\0' && !IS_IN_CUSTOM_HEADER("Subject")) {
4903 		str = g_strdup(entry_str);
4904 		g_strstrip(str);
4905 		if (*str != '\0') {
4906 			compose_convert_header(compose, buf, sizeof(buf), str,
4907 					       strlen("Subject: "), FALSE,
4908 					       charset);
4909 			fprintf(fp, "Subject: %s\n", buf);
4910 		}
4911 		g_free(str);
4912 	}
4913 
4914 	/* Message-ID */
4915 	if (compose->account->gen_msgid) {
4916 		compose_generate_msgid(compose, buf, sizeof(buf));
4917 		fprintf(fp, "Message-Id: <%s>\n", buf);
4918 		compose->msgid = g_strdup(buf);
4919 	}
4920 
4921 	/* In-Reply-To */
4922 	if (compose->inreplyto && compose->to_list)
4923 		fprintf(fp, "In-Reply-To: <%s>\n", compose->inreplyto);
4924 
4925 	/* References */
4926 	if (compose->references)
4927 		fprintf(fp, "References: %s\n", compose->references);
4928 
4929 	/* Followup-To */
4930 	if (compose->use_followupto && !IS_IN_CUSTOM_HEADER("Followup-To")) {
4931 		entry_str = gtk_entry_get_text
4932 			(GTK_ENTRY(compose->followup_entry));
4933 		if (*entry_str != '\0') {
4934 			str = g_strdup(entry_str);
4935 			g_strstrip(str);
4936 			remove_space(str);
4937 			if (*str != '\0') {
4938 				compose_convert_header(compose,
4939 						       buf, sizeof(buf), str,
4940 						       strlen("Followup-To: "),
4941 						       FALSE, charset);
4942 				fprintf(fp, "Followup-To: %s\n", buf);
4943 			}
4944 			g_free(str);
4945 		}
4946 	}
4947 
4948 	/* Reply-To */
4949 	if (compose->use_replyto && !IS_IN_CUSTOM_HEADER("Reply-To")) {
4950 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
4951 		if (*entry_str != '\0') {
4952 			str = g_strdup(entry_str);
4953 			g_strstrip(str);
4954 			if (*str != '\0') {
4955 				compose_convert_header(compose,
4956 						       buf, sizeof(buf), str,
4957 						       strlen("Reply-To: "),
4958 						       TRUE, charset);
4959 				fprintf(fp, "Reply-To: %s\n", buf);
4960 			}
4961 			g_free(str);
4962 		}
4963 	}
4964 
4965 	/* Disposition-Notification-To */
4966 	if (compose->use_mdn &&
4967 	    !IS_IN_CUSTOM_HEADER("Disposition-Notification-To")) {
4968 		fprintf(fp, "Disposition-Notification-To: %s\n",
4969 			compose->account->address);
4970 	}
4971 
4972 	/* Organization */
4973 	if (compose->account->organization &&
4974 	    !IS_IN_CUSTOM_HEADER("Organization")) {
4975 		compose_convert_header(compose, buf, sizeof(buf),
4976 				       compose->account->organization,
4977 				       strlen("Organization: "), FALSE,
4978 				       charset);
4979 		fprintf(fp, "Organization: %s\n", buf);
4980 	}
4981 
4982 	/* Program version and system info */
4983 	if (prefs_common.user_agent_str) {
4984 		if (compose->to_list && !IS_IN_CUSTOM_HEADER("X-Mailer")) {
4985 			fprintf(fp, "X-Mailer: %s\n",
4986 				prefs_common.user_agent_str);
4987 		}
4988 		if (compose->newsgroup_list && !IS_IN_CUSTOM_HEADER("X-Newsreader")) {
4989 			fprintf(fp, "X-Newsreader: %s\n",
4990 				prefs_common.user_agent_str);
4991 		}
4992 	}
4993 
4994 	/* custom headers */
4995 	if (compose->account->add_customhdr) {
4996 		GSList *cur;
4997 
4998 		for (cur = compose->account->customhdr_list; cur != NULL;
4999 		     cur = cur->next) {
5000 			CustomHeader *chdr = (CustomHeader *)cur->data;
5001 
5002 			if (g_ascii_strcasecmp(chdr->name, "Date") != 0 &&
5003 			    g_ascii_strcasecmp(chdr->name, "From") != 0 &&
5004 			    g_ascii_strcasecmp(chdr->name, "To") != 0 &&
5005 			 /* g_ascii_strcasecmp(chdr->name, "Sender") != 0 && */
5006 			    g_ascii_strcasecmp(chdr->name, "Message-Id") != 0 &&
5007 			    g_ascii_strcasecmp(chdr->name, "In-Reply-To") != 0 &&
5008 			    g_ascii_strcasecmp(chdr->name, "References") != 0 &&
5009 			    g_ascii_strcasecmp(chdr->name, "Mime-Version") != 0 &&
5010 			    g_ascii_strcasecmp(chdr->name, "Content-Type") != 0 &&
5011 			    g_ascii_strcasecmp(chdr->name, "Content-Transfer-Encoding") != 0) {
5012 				compose_convert_header
5013 					(compose, buf, sizeof(buf),
5014 					 chdr->value ? chdr->value : "",
5015 					 strlen(chdr->name) + 2, FALSE,
5016 					 charset);
5017 				fprintf(fp, "%s: %s\n", chdr->name, buf);
5018 			}
5019 		}
5020 	}
5021 
5022 	/* MIME */
5023 	fprintf(fp, "Mime-Version: 1.0\n");
5024 	if (compose->use_attach &&
5025 	    gtk_tree_model_iter_n_children
5026 		(GTK_TREE_MODEL(compose->attach_store), NULL) > 0) {
5027 		compose->boundary = generate_mime_boundary(NULL);
5028 		fprintf(fp,
5029 			"Content-Type: multipart/mixed;\n"
5030 			" boundary=\"%s\"\n", compose->boundary);
5031 	} else {
5032 		fprintf(fp, "Content-Type: text/plain; charset=%s\n",
5033 			body_charset);
5034 #if USE_GPGME
5035 		if (rfc2015_is_available() &&
5036 		    compose->use_signing && !compose->account->clearsign)
5037 			fprintf(fp, "Content-Disposition: inline\n");
5038 #endif
5039 		fprintf(fp, "Content-Transfer-Encoding: %s\n",
5040 			procmime_get_encoding_str(encoding));
5041 	}
5042 
5043 	/* X-Sylpheed headers */
5044 	if (is_draft) {
5045 		fprintf(fp, "X-Sylpheed-Account-Id: %d\n",
5046 			compose->account->account_id);
5047 		if (compose->reply_target)
5048 			fprintf(fp, "X-Sylpheed-Reply: %s\n",
5049 				compose->reply_target);
5050 		else if (compose->forward_targets)
5051 			fprintf(fp, "X-Sylpheed-Forward: %s\n",
5052 				compose->forward_targets);
5053 		fprintf(fp, "X-Sylpheed-Compose-AutoWrap: %s\n",
5054 			compose->autowrap ? "TRUE" : "FALSE");
5055 #if USE_GTKSPELL
5056 		fprintf(fp, "X-Sylpheed-Compose-CheckSpell: %s\n",
5057 			compose->check_spell ? "TRUE" : "FALSE");
5058 		if (compose->spell_lang)
5059 			fprintf(fp, "X-Sylpheed-Compose-SpellLang: %s\n",
5060 				compose->spell_lang);
5061 #endif
5062 #if USE_GPGME
5063 		fprintf(fp, "X-Sylpheed-Compose-UseSigning: %s\n",
5064 			compose->use_signing ? "TRUE" : "FALSE");
5065 		fprintf(fp, "X-Sylpheed-Compose-UseEncryption: %s\n",
5066 			compose->use_encryption ? "TRUE" : "FALSE");
5067 #endif
5068 	}
5069 
5070 	/* separator between header and body */
5071 	fputs("\n", fp);
5072 
5073 	return 0;
5074 }
5075 
compose_redirect_write_headers(Compose * compose,FILE * fp)5076 static gint compose_redirect_write_headers(Compose *compose, FILE *fp)
5077 {
5078 	gchar buf[BUFFSIZE];
5079 	const gchar *entry_str;
5080 	gchar *str;
5081 	const gchar *charset = NULL;
5082 
5083 	g_return_val_if_fail(fp != NULL, -1);
5084 	g_return_val_if_fail(compose->account != NULL, -1);
5085 	g_return_val_if_fail(compose->account->address != NULL, -1);
5086 
5087 	/* Resent-Date */
5088 	get_rfc822_date(buf, sizeof(buf));
5089 	fprintf(fp, "Resent-Date: %s\n", buf);
5090 
5091 	/* Resent-From */
5092 	if (compose->account->name) {
5093 		compose_convert_header
5094 			(compose, buf, sizeof(buf), compose->account->name,
5095 			 strlen("Resent-From: "), TRUE, NULL);
5096 		fprintf(fp, "Resent-From: %s <%s>\n",
5097 			buf, compose->account->address);
5098 	} else
5099 		fprintf(fp, "Resent-From: %s\n", compose->account->address);
5100 
5101 	slist_free_strings(compose->to_list);
5102 	g_slist_free(compose->to_list);
5103 	compose->to_list = NULL;
5104 
5105 	/* Resent-To */
5106 	if (compose->use_to) {
5107 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->to_entry));
5108 		PUT_RECIPIENT_HEADER("Resent-To", entry_str);
5109 	}
5110 	if (compose->use_cc) {
5111 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->cc_entry));
5112 		PUT_RECIPIENT_HEADER("Resent-Cc", entry_str);
5113 	}
5114 	if (compose->use_bcc) {
5115 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->bcc_entry));
5116 		PUT_RECIPIENT_HEADER("Bcc", entry_str);
5117 	}
5118 
5119 	slist_free_strings(compose->newsgroup_list);
5120 	g_slist_free(compose->newsgroup_list);
5121 	compose->newsgroup_list = NULL;
5122 
5123 	/* Newsgroups */
5124 	if (compose->use_newsgroups) {
5125 		entry_str = gtk_entry_get_text
5126 			(GTK_ENTRY(compose->newsgroups_entry));
5127 		if (*entry_str != '\0') {
5128 			str = g_strdup(entry_str);
5129 			g_strstrip(str);
5130 			remove_space(str);
5131 			if (*str != '\0') {
5132 				compose->newsgroup_list =
5133 					newsgroup_list_append
5134 						(compose->newsgroup_list, str);
5135 				compose_convert_header(compose,
5136 						       buf, sizeof(buf), str,
5137 						       strlen("Newsgroups: "),
5138 						       FALSE, NULL);
5139 				fprintf(fp, "Newsgroups: %s\n", buf);
5140 			}
5141 			g_free(str);
5142 		}
5143 	}
5144 
5145 	if (!compose->to_list && !compose->newsgroup_list)
5146 		return -1;
5147 
5148 	/* Subject */
5149 	entry_str = gtk_entry_get_text(GTK_ENTRY(compose->subject_entry));
5150 	if (*entry_str != '\0') {
5151 		str = g_strdup(entry_str);
5152 		g_strstrip(str);
5153 		if (*str != '\0') {
5154 			compose_convert_header(compose, buf, sizeof(buf), str,
5155 					       strlen("Subject: "), FALSE,
5156 					       NULL);
5157 			fprintf(fp, "Subject: %s\n", buf);
5158 		}
5159 		g_free(str);
5160 	}
5161 
5162 	/* Resent-Message-Id */
5163 	if (compose->account->gen_msgid) {
5164 		compose_generate_msgid(compose, buf, sizeof(buf));
5165 		fprintf(fp, "Resent-Message-Id: <%s>\n", buf);
5166 		compose->msgid = g_strdup(buf);
5167 	}
5168 
5169 	/* Followup-To */
5170 	if (compose->use_followupto) {
5171 		entry_str = gtk_entry_get_text
5172 			(GTK_ENTRY(compose->followup_entry));
5173 		if (*entry_str != '\0') {
5174 			str = g_strdup(entry_str);
5175 			g_strstrip(str);
5176 			remove_space(str);
5177 			if (*str != '\0') {
5178 				compose_convert_header(compose,
5179 						       buf, sizeof(buf), str,
5180 						       strlen("Followup-To: "),
5181 						       FALSE, NULL);
5182 				fprintf(fp, "Followup-To: %s\n", buf);
5183 			}
5184 			g_free(str);
5185 		}
5186 	}
5187 
5188 	/* Resent-Reply-To */
5189 	if (compose->use_replyto) {
5190 		entry_str = gtk_entry_get_text(GTK_ENTRY(compose->reply_entry));
5191 		if (*entry_str != '\0') {
5192 			str = g_strdup(entry_str);
5193 			g_strstrip(str);
5194 			if (*str != '\0') {
5195 				compose_convert_header
5196 					(compose, buf, sizeof(buf), str,
5197 					 strlen("Resent-Reply-To: "), TRUE,
5198 					 NULL);
5199 				fprintf(fp, "Resent-Reply-To: %s\n", buf);
5200 			}
5201 			g_free(str);
5202 		}
5203 	}
5204 
5205 	fputs("\n", fp);
5206 
5207 	return 0;
5208 }
5209 
5210 #undef IS_IN_CUSTOM_HEADER
5211 
compose_convert_header(Compose * compose,gchar * dest,gint len,const gchar * src,gint header_len,gboolean addr_field,const gchar * encoding)5212 static void compose_convert_header(Compose *compose, gchar *dest, gint len,
5213 				   const gchar *src, gint header_len,
5214 				   gboolean addr_field, const gchar *encoding)
5215 {
5216 	gchar *src_;
5217 
5218 	g_return_if_fail(src != NULL);
5219 	g_return_if_fail(dest != NULL);
5220 
5221 	if (len < 1) return;
5222 
5223 	if (addr_field)
5224 		src_ = normalize_address_field(src);
5225 	else
5226 		src_ = g_strdup(src);
5227 	g_strchomp(src_);
5228 	if (!encoding)
5229 		encoding = conv_get_charset_str(compose->out_encoding);
5230 
5231 	conv_encode_header(dest, len, src_, header_len, addr_field, encoding);
5232 
5233 	g_free(src_);
5234 }
5235 
compose_convert_filename(Compose * compose,const gchar * src,const gchar * param_name,const gchar * encoding)5236 static gchar *compose_convert_filename(Compose *compose, const gchar *src,
5237 				       const gchar *param_name,
5238 				       const gchar *encoding)
5239 {
5240 	gchar *str;
5241 
5242 	g_return_val_if_fail(src != NULL, NULL);
5243 
5244 	if (!encoding)
5245 		encoding = conv_get_charset_str(compose->out_encoding);
5246 
5247 	str = conv_encode_filename(src, param_name, encoding);
5248 
5249 	return str;
5250 }
5251 
compose_generate_msgid(Compose * compose,gchar * buf,gint len)5252 static void compose_generate_msgid(Compose *compose, gchar *buf, gint len)
5253 {
5254 	struct tm *lt;
5255 	time_t t;
5256 	const gchar *addr, *p;
5257 	gchar *addr_left;
5258 	gchar *addr_right;
5259 	gchar hash_str[64];
5260 	SMD5 *md5;
5261 	gchar *md5str;
5262 
5263 	t = time(NULL);
5264 	lt = localtime(&t);
5265 
5266 	if (compose->account && compose->account->address &&
5267 	    *compose->account->address) {
5268 		addr = compose->account->address;
5269 		if ((p = strchr(addr, '@'))) {
5270 			addr_left = g_strndup(addr, p - addr);
5271 			addr_right = g_strdup(p + 1);
5272 		} else {
5273 			addr_left = g_strdup(addr);
5274 			addr_right = g_strdup(get_domain_name());
5275 		}
5276 	} else {
5277 		addr_left = g_strdup(g_get_user_name());
5278 		addr_right = g_strdup(get_domain_name());
5279 	}
5280 
5281 	g_snprintf(hash_str, sizeof(hash_str), "%08x%s",
5282 		   g_random_int(), addr_left);
5283 	md5 = s_gnet_md5_new((guchar *)hash_str, strlen(hash_str));
5284 	md5str = s_gnet_md5_get_string(md5);
5285 
5286 	g_snprintf(buf, len, "%04d%02d%02d%02d%02d%02d.%.24s@%s",
5287 		   lt->tm_year + 1900, lt->tm_mon + 1,
5288 		   lt->tm_mday, lt->tm_hour,
5289 		   lt->tm_min, lt->tm_sec,
5290 		   md5str, addr_right);
5291 
5292 	g_free(md5str);
5293 	s_gnet_md5_delete(md5);
5294 	g_free(addr_right);
5295 	g_free(addr_left);
5296 
5297 	debug_print("generated Message-ID: %s\n", buf);
5298 }
5299 
compose_add_entry_field(GtkWidget * table,GtkWidget ** hbox,GtkWidget ** entry,gint * count,const gchar * label_str,gboolean is_addr_entry)5300 static void compose_add_entry_field(GtkWidget *table, GtkWidget **hbox,
5301 				    GtkWidget **entry, gint *count,
5302 				    const gchar *label_str,
5303 				    gboolean is_addr_entry)
5304 {
5305 	GtkWidget *label;
5306 
5307 	if (GTK_TABLE(table)->nrows < (*count) + 1)
5308 		gtk_table_resize(GTK_TABLE(table), (*count) + 1, 2);
5309 
5310 	*hbox = gtk_hbox_new(FALSE, 0);
5311 	label = gtk_label_new
5312 		(prefs_common.trans_hdr ? gettext(label_str) : label_str);
5313 	gtk_box_pack_end(GTK_BOX(*hbox), label, FALSE, FALSE, 0);
5314 	gtk_table_attach(GTK_TABLE(table), *hbox, 0, 1, *count, (*count) + 1,
5315 			 GTK_FILL, 0, 2, 0);
5316 	*entry = gtk_entry_new();
5317 	gtk_entry_set_max_length(GTK_ENTRY(*entry), MAX_ENTRY_LENGTH);
5318 	gtk_table_attach_defaults
5319 		(GTK_TABLE(table), *entry, 1, 2, *count, (*count) + 1);
5320 	if (GTK_TABLE(table)->nrows > (*count) + 1)
5321 		gtk_table_set_row_spacing(GTK_TABLE(table), *count, 4);
5322 
5323 	if (is_addr_entry && prefs_common.enable_address_completion)
5324 		address_completion_register_entry(GTK_ENTRY(*entry));
5325 
5326 	(*count)++;
5327 }
5328 
compose_create(PrefsAccount * account,ComposeMode mode)5329 static Compose *compose_create(PrefsAccount *account, ComposeMode mode)
5330 {
5331 	Compose   *compose;
5332 	GtkWidget *window;
5333 	GtkWidget *vbox;
5334 	GtkWidget *menubar;
5335 	GtkWidget *toolbar;
5336 
5337 	GtkWidget *vbox2;
5338 
5339 	GtkWidget *table_vbox;
5340 	GtkWidget *table;
5341 	GtkWidget *hbox;
5342 	GtkWidget *label;
5343 	GtkWidget *from_optmenu_hbox;
5344 	GtkWidget *sig_combo;
5345 	GtkWidget *to_entry;
5346 	GtkWidget *to_hbox;
5347 	GtkWidget *newsgroups_entry;
5348 	GtkWidget *newsgroups_hbox;
5349 	GtkWidget *subject_entry;
5350 	GtkWidget *cc_entry;
5351 	GtkWidget *cc_hbox;
5352 	GtkWidget *bcc_entry;
5353 	GtkWidget *bcc_hbox;
5354 	GtkWidget *reply_entry;
5355 	GtkWidget *reply_hbox;
5356 	GtkWidget *followup_entry;
5357 	GtkWidget *followup_hbox;
5358 
5359 #if USE_GPGME
5360 	GtkWidget *misc_hbox;
5361 	GtkWidget *signing_chkbtn;
5362 	GtkWidget *encrypt_chkbtn;
5363 #endif /* USE_GPGME */
5364 #if 0
5365 	GtkWidget *attach_img;
5366 	GtkWidget *attach_toggle;
5367 #endif
5368 
5369 	GtkWidget *paned;
5370 
5371 	GtkWidget *attach_scrwin;
5372 	GtkWidget *attach_treeview;
5373 	GtkListStore *store;
5374 	GtkTreeSelection *selection;
5375 	GtkTreeViewColumn *column;
5376 	GtkCellRenderer *renderer;
5377 
5378 	GtkWidget *edit_vbox;
5379 	GtkWidget *ruler_hbox;
5380 	GtkWidget *ruler;
5381 	GtkWidget *scrolledwin;
5382 	GtkWidget *text;
5383 
5384 	GtkTextBuffer *buffer;
5385 	GtkClipboard *clipboard;
5386 	GtkTextTag *sig_tag;
5387 
5388 #if USE_GTKSPELL
5389 	GtkWidget *spell_menu;
5390 #endif /* USE_GTKSPELL */
5391 
5392 	UndoMain *undostruct;
5393 
5394 	guint n_menu_entries;
5395 	GdkColormap *cmap;
5396 	GdkColor color[1];
5397 	gboolean success[1];
5398 	GtkWidget *popupmenu;
5399 	GtkItemFactory *popupfactory;
5400 	GtkItemFactory *ifactory;
5401 	GtkWidget *tmpl_menu;
5402 	gint n_entries;
5403 	gint count = 0;
5404 
5405 #ifndef G_OS_WIN32
5406 	static GdkGeometry geometry;
5407 #endif
5408 
5409 	g_return_val_if_fail(account != NULL, NULL);
5410 
5411 	debug_print(_("Creating compose window...\n"));
5412 	compose = g_new0(Compose, 1);
5413 
5414 	compose->account = account;
5415 
5416 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
5417 	gtk_window_set_wmclass(GTK_WINDOW(window), "compose", "Sylpheed");
5418 	gtk_window_set_policy(GTK_WINDOW(window), TRUE, TRUE, FALSE);
5419 	gtk_widget_set_size_request(window, -1, prefs_common.compose_height);
5420 	gtkut_window_move(GTK_WINDOW(window), prefs_common.compose_x,
5421 			  prefs_common.compose_y);
5422 
5423 #ifndef G_OS_WIN32
5424 	if (!geometry.max_width) {
5425 		geometry.max_width = gdk_screen_width();
5426 		geometry.max_height = gdk_screen_height();
5427 	}
5428 	gtk_window_set_geometry_hints(GTK_WINDOW(window), NULL,
5429 				      &geometry, GDK_HINT_MAX_SIZE);
5430 #endif
5431 
5432 	g_signal_connect(G_OBJECT(window), "delete_event",
5433 			 G_CALLBACK(compose_delete_cb), compose);
5434 	MANAGE_WINDOW_SIGNALS_CONNECT(window);
5435 
5436 	vbox = gtk_vbox_new(FALSE, 0);
5437 	gtk_container_add(GTK_CONTAINER(window), vbox);
5438 
5439 	n_menu_entries = sizeof(compose_entries) / sizeof(compose_entries[0]);
5440 	menubar = menubar_create(window, compose_entries,
5441 				 n_menu_entries, "<Compose>", compose);
5442 	gtk_widget_set_size_request(menubar, 300, -1);
5443 	gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, TRUE, 0);
5444 
5445 	compose->toolbar_tip = gtk_tooltips_new();
5446 	g_object_ref_sink(compose->toolbar_tip);
5447 	toolbar = compose_toolbar_create(compose);
5448 	gtk_widget_set_size_request(toolbar, 300, -1);
5449 	gtk_box_pack_start(GTK_BOX(vbox), toolbar, FALSE, FALSE, 0);
5450 
5451 	vbox2 = gtk_vbox_new(FALSE, 2);
5452 	gtk_box_pack_start(GTK_BOX(vbox), vbox2, TRUE, TRUE, 0);
5453 	gtk_container_set_border_width(GTK_CONTAINER(vbox2), BORDER_WIDTH);
5454 
5455 	table_vbox = gtk_vbox_new(FALSE, 0);
5456 	gtk_box_pack_start(GTK_BOX(vbox2), table_vbox, FALSE, TRUE, 0);
5457 	gtk_container_set_border_width(GTK_CONTAINER(table_vbox), BORDER_WIDTH);
5458 
5459 	table = gtk_table_new(8, 2, FALSE);
5460 	gtk_box_pack_start(GTK_BOX(table_vbox), table, FALSE, TRUE, 0);
5461 
5462 	/* option menu for selecting accounts */
5463 	hbox = gtk_hbox_new(FALSE, 0);
5464 	label = gtk_label_new(prefs_common.trans_hdr ? _("From:") : "From:");
5465 	gtk_box_pack_end(GTK_BOX(hbox), label, FALSE, FALSE, 0);
5466 	gtk_table_attach(GTK_TABLE(table), hbox, 0, 1, count, count + 1,
5467 			 GTK_FILL, 0, 2, 0);
5468 
5469 	from_optmenu_hbox = gtk_hbox_new(FALSE, 0);
5470 	gtk_table_attach_defaults(GTK_TABLE(table), from_optmenu_hbox,
5471 				  1, 2, count, count + 1);
5472 	//gtk_table_attach(GTK_TABLE(table), from_optmenu_hbox,
5473 			 //1, 2, count, count + 1, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
5474 	gtk_widget_set_size_request(from_optmenu_hbox, 300, -1);
5475 	gtk_table_set_row_spacing(GTK_TABLE(table), 0, 4);
5476 	count++;
5477 	compose_account_option_menu_create(compose, from_optmenu_hbox);
5478 	sig_combo = compose_signature_menu_create(compose, from_optmenu_hbox);
5479 
5480 	/* header labels and entries */
5481 	compose_add_entry_field(table, &to_hbox, &to_entry, &count,
5482 				"To:", TRUE);
5483 	compose_add_entry_field(table, &newsgroups_hbox, &newsgroups_entry,
5484 				&count, "Newsgroups:", FALSE);
5485 	compose_add_entry_field(table, &cc_hbox, &cc_entry, &count,
5486 				"Cc:", TRUE);
5487 	compose_add_entry_field(table, &bcc_hbox, &bcc_entry, &count,
5488 				"Bcc:", TRUE);
5489 	compose_add_entry_field(table, &reply_hbox, &reply_entry, &count,
5490 				"Reply-To:", TRUE);
5491 	compose_add_entry_field(table, &followup_hbox, &followup_entry, &count,
5492 				"Followup-To:", FALSE);
5493 	compose_add_entry_field(table, &hbox, &subject_entry, &count,
5494 				"Subject:", FALSE);
5495 
5496 	gtk_table_set_col_spacings(GTK_TABLE(table), 4);
5497 
5498 #ifndef __APPLE__
5499 	g_signal_connect(G_OBJECT(to_entry), "activate",
5500 			 G_CALLBACK(to_activated), compose);
5501 	g_signal_connect(G_OBJECT(newsgroups_entry), "activate",
5502 			 G_CALLBACK(newsgroups_activated), compose);
5503 	g_signal_connect(G_OBJECT(cc_entry), "activate",
5504 			 G_CALLBACK(cc_activated), compose);
5505 	g_signal_connect(G_OBJECT(bcc_entry), "activate",
5506 			 G_CALLBACK(bcc_activated), compose);
5507 	g_signal_connect(G_OBJECT(reply_entry), "activate",
5508 			 G_CALLBACK(replyto_activated), compose);
5509 	g_signal_connect(G_OBJECT(followup_entry), "activate",
5510 			 G_CALLBACK(followupto_activated), compose);
5511 	g_signal_connect(G_OBJECT(subject_entry), "activate",
5512 			 G_CALLBACK(subject_activated), compose);
5513 #endif
5514 
5515 	g_signal_connect(G_OBJECT(to_entry), "grab_focus",
5516 			 G_CALLBACK(compose_grab_focus_cb), compose);
5517 	g_signal_connect(G_OBJECT(newsgroups_entry), "grab_focus",
5518 			 G_CALLBACK(compose_grab_focus_cb), compose);
5519 	g_signal_connect(G_OBJECT(cc_entry), "grab_focus",
5520 			 G_CALLBACK(compose_grab_focus_cb), compose);
5521 	g_signal_connect(G_OBJECT(bcc_entry), "grab_focus",
5522 			 G_CALLBACK(compose_grab_focus_cb), compose);
5523 	g_signal_connect(G_OBJECT(reply_entry), "grab_focus",
5524 			 G_CALLBACK(compose_grab_focus_cb), compose);
5525 	g_signal_connect(G_OBJECT(followup_entry), "grab_focus",
5526 			 G_CALLBACK(compose_grab_focus_cb), compose);
5527 	g_signal_connect(G_OBJECT(subject_entry), "grab_focus",
5528 			 G_CALLBACK(compose_grab_focus_cb), compose);
5529 
5530 #if 0
5531 	attach_img = stock_pixbuf_widget(window, STOCK_PIXMAP_CLIP);
5532 	attach_toggle = gtk_toggle_button_new();
5533 	GTK_WIDGET_UNSET_FLAGS(attach_toggle, GTK_CAN_FOCUS);
5534 	gtk_container_add(GTK_CONTAINER(attach_toggle), attach_img);
5535 	gtk_box_pack_start(GTK_BOX(misc_hbox), attach_toggle, FALSE, FALSE, 8);
5536 	g_signal_connect(G_OBJECT(attach_toggle), "toggled",
5537 			 G_CALLBACK(compose_attach_toggled), compose);
5538 #endif
5539 
5540 #if USE_GPGME
5541 	misc_hbox = gtk_hbox_new(FALSE, 0);
5542 	gtk_box_pack_start(GTK_BOX(vbox2), misc_hbox, FALSE, FALSE, 0);
5543 
5544 	signing_chkbtn = gtk_check_button_new_with_label(_("PGP Sign"));
5545 	GTK_WIDGET_UNSET_FLAGS(signing_chkbtn, GTK_CAN_FOCUS);
5546 	gtk_box_pack_start(GTK_BOX(misc_hbox), signing_chkbtn, FALSE, FALSE, 8);
5547 	encrypt_chkbtn = gtk_check_button_new_with_label(_("PGP Encrypt"));
5548 	GTK_WIDGET_UNSET_FLAGS(encrypt_chkbtn, GTK_CAN_FOCUS);
5549 	gtk_box_pack_start(GTK_BOX(misc_hbox), encrypt_chkbtn, FALSE, FALSE, 8);
5550 
5551 	g_signal_connect(G_OBJECT(signing_chkbtn), "toggled",
5552 			 G_CALLBACK(compose_signing_toggled), compose);
5553 	g_signal_connect(G_OBJECT(encrypt_chkbtn), "toggled",
5554 			 G_CALLBACK(compose_encrypt_toggled), compose);
5555 #endif /* USE_GPGME */
5556 
5557 	/* attachment list */
5558 	attach_scrwin = gtk_scrolled_window_new(NULL, NULL);
5559 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(attach_scrwin),
5560 				       GTK_POLICY_AUTOMATIC,
5561 				       GTK_POLICY_ALWAYS);
5562 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(attach_scrwin),
5563 					    GTK_SHADOW_IN);
5564 	gtk_widget_set_size_request(attach_scrwin, -1, 80 * gtkut_get_dpi_multiplier());
5565 
5566 	store = gtk_list_store_new(N_ATTACH_COLS, G_TYPE_STRING, G_TYPE_STRING,
5567 				   G_TYPE_STRING, G_TYPE_POINTER);
5568 
5569 	attach_treeview = gtk_tree_view_new_with_model(GTK_TREE_MODEL(store));
5570 	g_object_unref(G_OBJECT(store));
5571 	gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(attach_treeview), TRUE);
5572 	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(attach_treeview), TRUE);
5573 	gtk_tree_view_set_search_column(GTK_TREE_VIEW(attach_treeview),
5574 					COL_NAME);
5575 	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(attach_treeview), FALSE);
5576 
5577 	selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(attach_treeview));
5578 	gtk_tree_selection_set_mode(selection, GTK_SELECTION_MULTIPLE);
5579 
5580 	gtk_container_add(GTK_CONTAINER(attach_scrwin), attach_treeview);
5581 
5582 	renderer = gtk_cell_renderer_text_new();
5583 	g_object_set(renderer, "ypad", 0, NULL);
5584 	column = gtk_tree_view_column_new_with_attributes
5585 		(_("Data type"), renderer, "text", COL_MIMETYPE, NULL);
5586 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5587 	gtk_tree_view_column_set_fixed_width(column, 240);
5588 	gtk_tree_view_column_set_resizable(column, TRUE);
5589 	gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5590 
5591 	renderer = gtk_cell_renderer_text_new();
5592 	g_object_set(renderer, "xalign", 1.0, "ypad", 0, NULL);
5593 	column = gtk_tree_view_column_new_with_attributes
5594 		(_("Size"), renderer, "text", COL_SIZE, NULL);
5595 	gtk_tree_view_column_set_alignment(column, 1.0);
5596 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5597 	gtk_tree_view_column_set_fixed_width(column, 64);
5598 	gtk_tree_view_column_set_resizable(column, TRUE);
5599 	gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5600 
5601 	renderer = gtk_cell_renderer_text_new();
5602 	g_object_set(renderer, "ypad", 0, NULL);
5603 	column = gtk_tree_view_column_new_with_attributes
5604 		(_("Name"), renderer, "text", COL_NAME, NULL);
5605 	gtk_tree_view_column_set_sizing(column, GTK_TREE_VIEW_COLUMN_FIXED);
5606 	gtk_tree_view_column_set_resizable(column, TRUE);
5607 	gtk_tree_view_append_column(GTK_TREE_VIEW(attach_treeview), column);
5608 
5609 	g_signal_connect(G_OBJECT(selection), "changed",
5610 			 G_CALLBACK(attach_selection_changed), compose);
5611 	g_signal_connect(G_OBJECT(attach_treeview), "button_press_event",
5612 			 G_CALLBACK(attach_button_pressed), compose);
5613 	g_signal_connect(G_OBJECT(attach_treeview), "key_press_event",
5614 			 G_CALLBACK(attach_key_pressed), compose);
5615 
5616 	/* drag and drop */
5617 	gtk_drag_dest_set(window,
5618 			  GTK_DEST_DEFAULT_ALL, compose_drag_types,
5619 			  N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE);
5620 	g_signal_connect(G_OBJECT(window), "drag-data-received",
5621 			 G_CALLBACK(compose_attach_drag_received_cb),
5622 			 compose);
5623 
5624 	/* pane between attach tree view and text */
5625 	paned = gtk_vpaned_new();
5626 	gtk_paned_add1(GTK_PANED(paned), attach_scrwin);
5627 	gtk_widget_ref(paned);
5628 	gtk_widget_show_all(paned);
5629 
5630 	edit_vbox = gtk_vbox_new(FALSE, 0);
5631 	gtk_box_pack_start(GTK_BOX(vbox2), edit_vbox, TRUE, TRUE, 0);
5632 
5633 	/* ruler */
5634 	ruler_hbox = gtk_hbox_new(FALSE, 0);
5635 	gtk_box_pack_start(GTK_BOX(edit_vbox), ruler_hbox, FALSE, FALSE, 0);
5636 
5637 	ruler = gtk_shruler_new();
5638 	gtk_ruler_set_range(GTK_RULER(ruler), 0.0, 100.0, 1.0, 100.0);
5639 	gtk_box_pack_start(GTK_BOX(ruler_hbox), ruler, TRUE, TRUE, 0);
5640 
5641 	/* text widget */
5642 	scrolledwin = gtk_scrolled_window_new(NULL, NULL);
5643 	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolledwin),
5644 				       GTK_POLICY_AUTOMATIC,
5645 				       GTK_POLICY_ALWAYS);
5646 	gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolledwin),
5647 					    GTK_SHADOW_IN);
5648 	gtk_box_pack_start(GTK_BOX(edit_vbox), scrolledwin, TRUE, TRUE, 0);
5649 	gtk_widget_set_size_request(scrolledwin, prefs_common.compose_width,
5650 				    -1);
5651 
5652 	text = gtk_text_view_new();
5653 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text));
5654 	gtk_text_view_set_editable(GTK_TEXT_VIEW(text), TRUE);
5655 	gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(text), GTK_WRAP_WORD);
5656 	gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN);
5657 	gtk_text_view_set_right_margin(GTK_TEXT_VIEW(text), TEXTVIEW_MARGIN);
5658 	clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
5659 	gtk_text_buffer_add_selection_clipboard(buffer, clipboard);
5660 	sig_tag = gtk_text_buffer_create_tag(buffer, "signature", NULL);
5661 	gtk_container_add(GTK_CONTAINER(scrolledwin), text);
5662 
5663 	gtk_shruler_set_start_pos(GTK_SHRULER(ruler),
5664 				  text->style->xthickness + TEXTVIEW_MARGIN);
5665 
5666 	g_signal_connect(G_OBJECT(text), "grab_focus",
5667 			 G_CALLBACK(compose_grab_focus_cb), compose);
5668 	g_signal_connect(G_OBJECT(buffer), "insert_text",
5669 			 G_CALLBACK(text_inserted), compose);
5670 	g_signal_connect_after(G_OBJECT(text), "size_allocate",
5671 			       G_CALLBACK(compose_edit_size_alloc),
5672 			       ruler);
5673 
5674 	/* drag and drop */
5675 	gtk_drag_dest_set(text, GTK_DEST_DEFAULT_ALL, compose_drag_types,
5676 			  N_DRAG_TYPES, GDK_ACTION_COPY | GDK_ACTION_MOVE);
5677 	g_signal_connect(G_OBJECT(text), "drag-data-received",
5678 			 G_CALLBACK(compose_insert_drag_received_cb),
5679 			 compose);
5680 
5681 	gtk_widget_show_all(vbox);
5682 
5683 	if (prefs_common.textfont) {
5684 		PangoFontDescription *font_desc;
5685 
5686 		font_desc = pango_font_description_from_string
5687 			(prefs_common.textfont);
5688 		if (font_desc) {
5689 			gtk_widget_modify_font(text, font_desc);
5690 			pango_font_description_free(font_desc);
5691 		}
5692 	}
5693 
5694 	gtk_text_view_set_pixels_above_lines
5695 		(GTK_TEXT_VIEW(text),
5696 		 prefs_common.line_space - prefs_common.line_space / 2);
5697 	gtk_text_view_set_pixels_below_lines
5698 		(GTK_TEXT_VIEW(text), prefs_common.line_space / 2);
5699 	gtk_text_view_set_pixels_inside_wrap
5700 		(GTK_TEXT_VIEW(text), prefs_common.line_space);
5701 
5702 	n_entries = sizeof(compose_popup_entries) /
5703 		sizeof(compose_popup_entries[0]);
5704 	popupmenu = menu_create_items(compose_popup_entries, n_entries,
5705 				      "<Compose>", &popupfactory,
5706 				      compose);
5707 
5708 	ifactory = gtk_item_factory_from_widget(menubar);
5709 	menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
5710 	menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
5711 
5712 	tmpl_menu = gtk_item_factory_get_item(ifactory, "/Tools/Template");
5713 
5714 #if USE_GTKSPELL
5715 	spell_menu = gtk_item_factory_get_item
5716 		(ifactory, "/Tools/Set spell language");
5717 #endif
5718 
5719 	gtk_widget_hide(bcc_hbox);
5720 	gtk_widget_hide(bcc_entry);
5721 	gtk_widget_hide(reply_hbox);
5722 	gtk_widget_hide(reply_entry);
5723 	gtk_widget_hide(followup_hbox);
5724 	gtk_widget_hide(followup_entry);
5725 	gtk_widget_hide(ruler_hbox);
5726 	gtk_table_set_row_spacing(GTK_TABLE(table), 4, 0);
5727 	gtk_table_set_row_spacing(GTK_TABLE(table), 5, 0);
5728 	gtk_table_set_row_spacing(GTK_TABLE(table), 6, 0);
5729 
5730 	if (account->protocol == A_NNTP) {
5731 		gtk_widget_hide(to_hbox);
5732 		gtk_widget_hide(to_entry);
5733 		gtk_widget_hide(cc_hbox);
5734 		gtk_widget_hide(cc_entry);
5735 		gtk_table_set_row_spacing(GTK_TABLE(table), 1, 0);
5736 		gtk_table_set_row_spacing(GTK_TABLE(table), 3, 0);
5737 	} else {
5738 		gtk_widget_hide(newsgroups_hbox);
5739 		gtk_widget_hide(newsgroups_entry);
5740 		gtk_table_set_row_spacing(GTK_TABLE(table), 2, 0);
5741 	}
5742 
5743 #if USE_GPGME
5744 	if (!rfc2015_is_available())
5745 		gtk_widget_hide(misc_hbox);
5746 #endif
5747 
5748 	undostruct = undo_init(text);
5749 	undo_set_change_state_func(undostruct, &compose_undo_state_changed,
5750 				   menubar);
5751 
5752 	address_completion_start(window);
5753 
5754 	compose->window        = window;
5755 	compose->vbox	       = vbox;
5756 	compose->menubar       = menubar;
5757 	compose->toolbar       = toolbar;
5758 
5759 	compose->vbox2	       = vbox2;
5760 
5761 	compose->table_vbox       = table_vbox;
5762 	compose->table	          = table;
5763 	compose->to_hbox          = to_hbox;
5764 	compose->to_entry         = to_entry;
5765 	compose->newsgroups_hbox  = newsgroups_hbox;
5766 	compose->newsgroups_entry = newsgroups_entry;
5767 	compose->subject_entry    = subject_entry;
5768 	compose->cc_hbox          = cc_hbox;
5769 	compose->cc_entry         = cc_entry;
5770 	compose->bcc_hbox         = bcc_hbox;
5771 	compose->bcc_entry        = bcc_entry;
5772 	compose->reply_hbox       = reply_hbox;
5773 	compose->reply_entry      = reply_entry;
5774 	compose->followup_hbox    = followup_hbox;
5775 	compose->followup_entry   = followup_entry;
5776 
5777 	compose->sig_combo        = sig_combo;
5778 
5779 	/* compose->attach_toggle = attach_toggle; */
5780 #if USE_GPGME
5781 	compose->misc_hbox      = misc_hbox;
5782 	compose->signing_chkbtn = signing_chkbtn;
5783 	compose->encrypt_chkbtn = encrypt_chkbtn;
5784 #endif /* USE_GPGME */
5785 
5786 	compose->paned = paned;
5787 
5788 	compose->attach_scrwin   = attach_scrwin;
5789 	compose->attach_treeview = attach_treeview;
5790 	compose->attach_store    = store;
5791 
5792 	compose->edit_vbox     = edit_vbox;
5793 	compose->ruler_hbox    = ruler_hbox;
5794 	compose->ruler         = ruler;
5795 	compose->scrolledwin   = scrolledwin;
5796 	compose->text	       = text;
5797 
5798 #ifdef USE_GTKSPELL
5799 	compose->check_spell = prefs_common.check_spell;
5800 	compose->spell_lang  = g_strdup(prefs_common.spell_lang);
5801 	compose->spell_menu  = spell_menu;
5802 	compose->dict_list   = NULL;
5803 #endif /* USE_GTKSPELL */
5804 
5805 	compose->focused_editable = NULL;
5806 
5807 	compose->popupmenu    = popupmenu;
5808 	compose->popupfactory = popupfactory;
5809 
5810 	compose->tmpl_menu = tmpl_menu;
5811 
5812 	compose->mode = mode;
5813 
5814 	compose->targetinfo      = NULL;
5815 	compose->reply_target    = NULL;
5816 	compose->forward_targets = NULL;
5817 
5818 	compose->replyto     = NULL;
5819 	compose->cc	     = NULL;
5820 	compose->bcc	     = NULL;
5821 	compose->followup_to = NULL;
5822 
5823 	compose->ml_post     = NULL;
5824 
5825 	compose->inreplyto   = NULL;
5826 	compose->references  = NULL;
5827 	compose->msgid       = NULL;
5828 	compose->boundary    = NULL;
5829 
5830 	compose->autowrap       = prefs_common.autowrap;
5831 
5832 	compose->use_to         = FALSE;
5833 	compose->use_cc         = FALSE;
5834 	compose->use_bcc        = FALSE;
5835 	compose->use_replyto    = FALSE;
5836 	compose->use_newsgroups = FALSE;
5837 	compose->use_followupto = FALSE;
5838 	compose->use_attach     = FALSE;
5839 
5840 	compose->out_encoding   = C_AUTO;
5841 
5842 	compose->use_mdn        = FALSE;
5843 
5844 #if USE_GPGME
5845 	compose->use_signing    = FALSE;
5846 	compose->use_encryption = FALSE;
5847 #endif /* USE_GPGME */
5848 
5849 	compose->modified = FALSE;
5850 
5851 	compose->to_list        = NULL;
5852 	compose->newsgroup_list = NULL;
5853 
5854 	compose->undostruct = undostruct;
5855 
5856 	compose->sig_tag = sig_tag;
5857 
5858 	compose->exteditor_file = NULL;
5859 	compose->exteditor_pid  = 0;
5860 	compose->exteditor_tag  = 0;
5861 
5862 	compose->autosave_tag = 0;
5863 
5864 	compose->window_maximized = prefs_common.compose_maximized;
5865 
5866 	compose->block_modified = FALSE;
5867 
5868 	compose_set_toolbar_button_visibility(compose);
5869 
5870 	compose_select_account(compose, account, TRUE);
5871 
5872 	if (prefs_common.compose_maximized)
5873 		gtk_window_maximize(GTK_WINDOW(window));
5874 
5875 	g_signal_connect(G_OBJECT(window), "window_state_event",
5876 			 G_CALLBACK(compose_window_state_cb), compose);
5877 
5878 	menu_set_active(ifactory, "/Edit/Auto wrapping", prefs_common.autowrap);
5879 	menu_set_active(ifactory, "/View/Ruler", prefs_common.show_ruler);
5880 
5881 	if (mode == COMPOSE_REDIRECT) {
5882 		menu_set_sensitive(ifactory, "/File/Save to draft folder", FALSE);
5883 		menu_set_sensitive(ifactory, "/File/Save and keep editing", FALSE);
5884 		menu_set_sensitive(ifactory, "/File/Attach file", FALSE);
5885 		menu_set_sensitive(ifactory, "/File/Insert file", FALSE);
5886 		menu_set_sensitive(ifactory, "/File/Insert signature", FALSE);
5887 		menu_set_sensitive(ifactory, "/Edit/Cut", FALSE);
5888 		menu_set_sensitive(ifactory, "/Edit/Paste", FALSE);
5889 		menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", FALSE);
5890 		menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", FALSE);
5891 		menu_set_sensitive(ifactory, "/Edit/Auto wrapping", FALSE);
5892 		menu_set_sensitive(ifactory, "/View/Attachment", FALSE);
5893 		menu_set_sensitive(ifactory, "/Tools/Template", FALSE);
5894 #ifndef G_OS_WIN32
5895 		menu_set_sensitive(ifactory, "/Tools/Actions", FALSE);
5896 #endif
5897 		menu_set_sensitive(ifactory, "/Tools/Edit with external editor", FALSE);
5898 #if USE_GPGME
5899 		menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE);
5900 		menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE);
5901 #endif /* USE_GPGME */
5902 #if USE_GTKSPELL
5903 		menu_set_sensitive(ifactory, "/Tools/Check spell", FALSE);
5904 		menu_set_sensitive(ifactory, "/Tools/Set spell language",
5905 				   FALSE);
5906 #endif
5907 
5908 		if (compose->insert_btn)
5909 			gtk_widget_set_sensitive(compose->insert_btn, FALSE);
5910 		if (compose->attach_btn)
5911 			gtk_widget_set_sensitive(compose->attach_btn, FALSE);
5912 		if (compose->sig_btn)
5913 			gtk_widget_set_sensitive(compose->sig_btn, FALSE);
5914 		if (compose->exteditor_btn)
5915 			gtk_widget_set_sensitive(compose->exteditor_btn, FALSE);
5916 		if (compose->linewrap_btn)
5917 			gtk_widget_set_sensitive(compose->linewrap_btn, FALSE);
5918 
5919 		/* gtk_widget_set_sensitive(compose->attach_toggle, FALSE); */
5920 
5921 		menu_set_sensitive_all(GTK_MENU_SHELL(compose->popupmenu),
5922 				       FALSE);
5923 	}
5924 
5925 #if USE_GPGME
5926 	if (!rfc2015_is_available()) {
5927 		menu_set_sensitive(ifactory, "/Tools/PGP Sign", FALSE);
5928 		menu_set_sensitive(ifactory, "/Tools/PGP Encrypt", FALSE);
5929 	}
5930 #endif /* USE_GPGME */
5931 
5932 	compose_set_out_encoding(compose);
5933 	addressbook_set_target_compose(compose);
5934 #ifndef G_OS_WIN32
5935 	action_update_compose_menu(ifactory, compose);
5936 #endif
5937 	compose_set_template_menu(compose);
5938 
5939 #if USE_GTKSPELL
5940 	compose_set_spell_lang_menu(compose);
5941 	if (mode != COMPOSE_REDIRECT)
5942 		menu_set_active(ifactory, "/Tools/Check spell",
5943 				prefs_common.check_spell);
5944 #endif
5945 
5946 	compose_list = g_list_append(compose_list, compose);
5947 
5948 	gtk_widget_show(window);
5949 
5950 	color[0] = quote_color;
5951 	cmap = gdk_window_get_colormap(window->window);
5952 	gdk_colormap_alloc_colors(cmap, color, 1, FALSE, TRUE, success);
5953 	if (success[0] == FALSE) {
5954 		GtkStyle *style;
5955 
5956 		g_warning("Compose: color allocation failed.\n");
5957 		style = gtk_widget_get_style(text);
5958 		quote_color = style->black;
5959 	}
5960 
5961 	return compose;
5962 }
5963 
compose_find_window_by_target(MsgInfo * msginfo)5964 static Compose *compose_find_window_by_target(MsgInfo *msginfo)
5965 {
5966 	GList *cur;
5967 	Compose *compose;
5968 
5969 	g_return_val_if_fail(msginfo != NULL, NULL);
5970 
5971 	for (cur = compose_list; cur != NULL; cur = cur->next) {
5972 		compose = cur->data;
5973 		if (procmsg_msginfo_equal(compose->targetinfo, msginfo))
5974 			return compose;
5975 	}
5976 
5977 	return NULL;
5978 }
5979 
compose_window_exist(gint x,gint y)5980 static gboolean compose_window_exist(gint x, gint y)
5981 {
5982 	GList *cur;
5983 	Compose *compose;
5984 	gint x_, y_;
5985 
5986 	for (cur = compose_list; cur != NULL; cur = cur->next) {
5987 		compose = cur->data;
5988 		gtkut_widget_get_uposition(compose->window, &x_, &y_);
5989 		if (x == x_ && y == y_)
5990 			return TRUE;
5991 	}
5992 
5993 	return FALSE;
5994 }
5995 
compose_connect_changed_callbacks(Compose * compose)5996 static void compose_connect_changed_callbacks(Compose *compose)
5997 {
5998 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
5999 	GtkTextBuffer *buffer;
6000 
6001 	buffer = gtk_text_view_get_buffer(text);
6002 
6003 	g_signal_connect(G_OBJECT(buffer), "changed",
6004 			 G_CALLBACK(compose_buffer_changed_cb), compose);
6005 	g_signal_connect(G_OBJECT(compose->to_entry), "changed",
6006 			 G_CALLBACK(compose_changed_cb), compose);
6007 	g_signal_connect(G_OBJECT(compose->newsgroups_entry), "changed",
6008 			 G_CALLBACK(compose_changed_cb), compose);
6009 	g_signal_connect(G_OBJECT(compose->cc_entry), "changed",
6010 			 G_CALLBACK(compose_changed_cb), compose);
6011 	g_signal_connect(G_OBJECT(compose->bcc_entry), "changed",
6012 			 G_CALLBACK(compose_changed_cb), compose);
6013 	g_signal_connect(G_OBJECT(compose->reply_entry), "changed",
6014 			 G_CALLBACK(compose_changed_cb), compose);
6015 	g_signal_connect(G_OBJECT(compose->followup_entry), "changed",
6016 			 G_CALLBACK(compose_changed_cb), compose);
6017 	g_signal_connect(G_OBJECT(compose->subject_entry), "changed",
6018 			 G_CALLBACK(compose_changed_cb), compose);
6019 }
6020 
6021 static PrefsToolbarItem items[] =
6022 {
6023 	{T_SEND,		TRUE, toolbar_send_cb},
6024 	{T_SEND_LATER,		TRUE, toolbar_send_later_cb},
6025 	{T_DRAFT,		TRUE, toolbar_draft_cb},
6026 	{T_INSERT_FILE,		FALSE, toolbar_insert_cb},
6027 	{T_ATTACH_FILE,		FALSE, toolbar_attach_cb},
6028 	{T_SIGNATURE,		FALSE, toolbar_sig_cb},
6029 	{T_EDITOR,		FALSE, toolbar_ext_editor_cb},
6030 	{T_LINEWRAP,		FALSE, toolbar_linewrap_cb},
6031 	{T_ADDRESS_BOOK,	FALSE, toolbar_address_cb},
6032 	{T_COMMON_PREFS,	FALSE, toolbar_prefs_common_cb},
6033 	{T_ACCOUNT_PREFS,	FALSE, toolbar_prefs_account_cb},
6034 
6035 	{-1, FALSE, NULL}
6036 };
6037 
compose_toolbar_create(Compose * compose)6038 static GtkWidget *compose_toolbar_create(Compose *compose)
6039 {
6040 	GtkWidget *toolbar;
6041 	const gchar *setting;
6042 	GList *item_list;
6043 
6044 	if (prefs_common.compose_toolbar_setting &&
6045 	    *prefs_common.compose_toolbar_setting != '\0')
6046 		setting = prefs_common.compose_toolbar_setting;
6047 	else
6048 		setting = prefs_toolbar_get_default_compose_setting_name_list();
6049 
6050 	item_list = prefs_toolbar_get_item_list_from_name_list(setting);
6051 	toolbar = compose_toolbar_create_from_list(compose, item_list);
6052 	g_list_free(item_list);
6053 
6054 	return toolbar;
6055 }
6056 
compose_toolbar_create_from_list(Compose * compose,GList * item_list)6057 static GtkWidget *compose_toolbar_create_from_list(Compose *compose,
6058 						   GList *item_list)
6059 {
6060 	GtkWidget *toolbar;
6061 	GtkWidget *icon_wid;
6062 	GtkToolItem *toolitem;
6063 	gint i;
6064 	GList *cur;
6065 
6066 	toolbar = gtk_toolbar_new();
6067 	gtk_toolbar_set_orientation(GTK_TOOLBAR(toolbar),
6068 				    GTK_ORIENTATION_HORIZONTAL);
6069 	gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH);
6070 	g_signal_connect(G_OBJECT(toolbar), "button_press_event",
6071 			 G_CALLBACK(toolbar_button_pressed), compose);
6072 
6073 	items[0].data = &compose->send_btn;
6074 	items[1].data = &compose->sendl_btn;
6075 	items[2].data = &compose->draft_btn;
6076 	items[3].data = &compose->insert_btn;
6077 	items[4].data = &compose->attach_btn;
6078 	items[5].data = &compose->sig_btn;
6079 	items[6].data = &compose->exteditor_btn;
6080 	items[7].data = &compose->linewrap_btn;
6081 	items[8].data = &compose->addrbook_btn;
6082 	items[9].data = &compose->prefs_common_btn;
6083 	items[10].data = &compose->prefs_account_btn;
6084 	for (i = 0; i <= 10; i++)
6085 		*(GtkWidget **)items[i].data = NULL;
6086 
6087 	for (cur = item_list; cur != NULL; cur = cur->next) {
6088 		const PrefsDisplayItem *ditem = cur->data;
6089 		PrefsToolbarItem *item;
6090 		gint width;
6091 
6092 		if (ditem->id == T_SEPARATOR) {
6093 			toolitem = gtk_separator_tool_item_new();
6094 			gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
6095 			continue;
6096 		}
6097 
6098 		for (item = items; item->id != -1; item++) {
6099 			if (ditem->id == item->id)
6100 				break;
6101 		}
6102 		if (item->id == -1)
6103 			continue;
6104 
6105 		icon_wid = stock_pixbuf_widget_for_toolbar(ditem->icon);
6106 		toolitem = gtk_tool_button_new(icon_wid, gettext(ditem->label));
6107 		if (ditem->description) {
6108 			gtk_tool_item_set_tooltip(toolitem,
6109 						  compose->toolbar_tip,
6110 						  gettext(ditem->description),
6111 						  ditem->name);
6112 		}
6113 
6114 		gtkut_get_str_size(GTK_WIDGET(toolitem), gettext(ditem->label),
6115 				   &width, NULL);
6116 		gtk_tool_item_set_homogeneous
6117 			(toolitem, width < 52 ? TRUE : FALSE);
6118 		gtk_tool_item_set_is_important(toolitem, item->is_important);
6119 
6120 		gtk_toolbar_insert(GTK_TOOLBAR(toolbar), toolitem, -1);
6121 
6122 		g_signal_connect(G_OBJECT(toolitem), "clicked",
6123 				 G_CALLBACK(item->callback), compose);
6124 		g_signal_connect(G_OBJECT(GTK_BIN(toolitem)->child),
6125 				 "button_press_event",
6126 				 G_CALLBACK(toolbar_button_pressed), compose);
6127 
6128 		*(GtkWidget **)item->data = GTK_WIDGET(toolitem);
6129 	}
6130 
6131 	gtk_widget_show_all(toolbar);
6132 
6133 	return toolbar;
6134 }
6135 
compose_set_toolbar_button_visibility(Compose * compose)6136 static void compose_set_toolbar_button_visibility(Compose *compose)
6137 {
6138 	GtkToolbarStyle style = GTK_TOOLBAR_BOTH_HORIZ;
6139 
6140 	if (prefs_common.toolbar_style == TOOLBAR_NONE)
6141 		style = -1;
6142 	else if (prefs_common.toolbar_style == TOOLBAR_ICON)
6143 		style = GTK_TOOLBAR_ICONS;
6144 	else if (prefs_common.toolbar_style == TOOLBAR_TEXT)
6145 		style = GTK_TOOLBAR_TEXT;
6146 	else if (prefs_common.toolbar_style == TOOLBAR_BOTH)
6147 		style = GTK_TOOLBAR_BOTH;
6148 	else if (prefs_common.toolbar_style == TOOLBAR_BOTH_HORIZ)
6149 		style = GTK_TOOLBAR_BOTH_HORIZ;
6150 
6151 	if (style != -1) {
6152 		gtk_toolbar_set_style(GTK_TOOLBAR(compose->toolbar), style);
6153 		gtk_widget_show(compose->toolbar);
6154 		gtk_widget_queue_resize(compose->toolbar);
6155 	} else
6156 		gtk_widget_hide(compose->toolbar);
6157 }
6158 
compose_account_option_menu_create(Compose * compose,GtkWidget * hbox)6159 static GtkWidget *compose_account_option_menu_create(Compose *compose,
6160 						     GtkWidget *hbox)
6161 {
6162 	GList *accounts;
6163 	GtkWidget *optmenu;
6164 	GtkWidget *menu;
6165 	gint num = 0, def_menu = 0;
6166 
6167 	accounts = account_get_list();
6168 	g_return_val_if_fail(accounts != NULL, NULL);
6169 
6170 	optmenu = gtk_option_menu_new();
6171 	gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
6172 	menu = gtk_menu_new();
6173 
6174 	for (; accounts != NULL; accounts = accounts->next, num++) {
6175 		PrefsAccount *ac = (PrefsAccount *)accounts->data;
6176 		GtkWidget *menuitem;
6177 		gchar *name;
6178 
6179 		if (ac == compose->account) def_menu = num;
6180 
6181 		if (ac->name)
6182 			name = g_strdup_printf("%s: %s <%s>",
6183 					       ac->account_name,
6184 					       ac->name, ac->address);
6185 		else
6186 			name = g_strdup_printf("%s: %s",
6187 					       ac->account_name, ac->address);
6188 		MENUITEM_ADD(menu, menuitem, name, ac);
6189 		g_free(name);
6190 		g_signal_connect(G_OBJECT(menuitem), "activate",
6191 				 G_CALLBACK(account_activated),
6192 				 compose);
6193 	}
6194 
6195 	gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), menu);
6196 	gtk_option_menu_set_history(GTK_OPTION_MENU(optmenu), def_menu);
6197 
6198 	return optmenu;
6199 }
6200 
compose_signature_menu_create(Compose * compose,GtkWidget * hbox)6201 static GtkWidget *compose_signature_menu_create(Compose *compose,
6202 						GtkWidget *hbox)
6203 {
6204 	GtkWidget *label;
6205 	GtkWidget *combo;
6206 	gint i;
6207 
6208 	label = gtk_label_new(_("Signature:"));
6209 	gtk_box_pack_start(GTK_BOX(hbox), label, FALSE, FALSE, 4);
6210 
6211 	combo = gtk_combo_box_new_text();
6212 	gtk_box_pack_start(GTK_BOX(hbox), combo, FALSE, FALSE, 0);
6213 
6214 	for (i = 0; i < 10; i++) {
6215 		gchar buf[256];
6216 		g_snprintf(buf, sizeof(buf), _("Signature %d"), i + 1);
6217 		gtk_combo_box_append_text(GTK_COMBO_BOX(combo), buf);
6218 	}
6219 	gtk_combo_box_set_active(GTK_COMBO_BOX(combo), 0);
6220 	g_signal_connect(GTK_COMBO_BOX(combo), "changed",
6221 			 G_CALLBACK(sig_combo_changed), compose);
6222 
6223 	gtk_widget_set_tooltip_text(combo, _("Change signature"));
6224 
6225 	return combo;
6226 }
6227 
compose_set_out_encoding(Compose * compose)6228 static void compose_set_out_encoding(Compose *compose)
6229 {
6230 	GtkItemFactoryEntry *entry;
6231 	GtkItemFactory *ifactory;
6232 	CharSet out_encoding;
6233 	gchar *path, *p, *q;
6234 	GtkWidget *item;
6235 
6236 	out_encoding = conv_get_charset_from_str(prefs_common.outgoing_charset);
6237 	ifactory = gtk_item_factory_from_widget(compose->menubar);
6238 
6239 	for (entry = compose_entries; entry->callback != compose_address_cb;
6240 	     entry++) {
6241 		if (entry->callback == compose_set_encoding_cb &&
6242 		    (CharSet)entry->callback_action == out_encoding) {
6243 			p = q = path = g_strdup(entry->path);
6244 			while (*p) {
6245 				if (*p == '_') {
6246 					if (p[1] == '_') {
6247 						p++;
6248 						*q++ = '_';
6249 					}
6250 				} else
6251 					*q++ = *p;
6252 				p++;
6253 			}
6254 			*q = '\0';
6255 			item = gtk_item_factory_get_item(ifactory, path);
6256 			gtk_widget_activate(item);
6257 			g_free(path);
6258 			break;
6259 		}
6260 	}
6261 }
6262 
6263 #if USE_GTKSPELL
6264 #if USE_ENCHANT
ench_dict_desc_cb(const char * const lang_tag,const char * const provider_name,const char * const provider_desc,const char * const provider_file,void * user_data)6265 static void ench_dict_desc_cb(const char *const lang_tag,
6266 			      const char *const provider_name,
6267 			      const char *const provider_desc,
6268 			      const char *const provider_file,
6269 			      void *user_data)
6270 {
6271 	GSList **dict_list = (GSList **)user_data;
6272 	*dict_list = g_slist_append(*dict_list, g_strdup((gchar*)lang_tag));
6273 }
6274 
compose_set_spell_lang_menu(Compose * compose)6275 static void compose_set_spell_lang_menu(Compose *compose)
6276 {
6277 	EnchantBroker *eb;
6278 	GSList *dict_list = NULL, *menu_list = NULL, *cur;
6279 	GtkWidget *menu;
6280 	gboolean lang_set = FALSE;
6281 
6282 	eb = enchant_broker_init();
6283 	enchant_broker_list_dicts(eb, ench_dict_desc_cb, &dict_list);
6284 	enchant_broker_free(eb);
6285 
6286 	for (cur = dict_list; cur != NULL; cur = cur->next) {
6287 		if (compose->spell_lang != NULL &&
6288 		    g_ascii_strcasecmp(compose->spell_lang,
6289 				       (gchar *)cur->data) == 0)
6290 			lang_set = TRUE;
6291 	}
6292 #else  /* !USE_ENCHANT */
6293 static void compose_set_spell_lang_menu(Compose *compose)
6294 {
6295 	AspellConfig *config;
6296 	AspellDictInfoList *dlist;
6297 	AspellDictInfoEnumeration *dels;
6298 	const AspellDictInfo *entry;
6299 	GSList *dict_list = NULL, *menu_list = NULL, *cur;
6300 	GtkWidget *menu;
6301 	gboolean lang_set = FALSE;
6302 
6303 	config = new_aspell_config();
6304 	dlist = get_aspell_dict_info_list(config);
6305 	delete_aspell_config(config);
6306 
6307 	dels = aspell_dict_info_list_elements(dlist);
6308 	while ((entry = aspell_dict_info_enumeration_next(dels)) != 0) {
6309 		dict_list = g_slist_append(dict_list, g_strdup(entry->name));
6310 		if (compose->spell_lang != NULL &&
6311 		    g_ascii_strcasecmp(compose->spell_lang, entry->name) == 0)
6312 			lang_set = TRUE;
6313 	}
6314 	delete_aspell_dict_info_enumeration(dels);
6315 #endif /* USE_ENCHANT */
6316 
6317 	compose->dict_list = dict_list;
6318 
6319 	menu = gtk_menu_new();
6320 
6321 	for (cur = dict_list; cur != NULL; cur = cur->next) {
6322 		gchar *dict = (gchar *)cur->data;
6323 		GtkWidget *item;
6324 
6325 		if (dict == NULL) continue;
6326 
6327 		item = gtk_radio_menu_item_new_with_label(menu_list, dict);
6328 		menu_list = gtk_radio_menu_item_get_group
6329 			(GTK_RADIO_MENU_ITEM(item));
6330 		if (compose->spell_lang != NULL &&
6331 		    g_ascii_strcasecmp(compose->spell_lang, dict) == 0)
6332 			gtk_check_menu_item_set_active
6333 				(GTK_CHECK_MENU_ITEM(item), TRUE);
6334 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6335 		g_signal_connect(G_OBJECT(item), "activate",
6336 				 G_CALLBACK(compose_set_spell_lang_cb),
6337 				 compose);
6338 		g_object_set_data(G_OBJECT(item), "spell-lang", dict);
6339 		gtk_widget_show(item);
6340 
6341 		if (!lang_set && g_ascii_strcasecmp("en", dict) == 0) {
6342 			g_free(compose->spell_lang);
6343 			compose->spell_lang = g_strdup("en");
6344 			gtk_check_menu_item_set_active
6345 				(GTK_CHECK_MENU_ITEM(item), TRUE);
6346 		}
6347 	}
6348 
6349 	gtk_widget_show(menu);
6350 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->spell_menu), menu);
6351 }
6352 
6353 static void compose_change_spell_lang_menu(Compose *compose, const gchar *lang)
6354 {
6355 	GtkWidget *menu;
6356 	GtkWidget *def_item = NULL;
6357 	GList *cur_item;
6358 	const gchar *dict;
6359 
6360 	if (!lang)
6361 		return;
6362 
6363 	menu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(compose->spell_menu));
6364 	for (cur_item = GTK_MENU_SHELL(menu)->children; cur_item != NULL;
6365 	     cur_item = cur_item->next) {
6366 		if (gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(cur_item->data)))
6367 			def_item = GTK_WIDGET(cur_item->data);
6368 		dict = g_object_get_data(G_OBJECT(cur_item->data), "spell-lang");
6369 		if (dict && !g_ascii_strcasecmp(dict, lang)) {
6370 			gtk_check_menu_item_set_active
6371 				(GTK_CHECK_MENU_ITEM(cur_item->data), TRUE);
6372 			return;
6373 		}
6374 	}
6375 
6376 	if (def_item) {
6377 		gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(def_item),
6378 					       TRUE);
6379 		compose_set_spell_lang_cb(def_item, compose);
6380 	}
6381 }
6382 #endif /* USE_GTKSPELL */
6383 
6384 static void compose_set_template_menu(Compose *compose)
6385 {
6386 	GSList *tmpl_list, *cur;
6387 	GtkWidget *menu;
6388 	GtkWidget *item;
6389 
6390 	tmpl_list = template_get_config();
6391 
6392 	menu = gtk_menu_new();
6393 
6394 	for (cur = tmpl_list; cur != NULL; cur = cur->next) {
6395 		Template *tmpl = (Template *)cur->data;
6396 
6397 		item = gtk_menu_item_new_with_label(tmpl->name);
6398 		gtk_menu_shell_append(GTK_MENU_SHELL(menu), item);
6399 		g_signal_connect(G_OBJECT(item), "activate",
6400 				 G_CALLBACK(compose_template_activate_cb),
6401 				 compose);
6402 		g_object_set_data(G_OBJECT(item), "template", tmpl);
6403 		gtk_widget_show(item);
6404 	}
6405 
6406 	gtk_widget_show(menu);
6407 	gtk_menu_item_set_submenu(GTK_MENU_ITEM(compose->tmpl_menu), menu);
6408 }
6409 
6410 void compose_reflect_prefs_all(void)
6411 {
6412 	GList *cur;
6413 	Compose *compose;
6414 
6415 	for (cur = compose_list; cur != NULL; cur = cur->next) {
6416 		compose = (Compose *)cur->data;
6417 
6418 		if (compose->autosave_tag > 0) {
6419 			g_source_remove(compose->autosave_tag);
6420 			compose->autosave_tag = 0;
6421 		}
6422 
6423 		compose_set_template_menu(compose);
6424 
6425 		if (prefs_common.enable_autosave &&
6426 		    prefs_common.autosave_itv > 0 &&
6427 		    compose->mode != COMPOSE_REDIRECT)
6428 			compose->autosave_tag =
6429 				g_timeout_add_full
6430 					(G_PRIORITY_LOW,
6431 					 prefs_common.autosave_itv * 60 * 1000,
6432 					 autosave_timeout, compose, NULL);
6433 	}
6434 }
6435 
6436 GtkWidget *compose_get_toolbar(Compose *compose)
6437 {
6438 	g_return_val_if_fail(compose != NULL, NULL);
6439 	return compose->toolbar;
6440 }
6441 
6442 GtkWidget *compose_get_misc_hbox(Compose *compose)
6443 {
6444 	g_return_val_if_fail(compose != NULL, NULL);
6445 	return compose->misc_hbox;
6446 }
6447 
6448 GtkWidget *compose_get_textview(Compose *compose)
6449 {
6450 	g_return_val_if_fail(compose != NULL, NULL);
6451 	return compose->text;
6452 }
6453 
6454 static void compose_template_apply(Compose *compose, Template *tmpl,
6455 				   gboolean replace)
6456 {
6457 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
6458 	GtkTextBuffer *buffer;
6459 	GtkTextMark *mark;
6460 	GtkTextIter iter;
6461 	gchar *parsed_str = NULL;
6462 
6463 	if (!tmpl || !tmpl->value) return;
6464 
6465 	buffer = gtk_text_view_get_buffer(text);
6466 
6467 	if (tmpl->to && *tmpl->to != '\0')
6468 		compose_entry_set(compose, tmpl->to, COMPOSE_ENTRY_TO);
6469 	if (tmpl->cc && *tmpl->cc != '\0')
6470 		compose_entry_set(compose, tmpl->cc, COMPOSE_ENTRY_CC);
6471 	if (tmpl->bcc && *tmpl->bcc != '\0')
6472 		compose_entry_set(compose, tmpl->bcc, COMPOSE_ENTRY_BCC);
6473 	if (tmpl->replyto && *tmpl->replyto != '\0')
6474 		compose_entry_set(compose, tmpl->replyto,
6475 				  COMPOSE_ENTRY_REPLY_TO);
6476 	if (tmpl->subject && *tmpl->subject != '\0')
6477 		compose_entry_set(compose, tmpl->subject,
6478 				  COMPOSE_ENTRY_SUBJECT);
6479 
6480 	if (replace)
6481 		gtk_text_buffer_set_text(buffer, "", 0);
6482 
6483 	mark = gtk_text_buffer_get_insert(buffer);
6484 	gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
6485 
6486 	if (compose->reply_target) {
6487 		FolderItem *item;
6488 		gint num;
6489 		MsgInfo *msginfo = NULL;
6490 		MsgInfo *full_msginfo;
6491 
6492 		item = folder_find_item_and_num_from_id(compose->reply_target,
6493 							&num);
6494 		if (item && num > 0) {
6495 			msginfo = folder_item_get_msginfo(item, num);
6496 			if (msginfo) {
6497 				full_msginfo = procmsg_msginfo_get_full_info(msginfo);
6498 				if (full_msginfo) {
6499 					procmsg_msginfo_free(msginfo);
6500 					msginfo = full_msginfo;
6501 				}
6502 			}
6503 		}
6504 		parsed_str = compose_quote_fmt(compose, msginfo,
6505 					       tmpl->value,
6506 					       prefs_common.quotemark, NULL);
6507 		procmsg_msginfo_free(msginfo);
6508 	} else if (compose->forward_targets) {
6509 		FolderItem *item;
6510 		gint num;
6511 		gchar **targets;
6512 		gint i;
6513 		MsgInfo *msginfo;
6514 		MsgInfo *full_msginfo;
6515 
6516 		targets = g_strsplit(compose->forward_targets, "\n", 0);
6517 
6518 		for (i = 0; targets[i] != NULL; i++) {
6519 			g_strstrip(targets[i]);
6520 			item = folder_find_item_and_num_from_id(targets[i], &num);
6521 			if (!item || num <= 0)
6522 				continue;
6523 			msginfo = procmsg_get_msginfo(item, num);
6524 			if (!msginfo)
6525 				continue;
6526 			full_msginfo = procmsg_msginfo_get_full_info(msginfo);
6527 			parsed_str = compose_quote_fmt(compose, full_msginfo ? full_msginfo : msginfo,
6528 						       tmpl->value,
6529 						       prefs_common.fw_quotemark, NULL);
6530 			procmsg_msginfo_free(full_msginfo);
6531 			procmsg_msginfo_free(msginfo);
6532 		}
6533 		g_strfreev(targets);
6534 	} else {
6535 		parsed_str = compose_quote_fmt(compose, NULL, tmpl->value,
6536 					       NULL, NULL);
6537 	}
6538 
6539 	if (replace && parsed_str && prefs_common.auto_sig)
6540 		compose_insert_sig(compose, TRUE, FALSE, FALSE);
6541 
6542 	if (replace && parsed_str) {
6543 		gtk_text_buffer_get_start_iter(buffer, &iter);
6544 		gtk_text_buffer_place_cursor(buffer, &iter);
6545 	}
6546 
6547 	if (parsed_str)
6548 		compose_changed_cb(NULL, compose);
6549 }
6550 
6551 static void compose_destroy(Compose *compose)
6552 {
6553 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6554 	GtkTreeIter iter;
6555 	gboolean valid;
6556 	AttachInfo *ainfo;
6557 	GtkTextBuffer *buffer;
6558 	GtkClipboard *clipboard;
6559 
6560 	compose_list = g_list_remove(compose_list, compose);
6561 
6562 	if (compose->autosave_tag > 0)
6563 		g_source_remove(compose->autosave_tag);
6564 
6565 	syl_plugin_signal_emit("compose-destroy", compose);
6566 
6567 	/* NOTE: address_completion_end() does nothing with the window
6568 	 * however this may change. */
6569 	address_completion_end(compose->window);
6570 
6571 #if USE_GTKSPELL
6572 	slist_free_strings(compose->dict_list);
6573 	g_slist_free(compose->dict_list);
6574 	g_free(compose->spell_lang);
6575 #endif
6576 
6577 	slist_free_strings(compose->to_list);
6578 	g_slist_free(compose->to_list);
6579 	slist_free_strings(compose->newsgroup_list);
6580 	g_slist_free(compose->newsgroup_list);
6581 
6582 	procmsg_msginfo_free(compose->targetinfo);
6583 	g_free(compose->reply_target);
6584 	g_free(compose->forward_targets);
6585 	g_free(compose->replyto);
6586 	g_free(compose->cc);
6587 	g_free(compose->bcc);
6588 	g_free(compose->newsgroups);
6589 	g_free(compose->followup_to);
6590 
6591 	g_free(compose->ml_post);
6592 
6593 	g_free(compose->inreplyto);
6594 	g_free(compose->references);
6595 	g_free(compose->msgid);
6596 	g_free(compose->boundary);
6597 
6598 	if (compose->undostruct)
6599 		undo_destroy(compose->undostruct);
6600 
6601 	if (compose->exteditor_file) {
6602 		g_unlink(compose->exteditor_file);
6603 		g_free(compose->exteditor_file);
6604 	}
6605 
6606 	for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
6607 	     valid = gtk_tree_model_iter_next(model, &iter)) {
6608 		gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
6609 		compose_attach_info_free(ainfo);
6610 	}
6611 
6612 	if (addressbook_get_target_compose() == compose)
6613 		addressbook_set_target_compose(NULL);
6614 
6615 	prefs_common.compose_maximized = compose->window_maximized;
6616 	if (!prefs_common.compose_maximized) {
6617 		gtkut_widget_get_uposition(compose->window,
6618 					   &prefs_common.compose_x,
6619 					   &prefs_common.compose_y);
6620 		prefs_common.compose_width =
6621 			compose->scrolledwin->allocation.width;
6622 		prefs_common.compose_height =
6623 			compose->window->allocation.height;
6624 	}
6625 
6626 	if (!gtk_widget_get_parent(compose->paned))
6627 		gtk_widget_destroy(compose->paned);
6628 	gtk_widget_destroy(compose->popupmenu);
6629 
6630 	buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
6631 	clipboard = gtk_clipboard_get(GDK_SELECTION_PRIMARY);
6632 	gtk_text_buffer_remove_selection_clipboard(buffer, clipboard);
6633 
6634 	g_object_unref(compose->toolbar_tip);
6635 
6636 	gtk_widget_destroy(compose->window);
6637 
6638 	g_free(compose);
6639 }
6640 
6641 static void compose_attach_info_free(AttachInfo *ainfo)
6642 {
6643 	g_free(ainfo->file);
6644 	g_free(ainfo->content_type);
6645 	g_free(ainfo->name);
6646 	g_free(ainfo);
6647 }
6648 
6649 static void compose_attach_remove_selected(Compose *compose)
6650 {
6651 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6652 	GtkTreeSelection *selection;
6653 	GtkTreeIter iter;
6654 	GList *rows, *cur;
6655 	AttachInfo *ainfo;
6656 
6657 	selection = gtk_tree_view_get_selection
6658 		(GTK_TREE_VIEW(compose->attach_treeview));
6659 
6660 	rows = gtk_tree_selection_get_selected_rows(selection, NULL);
6661 
6662 	/* delete from below so that GtkTreePath doesn't point wrong row */
6663 	rows = g_list_reverse(rows);
6664 
6665 	for (cur = rows; cur != NULL; cur = cur->next) {
6666 		gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)cur->data);
6667 		gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
6668 		compose_attach_info_free(ainfo);
6669 		gtk_list_store_remove(compose->attach_store, &iter);
6670 		gtk_tree_path_free((GtkTreePath *)cur->data);
6671 	}
6672 
6673 	g_list_free(rows);
6674 
6675 	syl_plugin_signal_emit("compose-attach-changed", compose);
6676 }
6677 
6678 void compose_attach_remove_all(Compose *compose)
6679 {
6680 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6681 	GtkTreeIter iter;
6682 	AttachInfo *ainfo;
6683 
6684 	g_return_if_fail(compose != NULL);
6685 
6686 	if (gtk_tree_model_get_iter_first(model, &iter) == FALSE)
6687 		return;
6688 
6689 	do {
6690 		gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
6691 		compose_attach_info_free(ainfo);
6692 	} while (gtk_tree_model_iter_next(model, &iter) == TRUE);
6693 
6694 	gtk_list_store_clear(GTK_LIST_STORE(model));
6695 
6696 	syl_plugin_signal_emit("compose-attach-changed", compose);
6697 }
6698 
6699 GSList *compose_get_attach_list(Compose *compose)
6700 {
6701 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6702 	GtkTreeIter iter;
6703 	gboolean valid;
6704 	AttachInfo *ainfo;
6705 	GSList *alist = NULL;
6706 
6707 	if (!compose->use_attach)
6708 		return NULL;
6709 
6710 	for (valid = gtk_tree_model_get_iter_first(model, &iter); valid;
6711 	     valid = gtk_tree_model_iter_next(model, &iter)) {
6712 		gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
6713 		alist = g_slist_append(alist, ainfo);
6714 	}
6715 
6716 	return alist;
6717 }
6718 
6719 static struct _AttachProperty
6720 {
6721 	GtkWidget *window;
6722 	GtkWidget *mimetype_entry;
6723 	GtkWidget *encoding_optmenu;
6724 	GtkWidget *path_entry;
6725 	GtkWidget *filename_entry;
6726 	GtkWidget *ok_btn;
6727 	GtkWidget *cancel_btn;
6728 } attach_prop;
6729 
6730 static void compose_attach_property(Compose *compose)
6731 {
6732 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6733 	GtkTreeSelection *selection;
6734 	GtkTreeIter iter;
6735 	GList *rows;
6736 	AttachInfo *ainfo;
6737 	gchar *path = NULL;
6738 	GtkOptionMenu *optmenu;
6739 	static gboolean cancelled;
6740 
6741 	selection = gtk_tree_view_get_selection
6742 		(GTK_TREE_VIEW(compose->attach_treeview));
6743 
6744 	rows = gtk_tree_selection_get_selected_rows(selection, NULL);
6745 
6746 	if (!rows)
6747 		return;
6748 
6749 	gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)rows->data);
6750 	gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
6751 
6752 	g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
6753 	g_list_free(rows);
6754 
6755 	compose_attach_property_create(&cancelled);
6756 	gtk_widget_grab_focus(attach_prop.ok_btn);
6757 	gtk_widget_show(attach_prop.window);
6758 	manage_window_focus_in(compose->window, NULL, NULL);
6759 	manage_window_set_transient(GTK_WINDOW(attach_prop.window));
6760 
6761 	optmenu = GTK_OPTION_MENU(attach_prop.encoding_optmenu);
6762 	if (ainfo->encoding == ENC_UNKNOWN)
6763 		gtk_option_menu_set_history(optmenu, ENC_BASE64);
6764 	else
6765 		gtk_option_menu_set_history(optmenu, ainfo->encoding);
6766 
6767 	if (ainfo->file)
6768 		path = conv_filename_to_utf8(ainfo->file);
6769 
6770 	gtk_entry_set_text(GTK_ENTRY(attach_prop.mimetype_entry),
6771 			   ainfo->content_type ? ainfo->content_type : "");
6772 	gtk_entry_set_text(GTK_ENTRY(attach_prop.path_entry), path ? path : "");
6773 	gtk_entry_set_text(GTK_ENTRY(attach_prop.filename_entry),
6774 			   ainfo->name ? ainfo->name : "");
6775 
6776 	g_free(path);
6777 
6778 	for (;;) {
6779 		const gchar *entry_text;
6780 		gchar *text;
6781 		gchar *cnttype = NULL;
6782 		gchar *file = NULL;
6783 		off_t size = 0;
6784 		GtkWidget *menu;
6785 		GtkWidget *menuitem;
6786 
6787 		cancelled = FALSE;
6788 		gtk_main();
6789 
6790 		if (cancelled == TRUE)
6791 			break;
6792 
6793 		entry_text = gtk_entry_get_text
6794 			(GTK_ENTRY(attach_prop.mimetype_entry));
6795 		if (*entry_text != '\0') {
6796 			gchar *p;
6797 
6798 			text = g_strstrip(g_strdup(entry_text));
6799 			if ((p = strchr(text, '/')) && !strchr(p + 1, '/')) {
6800 				cnttype = g_strdup(text);
6801 				g_free(text);
6802 			} else {
6803 				alertpanel_error(_("Invalid MIME type."));
6804 				g_free(text);
6805 				continue;
6806 			}
6807 		}
6808 
6809 		menu = gtk_option_menu_get_menu(optmenu);
6810 		menuitem = gtk_menu_get_active(GTK_MENU(menu));
6811 		ainfo->encoding = GPOINTER_TO_INT
6812 			(g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID));
6813 
6814 		entry_text = gtk_entry_get_text
6815 			(GTK_ENTRY(attach_prop.path_entry));
6816 		if (*entry_text != '\0') {
6817 			file = conv_filename_from_utf8(entry_text);
6818 			if (!is_file_exist(file) ||
6819 			    (size = get_file_size(file)) <= 0) {
6820 				alertpanel_error
6821 					(_("File doesn't exist or is empty."));
6822 				g_free(file);
6823 				g_free(cnttype);
6824 				continue;
6825 			}
6826 			g_free(ainfo->file);
6827 			ainfo->file = file;
6828 		}
6829 
6830 		entry_text = gtk_entry_get_text
6831 			(GTK_ENTRY(attach_prop.filename_entry));
6832 		if (*entry_text != '\0') {
6833 			g_free(ainfo->name);
6834 			ainfo->name = g_strdup(entry_text);
6835 		}
6836 
6837 		if (cnttype) {
6838 			g_free(ainfo->content_type);
6839 			ainfo->content_type = cnttype;
6840 		}
6841 		if (size)
6842 			ainfo->size = size;
6843 
6844 		gtk_list_store_set(compose->attach_store, &iter,
6845 				   COL_MIMETYPE, ainfo->content_type,
6846 				   COL_SIZE, to_human_readable(ainfo->size),
6847 				   COL_NAME, ainfo->name,
6848 				   -1);
6849 		break;
6850 	}
6851 
6852 	gtk_widget_destroy(attach_prop.window);
6853 	memset(&attach_prop, 0, sizeof(attach_prop));
6854 }
6855 
6856 #define SET_LABEL_AND_ENTRY(str, entry, top) \
6857 { \
6858 	label = gtk_label_new(str); \
6859 	gtk_table_attach(GTK_TABLE(table), label, 0, 1, top, (top + 1), \
6860 			 GTK_FILL, 0, 0, 0); \
6861 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5); \
6862  \
6863 	entry = gtk_entry_new(); \
6864 	gtk_table_attach(GTK_TABLE(table), entry, 1, 2, top, (top + 1), \
6865 			 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0); \
6866 }
6867 
6868 static void compose_attach_property_create(gboolean *cancelled)
6869 {
6870 	GtkWidget *window;
6871 	GtkWidget *vbox;
6872 	GtkWidget *table;
6873 	GtkWidget *label;
6874 	GtkWidget *mimetype_entry;
6875 	GtkWidget *hbox;
6876 	GtkWidget *optmenu;
6877 	GtkWidget *optmenu_menu;
6878 	GtkWidget *menuitem;
6879 	GtkWidget *path_entry;
6880 	GtkWidget *filename_entry;
6881 	GtkWidget *hbbox;
6882 	GtkWidget *ok_btn;
6883 	GtkWidget *cancel_btn;
6884 
6885 	debug_print("Creating attach property window...\n");
6886 
6887 	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
6888 	gtk_widget_set_size_request(window, 480 * gtkut_get_dpi_multiplier(), -1);
6889 	gtk_container_set_border_width(GTK_CONTAINER(window), 8);
6890 	gtk_window_set_title(GTK_WINDOW(window), _("Properties"));
6891 	gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
6892 	gtk_window_set_modal(GTK_WINDOW(window), TRUE);
6893 	g_signal_connect(G_OBJECT(window), "delete_event",
6894 			 G_CALLBACK(attach_property_delete_event),
6895 			 cancelled);
6896 	g_signal_connect(G_OBJECT(window), "key_press_event",
6897 			 G_CALLBACK(attach_property_key_pressed),
6898 			 cancelled);
6899 
6900 	vbox = gtk_vbox_new(FALSE, 8);
6901 	gtk_container_add(GTK_CONTAINER(window), vbox);
6902 
6903 	table = gtk_table_new(4, 2, FALSE);
6904 	gtk_box_pack_start(GTK_BOX(vbox), table, FALSE, FALSE, 0);
6905 	gtk_table_set_row_spacings(GTK_TABLE(table), 8);
6906 	gtk_table_set_col_spacings(GTK_TABLE(table), 8);
6907 
6908 	SET_LABEL_AND_ENTRY(_("MIME type"), mimetype_entry, 0);
6909 
6910 	label = gtk_label_new(_("Encoding"));
6911 	gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2,
6912 			 GTK_FILL, 0, 0, 0);
6913 	gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
6914 
6915 	hbox = gtk_hbox_new(FALSE, 0);
6916 	gtk_table_attach(GTK_TABLE(table), hbox, 1, 2, 1, 2,
6917 			 GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
6918 
6919 	optmenu = gtk_option_menu_new();
6920 	gtk_box_pack_start(GTK_BOX(hbox), optmenu, FALSE, FALSE, 0);
6921 
6922 	optmenu_menu = gtk_menu_new();
6923 	MENUITEM_ADD(optmenu_menu, menuitem, "7bit", ENC_7BIT);
6924 	gtk_widget_set_sensitive(menuitem, FALSE);
6925 	MENUITEM_ADD(optmenu_menu, menuitem, "8bit", ENC_8BIT);
6926 	gtk_widget_set_sensitive(menuitem, FALSE);
6927 	MENUITEM_ADD(optmenu_menu, menuitem, "quoted-printable",
6928 		     ENC_QUOTED_PRINTABLE);
6929 	MENUITEM_ADD(optmenu_menu, menuitem, "base64", ENC_BASE64);
6930 
6931 	gtk_option_menu_set_menu(GTK_OPTION_MENU(optmenu), optmenu_menu);
6932 
6933 	SET_LABEL_AND_ENTRY(_("Path"),      path_entry,     2);
6934 	SET_LABEL_AND_ENTRY(_("File name"), filename_entry, 3);
6935 
6936 	gtkut_stock_button_set_create(&hbbox, &ok_btn, GTK_STOCK_OK,
6937 				      &cancel_btn, GTK_STOCK_CANCEL,
6938 				      NULL, NULL);
6939 	gtk_box_pack_end(GTK_BOX(vbox), hbbox, FALSE, FALSE, 0);
6940 	gtk_widget_grab_default(ok_btn);
6941 
6942 	g_signal_connect(G_OBJECT(ok_btn), "clicked",
6943 			 G_CALLBACK(attach_property_ok),
6944 			 cancelled);
6945 	g_signal_connect(G_OBJECT(cancel_btn), "clicked",
6946 			 G_CALLBACK(attach_property_cancel),
6947 			 cancelled);
6948 
6949 	gtk_widget_show_all(vbox);
6950 
6951 	attach_prop.window           = window;
6952 	attach_prop.mimetype_entry   = mimetype_entry;
6953 	attach_prop.encoding_optmenu = optmenu;
6954 	attach_prop.path_entry       = path_entry;
6955 	attach_prop.filename_entry   = filename_entry;
6956 	attach_prop.ok_btn           = ok_btn;
6957 	attach_prop.cancel_btn       = cancel_btn;
6958 }
6959 
6960 #undef SET_LABEL_AND_ENTRY
6961 
6962 static void attach_property_ok(GtkWidget *widget, gboolean *cancelled)
6963 {
6964 	*cancelled = FALSE;
6965 	gtk_main_quit();
6966 }
6967 
6968 static void attach_property_cancel(GtkWidget *widget, gboolean *cancelled)
6969 {
6970 	*cancelled = TRUE;
6971 	gtk_main_quit();
6972 }
6973 
6974 static gint attach_property_delete_event(GtkWidget *widget, GdkEventAny *event,
6975 					 gboolean *cancelled)
6976 {
6977 	*cancelled = TRUE;
6978 	gtk_main_quit();
6979 
6980 	return TRUE;
6981 }
6982 
6983 static gboolean attach_property_key_pressed(GtkWidget *widget,
6984 					    GdkEventKey *event,
6985 					    gboolean *cancelled)
6986 {
6987 	if (event && event->keyval == GDK_Escape) {
6988 		*cancelled = TRUE;
6989 		gtk_main_quit();
6990 	}
6991 	return FALSE;
6992 }
6993 
6994 static void compose_attach_open(Compose *compose)
6995 {
6996 	GtkTreeModel *model = GTK_TREE_MODEL(compose->attach_store);
6997 	GtkTreeSelection *selection;
6998 	GtkTreeIter iter;
6999 	GList *rows;
7000 	AttachInfo *ainfo = NULL;
7001 #ifdef G_OS_WIN32
7002 	DWORD dwtype;
7003 #endif
7004 
7005 	selection = gtk_tree_view_get_selection
7006 		(GTK_TREE_VIEW(compose->attach_treeview));
7007 
7008 	rows = gtk_tree_selection_get_selected_rows(selection, NULL);
7009 
7010 	if (!rows)
7011 		return;
7012 
7013 	gtk_tree_model_get_iter(model, &iter, (GtkTreePath *)rows->data);
7014 	gtk_tree_model_get(model, &iter, COL_ATTACH_INFO, &ainfo, -1);
7015 
7016 	g_list_foreach(rows, (GFunc)gtk_tree_path_free, NULL);
7017 	g_list_free(rows);
7018 
7019 	if (!ainfo || !ainfo->file)
7020 		return;
7021 
7022 	if (!is_file_exist(ainfo->file)) {
7023 		alertpanel_error(_("File not exist."));
7024 		return;
7025 	}
7026 
7027 #ifdef G_OS_WIN32
7028 	if (g_file_test(ainfo->file, G_FILE_TEST_IS_EXECUTABLE) ||
7029 	    str_has_suffix_case(ainfo->file, ".scr") ||
7030 	    str_has_suffix_case(ainfo->file, ".pif") ||
7031 	    GetBinaryType(ainfo->file, &dwtype)) {
7032 		alertpanel_full
7033 			(_("Opening executable file"),
7034 			 _("This is an executable file. Opening executable file is restricted for security.\n"
7035 			   "If you want to launch it, save it to somewhere and make sure it is not an virus or something like a malicious program."),
7036 			 ALERT_WARNING, G_ALERTDEFAULT, FALSE,
7037 			 GTK_STOCK_OK, NULL, NULL);
7038 		return;
7039 	}
7040 	execute_open_file(ainfo->file, ainfo->content_type);
7041 #else
7042 	procmime_execute_open_file(ainfo->file, ainfo->content_type);
7043 #endif
7044 }
7045 
7046 static void compose_exec_ext_editor(Compose *compose)
7047 {
7048 	gchar *tmp;
7049 	GPid pid;
7050 	static gchar *def_cmd = "emacs %s";
7051 	gchar buf[1024];
7052 	gchar **cmdline;
7053 	GError *error = NULL;
7054 
7055 	tmp = g_strdup_printf("%s%ctmpmsg-%p.txt", get_tmp_dir(),
7056 			      G_DIR_SEPARATOR, compose);
7057 
7058 	if (compose_write_body_to_file(compose, tmp) < 0) {
7059 		g_warning("Coundn't write to file: %s\n", tmp);
7060 		g_free(tmp);
7061 		return;
7062 	}
7063 #ifdef G_OS_WIN32
7064 	if (canonicalize_file_replace(tmp) < 0) {
7065 		g_warning("Coundn't write to file: %s\n", tmp);
7066 		g_free(tmp);
7067 		return;
7068 	}
7069 #endif
7070 
7071 	if (prefs_common.ext_editor_cmd &&
7072 	    str_find_format_times(prefs_common.ext_editor_cmd, 's') == 1)
7073 		g_snprintf(buf, sizeof(buf), prefs_common.ext_editor_cmd, tmp);
7074 	else {
7075 		if (prefs_common.ext_editor_cmd)
7076 			g_warning(_("External editor command line is invalid: `%s'\n"),
7077 				  prefs_common.ext_editor_cmd);
7078 		g_snprintf(buf, sizeof(buf), def_cmd, tmp);
7079 	}
7080 
7081 	cmdline = strsplit_with_quote(buf, " ", 1024);
7082 
7083 	if (g_spawn_async(NULL, cmdline, NULL,
7084 			  G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_SEARCH_PATH,
7085 			  NULL, NULL, &pid, &error) == FALSE) {
7086 		g_warning("Couldn't execute external editor: %s\n", buf);
7087 		if (error) {
7088 			g_warning("g_spawn_async(): %s\n", error->message);
7089 			g_error_free(error);
7090 		}
7091 		g_strfreev(cmdline);
7092 		g_unlink(tmp);
7093 		g_free(tmp);
7094 		return;
7095 	}
7096 	if (pid == 0) {
7097 		g_warning("Couldn't get PID of external editor\n");
7098 		g_strfreev(cmdline);
7099 		g_unlink(tmp);
7100 		g_free(tmp);
7101 		return;
7102 	}
7103 
7104 	g_strfreev(cmdline);
7105 
7106 	compose_set_ext_editor_sensitive(compose, FALSE);
7107 
7108 	debug_print("compose_exec_ext_editor(): pid: %d file: %s\n", pid, tmp);
7109 
7110 	compose->exteditor_file = tmp;
7111 	compose->exteditor_pid = pid;
7112 	compose->exteditor_tag =
7113 		g_child_watch_add(pid, compose_ext_editor_child_exit, compose);
7114 }
7115 
7116 static gboolean compose_ext_editor_kill(Compose *compose)
7117 {
7118 #ifdef G_OS_WIN32
7119 	DWORD exitcode;
7120 #endif
7121 	gint ret;
7122 
7123 	g_return_val_if_fail(compose->exteditor_pid != 0, TRUE);
7124 
7125 #ifdef G_OS_WIN32
7126 	ret = GetExitCodeProcess(compose->exteditor_pid, &exitcode);
7127 
7128 	if (ret && exitcode == STILL_ACTIVE) {
7129 #else
7130 	ret = kill(compose->exteditor_pid, 0);
7131 
7132 	if (ret == 0 || (ret == -1 && EPERM == errno)) {
7133 #endif
7134 		AlertValue val;
7135 		gchar *msg;
7136 
7137 		msg = g_strdup_printf
7138 			(_("The external editor is still working.\n"
7139 			   "Force terminating the process (pid: %d)?\n"),
7140 			 compose->exteditor_pid);
7141 		val = alertpanel_full(_("Notice"), msg, ALERT_NOTICE,
7142 				      G_ALERTALTERNATE, FALSE,
7143 				      GTK_STOCK_YES, GTK_STOCK_NO, NULL);
7144 		g_free(msg);
7145 
7146 		if (val != G_ALERTDEFAULT)
7147 			return FALSE;
7148 	}
7149 
7150 	if (compose->exteditor_tag != 0) {
7151 		g_source_remove(compose->exteditor_tag);
7152 		compose->exteditor_tag = 0;
7153 	}
7154 
7155 	if (compose->exteditor_pid != 0) {
7156 #ifdef G_OS_WIN32
7157 		if (TerminateProcess(compose->exteditor_pid, 1) == 0)
7158 			g_warning("TerminateProcess() failed: %d\n",
7159 				  GetLastError());
7160 #else
7161 		if (kill(compose->exteditor_pid, SIGTERM) < 0)
7162 			perror("kill");
7163 #endif
7164 		g_message("Terminated process group id: %d\n",
7165 			  compose->exteditor_pid);
7166 		g_message("Temporary file: %s\n",
7167 			  compose->exteditor_file);
7168 		compose->exteditor_pid = 0;
7169 	}
7170 
7171 	if (compose->exteditor_file) {
7172 		g_unlink(compose->exteditor_file);
7173 		g_free(compose->exteditor_file);
7174 		compose->exteditor_file = NULL;
7175 	}
7176 
7177 	compose_set_ext_editor_sensitive(compose, TRUE);
7178 
7179 	return TRUE;
7180 }
7181 
7182 static void compose_ext_editor_child_exit(GPid pid, gint status, gpointer data)
7183 {
7184 	Compose *compose = (Compose *)data;
7185 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
7186 	GtkTextBuffer *buffer;
7187 	GtkTextMark *mark;
7188 	GtkTextIter iter;
7189 
7190 	debug_print("Compose: child exit (pid: %d status: %d)\n", pid, status);
7191 
7192 	compose_lock(compose);
7193 
7194 	g_spawn_close_pid(pid);
7195 
7196 	buffer = gtk_text_view_get_buffer(text);
7197 
7198 	gtk_text_buffer_set_text(buffer, "", 0);
7199 	compose_insert_file(compose, compose->exteditor_file, FALSE);
7200 	compose_enable_sig(compose);
7201 
7202 	gtk_text_buffer_get_start_iter(buffer, &iter);
7203 	gtk_text_buffer_place_cursor(buffer, &iter);
7204 	mark = gtk_text_buffer_get_insert(buffer);
7205 	gtk_text_view_scroll_mark_onscreen(text, mark);
7206 
7207 	compose_changed_cb(NULL, compose);
7208 
7209 	if (g_unlink(compose->exteditor_file) < 0)
7210 		FILE_OP_ERROR(compose->exteditor_file, "unlink");
7211 
7212 	compose_set_ext_editor_sensitive(compose, TRUE);
7213 	gtk_window_present(GTK_WINDOW(compose->window));
7214 
7215 	g_free(compose->exteditor_file);
7216 	compose->exteditor_file = NULL;
7217 	compose->exteditor_pid  = 0;
7218 	compose->exteditor_tag  = 0;
7219 
7220 	compose_unlock(compose);
7221 }
7222 
7223 static void compose_set_ext_editor_sensitive(Compose *compose,
7224 					     gboolean sensitive)
7225 {
7226 	GtkItemFactory *ifactory;
7227 
7228 	ifactory = gtk_item_factory_from_widget(compose->menubar);
7229 
7230 	menu_set_sensitive(ifactory, "/File/Send", sensitive);
7231 	menu_set_sensitive(ifactory, "/File/Send later", sensitive);
7232 	menu_set_sensitive(ifactory, "/File/Save to draft folder",
7233 			   sensitive);
7234 	menu_set_sensitive(ifactory, "/File/Insert file", sensitive);
7235 	menu_set_sensitive(ifactory, "/File/Insert signature", sensitive);
7236 	menu_set_sensitive(ifactory, "/File/Append signature", sensitive);
7237 	menu_set_sensitive(ifactory, "/Edit/Wrap current paragraph", sensitive);
7238 	menu_set_sensitive(ifactory, "/Edit/Wrap all long lines", sensitive);
7239 	menu_set_sensitive(ifactory, "/Tools/Edit with external editor",
7240 			   sensitive);
7241 
7242 #define SET_SENS(w) \
7243 	if (compose->w) \
7244 		gtk_widget_set_sensitive(compose->w, sensitive);
7245 
7246 	SET_SENS(text);
7247 	SET_SENS(send_btn);
7248 	SET_SENS(sendl_btn);
7249 	SET_SENS(draft_btn);
7250 	SET_SENS(insert_btn);
7251 	SET_SENS(sig_btn);
7252 	SET_SENS(exteditor_btn);
7253 	SET_SENS(linewrap_btn);
7254 
7255 #undef SET_SENS
7256 }
7257 
7258 /**
7259  * compose_undo_state_changed:
7260  *
7261  * Change the sensivity of the menuentries undo and redo
7262  **/
7263 static void compose_undo_state_changed(UndoMain *undostruct, gint undo_state,
7264 				       gint redo_state, gpointer data)
7265 {
7266 	GtkWidget *widget = GTK_WIDGET(data);
7267 	GtkItemFactory *ifactory;
7268 
7269 	g_return_if_fail(widget != NULL);
7270 
7271 	ifactory = gtk_item_factory_from_widget(widget);
7272 
7273 	switch (undo_state) {
7274 	case UNDO_STATE_TRUE:
7275 		if (!undostruct->undo_state) {
7276 			debug_print ("Set_undo - Testpoint\n");
7277 			undostruct->undo_state = TRUE;
7278 			menu_set_sensitive(ifactory, "/Edit/Undo", TRUE);
7279 		}
7280 		break;
7281 	case UNDO_STATE_FALSE:
7282 		if (undostruct->undo_state) {
7283 			undostruct->undo_state = FALSE;
7284 			menu_set_sensitive(ifactory, "/Edit/Undo", FALSE);
7285 		}
7286 		break;
7287 	case UNDO_STATE_UNCHANGED:
7288 		break;
7289 	case UNDO_STATE_REFRESH:
7290 		menu_set_sensitive(ifactory, "/Edit/Undo",
7291 				   undostruct->undo_state);
7292 		break;
7293 	default:
7294 		g_warning("Undo state not recognized");
7295 		break;
7296 	}
7297 
7298 	switch (redo_state) {
7299 	case UNDO_STATE_TRUE:
7300 		if (!undostruct->redo_state) {
7301 			undostruct->redo_state = TRUE;
7302 			menu_set_sensitive(ifactory, "/Edit/Redo", TRUE);
7303 		}
7304 		break;
7305 	case UNDO_STATE_FALSE:
7306 		if (undostruct->redo_state) {
7307 			undostruct->redo_state = FALSE;
7308 			menu_set_sensitive(ifactory, "/Edit/Redo", FALSE);
7309 		}
7310 		break;
7311 	case UNDO_STATE_UNCHANGED:
7312 		break;
7313 	case UNDO_STATE_REFRESH:
7314 		menu_set_sensitive(ifactory, "/Edit/Redo",
7315 				   undostruct->redo_state);
7316 		break;
7317 	default:
7318 		g_warning("Redo state not recognized");
7319 		break;
7320 	}
7321 }
7322 
7323 static gint calc_cursor_xpos(GtkTextView *text, gint extra, gint char_width)
7324 {
7325 #if 0
7326 	gint cursor_pos;
7327 
7328 	cursor_pos = (text->cursor_pos_x - extra) / char_width;
7329 	cursor_pos = MAX(cursor_pos, 0);
7330 
7331 	return cursor_pos;
7332 #endif
7333 	return 0;
7334 }
7335 
7336 /* callback functions */
7337 
7338 /* compose_edit_size_alloc() - called when resized. don't know whether Gtk
7339  * includes "non-client" (windows-izm) in calculation, so this calculation
7340  * may not be accurate.
7341  */
7342 static gboolean compose_edit_size_alloc(GtkEditable *widget,
7343 					GtkAllocation *allocation,
7344 					GtkSHRuler *shruler)
7345 {
7346 	if (prefs_common.show_ruler) {
7347 		gint char_width = 0, char_height = 0;
7348 		gint line_width_in_chars;
7349 
7350 		gtkut_get_font_size(GTK_WIDGET(widget),
7351 				    &char_width, &char_height);
7352 		line_width_in_chars =
7353 			(allocation->width - allocation->x) / char_width;
7354 
7355 		/* got the maximum */
7356 		gtk_ruler_set_range(GTK_RULER(shruler),
7357 				    0.0, line_width_in_chars,
7358 				    calc_cursor_xpos(GTK_TEXT_VIEW(widget),
7359 						     allocation->x,
7360 						     char_width),
7361 				    /*line_width_in_chars*/ char_width);
7362 	}
7363 
7364 	return TRUE;
7365 }
7366 
7367 static void toolbar_send_cb(GtkWidget *widget, gpointer data)
7368 {
7369 	compose_send((Compose *)data, TRUE);
7370 }
7371 
7372 static void toolbar_send_later_cb(GtkWidget *widget, gpointer data)
7373 {
7374 	compose_send_later_cb(data, 0, NULL);
7375 }
7376 
7377 static void toolbar_draft_cb(GtkWidget *widget, gpointer data)
7378 {
7379 	compose_draft_cb(data, 0, NULL);
7380 }
7381 
7382 static void toolbar_insert_cb(GtkWidget *widget, gpointer data)
7383 {
7384 	compose_insert_file_cb(data, 0, NULL);
7385 }
7386 
7387 static void toolbar_attach_cb(GtkWidget *widget, gpointer data)
7388 {
7389 	compose_attach_cb(data, 0, NULL);
7390 }
7391 
7392 static void toolbar_sig_cb(GtkWidget *widget, gpointer data)
7393 {
7394 	Compose *compose = (Compose *)data;
7395 
7396 	compose_insert_sig(compose, TRUE, TRUE, TRUE);
7397 }
7398 
7399 static void toolbar_ext_editor_cb(GtkWidget *widget, gpointer data)
7400 {
7401 	Compose *compose = (Compose *)data;
7402 
7403 	compose_exec_ext_editor(compose);
7404 }
7405 
7406 static void toolbar_linewrap_cb(GtkWidget *widget, gpointer data)
7407 {
7408 	Compose *compose = (Compose *)data;
7409 
7410 	compose_wrap_all(compose);
7411 }
7412 
7413 static void toolbar_address_cb(GtkWidget *widget, gpointer data)
7414 {
7415 	compose_address_cb(data, 0, NULL);
7416 }
7417 
7418 static void toolbar_prefs_common_cb(GtkWidget *widget, gpointer data)
7419 {
7420 	prefs_common_open();
7421 }
7422 
7423 static void toolbar_prefs_account_cb(GtkWidget *widget, gpointer data)
7424 {
7425 	account_open(cur_account);
7426 }
7427 
7428 static void toolbar_customize(GtkWidget *widget, gpointer data)
7429 {
7430 	Compose *compose = (Compose *)data;
7431 	gint *visible_items;
7432 	GList *item_list = NULL;
7433 	GtkWidget *toolbar;
7434 	gint ret;
7435 	const gchar *setting;
7436 
7437 	if (prefs_common.compose_toolbar_setting &&
7438 	    *prefs_common.compose_toolbar_setting != '\0')
7439 		setting = prefs_common.compose_toolbar_setting;
7440 	else
7441 		setting = prefs_toolbar_get_default_compose_setting_name_list();
7442 	visible_items = prefs_toolbar_get_id_list_from_name_list(setting);
7443 	ret = prefs_toolbar_open(TOOLBAR_COMPOSE, visible_items, &item_list);
7444 	g_free(visible_items);
7445 
7446 	if (ret == 0) {
7447 		gtk_widget_destroy(compose->toolbar);
7448 		toolbar = compose_toolbar_create_from_list(compose, item_list);
7449 		gtk_widget_set_size_request(toolbar, 300, -1);
7450 		gtk_box_pack_start(GTK_BOX(compose->vbox), toolbar,
7451 				   FALSE, FALSE, 0);
7452 		gtk_box_reorder_child(GTK_BOX(compose->vbox), toolbar, 1);
7453 		compose->toolbar = toolbar;
7454 		compose_set_toolbar_button_visibility(compose);
7455 		g_free(prefs_common.compose_toolbar_setting);
7456 		prefs_common.compose_toolbar_setting =
7457 			prefs_toolbar_get_name_list_from_item_list(item_list);
7458 		g_list_free(item_list);
7459 		prefs_common_write_config();
7460 
7461 		syl_plugin_signal_emit("compose-toolbar-changed", compose);
7462 	}
7463 }
7464 
7465 static gboolean toolbar_button_pressed(GtkWidget *widget, GdkEventButton *event,
7466 				       gpointer data)
7467 {
7468 	Compose *compose = (Compose *)data;
7469 	GtkWidget *menu;
7470 	GtkWidget *menuitem;
7471 
7472 	if (!event) return FALSE;
7473 	if (event->button != 3) return FALSE;
7474 
7475 	menu = gtk_menu_new();
7476 	gtk_widget_show(menu);
7477 
7478 	MENUITEM_ADD_WITH_MNEMONIC(menu, menuitem, _("_Customize toolbar..."),
7479 				   0);
7480 	g_signal_connect(G_OBJECT(menuitem), "activate",
7481 			 G_CALLBACK(toolbar_customize), compose);
7482 	g_signal_connect(G_OBJECT(menu), "selection_done",
7483 			 G_CALLBACK(gtk_widget_destroy), NULL);
7484 
7485 	gtk_menu_popup(GTK_MENU(menu), NULL, NULL, NULL, NULL,
7486 		       event->button, event->time);
7487 
7488 	return TRUE;
7489 }
7490 
7491 static void account_activated(GtkMenuItem *menuitem, gpointer data)
7492 {
7493 	Compose *compose = (Compose *)data;
7494 
7495 	PrefsAccount *ac;
7496 
7497 	ac = (PrefsAccount *)g_object_get_data(G_OBJECT(menuitem), MENU_VAL_ID);
7498 	g_return_if_fail(ac != NULL);
7499 
7500 	if (ac != compose->account)
7501 		compose_select_account(compose, ac, FALSE);
7502 }
7503 
7504 static void sig_combo_changed(GtkComboBox *combo, gpointer data)
7505 {
7506 	Compose *compose = (Compose *)data;
7507 
7508 	if (compose->mode != COMPOSE_REDIRECT && prefs_common.auto_sig)
7509 		compose_insert_sig(compose, TRUE, TRUE, FALSE);
7510 }
7511 
7512 static void attach_selection_changed(GtkTreeSelection *selection, gpointer data)
7513 {
7514 }
7515 
7516 static gboolean attach_button_pressed(GtkWidget *widget, GdkEventButton *event,
7517 				      gpointer data)
7518 {
7519 	Compose *compose = (Compose *)data;
7520 	GtkTreeView *treeview = GTK_TREE_VIEW(compose->attach_treeview);
7521 	GtkTreeSelection *selection;
7522 	GtkTreePath *path = NULL;
7523 
7524 	if (!event) return FALSE;
7525 
7526 	gtk_tree_view_get_path_at_pos(treeview, event->x, event->y,
7527 				      &path, NULL, NULL, NULL);
7528 
7529 	if (event->button == 2 && path)
7530 		gtk_tree_view_set_cursor(treeview, path, NULL, FALSE);
7531 
7532 	if (event->button == 2 ||
7533 	    (event->button == 1 && event->type == GDK_2BUTTON_PRESS)) {
7534 		compose_attach_property(compose);
7535 	} else if (event->button == 3) {
7536 		GList *rows;
7537 		gboolean has_selection = FALSE;
7538 
7539 		selection = gtk_tree_view_get_selection(treeview);
7540 		rows = gtk_tree_selection_get_selected_rows(selection, NULL);
7541 		if (rows) {
7542 			has_selection = TRUE;
7543 			g_list_free(rows);
7544 		}
7545 		if (path)
7546 			has_selection = TRUE;
7547 		menu_set_sensitive(compose->popupfactory, "/Open", has_selection);
7548 		menu_set_sensitive(compose->popupfactory, "/Add...", TRUE);
7549 		menu_set_sensitive(compose->popupfactory, "/Remove", has_selection);
7550 		menu_set_sensitive(compose->popupfactory, "/Properties...", has_selection);
7551 		gtk_menu_popup(GTK_MENU(compose->popupmenu), NULL, NULL,
7552 			       NULL, NULL, event->button, event->time);
7553 
7554 		if (path &&
7555 		    gtk_tree_selection_path_is_selected(selection, path)) {
7556 			gtk_tree_path_free(path);
7557 			return TRUE;
7558 		}
7559 	}
7560 
7561 	gtk_tree_path_free(path);
7562 	return FALSE;
7563 }
7564 
7565 static gboolean attach_key_pressed(GtkWidget *widget, GdkEventKey *event,
7566 				   gpointer data)
7567 {
7568 	Compose *compose = (Compose *)data;
7569 
7570 	if (!event) return FALSE;
7571 
7572 	switch (event->keyval) {
7573 	case GDK_Delete:
7574 		compose_attach_remove_selected(compose);
7575 		break;
7576 	}
7577 
7578 	return FALSE;
7579 }
7580 
7581 gint compose_send(Compose *compose, gboolean close_on_success)
7582 {
7583 	gint val;
7584 
7585 	if (compose->lock_count > 0)
7586 		return 1;
7587 
7588 	gtk_widget_set_sensitive(compose->vbox, FALSE);
7589 	val = compose_send_real(compose);
7590 	gtk_widget_set_sensitive(compose->vbox, TRUE);
7591 
7592 	if (val == 0 && close_on_success)
7593 		compose_destroy(compose);
7594 
7595 	return val;
7596 }
7597 
7598 static void compose_send_cb(gpointer data, guint action, GtkWidget *widget)
7599 {
7600 	Compose *compose = (Compose *)data;
7601 
7602 	compose_send(compose, TRUE);
7603 }
7604 
7605 static void compose_send_later_cb(gpointer data, guint action,
7606 				  GtkWidget *widget)
7607 {
7608 	Compose *compose = (Compose *)data;
7609 	FolderItem *queue;
7610 	gchar tmp[MAXPATHLEN + 1];
7611 	gboolean cancel = FALSE;
7612 
7613 	if (compose->lock_count > 0)
7614 		return;
7615 
7616 	C_LOCK();
7617 
7618 	if (compose_check_entries(compose) == FALSE) {
7619 		C_UNLOCK();
7620 		return;
7621 	}
7622 	if (compose_check_attachments(compose) == FALSE) {
7623 		C_UNLOCK();
7624 		return;
7625 	}
7626 	if (compose_check_recipients(compose) == FALSE) {
7627 		C_UNLOCK();
7628 		return;
7629 	}
7630 
7631 	queue = account_get_special_folder(compose->account, F_QUEUE);
7632 	if (!queue) {
7633 		g_warning("can't find queue folder\n");
7634 		C_UNLOCK();
7635 		return;
7636 	}
7637 
7638 	if (!FOLDER_IS_LOCAL(queue->folder)) {
7639 		if (compose_check_activities(compose) == FALSE) {
7640 			C_UNLOCK();
7641 			return;
7642 		}
7643 		if (!main_window_toggle_online_if_offline(main_window_get())) {
7644 			C_UNLOCK();
7645 			return;
7646 		}
7647 	}
7648 
7649 	g_snprintf(tmp, sizeof(tmp), "%s%ctmpmsg.%p",
7650 		   get_tmp_dir(), G_DIR_SEPARATOR, compose);
7651 
7652 	if (compose->mode == COMPOSE_REDIRECT) {
7653 		if (compose_redirect_write_to_file(compose, tmp) < 0) {
7654 			alertpanel_error(_("Can't queue the message."));
7655 			C_UNLOCK();
7656 			return;
7657 		}
7658 	} else {
7659 		if (compose_write_to_file(compose, tmp, FALSE) < 0) {
7660 			alertpanel_error(_("Can't queue the message."));
7661 			C_UNLOCK();
7662 			return;
7663 		}
7664 	}
7665 
7666 	if (!compose->to_list && !compose->newsgroup_list) {
7667 		g_warning("can't get recipient list.");
7668 		g_unlink(tmp);
7669 		C_UNLOCK();
7670 		return;
7671 	}
7672 
7673 	syl_plugin_signal_emit("compose-send", compose, compose->mode, 1,
7674 			       tmp, compose->to_list, &cancel);
7675 	if (cancel) {
7676 		g_unlink(tmp);
7677 		C_UNLOCK();
7678 		return;
7679 	}
7680 
7681 	if (compose_queue(compose, tmp) < 0) {
7682 		alertpanel_error(_("Can't queue the message."));
7683 		g_unlink(tmp);
7684 		C_UNLOCK();
7685 		return;
7686 	}
7687 
7688 	if (g_unlink(tmp) < 0)
7689 		FILE_OP_ERROR(tmp, "unlink");
7690 
7691 	C_UNLOCK();
7692 	compose_destroy(compose);
7693 }
7694 
7695 static void compose_draft_cb(gpointer data, guint action, GtkWidget *widget)
7696 {
7697 	Compose *compose = (Compose *)data;
7698 	FolderItem *draft;
7699 	gchar *tmp;
7700 	gint msgnum;
7701 	MsgFlags flag = {0, 0};
7702 
7703 	if (compose->lock_count > 0)
7704 		return;
7705 
7706 	draft = account_get_special_folder(compose->account, F_DRAFT);
7707 	g_return_if_fail(draft != NULL);
7708 
7709 	C_LOCK();
7710 
7711 	if (!FOLDER_IS_LOCAL(draft->folder)) {
7712 		if (compose_check_activities(compose) == FALSE) {
7713 			C_UNLOCK();
7714 			return;
7715 		}
7716 		if (!main_window_toggle_online_if_offline(main_window_get())) {
7717 			C_UNLOCK();
7718 			return;
7719 		}
7720 	}
7721 
7722 	tmp = g_strdup_printf("%s%cdraft.%p", get_tmp_dir(),
7723 			      G_DIR_SEPARATOR, compose);
7724 
7725 	if (compose_write_to_file(compose, tmp, TRUE) < 0) {
7726 		g_free(tmp);
7727 		C_UNLOCK();
7728 		return;
7729 	}
7730 
7731 	folder_item_scan(draft);
7732 	if ((msgnum = folder_item_add_msg(draft, tmp, &flag, TRUE)) < 0) {
7733 		g_unlink(tmp);
7734 		g_free(tmp);
7735 		C_UNLOCK();
7736 		return;
7737 	}
7738 	g_free(tmp);
7739 
7740 	if (compose->mode == COMPOSE_REEDIT) {
7741 		compose_remove_reedit_target(compose);
7742 		if (compose->targetinfo &&
7743 		    compose->targetinfo->folder != draft)
7744 			folderview_update_item(compose->targetinfo->folder,
7745 					       TRUE);
7746 	}
7747 
7748 	folder_item_scan(draft);
7749 	folderview_update_item(draft, TRUE);
7750 
7751 	/* 0: quit editing  1: keep editing */
7752 	if (action == 0) {
7753 		C_UNLOCK();
7754 		compose_destroy(compose);
7755 	} else {
7756 		GStatBuf s;
7757 		gchar *path;
7758 
7759 		path = folder_item_fetch_msg(draft, msgnum);
7760 		C_UNLOCK();
7761 		g_return_if_fail(path != NULL);
7762 		if (g_stat(path, &s) < 0) {
7763 			FILE_OP_ERROR(path, "stat");
7764 			g_free(path);
7765 			return;
7766 		}
7767 		g_free(path);
7768 
7769 		procmsg_msginfo_free(compose->targetinfo);
7770 		compose->targetinfo = g_new0(MsgInfo, 1);
7771 		compose->targetinfo->msgnum = msgnum;
7772 		compose->targetinfo->size = s.st_size;
7773 		compose->targetinfo->mtime = s.st_mtime;
7774 		compose->targetinfo->folder = draft;
7775 		compose->mode = COMPOSE_REEDIT;
7776 		compose->modified = FALSE;
7777 		compose_set_title(compose);
7778 	}
7779 }
7780 
7781 static void compose_attach_open_cb(gpointer data, guint action,
7782 				   GtkWidget *widget)
7783 {
7784 	Compose *compose = (Compose *)data;
7785 
7786 	compose_attach_open(compose);
7787 }
7788 
7789 static void compose_attach_cb(gpointer data, guint action, GtkWidget *widget)
7790 {
7791 	Compose *compose = (Compose *)data;
7792 	GSList *files;
7793 	GSList *cur;
7794 
7795 	files = filesel_select_files(_("Select files"), NULL,
7796 				     GTK_FILE_CHOOSER_ACTION_OPEN);
7797 
7798 	for (cur = files; cur != NULL; cur = cur->next) {
7799 		gchar *file = (gchar *)cur->data;
7800 		gchar *utf8_filename;
7801 
7802 		utf8_filename = conv_filename_to_utf8(file);
7803 		compose_attach_append(compose, file, utf8_filename, NULL);
7804 		compose_changed_cb(NULL, compose);
7805 		g_free(utf8_filename);
7806 		g_free(file);
7807 	}
7808 
7809 	g_slist_free(files);
7810 }
7811 
7812 static void compose_insert_file_cb(gpointer data, guint action,
7813 				   GtkWidget *widget)
7814 {
7815 	Compose *compose = (Compose *)data;
7816 	gchar *file;
7817 
7818 	file = filesel_select_file(_("Select file"), NULL,
7819 				   GTK_FILE_CHOOSER_ACTION_OPEN);
7820 
7821 	if (file && *file)
7822 		compose_insert_file(compose, file, TRUE);
7823 
7824 	g_free(file);
7825 }
7826 
7827 static void compose_insert_sig_cb(gpointer data, guint action,
7828 				  GtkWidget *widget)
7829 {
7830 	Compose *compose = (Compose *)data;
7831 
7832 	compose_insert_sig(compose, action, TRUE, TRUE);
7833 }
7834 
7835 static gint compose_delete_cb(GtkWidget *widget, GdkEventAny *event,
7836 			      gpointer data)
7837 {
7838 	compose_close_cb(data, 0, NULL);
7839 	return TRUE;
7840 }
7841 
7842 static gint compose_window_state_cb(GtkWidget *widget,
7843 				    GdkEventWindowState *event,
7844 				    gpointer data)
7845 {
7846 	Compose *compose = (Compose *)data;
7847 
7848 	if ((event->changed_mask & GDK_WINDOW_STATE_MAXIMIZED) != 0) {
7849 		if ((event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED) != 0)
7850 			compose->window_maximized = TRUE;
7851 		else
7852 			compose->window_maximized = FALSE;
7853 	}
7854 
7855 	return FALSE;
7856 }
7857 
7858 static void compose_close_cb(gpointer data, guint action, GtkWidget *widget)
7859 {
7860 	Compose *compose = (Compose *)data;
7861 	AlertValue val;
7862 
7863 	if (compose->lock_count > 0)
7864 		return;
7865 
7866 	if (compose->exteditor_pid != 0) {
7867 		if (!compose_ext_editor_kill(compose))
7868 			return;
7869 	}
7870 
7871 	if (compose->modified) {
7872 		val = alertpanel(_("Save message"),
7873 				 _("This message has been modified. Save it to draft folder?"),
7874 #ifdef G_OS_WIN32
7875 				 GTK_STOCK_SAVE, _("Close _without saving"),
7876 				 GTK_STOCK_CANCEL);
7877 #else
7878 				 GTK_STOCK_SAVE, GTK_STOCK_CANCEL,
7879 				 _("Close _without saving"));
7880 #endif
7881 
7882 		switch (val) {
7883 		case G_ALERTDEFAULT:
7884 			compose_draft_cb(data, 0, NULL);
7885 			return;
7886 #ifdef G_OS_WIN32
7887 		case G_ALERTALTERNATE:
7888 #else
7889 		case G_ALERTOTHER:
7890 #endif
7891 			break;
7892 		default:
7893 			return;
7894 		}
7895 	}
7896 
7897 	compose_destroy(compose);
7898 }
7899 
7900 static void compose_set_encoding_cb(gpointer data, guint action,
7901 				    GtkWidget *widget)
7902 {
7903 	Compose *compose = (Compose *)data;
7904 
7905 	if (GTK_CHECK_MENU_ITEM(widget)->active)
7906 		compose->out_encoding = (CharSet)action;
7907 }
7908 
7909 static void compose_address_cb(gpointer data, guint action, GtkWidget *widget)
7910 {
7911 	Compose *compose = (Compose *)data;
7912 
7913 	addressbook_open(compose);
7914 }
7915 
7916 static void compose_template_activate_cb(GtkWidget *widget, gpointer data)
7917 {
7918 	Compose *compose = (Compose *)data;
7919 	Template *tmpl;
7920 	gchar *msg;
7921 	AlertValue val;
7922 
7923 	tmpl = g_object_get_data(G_OBJECT(widget), "template");
7924 	g_return_if_fail(tmpl != NULL);
7925 
7926 	msg = g_strdup_printf(_("Do you want to apply the template `%s' ?"),
7927 			      tmpl->name);
7928 	val = alertpanel(_("Apply template"), msg,
7929 			 _("_Replace"), _("_Insert"), GTK_STOCK_CANCEL);
7930 	g_free(msg);
7931 
7932 	if (val == G_ALERTDEFAULT)
7933 		compose_template_apply(compose, tmpl, TRUE);
7934 	else if (val == G_ALERTALTERNATE)
7935 		compose_template_apply(compose, tmpl, FALSE);
7936 }
7937 
7938 static void compose_ext_editor_cb(gpointer data, guint action,
7939 				  GtkWidget *widget)
7940 {
7941 	Compose *compose = (Compose *)data;
7942 
7943 	compose_exec_ext_editor(compose);
7944 }
7945 
7946 static void compose_undo_cb(Compose *compose)
7947 {
7948 	gboolean prev_autowrap = compose->autowrap;
7949 
7950 	compose->autowrap = FALSE;
7951 	undo_undo(compose->undostruct);
7952 	compose->autowrap = prev_autowrap;
7953 }
7954 
7955 static void compose_redo_cb(Compose *compose)
7956 {
7957 	gboolean prev_autowrap = compose->autowrap;
7958 
7959 	compose->autowrap = FALSE;
7960 	undo_redo(compose->undostruct);
7961 	compose->autowrap = prev_autowrap;
7962 }
7963 
7964 static void compose_cut_cb(Compose *compose)
7965 {
7966 	if (compose->focused_editable &&
7967 	    GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
7968 		if (GTK_IS_EDITABLE(compose->focused_editable)) {
7969 			gtk_editable_cut_clipboard
7970 				(GTK_EDITABLE(compose->focused_editable));
7971 		} else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) {
7972 			GtkTextView *text = GTK_TEXT_VIEW(compose->text);
7973 			GtkTextBuffer *buffer;
7974 			GtkClipboard *clipboard;
7975 
7976 			buffer = gtk_text_view_get_buffer(text);
7977 			clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
7978 
7979 			gtk_text_buffer_cut_clipboard(buffer, clipboard, TRUE);
7980 		}
7981 	}
7982 }
7983 
7984 static void compose_copy_cb(Compose *compose)
7985 {
7986 	if (compose->focused_editable &&
7987 	    GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
7988 		if (GTK_IS_EDITABLE(compose->focused_editable)) {
7989 			gtk_editable_copy_clipboard
7990 				(GTK_EDITABLE(compose->focused_editable));
7991 		} else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) {
7992 			GtkTextView *text = GTK_TEXT_VIEW(compose->text);
7993 			GtkTextBuffer *buffer;
7994 			GtkClipboard *clipboard;
7995 
7996 			buffer = gtk_text_view_get_buffer(text);
7997 			clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
7998 
7999 			gtk_text_buffer_copy_clipboard(buffer, clipboard);
8000 		}
8001 	}
8002 }
8003 
8004 static void compose_paste_cb(Compose *compose)
8005 {
8006 	if (compose->focused_editable &&
8007 	    GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
8008 		if (GTK_IS_EDITABLE(compose->focused_editable)) {
8009 			gtk_editable_paste_clipboard
8010 				(GTK_EDITABLE(compose->focused_editable));
8011 		} else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) {
8012 			GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8013 			GtkTextBuffer *buffer;
8014 			GtkTextMark *mark;
8015 			GtkClipboard *clipboard;
8016 
8017 			buffer = gtk_text_view_get_buffer(text);
8018 			mark = gtk_text_buffer_get_insert(buffer);
8019 			clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
8020 
8021 			gtk_text_buffer_paste_clipboard(buffer, clipboard,
8022 							NULL, TRUE);
8023 
8024 			gtk_text_view_scroll_mark_onscreen(text, mark);
8025 		}
8026 	}
8027 }
8028 
8029 static void compose_paste_as_quote_cb(Compose *compose)
8030 {
8031 	GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8032 	GtkTextBuffer *buffer;
8033 	GtkTextMark *mark;
8034 	GtkClipboard *clipboard;
8035 	gchar *str = NULL;
8036 
8037 	if (!compose->focused_editable ||
8038 	    !GTK_WIDGET_HAS_FOCUS(compose->focused_editable) ||
8039 	    !GTK_IS_TEXT_VIEW(compose->focused_editable))
8040 			return;
8041 
8042 	buffer = gtk_text_view_get_buffer(text);
8043 	mark = gtk_text_buffer_get_insert(buffer);
8044 	clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
8045 	str = gtk_clipboard_wait_for_text(clipboard);
8046 	if (!str)
8047 		return;
8048 
8049 	compose_quote_fmt(compose, NULL, "%Q", prefs_common.quotemark, str);
8050 
8051 	g_free(str);
8052 
8053 	gtk_text_view_scroll_mark_onscreen(text, mark);
8054 }
8055 
8056 static void compose_allsel_cb(Compose *compose)
8057 {
8058 	if (compose->focused_editable &&
8059 	    GTK_WIDGET_HAS_FOCUS(compose->focused_editable)) {
8060 		if (GTK_IS_EDITABLE(compose->focused_editable)) {
8061 			gtk_editable_select_region
8062 				(GTK_EDITABLE(compose->focused_editable),
8063 				 0, -1);
8064 		} else if (GTK_IS_TEXT_VIEW(compose->focused_editable)) {
8065 			GtkTextView *text = GTK_TEXT_VIEW(compose->text);
8066 			GtkTextBuffer *buffer;
8067 			GtkTextIter iter;
8068 
8069 			buffer = gtk_text_view_get_buffer(text);
8070 			gtk_text_buffer_get_start_iter(buffer, &iter);
8071 			gtk_text_buffer_place_cursor(buffer, &iter);
8072 			gtk_text_buffer_get_end_iter(buffer, &iter);
8073 			gtk_text_buffer_move_mark_by_name
8074 				(buffer, "selection_bound", &iter);
8075 		}
8076 	}
8077 }
8078 
8079 static void compose_grab_focus_cb(GtkWidget *widget, Compose *compose)
8080 {
8081 	if (GTK_IS_EDITABLE(widget) || GTK_IS_TEXT_VIEW(widget))
8082 		compose->focused_editable = widget;
8083 }
8084 
8085 #if USE_GPGME
8086 static void compose_signing_toggled(GtkWidget *widget, Compose *compose)
8087 {
8088 	GtkItemFactory *ifactory;
8089 
8090 	if (!rfc2015_is_available())
8091 		return;
8092 
8093 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
8094 		compose->use_signing = TRUE;
8095 	else
8096 		compose->use_signing = FALSE;
8097 
8098 	ifactory = gtk_item_factory_from_widget(compose->menubar);
8099 	menu_set_active(ifactory, "/Tools/PGP Sign", compose->use_signing);
8100 }
8101 
8102 static void compose_encrypt_toggled(GtkWidget *widget, Compose *compose)
8103 {
8104 	GtkItemFactory *ifactory;
8105 
8106 	if (!rfc2015_is_available())
8107 		return;
8108 
8109 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
8110 		compose->use_encryption = TRUE;
8111 	else
8112 		compose->use_encryption = FALSE;
8113 
8114 	ifactory = gtk_item_factory_from_widget(compose->menubar);
8115 	menu_set_active(ifactory, "/Tools/PGP Encrypt",
8116 			compose->use_encryption);
8117 }
8118 #endif /* USE_GPGME */
8119 
8120 #if 0
8121 static void compose_attach_toggled(GtkWidget *widget, Compose *compose)
8122 {
8123 	GtkItemFactory *ifactory;
8124 
8125 	if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)))
8126 		compose->use_attach = TRUE;
8127 	else
8128 		compose->use_attach = FALSE;
8129 
8130 	ifactory = gtk_item_factory_from_widget(compose->menubar);
8131 	menu_set_active(ifactory, "/View/Attachment", compose->use_attach);
8132 }
8133 #endif
8134 
8135 static void compose_buffer_changed_cb(GtkTextBuffer *textbuf, Compose *compose)
8136 {
8137 	if (compose->modified == FALSE && compose->block_modified == FALSE) {
8138 		compose->modified = TRUE;
8139 		compose_set_title(compose);
8140 	}
8141 }
8142 
8143 static void compose_changed_cb(GtkEditable *editable, Compose *compose)
8144 {
8145 	if (compose->block_modified == FALSE &&
8146 	    (compose->modified == FALSE ||
8147 	     editable == GTK_EDITABLE(compose->subject_entry))) {
8148 		compose->modified = TRUE;
8149 		compose_set_title(compose);
8150 	}
8151 }
8152 
8153 static void compose_wrap_cb(gpointer data, guint action, GtkWidget *widget)
8154 {
8155 	Compose *compose = (Compose *)data;
8156 
8157 	if (action == 1)
8158 		compose_wrap_all(compose);
8159 	else
8160 		compose_wrap_paragraph(compose, NULL);
8161 }
8162 
8163 static void compose_toggle_autowrap_cb(gpointer data, guint action,
8164 				       GtkWidget *widget)
8165 {
8166 	Compose *compose = (Compose *)data;
8167 
8168 	compose->autowrap = GTK_CHECK_MENU_ITEM(widget)->active;
8169 	if (compose->autowrap)
8170 		compose_wrap_all_full(compose, TRUE);
8171 }
8172 
8173 static void compose_toggle_to_cb(gpointer data, guint action,
8174 				 GtkWidget *widget)
8175 {
8176 	Compose *compose = (Compose *)data;
8177 
8178 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8179 		gtk_widget_show(compose->to_hbox);
8180 		gtk_widget_show(compose->to_entry);
8181 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 4);
8182 		compose->use_to = TRUE;
8183 	} else {
8184 		gtk_widget_hide(compose->to_hbox);
8185 		gtk_widget_hide(compose->to_entry);
8186 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 1, 0);
8187 		gtk_widget_queue_resize(compose->table_vbox);
8188 		compose->use_to = FALSE;
8189 	}
8190 }
8191 
8192 static void compose_toggle_cc_cb(gpointer data, guint action,
8193 				 GtkWidget *widget)
8194 {
8195 	Compose *compose = (Compose *)data;
8196 
8197 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8198 		gtk_widget_show(compose->cc_hbox);
8199 		gtk_widget_show(compose->cc_entry);
8200 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 4);
8201 		compose->use_cc = TRUE;
8202 	} else {
8203 		gtk_widget_hide(compose->cc_hbox);
8204 		gtk_widget_hide(compose->cc_entry);
8205 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 3, 0);
8206 		gtk_widget_queue_resize(compose->table_vbox);
8207 		compose->use_cc = FALSE;
8208 	}
8209 }
8210 
8211 static void compose_toggle_bcc_cb(gpointer data, guint action,
8212 				  GtkWidget *widget)
8213 {
8214 	Compose *compose = (Compose *)data;
8215 
8216 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8217 		gtk_widget_show(compose->bcc_hbox);
8218 		gtk_widget_show(compose->bcc_entry);
8219 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 4);
8220 		compose->use_bcc = TRUE;
8221 	} else {
8222 		gtk_widget_hide(compose->bcc_hbox);
8223 		gtk_widget_hide(compose->bcc_entry);
8224 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 4, 0);
8225 		gtk_widget_queue_resize(compose->table_vbox);
8226 		compose->use_bcc = FALSE;
8227 	}
8228 }
8229 
8230 static void compose_toggle_replyto_cb(gpointer data, guint action,
8231 				      GtkWidget *widget)
8232 {
8233 	Compose *compose = (Compose *)data;
8234 
8235 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8236 		gtk_widget_show(compose->reply_hbox);
8237 		gtk_widget_show(compose->reply_entry);
8238 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 4);
8239 		compose->use_replyto = TRUE;
8240 	} else {
8241 		gtk_widget_hide(compose->reply_hbox);
8242 		gtk_widget_hide(compose->reply_entry);
8243 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 5, 0);
8244 		gtk_widget_queue_resize(compose->table_vbox);
8245 		compose->use_replyto = FALSE;
8246 	}
8247 }
8248 
8249 static void compose_toggle_followupto_cb(gpointer data, guint action,
8250 					 GtkWidget *widget)
8251 {
8252 	Compose *compose = (Compose *)data;
8253 
8254 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8255 		gtk_widget_show(compose->followup_hbox);
8256 		gtk_widget_show(compose->followup_entry);
8257 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 4);
8258 		compose->use_followupto = TRUE;
8259 	} else {
8260 		gtk_widget_hide(compose->followup_hbox);
8261 		gtk_widget_hide(compose->followup_entry);
8262 		gtk_table_set_row_spacing(GTK_TABLE(compose->table), 6, 0);
8263 		gtk_widget_queue_resize(compose->table_vbox);
8264 		compose->use_followupto = FALSE;
8265 	}
8266 }
8267 
8268 static void compose_toggle_attach_cb(gpointer data, guint action,
8269 				     GtkWidget *widget)
8270 {
8271 	Compose *compose = (Compose *)data;
8272 
8273 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8274 		gtk_widget_ref(compose->edit_vbox);
8275 
8276 		gtkut_container_remove(GTK_CONTAINER(compose->vbox2),
8277 		 		       compose->edit_vbox);
8278 		gtk_paned_add2(GTK_PANED(compose->paned), compose->edit_vbox);
8279 		gtk_box_pack_start(GTK_BOX(compose->vbox2), compose->paned,
8280 				   TRUE, TRUE, 0);
8281 		gtk_widget_show(compose->paned);
8282 
8283 		gtk_widget_unref(compose->edit_vbox);
8284 		gtk_widget_unref(compose->paned);
8285 
8286 		compose->use_attach = TRUE;
8287 	} else {
8288 		gtk_widget_ref(compose->paned);
8289 		gtk_widget_ref(compose->edit_vbox);
8290 
8291 		gtkut_container_remove(GTK_CONTAINER(compose->vbox2),
8292 		  		       compose->paned);
8293 		gtkut_container_remove(GTK_CONTAINER(compose->paned),
8294 		  		       compose->edit_vbox);
8295 		gtk_box_pack_start(GTK_BOX(compose->vbox2),
8296 				   compose->edit_vbox, TRUE, TRUE, 0);
8297 
8298 		gtk_widget_unref(compose->edit_vbox);
8299 
8300 		compose->use_attach = FALSE;
8301 	}
8302 
8303 #if 0
8304 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->attach_toggle),
8305 				     compose->use_attach);
8306 #endif
8307 
8308 	syl_plugin_signal_emit("compose-attach-changed", compose);
8309 }
8310 
8311 static void compose_customize_toolbar_cb(gpointer data, guint action,
8312 					 GtkWidget *widget)
8313 {
8314 	toolbar_customize(widget, data);
8315 }
8316 
8317 static void compose_toggle_mdn_cb(gpointer data, guint action,
8318 				  GtkWidget *widget)
8319 {
8320 	Compose *compose = (Compose *)data;
8321 
8322 	if (GTK_CHECK_MENU_ITEM(widget)->active)
8323 		compose->use_mdn = TRUE;
8324 	else
8325 		compose->use_mdn = FALSE;
8326 }
8327 
8328 #if USE_GPGME
8329 static void compose_toggle_sign_cb(gpointer data, guint action,
8330 				   GtkWidget *widget)
8331 {
8332 	Compose *compose = (Compose *)data;
8333 
8334 	if (!rfc2015_is_available())
8335 		return;
8336 
8337 	if (GTK_CHECK_MENU_ITEM(widget)->active)
8338 		compose->use_signing = TRUE;
8339 	else
8340 		compose->use_signing = FALSE;
8341 
8342 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->signing_chkbtn),
8343 				     compose->use_signing);
8344 }
8345 
8346 static void compose_toggle_encrypt_cb(gpointer data, guint action,
8347 				      GtkWidget *widget)
8348 {
8349 	Compose *compose = (Compose *)data;
8350 
8351 	if (!rfc2015_is_available())
8352 		return;
8353 
8354 	if (GTK_CHECK_MENU_ITEM(widget)->active)
8355 		compose->use_encryption = TRUE;
8356 	else
8357 		compose->use_encryption = FALSE;
8358 
8359 	gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->encrypt_chkbtn),
8360 				     compose->use_encryption);
8361 }
8362 #endif /* USE_GPGME */
8363 
8364 #if USE_GTKSPELL
8365 static void compose_toggle_spell_cb(gpointer data, guint action,
8366 				    GtkWidget *widget)
8367 {
8368 	Compose *compose = (Compose *)data;
8369 	GtkSpell *speller;
8370 
8371 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8372 		debug_print("Spell checking enabled: %s\n",
8373 			    compose->spell_lang ? compose->spell_lang : "(none)");
8374 		speller = gtkspell_new_attach(GTK_TEXT_VIEW(compose->text),
8375 					      compose->spell_lang, NULL);
8376 		compose->check_spell = TRUE;
8377 	} else {
8378 		debug_print("Spell checking disabled\n");
8379 		speller = gtkspell_get_from_text_view
8380 			(GTK_TEXT_VIEW(compose->text));
8381 		if (speller != NULL)
8382 			gtkspell_detach(speller);
8383 		compose->check_spell = FALSE;
8384 	}
8385 }
8386 
8387 static void compose_set_spell_lang_cb(GtkWidget *widget,
8388 				      gpointer data)
8389 {
8390 	Compose *compose = (Compose *)data;
8391 	gchar *dict;
8392 	GtkSpell *speller;
8393 
8394 	if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(widget)))
8395 		return;
8396 
8397 	dict = g_object_get_data(G_OBJECT(widget), "spell-lang");
8398 
8399 	g_free(compose->spell_lang);
8400 	compose->spell_lang = g_strdup(dict);
8401 
8402 	speller = gtkspell_get_from_text_view(GTK_TEXT_VIEW(compose->text));
8403 	if (speller != NULL)
8404 		gtkspell_set_language(speller, dict, NULL);
8405 
8406 	debug_print("Spell lang set to \"%s\"\n", dict);
8407 }
8408 #endif /* USE_GTKSPELL */
8409 
8410 static void compose_toggle_ruler_cb(gpointer data, guint action,
8411 				    GtkWidget *widget)
8412 {
8413 	Compose *compose = (Compose *)data;
8414 
8415 	if (GTK_CHECK_MENU_ITEM(widget)->active) {
8416 		gtk_widget_show(compose->ruler_hbox);
8417 		prefs_common.show_ruler = TRUE;
8418 	} else {
8419 		gtk_widget_hide(compose->ruler_hbox);
8420 		gtk_widget_queue_resize(compose->edit_vbox);
8421 		prefs_common.show_ruler = FALSE;
8422 	}
8423 }
8424 
8425 static void compose_attach_drag_received_cb (GtkWidget		*widget,
8426 					     GdkDragContext	*drag_context,
8427 					     gint		 x,
8428 					     gint		 y,
8429 					     GtkSelectionData	*data,
8430 					     guint		 info,
8431 					     guint		 time,
8432 					     gpointer		 user_data)
8433 {
8434 	Compose *compose = (Compose *)user_data;
8435 	GList *list, *cur;
8436 	gchar *path, *filename;
8437 	gchar *content_type = NULL;
8438 
8439 	if (info == DRAG_TYPE_RFC822)
8440 		content_type = "message/rfc822";
8441 
8442 	debug_print("compose_attach_drag_received_cb(): received %s\n",
8443 		    (const gchar *)data->data);
8444 
8445 	list = uri_list_extract_filenames((const gchar *)data->data);
8446 	for (cur = list; cur != NULL; cur = cur->next) {
8447 		path = (gchar *)cur->data;
8448 		filename = conv_filename_to_utf8(path);
8449 		compose_attach_append(compose, path, filename, content_type);
8450 		compose_changed_cb(NULL, compose);
8451 		g_free(filename);
8452 		g_free(path);
8453 	}
8454 	if (list) compose_changed_cb(NULL, compose);
8455 	g_list_free(list);
8456 
8457 	if ((drag_context->actions & GDK_ACTION_MOVE) != 0)
8458 		drag_context->action = 0;
8459 	gtk_drag_finish(drag_context, TRUE, FALSE, time);
8460 }
8461 
8462 static void compose_insert_drag_received_cb (GtkWidget		*widget,
8463 					     GdkDragContext	*drag_context,
8464 					     gint		 x,
8465 					     gint		 y,
8466 					     GtkSelectionData	*data,
8467 					     guint		 info,
8468 					     guint		 time,
8469 					     gpointer		 user_data)
8470 {
8471 	static GdkDragContext *context_ = NULL;
8472 	static gint x_ = -1, y_ = -1;
8473 	static guint info_ = N_DRAG_TYPES;
8474 	static guint time_ = G_MAXUINT;
8475 
8476 	debug_print("compose_insert_drag_received_cb(): received %s\n",
8477 		    (const gchar *)data->data);
8478 
8479 	/* FIXME: somehow drag-data-received signal is emitted twice.
8480 	 * This hack prevents duplicated insertion. */
8481 	if (context_ == drag_context && x_ == x && y_ == y && info_ == info &&
8482 	    time_ == time) {
8483 		debug_print("dup drag-data-received event\n");
8484 		context_ = NULL;
8485 		x_ = y_ = -1;
8486 		info_ = N_DRAG_TYPES;
8487 		time_ = G_MAXUINT;
8488 		return;
8489 	}
8490 	context_ = drag_context;
8491 	x_ = x;
8492 	y_ = y;
8493 	info_ = info;
8494 	time_ = time;
8495 
8496 	compose_attach_drag_received_cb(widget, drag_context, x, y, data,
8497 					info, time, user_data);
8498 }
8499 
8500 static void to_activated(GtkWidget *widget, Compose *compose)
8501 {
8502 	if (GTK_WIDGET_VISIBLE(compose->newsgroups_entry))
8503 		gtk_widget_grab_focus(compose->newsgroups_entry);
8504 	else if (GTK_WIDGET_VISIBLE(compose->cc_entry))
8505 		gtk_widget_grab_focus(compose->cc_entry);
8506 	else if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
8507 		gtk_widget_grab_focus(compose->bcc_entry);
8508 	else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
8509 		gtk_widget_grab_focus(compose->reply_entry);
8510 	else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
8511 		gtk_widget_grab_focus(compose->followup_entry);
8512 	else
8513 		gtk_widget_grab_focus(compose->subject_entry);
8514 }
8515 
8516 static void newsgroups_activated(GtkWidget *widget, Compose *compose)
8517 {
8518 	if (GTK_WIDGET_VISIBLE(compose->cc_entry))
8519 		gtk_widget_grab_focus(compose->cc_entry);
8520 	else if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
8521 		gtk_widget_grab_focus(compose->bcc_entry);
8522 	else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
8523 		gtk_widget_grab_focus(compose->reply_entry);
8524 	else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
8525 		gtk_widget_grab_focus(compose->followup_entry);
8526 	else
8527 		gtk_widget_grab_focus(compose->subject_entry);
8528 }
8529 
8530 static void cc_activated(GtkWidget *widget, Compose *compose)
8531 {
8532 	if (GTK_WIDGET_VISIBLE(compose->bcc_entry))
8533 		gtk_widget_grab_focus(compose->bcc_entry);
8534 	else if (GTK_WIDGET_VISIBLE(compose->reply_entry))
8535 		gtk_widget_grab_focus(compose->reply_entry);
8536 	else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
8537 		gtk_widget_grab_focus(compose->followup_entry);
8538 	else
8539 		gtk_widget_grab_focus(compose->subject_entry);
8540 }
8541 
8542 static void bcc_activated(GtkWidget *widget, Compose *compose)
8543 {
8544 	if (GTK_WIDGET_VISIBLE(compose->reply_entry))
8545 		gtk_widget_grab_focus(compose->reply_entry);
8546 	else if (GTK_WIDGET_VISIBLE(compose->followup_entry))
8547 		gtk_widget_grab_focus(compose->followup_entry);
8548 	else
8549 		gtk_widget_grab_focus(compose->subject_entry);
8550 }
8551 
8552 static void replyto_activated(GtkWidget *widget, Compose *compose)
8553 {
8554 	if (GTK_WIDGET_VISIBLE(compose->followup_entry))
8555 		gtk_widget_grab_focus(compose->followup_entry);
8556 	else
8557 		gtk_widget_grab_focus(compose->subject_entry);
8558 }
8559 
8560 static void followupto_activated(GtkWidget *widget, Compose *compose)
8561 {
8562 	gtk_widget_grab_focus(compose->subject_entry);
8563 }
8564 
8565 static void subject_activated(GtkWidget *widget, Compose *compose)
8566 {
8567 	gtk_widget_grab_focus(compose->text);
8568 }
8569 
8570 static void text_inserted(GtkTextBuffer *buffer, GtkTextIter *iter,
8571 			  const gchar *text, gint len, Compose *compose)
8572 {
8573 	GtkTextMark *mark;
8574 
8575 	/* pass to the default handler */
8576 	if (!compose->autowrap)
8577 		return;
8578 
8579 	g_return_if_fail(text != NULL);
8580 
8581 	g_signal_handlers_block_by_func(G_OBJECT(buffer),
8582 					G_CALLBACK(text_inserted),
8583 					compose);
8584 
8585 	gtk_text_buffer_insert(buffer, iter, text, len);
8586 
8587 	mark = gtk_text_buffer_create_mark(buffer, NULL, iter, FALSE);
8588 	compose_wrap_all_full(compose, TRUE);
8589 	gtk_text_buffer_get_iter_at_mark(buffer, iter, mark);
8590 	gtk_text_buffer_delete_mark(buffer, mark);
8591 
8592 	g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
8593 					  G_CALLBACK(text_inserted),
8594 					  compose);
8595 	g_signal_stop_emission_by_name(G_OBJECT(buffer), "insert-text");
8596 }
8597 
8598 static gboolean autosave_timeout(gpointer data)
8599 {
8600 	Compose *compose = (Compose *)data;
8601 
8602 	gdk_threads_enter();
8603 
8604 	debug_print("auto-saving...\n");
8605 
8606 	if (compose->modified)
8607 		compose_draft_cb(data, 1, NULL);
8608 
8609 	gdk_threads_leave();
8610 
8611 	return TRUE;
8612 }
8613