2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2009 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
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.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static ComposeInsertResult compose_insert_file (Compose *compose,
244 static gboolean compose_attach_append (Compose *compose,
247 const gchar *content_type);
248 static void compose_attach_parts (Compose *compose,
251 static gboolean compose_beautify_paragraph (Compose *compose,
252 GtkTextIter *par_iter,
254 static void compose_wrap_all (Compose *compose);
255 static void compose_wrap_all_full (Compose *compose,
258 static void compose_set_title (Compose *compose);
259 static void compose_select_account (Compose *compose,
260 PrefsAccount *account,
263 static PrefsAccount *compose_current_mail_account(void);
264 /* static gint compose_send (Compose *compose); */
265 static gboolean compose_check_for_valid_recipient
267 static gboolean compose_check_entries (Compose *compose,
268 gboolean check_everything);
269 static gint compose_write_to_file (Compose *compose,
272 gboolean attach_parts);
273 static gint compose_write_body_to_file (Compose *compose,
275 static gint compose_remove_reedit_target (Compose *compose,
277 static void compose_remove_draft (Compose *compose);
278 static gint compose_queue_sub (Compose *compose,
282 gboolean check_subject,
283 gboolean remove_reedit_target);
284 static void compose_add_attachments (Compose *compose,
286 static gchar *compose_get_header (Compose *compose);
288 static void compose_convert_header (Compose *compose,
293 gboolean addr_field);
295 static void compose_attach_info_free (AttachInfo *ainfo);
296 static void compose_attach_remove_selected (GtkAction *action,
299 static void compose_attach_property (GtkAction *action,
301 static void compose_attach_property_create (gboolean *cancelled);
302 static void attach_property_ok (GtkWidget *widget,
303 gboolean *cancelled);
304 static void attach_property_cancel (GtkWidget *widget,
305 gboolean *cancelled);
306 static gint attach_property_delete_event (GtkWidget *widget,
308 gboolean *cancelled);
309 static gboolean attach_property_key_pressed (GtkWidget *widget,
311 gboolean *cancelled);
313 static void compose_exec_ext_editor (Compose *compose);
315 static gint compose_exec_ext_editor_real (const gchar *file);
316 static gboolean compose_ext_editor_kill (Compose *compose);
317 static gboolean compose_input_cb (GIOChannel *source,
318 GIOCondition condition,
320 static void compose_set_ext_editor_sensitive (Compose *compose,
322 #endif /* G_OS_UNIX */
324 static void compose_undo_state_changed (UndoMain *undostruct,
329 static void compose_create_header_entry (Compose *compose);
330 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
331 static void compose_remove_header_entries(Compose *compose);
333 static void compose_update_priority_menu_item(Compose * compose);
335 static void compose_spell_menu_changed (void *data);
337 static void compose_add_field_list ( Compose *compose,
338 GList *listAddress );
340 /* callback functions */
342 static gboolean compose_edit_size_alloc (GtkEditable *widget,
343 GtkAllocation *allocation,
344 GtkSHRuler *shruler);
345 static void account_activated (GtkComboBox *optmenu,
347 static void attach_selected (GtkTreeView *tree_view,
348 GtkTreePath *tree_path,
349 GtkTreeViewColumn *column,
351 static gboolean attach_button_pressed (GtkWidget *widget,
352 GdkEventButton *event,
354 static gboolean attach_key_pressed (GtkWidget *widget,
357 static void compose_send_cb (GtkAction *action, gpointer data);
358 static void compose_send_later_cb (GtkAction *action, gpointer data);
360 static void compose_save_cb (GtkAction *action,
363 static void compose_attach_cb (GtkAction *action,
365 static void compose_insert_file_cb (GtkAction *action,
367 static void compose_insert_sig_cb (GtkAction *action,
370 static void compose_close_cb (GtkAction *action,
373 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
375 static void compose_address_cb (GtkAction *action,
377 static void about_show_cb (GtkAction *action,
379 static void compose_template_activate_cb(GtkWidget *widget,
382 static void compose_ext_editor_cb (GtkAction *action,
385 static gint compose_delete_cb (GtkWidget *widget,
389 static void compose_undo_cb (GtkAction *action,
391 static void compose_redo_cb (GtkAction *action,
393 static void compose_cut_cb (GtkAction *action,
395 static void compose_copy_cb (GtkAction *action,
397 static void compose_paste_cb (GtkAction *action,
399 static void compose_paste_as_quote_cb (GtkAction *action,
401 static void compose_paste_no_wrap_cb (GtkAction *action,
403 static void compose_paste_wrap_cb (GtkAction *action,
405 static void compose_allsel_cb (GtkAction *action,
408 static void compose_advanced_action_cb (GtkAction *action,
411 static void compose_grab_focus_cb (GtkWidget *widget,
414 static void compose_changed_cb (GtkTextBuffer *textbuf,
417 static void compose_wrap_cb (GtkAction *action,
419 static void compose_wrap_all_cb (GtkAction *action,
421 static void compose_find_cb (GtkAction *action,
423 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
425 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
428 static void compose_toggle_ruler_cb (GtkToggleAction *action,
430 static void compose_toggle_sign_cb (GtkToggleAction *action,
432 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
434 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
435 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
436 static void activate_privacy_system (Compose *compose,
437 PrefsAccount *account,
439 static void compose_use_signing(Compose *compose, gboolean use_signing);
440 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
441 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
443 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
445 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
446 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
447 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
449 static void compose_attach_drag_received_cb (GtkWidget *widget,
450 GdkDragContext *drag_context,
453 GtkSelectionData *data,
457 static void compose_insert_drag_received_cb (GtkWidget *widget,
458 GdkDragContext *drag_context,
461 GtkSelectionData *data,
465 static void compose_header_drag_received_cb (GtkWidget *widget,
466 GdkDragContext *drag_context,
469 GtkSelectionData *data,
474 static gboolean compose_drag_drop (GtkWidget *widget,
475 GdkDragContext *drag_context,
477 guint time, gpointer user_data);
479 static void text_inserted (GtkTextBuffer *buffer,
484 static Compose *compose_generic_reply(MsgInfo *msginfo,
485 ComposeQuoteMode quote_mode,
489 gboolean followup_and_reply_to,
492 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
493 ComposeHeaderEntry *headerentry);
494 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
496 ComposeHeaderEntry *headerentry);
498 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
500 static void compose_allow_user_actions (Compose *compose, gboolean allow);
502 static void compose_nothing_cb (GtkAction *action, gpointer data)
508 static void compose_check_all (GtkAction *action, gpointer data);
509 static void compose_highlight_all (GtkAction *action, gpointer data);
510 static void compose_check_backwards (GtkAction *action, gpointer data);
511 static void compose_check_forwards_go (GtkAction *action, gpointer data);
514 static gint compose_defer_auto_save_draft (Compose *compose);
515 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
517 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
520 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
521 FolderItem *folder_item);
523 static void compose_attach_update_label(Compose *compose);
525 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
527 static GtkActionEntry compose_popup_entries[] =
529 {"Compose", NULL, "Compose" },
530 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
531 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
532 {"Compose/---", NULL, "---", NULL, NULL, NULL },
533 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
536 static GtkActionEntry compose_entries[] =
538 {"Menu", NULL, "Menu" },
540 {"Message", NULL, N_("_Message") },
541 {"Edit", NULL, N_("_Edit") },
543 {"Spelling", NULL, N_("_Spelling") },
545 {"Options", NULL, N_("_Options") },
546 {"Tools", NULL, N_("_Tools") },
547 {"Help", NULL, N_("_Help") },
549 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
550 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
551 {"Message/---", NULL, "---" },
553 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
554 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
555 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
556 /* {"Message/---", NULL, "---" }, */
557 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
558 /* {"Message/---", NULL, "---" }, */
559 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
562 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
563 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
564 {"Edit/---", NULL, "---" },
566 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
567 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
568 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
570 {"Edit/SpecialPaste", NULL, N_("Special paste") },
571 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
572 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
573 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
575 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
577 {"Edit/Advanced", NULL, N_("A_dvanced") },
578 {"Edit/Advanced/BackChar", NULL, N_("Move a character backward"), "<shift><control>B", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER*/
579 {"Edit/Advanced/ForwChar", NULL, N_("Move a character forward"), "<shift><control>F", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER*/
580 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
581 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
582 {"Edit/Advanced/BegLine", NULL, N_("Move to beginning of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE*/
583 {"Edit/Advanced/EndLine", NULL, N_("Move to end of line"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE*/
584 {"Edit/Advanced/PrevLine", NULL, N_("Move to previous line"), "<control>P", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE*/
585 {"Edit/Advanced/NextLine", NULL, N_("Move to next line"), "<control>N", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE*/
586 {"Edit/Advanced/DelBackChar", NULL, N_("Delete a character backward"), "<control>H", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER*/
587 {"Edit/Advanced/DelForwChar", NULL, N_("Delete a character forward"), "<control>D", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER*/
588 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
589 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
590 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
591 {"Edit/Advanced/DelEndLine", NULL, N_("Delete to end of line"), "<control>K", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END*/
593 /* {"Edit/---", NULL, "---" }, */
594 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
596 /* {"Edit/---", NULL, "---" }, */
597 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
598 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
599 /* {"Edit/---", NULL, "---" }, */
600 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
603 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
604 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
605 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
606 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
608 {"Spelling/---", NULL, "---" },
609 {"Spelling/Options", NULL, N_("_Options") },
614 {"Options/ReplyMode", NULL, N_("Reply _mode") },
615 {"Options/---", NULL, "---" },
616 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
617 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
619 /* {"Options/---", NULL, "---" }, */
621 {"Options/Priority", NULL, N_("_Priority") },
623 {"Options/Encoding", NULL, N_("Character _encoding") },
624 {"Options/Encoding/---", NULL, "---" },
625 #define ENC_ACTION(cs_char,c_char,string) \
626 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
628 {"Options/Encoding/Western", NULL, N_("Western European") },
629 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
630 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
631 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
632 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
633 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
634 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
635 {"Options/Encoding/Korean", NULL, N_("Korean") },
636 {"Options/Encoding/Thai", NULL, N_("Thai") },
639 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
641 {"Tools/Template", NULL, N_("_Template") },
642 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
643 {"Tools/Actions", NULL, N_("Actio_ns") },
644 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
650 static GtkToggleActionEntry compose_toggle_entries[] =
652 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
653 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
654 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
655 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
656 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
657 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
658 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
661 static GtkRadioActionEntry compose_radio_rm_entries[] =
663 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
665 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
666 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
669 static GtkRadioActionEntry compose_radio_prio_entries[] =
671 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
674 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
675 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
678 static GtkRadioActionEntry compose_radio_enc_entries[] =
680 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
713 static GtkTargetEntry compose_mime_types[] =
715 {"text/uri-list", 0, 0},
716 {"UTF8_STRING", 0, 0},
720 static gboolean compose_put_existing_to_front(MsgInfo *info)
722 GList *compose_list = compose_get_compose_list();
726 for (elem = compose_list; elem != NULL && elem->data != NULL;
728 Compose *c = (Compose*)elem->data;
730 if (!c->targetinfo || !c->targetinfo->msgid ||
734 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
735 gtkut_window_popup(c->window);
743 static GdkColor quote_color1 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color2 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
747 static GdkColor quote_color3 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor1 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor2 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor3 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
757 static GdkColor signature_color = {
764 static GdkColor uri_color = {
771 static void compose_create_tags(GtkTextView *text, Compose *compose)
773 GtkTextBuffer *buffer;
774 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
780 buffer = gtk_text_view_get_buffer(text);
782 if (prefs_common.enable_color) {
783 /* grab the quote colors, converting from an int to a GdkColor */
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
798 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
801 signature_color = quote_color1 = quote_color2 = quote_color3 =
802 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
805 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
806 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
807 "foreground-gdk", "e_color1,
808 "paragraph-background-gdk", "e_bgcolor1,
810 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
811 "foreground-gdk", "e_color2,
812 "paragraph-background-gdk", "e_bgcolor2,
814 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
815 "foreground-gdk", "e_color3,
816 "paragraph-background-gdk", "e_bgcolor3,
819 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
820 "foreground-gdk", "e_color1,
822 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
823 "foreground-gdk", "e_color2,
825 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
826 "foreground-gdk", "e_color3,
830 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
831 "foreground-gdk", &signature_color,
834 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
835 "foreground-gdk", &uri_color,
837 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
838 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
840 color[0] = quote_color1;
841 color[1] = quote_color2;
842 color[2] = quote_color3;
843 color[3] = quote_bgcolor1;
844 color[4] = quote_bgcolor2;
845 color[5] = quote_bgcolor3;
846 color[6] = signature_color;
847 color[7] = uri_color;
848 cmap = gdk_drawable_get_colormap(compose->window->window);
849 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
851 for (i = 0; i < 8; i++) {
852 if (success[i] == FALSE) {
855 g_warning("Compose: color allocation failed.\n");
856 style = gtk_widget_get_style(GTK_WIDGET(text));
857 quote_color1 = quote_color2 = quote_color3 =
858 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
859 signature_color = uri_color = black;
864 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
865 GPtrArray *attach_files)
867 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
870 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
872 return compose_generic_new(account, mailto, item, NULL, NULL);
875 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
877 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
880 #define SCROLL_TO_CURSOR(compose) { \
881 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
882 gtk_text_view_get_buffer( \
883 GTK_TEXT_VIEW(compose->text))); \
884 gtk_text_view_scroll_mark_onscreen( \
885 GTK_TEXT_VIEW(compose->text), \
889 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
892 if (folderidentifier) {
893 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
894 prefs_common.compose_save_to_history = add_history(
895 prefs_common.compose_save_to_history, folderidentifier);
896 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
897 prefs_common.compose_save_to_history);
900 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
901 if (folderidentifier)
902 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
904 gtk_entry_set_text(GTK_ENTRY(entry), "");
907 static gchar *compose_get_save_to(Compose *compose)
910 gchar *result = NULL;
911 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
912 result = gtk_editable_get_chars(entry, 0, -1);
915 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
916 prefs_common.compose_save_to_history = add_history(
917 prefs_common.compose_save_to_history, result);
918 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
919 prefs_common.compose_save_to_history);
924 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
925 GPtrArray *attach_files, GList *listAddress )
928 GtkTextView *textview;
929 GtkTextBuffer *textbuf;
931 const gchar *subject_format = NULL;
932 const gchar *body_format = NULL;
933 gchar *mailto_from = NULL;
934 PrefsAccount *mailto_account = NULL;
935 MsgInfo* dummyinfo = NULL;
937 /* check if mailto defines a from */
938 if (mailto && *mailto != '\0') {
939 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
940 /* mailto defines a from, check if we can get account prefs from it,
941 if not, the account prefs will be guessed using other ways, but we'll keep
944 mailto_account = account_find_from_address(mailto_from, TRUE);
946 account = mailto_account;
949 /* if no account prefs set from mailto, set if from folder prefs (if any) */
950 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
951 account = account_find_from_id(item->prefs->default_account);
953 /* if no account prefs set, fallback to the current one */
954 if (!account) account = cur_account;
955 g_return_val_if_fail(account != NULL, NULL);
957 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
959 /* override from name if mailto asked for it */
961 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
964 /* override from name according to folder properties */
965 if (item && item->prefs &&
966 item->prefs->compose_with_format &&
967 item->prefs->compose_override_from_format &&
968 *item->prefs->compose_override_from_format != '\0') {
973 dummyinfo = compose_msginfo_new_from_compose(compose);
975 /* decode \-escape sequences in the internal representation of the quote format */
976 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
977 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
980 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
983 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
985 quote_fmt_scan_string(tmp);
988 buf = quote_fmt_get_buffer();
990 alertpanel_error(_("New message From format error."));
992 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
993 quote_fmt_reset_vartable();
998 compose->replyinfo = NULL;
999 compose->fwdinfo = NULL;
1001 textview = GTK_TEXT_VIEW(compose->text);
1002 textbuf = gtk_text_view_get_buffer(textview);
1003 compose_create_tags(textview, compose);
1005 undo_block(compose->undostruct);
1007 compose_set_dictionaries_from_folder_prefs(compose, item);
1010 if (account->auto_sig)
1011 compose_insert_sig(compose, FALSE);
1012 gtk_text_buffer_get_start_iter(textbuf, &iter);
1013 gtk_text_buffer_place_cursor(textbuf, &iter);
1015 if (account->protocol != A_NNTP) {
1016 if (mailto && *mailto != '\0') {
1017 compose_entries_set(compose, mailto, COMPOSE_TO);
1019 } else if (item && item->prefs->enable_default_to) {
1020 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1021 compose_entry_mark_default_to(compose, item->prefs->default_to);
1023 if (item && item->ret_rcpt) {
1024 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1027 if (mailto && *mailto != '\0') {
1028 if (!strchr(mailto, '@'))
1029 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1031 compose_entries_set(compose, mailto, COMPOSE_TO);
1032 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1033 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1036 * CLAWS: just don't allow return receipt request, even if the user
1037 * may want to send an email. simple but foolproof.
1039 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1041 compose_add_field_list( compose, listAddress );
1043 if (item && item->prefs && item->prefs->compose_with_format) {
1044 subject_format = item->prefs->compose_subject_format;
1045 body_format = item->prefs->compose_body_format;
1046 } else if (account->compose_with_format) {
1047 subject_format = account->compose_subject_format;
1048 body_format = account->compose_body_format;
1049 } else if (prefs_common.compose_with_format) {
1050 subject_format = prefs_common.compose_subject_format;
1051 body_format = prefs_common.compose_body_format;
1054 if (subject_format || body_format) {
1057 && *subject_format != '\0' )
1059 gchar *subject = NULL;
1064 dummyinfo = compose_msginfo_new_from_compose(compose);
1066 /* decode \-escape sequences in the internal representation of the quote format */
1067 tmp = malloc(strlen(subject_format)+1);
1068 pref_get_unescaped_pref(tmp, subject_format);
1070 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1072 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1073 compose->gtkaspell);
1075 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1077 quote_fmt_scan_string(tmp);
1080 buf = quote_fmt_get_buffer();
1082 alertpanel_error(_("New message subject format error."));
1084 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1085 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1086 quote_fmt_reset_vartable();
1093 && *body_format != '\0' )
1096 GtkTextBuffer *buffer;
1097 GtkTextIter start, end;
1101 dummyinfo = compose_msginfo_new_from_compose(compose);
1103 text = GTK_TEXT_VIEW(compose->text);
1104 buffer = gtk_text_view_get_buffer(text);
1105 gtk_text_buffer_get_start_iter(buffer, &start);
1106 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1107 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1109 compose_quote_fmt(compose, dummyinfo,
1111 NULL, tmp, FALSE, TRUE,
1112 _("New message body format error at line %d."));
1113 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1114 quote_fmt_reset_vartable();
1120 procmsg_msginfo_free( dummyinfo );
1126 for (i = 0; i < attach_files->len; i++) {
1127 file = g_ptr_array_index(attach_files, i);
1128 compose_attach_append(compose, file, file, NULL);
1132 compose_show_first_last_header(compose, TRUE);
1134 /* Set save folder */
1135 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1136 gchar *folderidentifier;
1138 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1139 folderidentifier = folder_item_get_identifier(item);
1140 compose_set_save_to(compose, folderidentifier);
1141 g_free(folderidentifier);
1144 gtk_widget_grab_focus(compose->header_last->entry);
1146 undo_unblock(compose->undostruct);
1148 if (prefs_common.auto_exteditor)
1149 compose_exec_ext_editor(compose);
1151 compose->draft_timeout_tag = -1;
1152 SCROLL_TO_CURSOR(compose);
1154 compose->modified = FALSE;
1155 compose_set_title(compose);
1159 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1160 gboolean override_pref)
1162 gchar *privacy = NULL;
1164 g_return_if_fail(compose != NULL);
1165 g_return_if_fail(account != NULL);
1167 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1170 if (account->default_privacy_system
1171 && strlen(account->default_privacy_system)) {
1172 privacy = account->default_privacy_system;
1174 GSList *privacy_avail = privacy_get_system_ids();
1175 if (privacy_avail && g_slist_length(privacy_avail)) {
1176 privacy = (gchar *)(privacy_avail->data);
1179 if (privacy != NULL) {
1180 if (compose->privacy_system == NULL)
1181 compose->privacy_system = g_strdup(privacy);
1182 else if (*(compose->privacy_system) == '\0') {
1183 g_free(compose->privacy_system);
1184 compose->privacy_system = g_strdup(privacy);
1186 compose_update_privacy_system_menu_item(compose, FALSE);
1187 compose_use_encryption(compose, TRUE);
1191 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1193 gchar *privacy = NULL;
1195 if (account->default_privacy_system
1196 && strlen(account->default_privacy_system)) {
1197 privacy = account->default_privacy_system;
1199 GSList *privacy_avail = privacy_get_system_ids();
1200 if (privacy_avail && g_slist_length(privacy_avail)) {
1201 privacy = (gchar *)(privacy_avail->data);
1204 if (privacy != NULL) {
1205 if (compose->privacy_system == NULL)
1206 compose->privacy_system = g_strdup(privacy);
1207 compose_update_privacy_system_menu_item(compose, FALSE);
1208 compose_use_signing(compose, TRUE);
1212 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1216 Compose *compose = NULL;
1218 g_return_val_if_fail(msginfo_list != NULL, NULL);
1220 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1221 g_return_val_if_fail(msginfo != NULL, NULL);
1223 list_len = g_slist_length(msginfo_list);
1227 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1228 FALSE, prefs_common.default_reply_list, FALSE, body);
1230 case COMPOSE_REPLY_WITH_QUOTE:
1231 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1232 FALSE, prefs_common.default_reply_list, FALSE, body);
1234 case COMPOSE_REPLY_WITHOUT_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1236 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1238 case COMPOSE_REPLY_TO_SENDER:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1240 FALSE, FALSE, TRUE, body);
1242 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1243 compose = compose_followup_and_reply_to(msginfo,
1244 COMPOSE_QUOTE_CHECK,
1245 FALSE, FALSE, body);
1247 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1248 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1249 FALSE, FALSE, TRUE, body);
1251 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1252 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1253 FALSE, FALSE, TRUE, NULL);
1255 case COMPOSE_REPLY_TO_ALL:
1256 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1257 TRUE, FALSE, FALSE, body);
1259 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1260 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1261 TRUE, FALSE, FALSE, body);
1263 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1264 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1265 TRUE, FALSE, FALSE, NULL);
1267 case COMPOSE_REPLY_TO_LIST:
1268 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1269 FALSE, TRUE, FALSE, body);
1271 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1272 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1273 FALSE, TRUE, FALSE, body);
1275 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1277 FALSE, TRUE, FALSE, NULL);
1279 case COMPOSE_FORWARD:
1280 if (prefs_common.forward_as_attachment) {
1281 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1284 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1288 case COMPOSE_FORWARD_INLINE:
1289 /* check if we reply to more than one Message */
1290 if (list_len == 1) {
1291 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1294 /* more messages FALL THROUGH */
1295 case COMPOSE_FORWARD_AS_ATTACH:
1296 compose = compose_forward_multiple(NULL, msginfo_list);
1298 case COMPOSE_REDIRECT:
1299 compose = compose_redirect(NULL, msginfo, FALSE);
1302 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1305 if (compose == NULL) {
1306 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1310 compose->rmode = mode;
1311 switch (compose->rmode) {
1313 case COMPOSE_REPLY_WITH_QUOTE:
1314 case COMPOSE_REPLY_WITHOUT_QUOTE:
1315 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1316 debug_print("reply mode Normal\n");
1317 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1318 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1320 case COMPOSE_REPLY_TO_SENDER:
1321 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1322 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1323 debug_print("reply mode Sender\n");
1324 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1326 case COMPOSE_REPLY_TO_ALL:
1327 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1328 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1329 debug_print("reply mode All\n");
1330 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1332 case COMPOSE_REPLY_TO_LIST:
1333 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1334 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1335 debug_print("reply mode List\n");
1336 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1344 static Compose *compose_reply(MsgInfo *msginfo,
1345 ComposeQuoteMode quote_mode,
1351 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1352 to_sender, FALSE, body);
1355 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1356 ComposeQuoteMode quote_mode,
1361 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1362 to_sender, TRUE, body);
1365 static void compose_extract_original_charset(Compose *compose)
1367 MsgInfo *info = NULL;
1368 if (compose->replyinfo) {
1369 info = compose->replyinfo;
1370 } else if (compose->fwdinfo) {
1371 info = compose->fwdinfo;
1372 } else if (compose->targetinfo) {
1373 info = compose->targetinfo;
1376 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1377 MimeInfo *partinfo = mimeinfo;
1378 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1379 partinfo = procmime_mimeinfo_next(partinfo);
1381 compose->orig_charset =
1382 g_strdup(procmime_mimeinfo_get_parameter(
1383 partinfo, "charset"));
1385 procmime_mimeinfo_free_all(mimeinfo);
1389 #define SIGNAL_BLOCK(buffer) { \
1390 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1391 G_CALLBACK(compose_changed_cb), \
1393 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1394 G_CALLBACK(text_inserted), \
1398 #define SIGNAL_UNBLOCK(buffer) { \
1399 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1400 G_CALLBACK(compose_changed_cb), \
1402 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1403 G_CALLBACK(text_inserted), \
1407 static Compose *compose_generic_reply(MsgInfo *msginfo,
1408 ComposeQuoteMode quote_mode,
1409 gboolean to_all, gboolean to_ml,
1411 gboolean followup_and_reply_to,
1415 PrefsAccount *account = NULL;
1416 GtkTextView *textview;
1417 GtkTextBuffer *textbuf;
1418 gboolean quote = FALSE;
1419 const gchar *qmark = NULL;
1420 const gchar *body_fmt = NULL;
1422 g_return_val_if_fail(msginfo != NULL, NULL);
1423 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1425 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1427 g_return_val_if_fail(account != NULL, NULL);
1429 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1431 compose->updating = TRUE;
1433 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1434 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1436 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1437 if (!compose->replyinfo)
1438 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1440 compose_extract_original_charset(compose);
1442 if (msginfo->folder && msginfo->folder->ret_rcpt)
1443 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1445 /* Set save folder */
1446 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1447 gchar *folderidentifier;
1449 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1450 folderidentifier = folder_item_get_identifier(msginfo->folder);
1451 compose_set_save_to(compose, folderidentifier);
1452 g_free(folderidentifier);
1455 if (compose_parse_header(compose, msginfo) < 0) {
1456 compose->updating = FALSE;
1457 compose_destroy(compose);
1461 /* override from name according to folder properties */
1462 if (msginfo->folder && msginfo->folder->prefs &&
1463 msginfo->folder->prefs->reply_with_format &&
1464 msginfo->folder->prefs->reply_override_from_format &&
1465 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1470 /* decode \-escape sequences in the internal representation of the quote format */
1471 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1472 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1475 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1476 compose->gtkaspell);
1478 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1480 quote_fmt_scan_string(tmp);
1483 buf = quote_fmt_get_buffer();
1485 alertpanel_error(_("Message reply From format error."));
1487 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1488 quote_fmt_reset_vartable();
1493 textview = (GTK_TEXT_VIEW(compose->text));
1494 textbuf = gtk_text_view_get_buffer(textview);
1495 compose_create_tags(textview, compose);
1497 undo_block(compose->undostruct);
1499 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1502 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1503 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1504 /* use the reply format of folder (if enabled), or the account's one
1505 (if enabled) or fallback to the global reply format, which is always
1506 enabled (even if empty), and use the relevant quotemark */
1508 if (msginfo->folder && msginfo->folder->prefs &&
1509 msginfo->folder->prefs->reply_with_format) {
1510 qmark = msginfo->folder->prefs->reply_quotemark;
1511 body_fmt = msginfo->folder->prefs->reply_body_format;
1513 } else if (account->reply_with_format) {
1514 qmark = account->reply_quotemark;
1515 body_fmt = account->reply_body_format;
1518 qmark = prefs_common.quotemark;
1519 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1520 body_fmt = gettext(prefs_common.quotefmt);
1527 /* empty quotemark is not allowed */
1528 if (qmark == NULL || *qmark == '\0')
1530 compose_quote_fmt(compose, compose->replyinfo,
1531 body_fmt, qmark, body, FALSE, TRUE,
1532 _("Message reply format error at line %d."));
1533 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1534 quote_fmt_reset_vartable();
1537 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1538 compose_force_encryption(compose, account, FALSE);
1541 privacy_msginfo_get_signed_state(compose->replyinfo);
1542 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1543 compose_force_signing(compose, account);
1546 SIGNAL_BLOCK(textbuf);
1548 if (account->auto_sig)
1549 compose_insert_sig(compose, FALSE);
1551 compose_wrap_all(compose);
1553 SIGNAL_UNBLOCK(textbuf);
1555 gtk_widget_grab_focus(compose->text);
1557 undo_unblock(compose->undostruct);
1559 if (prefs_common.auto_exteditor)
1560 compose_exec_ext_editor(compose);
1562 compose->modified = FALSE;
1563 compose_set_title(compose);
1565 compose->updating = FALSE;
1566 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1567 SCROLL_TO_CURSOR(compose);
1569 if (compose->deferred_destroy) {
1570 compose_destroy(compose);
1577 #define INSERT_FW_HEADER(var, hdr) \
1578 if (msginfo->var && *msginfo->var) { \
1579 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1580 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1581 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1584 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1585 gboolean as_attach, const gchar *body,
1586 gboolean no_extedit,
1590 GtkTextView *textview;
1591 GtkTextBuffer *textbuf;
1594 g_return_val_if_fail(msginfo != NULL, NULL);
1595 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1598 !(account = compose_guess_forward_account_from_msginfo
1600 account = cur_account;
1602 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1604 compose->updating = TRUE;
1605 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1606 if (!compose->fwdinfo)
1607 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1609 compose_extract_original_charset(compose);
1611 if (msginfo->subject && *msginfo->subject) {
1612 gchar *buf, *buf2, *p;
1614 buf = p = g_strdup(msginfo->subject);
1615 p += subject_get_prefix_length(p);
1616 memmove(buf, p, strlen(p) + 1);
1618 buf2 = g_strdup_printf("Fw: %s", buf);
1619 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1625 /* override from name according to folder properties */
1626 if (msginfo->folder && msginfo->folder->prefs &&
1627 msginfo->folder->prefs->forward_with_format &&
1628 msginfo->folder->prefs->forward_override_from_format &&
1629 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1633 MsgInfo *full_msginfo = NULL;
1636 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1638 full_msginfo = procmsg_msginfo_copy(msginfo);
1640 /* decode \-escape sequences in the internal representation of the quote format */
1641 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1642 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1645 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1646 compose->gtkaspell);
1648 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1650 quote_fmt_scan_string(tmp);
1653 buf = quote_fmt_get_buffer();
1655 alertpanel_error(_("Message forward From format error."));
1657 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1658 quote_fmt_reset_vartable();
1661 procmsg_msginfo_free(full_msginfo);
1664 textview = GTK_TEXT_VIEW(compose->text);
1665 textbuf = gtk_text_view_get_buffer(textview);
1666 compose_create_tags(textview, compose);
1668 undo_block(compose->undostruct);
1672 msgfile = procmsg_get_message_file(msginfo);
1673 if (!is_file_exist(msgfile))
1674 g_warning("%s: file not exist\n", msgfile);
1676 compose_attach_append(compose, msgfile, msgfile,
1681 const gchar *qmark = NULL;
1682 const gchar *body_fmt = NULL;
1683 MsgInfo *full_msginfo;
1685 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1686 body_fmt = gettext(prefs_common.fw_quotefmt);
1690 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1692 full_msginfo = procmsg_msginfo_copy(msginfo);
1694 /* use the forward format of folder (if enabled), or the account's one
1695 (if enabled) or fallback to the global forward format, which is always
1696 enabled (even if empty), and use the relevant quotemark */
1697 if (msginfo->folder && msginfo->folder->prefs &&
1698 msginfo->folder->prefs->forward_with_format) {
1699 qmark = msginfo->folder->prefs->forward_quotemark;
1700 body_fmt = msginfo->folder->prefs->forward_body_format;
1702 } else if (account->forward_with_format) {
1703 qmark = account->forward_quotemark;
1704 body_fmt = account->forward_body_format;
1707 qmark = prefs_common.fw_quotemark;
1708 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1709 body_fmt = gettext(prefs_common.fw_quotefmt);
1714 /* empty quotemark is not allowed */
1715 if (qmark == NULL || *qmark == '\0')
1718 compose_quote_fmt(compose, full_msginfo,
1719 body_fmt, qmark, body, FALSE, TRUE,
1720 _("Message forward format error at line %d."));
1721 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1722 quote_fmt_reset_vartable();
1723 compose_attach_parts(compose, msginfo);
1725 procmsg_msginfo_free(full_msginfo);
1728 SIGNAL_BLOCK(textbuf);
1730 if (account->auto_sig)
1731 compose_insert_sig(compose, FALSE);
1733 compose_wrap_all(compose);
1735 SIGNAL_UNBLOCK(textbuf);
1737 gtk_text_buffer_get_start_iter(textbuf, &iter);
1738 gtk_text_buffer_place_cursor(textbuf, &iter);
1740 gtk_widget_grab_focus(compose->header_last->entry);
1742 if (!no_extedit && prefs_common.auto_exteditor)
1743 compose_exec_ext_editor(compose);
1746 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1747 gchar *folderidentifier;
1749 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1750 folderidentifier = folder_item_get_identifier(msginfo->folder);
1751 compose_set_save_to(compose, folderidentifier);
1752 g_free(folderidentifier);
1755 undo_unblock(compose->undostruct);
1757 compose->modified = FALSE;
1758 compose_set_title(compose);
1760 compose->updating = FALSE;
1761 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1762 SCROLL_TO_CURSOR(compose);
1764 if (compose->deferred_destroy) {
1765 compose_destroy(compose);
1772 #undef INSERT_FW_HEADER
1774 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1777 GtkTextView *textview;
1778 GtkTextBuffer *textbuf;
1782 gboolean single_mail = TRUE;
1784 g_return_val_if_fail(msginfo_list != NULL, NULL);
1786 if (g_slist_length(msginfo_list) > 1)
1787 single_mail = FALSE;
1789 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1790 if (((MsgInfo *)msginfo->data)->folder == NULL)
1793 /* guess account from first selected message */
1795 !(account = compose_guess_forward_account_from_msginfo
1796 (msginfo_list->data)))
1797 account = cur_account;
1799 g_return_val_if_fail(account != NULL, NULL);
1801 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1802 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1803 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1806 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1808 compose->updating = TRUE;
1810 /* override from name according to folder properties */
1811 if (msginfo_list->data) {
1812 MsgInfo *msginfo = msginfo_list->data;
1814 if (msginfo->folder && msginfo->folder->prefs &&
1815 msginfo->folder->prefs->forward_with_format &&
1816 msginfo->folder->prefs->forward_override_from_format &&
1817 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1822 /* decode \-escape sequences in the internal representation of the quote format */
1823 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1824 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1827 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1828 compose->gtkaspell);
1830 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1832 quote_fmt_scan_string(tmp);
1835 buf = quote_fmt_get_buffer();
1837 alertpanel_error(_("Message forward From format error."));
1839 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1840 quote_fmt_reset_vartable();
1846 textview = GTK_TEXT_VIEW(compose->text);
1847 textbuf = gtk_text_view_get_buffer(textview);
1848 compose_create_tags(textview, compose);
1850 undo_block(compose->undostruct);
1851 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1852 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1854 if (!is_file_exist(msgfile))
1855 g_warning("%s: file not exist\n", msgfile);
1857 compose_attach_append(compose, msgfile, msgfile,
1863 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1864 if (info->subject && *info->subject) {
1865 gchar *buf, *buf2, *p;
1867 buf = p = g_strdup(info->subject);
1868 p += subject_get_prefix_length(p);
1869 memmove(buf, p, strlen(p) + 1);
1871 buf2 = g_strdup_printf("Fw: %s", buf);
1872 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1878 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1879 _("Fw: multiple emails"));
1882 SIGNAL_BLOCK(textbuf);
1884 if (account->auto_sig)
1885 compose_insert_sig(compose, FALSE);
1887 compose_wrap_all(compose);
1889 SIGNAL_UNBLOCK(textbuf);
1891 gtk_text_buffer_get_start_iter(textbuf, &iter);
1892 gtk_text_buffer_place_cursor(textbuf, &iter);
1894 gtk_widget_grab_focus(compose->header_last->entry);
1895 undo_unblock(compose->undostruct);
1896 compose->modified = FALSE;
1897 compose_set_title(compose);
1899 compose->updating = FALSE;
1900 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1901 SCROLL_TO_CURSOR(compose);
1903 if (compose->deferred_destroy) {
1904 compose_destroy(compose);
1911 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1913 GtkTextIter start = *iter;
1914 GtkTextIter end_iter;
1915 int start_pos = gtk_text_iter_get_offset(&start);
1917 if (!compose->account->sig_sep)
1920 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1921 start_pos+strlen(compose->account->sig_sep));
1923 /* check sig separator */
1924 str = gtk_text_iter_get_text(&start, &end_iter);
1925 if (!strcmp(str, compose->account->sig_sep)) {
1927 /* check end of line (\n) */
1928 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1929 start_pos+strlen(compose->account->sig_sep));
1930 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1931 start_pos+strlen(compose->account->sig_sep)+1);
1932 tmp = gtk_text_iter_get_text(&start, &end_iter);
1933 if (!strcmp(tmp,"\n")) {
1945 static void compose_colorize_signature(Compose *compose)
1947 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1949 GtkTextIter end_iter;
1950 gtk_text_buffer_get_start_iter(buffer, &iter);
1951 while (gtk_text_iter_forward_line(&iter))
1952 if (compose_is_sig_separator(compose, buffer, &iter)) {
1953 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1954 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1958 #define BLOCK_WRAP() { \
1959 prev_autowrap = compose->autowrap; \
1960 buffer = gtk_text_view_get_buffer( \
1961 GTK_TEXT_VIEW(compose->text)); \
1962 compose->autowrap = FALSE; \
1964 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1965 G_CALLBACK(compose_changed_cb), \
1967 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1968 G_CALLBACK(text_inserted), \
1971 #define UNBLOCK_WRAP() { \
1972 compose->autowrap = prev_autowrap; \
1973 if (compose->autowrap) { \
1974 gint old = compose->draft_timeout_tag; \
1975 compose->draft_timeout_tag = -2; \
1976 compose_wrap_all(compose); \
1977 compose->draft_timeout_tag = old; \
1980 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1981 G_CALLBACK(compose_changed_cb), \
1983 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1984 G_CALLBACK(text_inserted), \
1988 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1990 Compose *compose = NULL;
1991 PrefsAccount *account = NULL;
1992 GtkTextView *textview;
1993 GtkTextBuffer *textbuf;
1997 gchar buf[BUFFSIZE];
1998 gboolean use_signing = FALSE;
1999 gboolean use_encryption = FALSE;
2000 gchar *privacy_system = NULL;
2001 int priority = PRIORITY_NORMAL;
2002 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2003 gboolean autowrap = prefs_common.autowrap;
2004 gboolean autoindent = prefs_common.auto_indent;
2006 g_return_val_if_fail(msginfo != NULL, NULL);
2007 g_return_val_if_fail(msginfo->folder != NULL, NULL);
2009 if (compose_put_existing_to_front(msginfo)) {
2013 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2014 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2015 gchar queueheader_buf[BUFFSIZE];
2018 /* Select Account from queue headers */
2019 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2020 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2021 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2022 account = account_find_from_id(id);
2024 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2025 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2026 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2027 account = account_find_from_id(id);
2029 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2030 sizeof(queueheader_buf), "NAID:")) {
2031 id = atoi(&queueheader_buf[strlen("NAID:")]);
2032 account = account_find_from_id(id);
2034 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2035 sizeof(queueheader_buf), "MAID:")) {
2036 id = atoi(&queueheader_buf[strlen("MAID:")]);
2037 account = account_find_from_id(id);
2039 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2040 sizeof(queueheader_buf), "S:")) {
2041 account = account_find_from_address(queueheader_buf, FALSE);
2043 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2044 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2045 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2046 use_signing = param;
2049 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2050 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2051 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2052 use_signing = param;
2055 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2056 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2057 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2058 use_encryption = param;
2060 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2061 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2062 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2063 use_encryption = param;
2065 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2066 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2067 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2070 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2071 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2072 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2075 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2076 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2077 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2079 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2080 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2081 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2083 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2084 sizeof(queueheader_buf), "X-Priority: ")) {
2085 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2088 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2089 sizeof(queueheader_buf), "RMID:")) {
2090 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2091 if (tokens[0] && tokens[1] && tokens[2]) {
2092 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2093 if (orig_item != NULL) {
2094 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2099 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2100 sizeof(queueheader_buf), "FMID:")) {
2101 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2102 if (tokens[0] && tokens[1] && tokens[2]) {
2103 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2104 if (orig_item != NULL) {
2105 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2111 account = msginfo->folder->folder->account;
2114 if (!account && prefs_common.reedit_account_autosel) {
2115 gchar from[BUFFSIZE];
2116 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2117 extract_address(from);
2118 account = account_find_from_address(from, FALSE);
2122 account = cur_account;
2124 g_return_val_if_fail(account != NULL, NULL);
2126 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2128 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2129 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2130 compose->autowrap = autowrap;
2131 compose->replyinfo = replyinfo;
2132 compose->fwdinfo = fwdinfo;
2134 compose->updating = TRUE;
2135 compose->priority = priority;
2137 if (privacy_system != NULL) {
2138 compose->privacy_system = privacy_system;
2139 compose_use_signing(compose, use_signing);
2140 compose_use_encryption(compose, use_encryption);
2141 compose_update_privacy_system_menu_item(compose, FALSE);
2143 activate_privacy_system(compose, account, FALSE);
2146 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2148 compose_extract_original_charset(compose);
2150 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2151 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2152 gchar queueheader_buf[BUFFSIZE];
2154 /* Set message save folder */
2155 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2156 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2157 compose_set_save_to(compose, &queueheader_buf[4]);
2159 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2160 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2162 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2167 if (compose_parse_header(compose, msginfo) < 0) {
2168 compose->updating = FALSE;
2169 compose_destroy(compose);
2172 compose_reedit_set_entry(compose, msginfo);
2174 textview = GTK_TEXT_VIEW(compose->text);
2175 textbuf = gtk_text_view_get_buffer(textview);
2176 compose_create_tags(textview, compose);
2178 mark = gtk_text_buffer_get_insert(textbuf);
2179 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2181 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2182 G_CALLBACK(compose_changed_cb),
2185 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2186 fp = procmime_get_first_encrypted_text_content(msginfo);
2188 compose_force_encryption(compose, account, TRUE);
2191 fp = procmime_get_first_text_content(msginfo);
2194 g_warning("Can't get text part\n");
2198 gboolean prev_autowrap = compose->autowrap;
2199 GtkTextBuffer *buffer = textbuf;
2201 while (fgets(buf, sizeof(buf), fp) != NULL) {
2203 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2209 compose_attach_parts(compose, msginfo);
2211 compose_colorize_signature(compose);
2213 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2214 G_CALLBACK(compose_changed_cb),
2217 gtk_widget_grab_focus(compose->text);
2219 if (prefs_common.auto_exteditor) {
2220 compose_exec_ext_editor(compose);
2222 compose->modified = FALSE;
2223 compose_set_title(compose);
2225 compose->updating = FALSE;
2226 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2227 SCROLL_TO_CURSOR(compose);
2229 if (compose->deferred_destroy) {
2230 compose_destroy(compose);
2234 compose->sig_str = account_get_signature_str(compose->account);
2239 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2246 g_return_val_if_fail(msginfo != NULL, NULL);
2249 account = account_get_reply_account(msginfo,
2250 prefs_common.reply_account_autosel);
2251 g_return_val_if_fail(account != NULL, NULL);
2253 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2255 compose->updating = TRUE;
2257 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2258 compose->replyinfo = NULL;
2259 compose->fwdinfo = NULL;
2261 compose_show_first_last_header(compose, TRUE);
2263 gtk_widget_grab_focus(compose->header_last->entry);
2265 filename = procmsg_get_message_file(msginfo);
2267 if (filename == NULL) {
2268 compose->updating = FALSE;
2269 compose_destroy(compose);
2274 compose->redirect_filename = filename;
2276 /* Set save folder */
2277 item = msginfo->folder;
2278 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2279 gchar *folderidentifier;
2281 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2282 folderidentifier = folder_item_get_identifier(item);
2283 compose_set_save_to(compose, folderidentifier);
2284 g_free(folderidentifier);
2287 compose_attach_parts(compose, msginfo);
2289 if (msginfo->subject)
2290 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2292 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2294 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2295 _("Message redirect format error at line %d."));
2296 quote_fmt_reset_vartable();
2297 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2299 compose_colorize_signature(compose);
2302 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2303 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2304 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2306 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2307 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2308 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2309 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2310 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2311 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2312 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2313 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2315 if (compose->toolbar->draft_btn)
2316 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2317 if (compose->toolbar->insert_btn)
2318 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2319 if (compose->toolbar->attach_btn)
2320 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2321 if (compose->toolbar->sig_btn)
2322 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2323 if (compose->toolbar->exteditor_btn)
2324 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2325 if (compose->toolbar->linewrap_current_btn)
2326 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2327 if (compose->toolbar->linewrap_all_btn)
2328 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2330 compose->modified = FALSE;
2331 compose_set_title(compose);
2332 compose->updating = FALSE;
2333 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2334 SCROLL_TO_CURSOR(compose);
2336 if (compose->deferred_destroy) {
2337 compose_destroy(compose);
2344 GList *compose_get_compose_list(void)
2346 return compose_list;
2349 void compose_entry_append(Compose *compose, const gchar *address,
2350 ComposeEntryType type)
2352 const gchar *header;
2354 gboolean in_quote = FALSE;
2355 if (!address || *address == '\0') return;
2362 header = N_("Bcc:");
2364 case COMPOSE_REPLYTO:
2365 header = N_("Reply-To:");
2367 case COMPOSE_NEWSGROUPS:
2368 header = N_("Newsgroups:");
2370 case COMPOSE_FOLLOWUPTO:
2371 header = N_( "Followup-To:");
2378 header = prefs_common_translated_header_name(header);
2380 cur = begin = (gchar *)address;
2382 /* we separate the line by commas, but not if we're inside a quoted
2384 while (*cur != '\0') {
2386 in_quote = !in_quote;
2387 if (*cur == ',' && !in_quote) {
2388 gchar *tmp = g_strdup(begin);
2390 tmp[cur-begin]='\0';
2393 while (*tmp == ' ' || *tmp == '\t')
2395 compose_add_header_entry(compose, header, tmp);
2402 gchar *tmp = g_strdup(begin);
2404 tmp[cur-begin]='\0';
2407 while (*tmp == ' ' || *tmp == '\t')
2409 compose_add_header_entry(compose, header, tmp);
2414 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2416 static GdkColor yellow;
2417 static GdkColor black;
2418 static gboolean yellow_initialised = FALSE;
2422 if (!yellow_initialised) {
2423 gdk_color_parse("#f5f6be", &yellow);
2424 gdk_color_parse("#000000", &black);
2425 yellow_initialised = gdk_colormap_alloc_color(
2426 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2427 yellow_initialised &= gdk_colormap_alloc_color(
2428 gdk_colormap_get_system(), &black, FALSE, TRUE);
2431 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2432 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2433 if (gtk_entry_get_text(entry) &&
2434 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2435 if (yellow_initialised) {
2436 gtk_widget_modify_base(
2437 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2438 GTK_STATE_NORMAL, &yellow);
2439 gtk_widget_modify_text(
2440 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2441 GTK_STATE_NORMAL, &black);
2447 void compose_toolbar_cb(gint action, gpointer data)
2449 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2450 Compose *compose = (Compose*)toolbar_item->parent;
2452 g_return_if_fail(compose != NULL);
2456 compose_send_cb(NULL, compose);
2459 compose_send_later_cb(NULL, compose);
2462 compose_draft(compose, COMPOSE_QUIT_EDITING);
2465 compose_insert_file_cb(NULL, compose);
2468 compose_attach_cb(NULL, compose);
2471 compose_insert_sig(compose, FALSE);
2474 compose_ext_editor_cb(NULL, compose);
2476 case A_LINEWRAP_CURRENT:
2477 compose_beautify_paragraph(compose, NULL, TRUE);
2479 case A_LINEWRAP_ALL:
2480 compose_wrap_all_full(compose, TRUE);
2483 compose_address_cb(NULL, compose);
2486 case A_CHECK_SPELLING:
2487 compose_check_all(NULL, compose);
2495 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2500 gchar *subject = NULL;
2504 gchar **attach = NULL;
2506 /* get mailto parts but skip from */
2507 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2510 compose_entry_append(compose, to, to_type);
2512 compose_entry_append(compose, cc, COMPOSE_CC);
2514 compose_entry_append(compose, bcc, COMPOSE_BCC);
2516 if (!g_utf8_validate (subject, -1, NULL)) {
2517 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2518 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2521 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2525 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2526 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2529 gboolean prev_autowrap = compose->autowrap;
2531 compose->autowrap = FALSE;
2533 mark = gtk_text_buffer_get_insert(buffer);
2534 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2536 if (!g_utf8_validate (body, -1, NULL)) {
2537 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2538 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2541 gtk_text_buffer_insert(buffer, &iter, body, -1);
2543 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2545 compose->autowrap = prev_autowrap;
2546 if (compose->autowrap)
2547 compose_wrap_all(compose);
2551 gint i = 0, att = 0;
2552 gchar *warn_files = NULL;
2553 while (attach[i] != NULL) {
2554 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2555 if (utf8_filename) {
2556 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2557 gchar *tmp = g_strdup_printf("%s%s\n",
2558 warn_files?warn_files:"",
2564 g_free(utf8_filename);
2566 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2571 alertpanel_notice(ngettext(
2572 "The following file has been attached: \n%s",
2573 "The following files have been attached: \n%s", att), warn_files);
2585 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2587 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2588 {"Cc:", NULL, TRUE},
2589 {"References:", NULL, FALSE},
2590 {"Bcc:", NULL, TRUE},
2591 {"Newsgroups:", NULL, TRUE},
2592 {"Followup-To:", NULL, TRUE},
2593 {"List-Post:", NULL, FALSE},
2594 {"X-Priority:", NULL, FALSE},
2595 {NULL, NULL, FALSE}};
2611 g_return_val_if_fail(msginfo != NULL, -1);
2613 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2614 procheader_get_header_fields(fp, hentry);
2617 if (hentry[H_REPLY_TO].body != NULL) {
2618 if (hentry[H_REPLY_TO].body[0] != '\0') {
2620 conv_unmime_header(hentry[H_REPLY_TO].body,
2623 g_free(hentry[H_REPLY_TO].body);
2624 hentry[H_REPLY_TO].body = NULL;
2626 if (hentry[H_CC].body != NULL) {
2627 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2628 g_free(hentry[H_CC].body);
2629 hentry[H_CC].body = NULL;
2631 if (hentry[H_REFERENCES].body != NULL) {
2632 if (compose->mode == COMPOSE_REEDIT)
2633 compose->references = hentry[H_REFERENCES].body;
2635 compose->references = compose_parse_references
2636 (hentry[H_REFERENCES].body, msginfo->msgid);
2637 g_free(hentry[H_REFERENCES].body);
2639 hentry[H_REFERENCES].body = NULL;
2641 if (hentry[H_BCC].body != NULL) {
2642 if (compose->mode == COMPOSE_REEDIT)
2644 conv_unmime_header(hentry[H_BCC].body, NULL);
2645 g_free(hentry[H_BCC].body);
2646 hentry[H_BCC].body = NULL;
2648 if (hentry[H_NEWSGROUPS].body != NULL) {
2649 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2650 hentry[H_NEWSGROUPS].body = NULL;
2652 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2653 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2654 compose->followup_to =
2655 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2658 g_free(hentry[H_FOLLOWUP_TO].body);
2659 hentry[H_FOLLOWUP_TO].body = NULL;
2661 if (hentry[H_LIST_POST].body != NULL) {
2664 extract_address(hentry[H_LIST_POST].body);
2665 if (hentry[H_LIST_POST].body[0] != '\0') {
2666 scan_mailto_url(hentry[H_LIST_POST].body,
2667 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2669 g_free(compose->ml_post);
2670 compose->ml_post = to;
2673 g_free(hentry[H_LIST_POST].body);
2674 hentry[H_LIST_POST].body = NULL;
2677 /* CLAWS - X-Priority */
2678 if (compose->mode == COMPOSE_REEDIT)
2679 if (hentry[H_X_PRIORITY].body != NULL) {
2682 priority = atoi(hentry[H_X_PRIORITY].body);
2683 g_free(hentry[H_X_PRIORITY].body);
2685 hentry[H_X_PRIORITY].body = NULL;
2687 if (priority < PRIORITY_HIGHEST ||
2688 priority > PRIORITY_LOWEST)
2689 priority = PRIORITY_NORMAL;
2691 compose->priority = priority;
2694 if (compose->mode == COMPOSE_REEDIT) {
2695 if (msginfo->inreplyto && *msginfo->inreplyto)
2696 compose->inreplyto = g_strdup(msginfo->inreplyto);
2700 if (msginfo->msgid && *msginfo->msgid)
2701 compose->inreplyto = g_strdup(msginfo->msgid);
2703 if (!compose->references) {
2704 if (msginfo->msgid && *msginfo->msgid) {
2705 if (msginfo->inreplyto && *msginfo->inreplyto)
2706 compose->references =
2707 g_strdup_printf("<%s>\n\t<%s>",
2711 compose->references =
2712 g_strconcat("<", msginfo->msgid, ">",
2714 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2715 compose->references =
2716 g_strconcat("<", msginfo->inreplyto, ">",
2724 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2726 GSList *ref_id_list, *cur;
2730 ref_id_list = references_list_append(NULL, ref);
2731 if (!ref_id_list) return NULL;
2732 if (msgid && *msgid)
2733 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2738 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2739 /* "<" + Message-ID + ">" + CR+LF+TAB */
2740 len += strlen((gchar *)cur->data) + 5;
2742 if (len > MAX_REFERENCES_LEN) {
2743 /* remove second message-ID */
2744 if (ref_id_list && ref_id_list->next &&
2745 ref_id_list->next->next) {
2746 g_free(ref_id_list->next->data);
2747 ref_id_list = g_slist_remove
2748 (ref_id_list, ref_id_list->next->data);
2750 slist_free_strings(ref_id_list);
2751 g_slist_free(ref_id_list);
2758 new_ref = g_string_new("");
2759 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2760 if (new_ref->len > 0)
2761 g_string_append(new_ref, "\n\t");
2762 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2765 slist_free_strings(ref_id_list);
2766 g_slist_free(ref_id_list);
2768 new_ref_str = new_ref->str;
2769 g_string_free(new_ref, FALSE);
2774 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2775 const gchar *fmt, const gchar *qmark,
2776 const gchar *body, gboolean rewrap,
2777 gboolean need_unescape,
2778 const gchar *err_msg)
2780 MsgInfo* dummyinfo = NULL;
2781 gchar *quote_str = NULL;
2783 gboolean prev_autowrap;
2784 const gchar *trimmed_body = body;
2785 gint cursor_pos = -1;
2786 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2787 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2792 SIGNAL_BLOCK(buffer);
2795 dummyinfo = compose_msginfo_new_from_compose(compose);
2796 msginfo = dummyinfo;
2799 if (qmark != NULL) {
2801 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2802 compose->gtkaspell);
2804 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2806 quote_fmt_scan_string(qmark);
2809 buf = quote_fmt_get_buffer();
2811 alertpanel_error(_("Quote mark format error."));
2813 Xstrdup_a(quote_str, buf, goto error)
2816 if (fmt && *fmt != '\0') {
2819 while (*trimmed_body == '\n')
2823 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2824 compose->gtkaspell);
2826 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2828 if (need_unescape) {
2831 /* decode \-escape sequences in the internal representation of the quote format */
2832 tmp = malloc(strlen(fmt)+1);
2833 pref_get_unescaped_pref(tmp, fmt);
2834 quote_fmt_scan_string(tmp);
2838 quote_fmt_scan_string(fmt);
2842 buf = quote_fmt_get_buffer();
2844 gint line = quote_fmt_get_line();
2845 alertpanel_error(err_msg, line);
2851 prev_autowrap = compose->autowrap;
2852 compose->autowrap = FALSE;
2854 mark = gtk_text_buffer_get_insert(buffer);
2855 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2856 if (g_utf8_validate(buf, -1, NULL)) {
2857 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2859 gchar *tmpout = NULL;
2860 tmpout = conv_codeset_strdup
2861 (buf, conv_get_locale_charset_str_no_utf8(),
2863 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2865 tmpout = g_malloc(strlen(buf)*2+1);
2866 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2868 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2872 cursor_pos = quote_fmt_get_cursor_pos();
2873 if (cursor_pos == -1)
2874 cursor_pos = gtk_text_iter_get_offset(&iter);
2875 compose->set_cursor_pos = cursor_pos;
2877 gtk_text_buffer_get_start_iter(buffer, &iter);
2878 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2879 gtk_text_buffer_place_cursor(buffer, &iter);
2881 compose->autowrap = prev_autowrap;
2882 if (compose->autowrap && rewrap)
2883 compose_wrap_all(compose);
2890 SIGNAL_UNBLOCK(buffer);
2892 procmsg_msginfo_free( dummyinfo );
2897 /* if ml_post is of type addr@host and from is of type
2898 * addr-anything@host, return TRUE
2900 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2902 gchar *left_ml = NULL;
2903 gchar *right_ml = NULL;
2904 gchar *left_from = NULL;
2905 gchar *right_from = NULL;
2906 gboolean result = FALSE;
2908 if (!ml_post || !from)
2911 left_ml = g_strdup(ml_post);
2912 if (strstr(left_ml, "@")) {
2913 right_ml = strstr(left_ml, "@")+1;
2914 *(strstr(left_ml, "@")) = '\0';
2917 left_from = g_strdup(from);
2918 if (strstr(left_from, "@")) {
2919 right_from = strstr(left_from, "@")+1;
2920 *(strstr(left_from, "@")) = '\0';
2923 if (left_ml && left_from && right_ml && right_from
2924 && !strncmp(left_from, left_ml, strlen(left_ml))
2925 && !strcmp(right_from, right_ml)) {
2934 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2936 gchar *my_addr1, *my_addr2;
2938 if (!addr1 || !addr2)
2941 Xstrdup_a(my_addr1, addr1, return FALSE);
2942 Xstrdup_a(my_addr2, addr2, return FALSE);
2944 extract_address(my_addr1);
2945 extract_address(my_addr2);
2947 return !strcasecmp(my_addr1, my_addr2);
2950 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2951 gboolean to_all, gboolean to_ml,
2953 gboolean followup_and_reply_to)
2955 GSList *cc_list = NULL;
2958 gchar *replyto = NULL;
2959 GHashTable *to_table;
2961 gboolean reply_to_ml = FALSE;
2962 gboolean default_reply_to = FALSE;
2964 g_return_if_fail(compose->account != NULL);
2965 g_return_if_fail(msginfo != NULL);
2967 reply_to_ml = to_ml && compose->ml_post;
2969 default_reply_to = msginfo->folder &&
2970 msginfo->folder->prefs->enable_default_reply_to;
2972 if (compose->account->protocol != A_NNTP) {
2973 if (reply_to_ml && !default_reply_to) {
2975 gboolean is_subscr = is_subscription(compose->ml_post,
2978 /* normal answer to ml post with a reply-to */
2979 compose_entry_append(compose,
2982 if (compose->replyto
2983 && !same_address(compose->ml_post, compose->replyto))
2984 compose_entry_append(compose,
2988 /* answer to subscription confirmation */
2989 if (compose->replyto)
2990 compose_entry_append(compose,
2993 else if (msginfo->from)
2994 compose_entry_append(compose,
2999 else if (!(to_all || to_sender) && default_reply_to) {
3000 compose_entry_append(compose,
3001 msginfo->folder->prefs->default_reply_to,
3003 compose_entry_mark_default_to(compose,
3004 msginfo->folder->prefs->default_reply_to);
3009 Xstrdup_a(tmp1, msginfo->from, return);
3010 extract_address(tmp1);
3011 if (to_all || to_sender ||
3012 !account_find_from_address(tmp1, FALSE))
3013 compose_entry_append(compose,
3014 (compose->replyto && !to_sender)
3015 ? compose->replyto :
3016 msginfo->from ? msginfo->from : "",
3018 else if (!to_all && !to_sender) {
3019 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3020 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3021 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3022 if (compose->replyto) {
3023 compose_entry_append(compose,
3027 compose_entry_append(compose,
3028 msginfo->from ? msginfo->from : "",
3032 /* replying to own mail, use original recp */
3033 compose_entry_append(compose,
3034 msginfo->to ? msginfo->to : "",
3036 compose_entry_append(compose,
3037 msginfo->cc ? msginfo->cc : "",
3043 if (to_sender || (compose->followup_to &&
3044 !strncmp(compose->followup_to, "poster", 6)))
3045 compose_entry_append
3047 (compose->replyto ? compose->replyto :
3048 msginfo->from ? msginfo->from : ""),
3051 else if (followup_and_reply_to || to_all) {
3052 compose_entry_append
3054 (compose->replyto ? compose->replyto :
3055 msginfo->from ? msginfo->from : ""),
3058 compose_entry_append
3060 compose->followup_to ? compose->followup_to :
3061 compose->newsgroups ? compose->newsgroups : "",
3062 COMPOSE_NEWSGROUPS);
3065 compose_entry_append
3067 compose->followup_to ? compose->followup_to :
3068 compose->newsgroups ? compose->newsgroups : "",
3069 COMPOSE_NEWSGROUPS);
3072 if (msginfo->subject && *msginfo->subject) {
3076 buf = p = g_strdup(msginfo->subject);
3077 p += subject_get_prefix_length(p);
3078 memmove(buf, p, strlen(p) + 1);
3080 buf2 = g_strdup_printf("Re: %s", buf);
3081 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3086 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3088 if (to_ml && compose->ml_post) return;
3089 if (!to_all || compose->account->protocol == A_NNTP) return;
3091 if (compose->replyto) {
3092 Xstrdup_a(replyto, compose->replyto, return);
3093 extract_address(replyto);
3095 if (msginfo->from) {
3096 Xstrdup_a(from, msginfo->from, return);
3097 extract_address(from);
3100 if (replyto && from)
3101 cc_list = address_list_append_with_comments(cc_list, from);
3102 if (to_all && msginfo->folder &&
3103 msginfo->folder->prefs->enable_default_reply_to)
3104 cc_list = address_list_append_with_comments(cc_list,
3105 msginfo->folder->prefs->default_reply_to);
3106 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3107 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3109 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3111 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3112 if (compose->account) {
3113 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3114 GINT_TO_POINTER(1));
3116 /* remove address on To: and that of current account */
3117 for (cur = cc_list; cur != NULL; ) {
3118 GSList *next = cur->next;
3121 addr = g_utf8_strdown(cur->data, -1);
3122 extract_address(addr);
3124 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3125 cc_list = g_slist_remove(cc_list, cur->data);
3127 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3131 hash_free_strings(to_table);
3132 g_hash_table_destroy(to_table);
3135 for (cur = cc_list; cur != NULL; cur = cur->next)
3136 compose_entry_append(compose, (gchar *)cur->data,
3138 slist_free_strings(cc_list);
3139 g_slist_free(cc_list);
3144 #define SET_ENTRY(entry, str) \
3147 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3150 #define SET_ADDRESS(type, str) \
3153 compose_entry_append(compose, str, type); \
3156 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3158 g_return_if_fail(msginfo != NULL);
3160 SET_ENTRY(subject_entry, msginfo->subject);
3161 SET_ENTRY(from_name, msginfo->from);
3162 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3163 SET_ADDRESS(COMPOSE_CC, compose->cc);
3164 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3165 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3166 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3167 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);