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"
103 #include "spell_entry.h"
115 #define N_ATTACH_COLS (N_COL_COLUMNS)
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
132 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
133 } ComposeCallAdvancedAction;
137 PRIORITY_HIGHEST = 1,
146 COMPOSE_INSERT_SUCCESS,
147 COMPOSE_INSERT_READ_ERROR,
148 COMPOSE_INSERT_INVALID_CHARACTER,
149 COMPOSE_INSERT_NO_FILE
150 } ComposeInsertResult;
154 COMPOSE_WRITE_FOR_SEND,
155 COMPOSE_WRITE_FOR_STORE
160 COMPOSE_QUOTE_FORCED,
165 #define B64_LINE_SIZE 57
166 #define B64_BUFFSIZE 77
168 #define MAX_REFERENCES_LEN 999
170 static GList *compose_list = NULL;
172 static Compose *compose_generic_new (PrefsAccount *account,
175 GPtrArray *attach_files,
176 GList *listAddress );
178 static Compose *compose_create (PrefsAccount *account,
183 static void compose_entry_mark_default_to (Compose *compose,
184 const gchar *address);
185 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
186 ComposeQuoteMode quote_mode,
190 static Compose *compose_forward_multiple (PrefsAccount *account,
191 GSList *msginfo_list);
192 static Compose *compose_reply (MsgInfo *msginfo,
193 ComposeQuoteMode quote_mode,
198 static Compose *compose_reply_mode (ComposeMode mode,
199 GSList *msginfo_list,
201 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
202 static void compose_update_privacy_systems_menu(Compose *compose);
204 static GtkWidget *compose_account_option_menu_create
206 static void compose_set_out_encoding (Compose *compose);
207 static void compose_set_template_menu (Compose *compose);
208 static void compose_template_apply (Compose *compose,
211 static void compose_destroy (Compose *compose);
213 static void compose_entries_set (Compose *compose,
215 ComposeEntryType to_type);
216 static gint compose_parse_header (Compose *compose,
218 static gchar *compose_parse_references (const gchar *ref,
221 static gchar *compose_quote_fmt (Compose *compose,
227 gboolean need_unescape,
228 const gchar *err_msg);
230 static void compose_reply_set_entry (Compose *compose,
236 followup_and_reply_to);
237 static void compose_reedit_set_entry (Compose *compose,
240 static void compose_insert_sig (Compose *compose,
242 static ComposeInsertResult compose_insert_file (Compose *compose,
245 static gboolean compose_attach_append (Compose *compose,
248 const gchar *content_type);
249 static void compose_attach_parts (Compose *compose,
252 static gboolean compose_beautify_paragraph (Compose *compose,
253 GtkTextIter *par_iter,
255 static void compose_wrap_all (Compose *compose);
256 static void compose_wrap_all_full (Compose *compose,
259 static void compose_set_title (Compose *compose);
260 static void compose_select_account (Compose *compose,
261 PrefsAccount *account,
264 static PrefsAccount *compose_current_mail_account(void);
265 /* static gint compose_send (Compose *compose); */
266 static gboolean compose_check_for_valid_recipient
268 static gboolean compose_check_entries (Compose *compose,
269 gboolean check_everything);
270 static gint compose_write_to_file (Compose *compose,
273 gboolean attach_parts);
274 static gint compose_write_body_to_file (Compose *compose,
276 static gint compose_remove_reedit_target (Compose *compose,
278 static void compose_remove_draft (Compose *compose);
279 static gint compose_queue_sub (Compose *compose,
283 gboolean check_subject,
284 gboolean remove_reedit_target);
285 static int compose_add_attachments (Compose *compose,
287 static gchar *compose_get_header (Compose *compose);
289 static void compose_convert_header (Compose *compose,
294 gboolean addr_field);
296 static void compose_attach_info_free (AttachInfo *ainfo);
297 static void compose_attach_remove_selected (GtkAction *action,
300 static void compose_attach_property (GtkAction *action,
302 static void compose_attach_property_create (gboolean *cancelled);
303 static void attach_property_ok (GtkWidget *widget,
304 gboolean *cancelled);
305 static void attach_property_cancel (GtkWidget *widget,
306 gboolean *cancelled);
307 static gint attach_property_delete_event (GtkWidget *widget,
309 gboolean *cancelled);
310 static gboolean attach_property_key_pressed (GtkWidget *widget,
312 gboolean *cancelled);
314 static void compose_exec_ext_editor (Compose *compose);
316 static gint compose_exec_ext_editor_real (const gchar *file);
317 static gboolean compose_ext_editor_kill (Compose *compose);
318 static gboolean compose_input_cb (GIOChannel *source,
319 GIOCondition condition,
321 static void compose_set_ext_editor_sensitive (Compose *compose,
323 #endif /* G_OS_UNIX */
325 static void compose_undo_state_changed (UndoMain *undostruct,
330 static void compose_create_header_entry (Compose *compose);
331 static void compose_add_header_entry (Compose *compose, const gchar *header, gchar *text);
332 static void compose_remove_header_entries(Compose *compose);
334 static void compose_update_priority_menu_item(Compose * compose);
336 static void compose_spell_menu_changed (void *data);
337 static void compose_dict_changed (void *data);
339 static void compose_add_field_list ( Compose *compose,
340 GList *listAddress );
342 /* callback functions */
344 static gboolean compose_edit_size_alloc (GtkEditable *widget,
345 GtkAllocation *allocation,
346 GtkSHRuler *shruler);
347 static void account_activated (GtkComboBox *optmenu,
349 static void attach_selected (GtkTreeView *tree_view,
350 GtkTreePath *tree_path,
351 GtkTreeViewColumn *column,
353 static gboolean attach_button_pressed (GtkWidget *widget,
354 GdkEventButton *event,
356 static gboolean attach_key_pressed (GtkWidget *widget,
359 static void compose_send_cb (GtkAction *action, gpointer data);
360 static void compose_send_later_cb (GtkAction *action, gpointer data);
362 static void compose_save_cb (GtkAction *action,
365 static void compose_attach_cb (GtkAction *action,
367 static void compose_insert_file_cb (GtkAction *action,
369 static void compose_insert_sig_cb (GtkAction *action,
372 static void compose_close_cb (GtkAction *action,
375 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
377 static void compose_address_cb (GtkAction *action,
379 static void about_show_cb (GtkAction *action,
381 static void compose_template_activate_cb(GtkWidget *widget,
384 static void compose_ext_editor_cb (GtkAction *action,
387 static gint compose_delete_cb (GtkWidget *widget,
391 static void compose_undo_cb (GtkAction *action,
393 static void compose_redo_cb (GtkAction *action,
395 static void compose_cut_cb (GtkAction *action,
397 static void compose_copy_cb (GtkAction *action,
399 static void compose_paste_cb (GtkAction *action,
401 static void compose_paste_as_quote_cb (GtkAction *action,
403 static void compose_paste_no_wrap_cb (GtkAction *action,
405 static void compose_paste_wrap_cb (GtkAction *action,
407 static void compose_allsel_cb (GtkAction *action,
410 static void compose_advanced_action_cb (GtkAction *action,
413 static void compose_grab_focus_cb (GtkWidget *widget,
416 static void compose_changed_cb (GtkTextBuffer *textbuf,
419 static void compose_wrap_cb (GtkAction *action,
421 static void compose_wrap_all_cb (GtkAction *action,
423 static void compose_find_cb (GtkAction *action,
425 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_autoindent_cb(GtkToggleAction *action,
430 static void compose_toggle_ruler_cb (GtkToggleAction *action,
432 static void compose_toggle_sign_cb (GtkToggleAction *action,
434 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
436 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
437 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
438 static void activate_privacy_system (Compose *compose,
439 PrefsAccount *account,
441 static void compose_use_signing(Compose *compose, gboolean use_signing);
442 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
443 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
445 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
447 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
449 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
451 static void compose_attach_drag_received_cb (GtkWidget *widget,
452 GdkDragContext *drag_context,
455 GtkSelectionData *data,
459 static void compose_insert_drag_received_cb (GtkWidget *widget,
460 GdkDragContext *drag_context,
463 GtkSelectionData *data,
467 static void compose_header_drag_received_cb (GtkWidget *widget,
468 GdkDragContext *drag_context,
471 GtkSelectionData *data,
476 static gboolean compose_drag_drop (GtkWidget *widget,
477 GdkDragContext *drag_context,
479 guint time, gpointer user_data);
481 static void text_inserted (GtkTextBuffer *buffer,
486 static Compose *compose_generic_reply(MsgInfo *msginfo,
487 ComposeQuoteMode quote_mode,
491 gboolean followup_and_reply_to,
494 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
496 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
498 ComposeHeaderEntry *headerentry);
499 static gboolean compose_headerentry_button_clicked_cb (GtkWidget *button,
500 ComposeHeaderEntry *headerentry);
502 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
504 static void compose_allow_user_actions (Compose *compose, gboolean allow);
506 static void compose_nothing_cb (GtkAction *action, gpointer data)
512 static void compose_check_all (GtkAction *action, gpointer data);
513 static void compose_highlight_all (GtkAction *action, gpointer data);
514 static void compose_check_backwards (GtkAction *action, gpointer data);
515 static void compose_check_forwards_go (GtkAction *action, gpointer data);
518 static gint compose_defer_auto_save_draft (Compose *compose);
519 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
521 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
524 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
525 FolderItem *folder_item);
527 static void compose_attach_update_label(Compose *compose);
529 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
531 static GtkActionEntry compose_popup_entries[] =
533 {"Compose", NULL, "Compose" },
534 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
535 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
536 {"Compose/---", NULL, "---", NULL, NULL, NULL },
537 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
540 static GtkActionEntry compose_entries[] =
542 {"Menu", NULL, "Menu" },
544 {"Message", NULL, N_("_Message") },
545 {"Edit", NULL, N_("_Edit") },
547 {"Spelling", NULL, N_("_Spelling") },
549 {"Options", NULL, N_("_Options") },
550 {"Tools", NULL, N_("_Tools") },
551 {"Help", NULL, N_("_Help") },
553 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
554 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
555 {"Message/---", NULL, "---" },
557 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
558 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
559 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
560 /* {"Message/---", NULL, "---" }, */
561 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
562 /* {"Message/---", NULL, "---" }, */
563 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
566 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
567 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
568 {"Edit/---", NULL, "---" },
570 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
571 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
572 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
574 {"Edit/SpecialPaste", NULL, N_("Special paste") },
575 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
576 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
577 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
579 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
581 {"Edit/Advanced", NULL, N_("A_dvanced") },
582 {"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*/
583 {"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*/
584 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
585 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
586 {"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*/
587 {"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*/
588 {"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*/
589 {"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*/
590 {"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*/
591 {"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*/
592 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
593 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
594 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
595 {"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*/
597 /* {"Edit/---", NULL, "---" }, */
598 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
600 /* {"Edit/---", NULL, "---" }, */
601 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
602 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
603 /* {"Edit/---", NULL, "---" }, */
604 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
607 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
608 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
609 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
610 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
612 {"Spelling/---", NULL, "---" },
613 {"Spelling/Options", NULL, N_("_Options") },
618 {"Options/ReplyMode", NULL, N_("Reply _mode") },
619 {"Options/---", NULL, "---" },
620 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
621 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
623 /* {"Options/---", NULL, "---" }, */
625 {"Options/Priority", NULL, N_("_Priority") },
627 {"Options/Encoding", NULL, N_("Character _encoding") },
628 {"Options/Encoding/---", NULL, "---" },
629 #define ENC_ACTION(cs_char,c_char,string) \
630 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
632 {"Options/Encoding/Western", NULL, N_("Western European") },
633 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
634 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
635 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
636 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
637 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
638 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
639 {"Options/Encoding/Korean", NULL, N_("Korean") },
640 {"Options/Encoding/Thai", NULL, N_("Thai") },
643 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
645 {"Tools/Template", NULL, N_("_Template") },
646 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
647 {"Tools/Actions", NULL, N_("Actio_ns") },
648 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
651 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
654 static GtkToggleActionEntry compose_toggle_entries[] =
656 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
657 {"Edit/AutoIndent", NULL, N_("Auto _indent"), NULL, NULL, G_CALLBACK(compose_toggle_autoindent_cb) }, /* TOGGLE */
658 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
659 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
660 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
661 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
662 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
665 static GtkRadioActionEntry compose_radio_rm_entries[] =
667 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
668 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
669 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
670 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
673 static GtkRadioActionEntry compose_radio_prio_entries[] =
675 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
676 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
677 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
678 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
679 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
682 static GtkRadioActionEntry compose_radio_enc_entries[] =
684 ENC_ACTION(CS_AUTO, C_AUTO, N_("_Automatic")), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION(CS_US_ASCII, C_US_ASCII, N_("7bit ASCII (US-ASC_II)")), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION(CS_UTF_8, C_UTF_8, N_("Unicode (_UTF-8)")), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, N_("Central European (ISO-8859-_2)")), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, N_("Greek (ISO-8859-_7)")), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, N_("Turkish (ISO-8859-_9)")), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
709 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
710 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
711 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
712 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
713 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
714 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
717 static GtkTargetEntry compose_mime_types[] =
719 {"text/uri-list", 0, 0},
720 {"UTF8_STRING", 0, 0},
724 static gboolean compose_put_existing_to_front(MsgInfo *info)
726 GList *compose_list = compose_get_compose_list();
730 for (elem = compose_list; elem != NULL && elem->data != NULL;
732 Compose *c = (Compose*)elem->data;
734 if (!c->targetinfo || !c->targetinfo->msgid ||
738 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
739 gtkut_window_popup(c->window);
747 static GdkColor quote_color1 =
748 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
749 static GdkColor quote_color2 =
750 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
751 static GdkColor quote_color3 =
752 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
754 static GdkColor quote_bgcolor1 =
755 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
756 static GdkColor quote_bgcolor2 =
757 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
758 static GdkColor quote_bgcolor3 =
759 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
761 static GdkColor signature_color = {
768 static GdkColor uri_color = {
775 static void compose_create_tags(GtkTextView *text, Compose *compose)
777 GtkTextBuffer *buffer;
778 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
784 buffer = gtk_text_view_get_buffer(text);
786 if (prefs_common.enable_color) {
787 /* grab the quote colors, converting from an int to a GdkColor */
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
794 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
796 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
798 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
800 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
802 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
805 signature_color = quote_color1 = quote_color2 = quote_color3 =
806 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
809 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
810 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
811 "foreground-gdk", "e_color1,
812 "paragraph-background-gdk", "e_bgcolor1,
814 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
815 "foreground-gdk", "e_color2,
816 "paragraph-background-gdk", "e_bgcolor2,
818 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
819 "foreground-gdk", "e_color3,
820 "paragraph-background-gdk", "e_bgcolor3,
823 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
824 "foreground-gdk", "e_color1,
826 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
827 "foreground-gdk", "e_color2,
829 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
830 "foreground-gdk", "e_color3,
834 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
835 "foreground-gdk", &signature_color,
838 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
839 "foreground-gdk", &uri_color,
841 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
842 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
844 color[0] = quote_color1;
845 color[1] = quote_color2;
846 color[2] = quote_color3;
847 color[3] = quote_bgcolor1;
848 color[4] = quote_bgcolor2;
849 color[5] = quote_bgcolor3;
850 color[6] = signature_color;
851 color[7] = uri_color;
852 cmap = gdk_drawable_get_colormap(compose->window->window);
853 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
855 for (i = 0; i < 8; i++) {
856 if (success[i] == FALSE) {
859 g_warning("Compose: color allocation failed.\n");
860 style = gtk_widget_get_style(GTK_WIDGET(text));
861 quote_color1 = quote_color2 = quote_color3 =
862 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
863 signature_color = uri_color = black;
868 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
869 GPtrArray *attach_files)
871 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
874 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
876 return compose_generic_new(account, mailto, item, NULL, NULL);
879 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
881 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
884 #define SCROLL_TO_CURSOR(compose) { \
885 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
886 gtk_text_view_get_buffer( \
887 GTK_TEXT_VIEW(compose->text))); \
888 gtk_text_view_scroll_mark_onscreen( \
889 GTK_TEXT_VIEW(compose->text), \
893 static void compose_set_save_to(Compose *compose, const gchar *folderidentifier)
896 if (folderidentifier) {
897 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
898 prefs_common.compose_save_to_history = add_history(
899 prefs_common.compose_save_to_history, folderidentifier);
900 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
901 prefs_common.compose_save_to_history);
904 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
905 if (folderidentifier)
906 gtk_entry_set_text(GTK_ENTRY(entry), folderidentifier);
908 gtk_entry_set_text(GTK_ENTRY(entry), "");
911 static gchar *compose_get_save_to(Compose *compose)
914 gchar *result = NULL;
915 entry = GTK_EDITABLE(gtk_bin_get_child(GTK_BIN(compose->savemsg_combo)));
916 result = gtk_editable_get_chars(entry, 0, -1);
919 combobox_unset_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo));
920 prefs_common.compose_save_to_history = add_history(
921 prefs_common.compose_save_to_history, result);
922 combobox_set_popdown_strings(GTK_COMBO_BOX(compose->savemsg_combo),
923 prefs_common.compose_save_to_history);
928 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
929 GPtrArray *attach_files, GList *listAddress )
932 GtkTextView *textview;
933 GtkTextBuffer *textbuf;
935 const gchar *subject_format = NULL;
936 const gchar *body_format = NULL;
937 gchar *mailto_from = NULL;
938 PrefsAccount *mailto_account = NULL;
939 MsgInfo* dummyinfo = NULL;
941 /* check if mailto defines a from */
942 if (mailto && *mailto != '\0') {
943 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
944 /* mailto defines a from, check if we can get account prefs from it,
945 if not, the account prefs will be guessed using other ways, but we'll keep
948 mailto_account = account_find_from_address(mailto_from, TRUE);
950 account = mailto_account;
953 /* if no account prefs set from mailto, set if from folder prefs (if any) */
954 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
955 account = account_find_from_id(item->prefs->default_account);
957 /* if no account prefs set, fallback to the current one */
958 if (!account) account = cur_account;
959 cm_return_val_if_fail(account != NULL, NULL);
961 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
963 /* override from name if mailto asked for it */
965 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
968 /* override from name according to folder properties */
969 if (item && item->prefs &&
970 item->prefs->compose_with_format &&
971 item->prefs->compose_override_from_format &&
972 *item->prefs->compose_override_from_format != '\0') {
977 dummyinfo = compose_msginfo_new_from_compose(compose);
979 /* decode \-escape sequences in the internal representation of the quote format */
980 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
981 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
984 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
987 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
989 quote_fmt_scan_string(tmp);
992 buf = quote_fmt_get_buffer();
994 alertpanel_error(_("New message From format error."));
996 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
997 quote_fmt_reset_vartable();
1002 compose->replyinfo = NULL;
1003 compose->fwdinfo = NULL;
1005 textview = GTK_TEXT_VIEW(compose->text);
1006 textbuf = gtk_text_view_get_buffer(textview);
1007 compose_create_tags(textview, compose);
1009 undo_block(compose->undostruct);
1011 compose_set_dictionaries_from_folder_prefs(compose, item);
1014 if (account->auto_sig)
1015 compose_insert_sig(compose, FALSE);
1016 gtk_text_buffer_get_start_iter(textbuf, &iter);
1017 gtk_text_buffer_place_cursor(textbuf, &iter);
1019 if (account->protocol != A_NNTP) {
1020 if (mailto && *mailto != '\0') {
1021 compose_entries_set(compose, mailto, COMPOSE_TO);
1023 } else if (item && item->prefs) {
1024 if (item->prefs->enable_default_bcc) {
1025 compose_entry_append(compose, item->prefs->default_bcc, COMPOSE_BCC);
1027 if (item->prefs->enable_default_cc) {
1028 compose_entry_append(compose, item->prefs->default_cc, COMPOSE_CC);
1030 if (item->prefs->enable_default_replyto) {
1031 compose_entry_append(compose, item->prefs->default_replyto, COMPOSE_REPLYTO);
1033 if (item->prefs->enable_default_to) {
1034 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
1035 compose_entry_mark_default_to(compose, item->prefs->default_to);
1038 if (item && item->ret_rcpt) {
1039 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1042 if (mailto && *mailto != '\0') {
1043 if (!strchr(mailto, '@'))
1044 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
1046 compose_entries_set(compose, mailto, COMPOSE_TO);
1047 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
1048 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
1051 * CLAWS: just don't allow return receipt request, even if the user
1052 * may want to send an email. simple but foolproof.
1054 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1056 compose_add_field_list( compose, listAddress );
1058 if (item && item->prefs && item->prefs->compose_with_format) {
1059 subject_format = item->prefs->compose_subject_format;
1060 body_format = item->prefs->compose_body_format;
1061 } else if (account->compose_with_format) {
1062 subject_format = account->compose_subject_format;
1063 body_format = account->compose_body_format;
1064 } else if (prefs_common.compose_with_format) {
1065 subject_format = prefs_common.compose_subject_format;
1066 body_format = prefs_common.compose_body_format;
1069 if (subject_format || body_format) {
1072 && *subject_format != '\0' )
1074 gchar *subject = NULL;
1079 dummyinfo = compose_msginfo_new_from_compose(compose);
1081 /* decode \-escape sequences in the internal representation of the quote format */
1082 tmp = malloc(strlen(subject_format)+1);
1083 pref_get_unescaped_pref(tmp, subject_format);
1085 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1087 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1088 compose->gtkaspell);
1090 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1092 quote_fmt_scan_string(tmp);
1095 buf = quote_fmt_get_buffer();
1097 alertpanel_error(_("New message subject format error."));
1099 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1100 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1101 quote_fmt_reset_vartable();
1108 && *body_format != '\0' )
1111 GtkTextBuffer *buffer;
1112 GtkTextIter start, end;
1116 dummyinfo = compose_msginfo_new_from_compose(compose);
1118 text = GTK_TEXT_VIEW(compose->text);
1119 buffer = gtk_text_view_get_buffer(text);
1120 gtk_text_buffer_get_start_iter(buffer, &start);
1121 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1122 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1124 compose_quote_fmt(compose, dummyinfo,
1126 NULL, tmp, FALSE, TRUE,
1127 _("New message body format error at line %d."));
1128 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1129 quote_fmt_reset_vartable();
1135 procmsg_msginfo_free( dummyinfo );
1141 for (i = 0; i < attach_files->len; i++) {
1142 file = g_ptr_array_index(attach_files, i);
1143 compose_attach_append(compose, file, file, NULL);
1147 compose_show_first_last_header(compose, TRUE);
1149 /* Set save folder */
1150 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1151 gchar *folderidentifier;
1153 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1154 folderidentifier = folder_item_get_identifier(item);
1155 compose_set_save_to(compose, folderidentifier);
1156 g_free(folderidentifier);
1159 gtk_widget_grab_focus(compose->header_last->entry);
1161 undo_unblock(compose->undostruct);
1163 if (prefs_common.auto_exteditor)
1164 compose_exec_ext_editor(compose);
1166 compose->draft_timeout_tag = -1;
1167 SCROLL_TO_CURSOR(compose);
1169 compose->modified = FALSE;
1170 compose_set_title(compose);
1174 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1175 gboolean override_pref, const gchar *system)
1177 const gchar *privacy = NULL;
1179 cm_return_if_fail(compose != NULL);
1180 cm_return_if_fail(account != NULL);
1182 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1187 else if (account->default_privacy_system
1188 && strlen(account->default_privacy_system)) {
1189 privacy = account->default_privacy_system;
1191 GSList *privacy_avail = privacy_get_system_ids();
1192 if (privacy_avail && g_slist_length(privacy_avail)) {
1193 privacy = (gchar *)(privacy_avail->data);
1196 if (privacy != NULL) {
1198 g_free(compose->privacy_system);
1199 compose->privacy_system = NULL;
1201 if (compose->privacy_system == NULL)
1202 compose->privacy_system = g_strdup(privacy);
1203 else if (*(compose->privacy_system) == '\0') {
1204 g_free(compose->privacy_system);
1205 compose->privacy_system = g_strdup(privacy);
1207 compose_update_privacy_system_menu_item(compose, FALSE);
1208 compose_use_encryption(compose, TRUE);
1212 static void compose_force_signing(Compose *compose, PrefsAccount *account, const gchar *system)
1214 const gchar *privacy = NULL;
1218 else if (account->default_privacy_system
1219 && strlen(account->default_privacy_system)) {
1220 privacy = account->default_privacy_system;
1222 GSList *privacy_avail = privacy_get_system_ids();
1223 if (privacy_avail && g_slist_length(privacy_avail)) {
1224 privacy = (gchar *)(privacy_avail->data);
1228 if (privacy != NULL) {
1230 g_free(compose->privacy_system);
1231 compose->privacy_system = NULL;
1233 if (compose->privacy_system == NULL)
1234 compose->privacy_system = g_strdup(privacy);
1235 compose_update_privacy_system_menu_item(compose, FALSE);
1236 compose_use_signing(compose, TRUE);
1240 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1244 Compose *compose = NULL;
1246 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1248 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1249 cm_return_val_if_fail(msginfo != NULL, NULL);
1251 list_len = g_slist_length(msginfo_list);
1255 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1256 FALSE, prefs_common.default_reply_list, FALSE, body);
1258 case COMPOSE_REPLY_WITH_QUOTE:
1259 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1260 FALSE, prefs_common.default_reply_list, FALSE, body);
1262 case COMPOSE_REPLY_WITHOUT_QUOTE:
1263 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1264 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1266 case COMPOSE_REPLY_TO_SENDER:
1267 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1268 FALSE, FALSE, TRUE, body);
1270 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1271 compose = compose_followup_and_reply_to(msginfo,
1272 COMPOSE_QUOTE_CHECK,
1273 FALSE, FALSE, body);
1275 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1276 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1277 FALSE, FALSE, TRUE, body);
1279 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1280 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1281 FALSE, FALSE, TRUE, NULL);
1283 case COMPOSE_REPLY_TO_ALL:
1284 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1285 TRUE, FALSE, FALSE, body);
1287 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1288 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1289 TRUE, FALSE, FALSE, body);
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1293 TRUE, FALSE, FALSE, NULL);
1295 case COMPOSE_REPLY_TO_LIST:
1296 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1297 FALSE, TRUE, FALSE, body);
1299 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1300 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1301 FALSE, TRUE, FALSE, body);
1303 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1304 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1305 FALSE, TRUE, FALSE, NULL);
1307 case COMPOSE_FORWARD:
1308 if (prefs_common.forward_as_attachment) {
1309 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1312 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1316 case COMPOSE_FORWARD_INLINE:
1317 /* check if we reply to more than one Message */
1318 if (list_len == 1) {
1319 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1322 /* more messages FALL THROUGH */
1323 case COMPOSE_FORWARD_AS_ATTACH:
1324 compose = compose_forward_multiple(NULL, msginfo_list);
1326 case COMPOSE_REDIRECT:
1327 compose = compose_redirect(NULL, msginfo, FALSE);
1330 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1333 if (compose == NULL) {
1334 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1338 compose->rmode = mode;
1339 switch (compose->rmode) {
1341 case COMPOSE_REPLY_WITH_QUOTE:
1342 case COMPOSE_REPLY_WITHOUT_QUOTE:
1343 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1344 debug_print("reply mode Normal\n");
1345 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1346 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1348 case COMPOSE_REPLY_TO_SENDER:
1349 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1350 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1351 debug_print("reply mode Sender\n");
1352 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1354 case COMPOSE_REPLY_TO_ALL:
1355 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1356 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1357 debug_print("reply mode All\n");
1358 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1360 case COMPOSE_REPLY_TO_LIST:
1361 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1362 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1363 debug_print("reply mode List\n");
1364 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1372 static Compose *compose_reply(MsgInfo *msginfo,
1373 ComposeQuoteMode quote_mode,
1379 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1380 to_sender, FALSE, body);
1383 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1384 ComposeQuoteMode quote_mode,
1389 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1390 to_sender, TRUE, body);
1393 static void compose_extract_original_charset(Compose *compose)
1395 MsgInfo *info = NULL;
1396 if (compose->replyinfo) {
1397 info = compose->replyinfo;
1398 } else if (compose->fwdinfo) {
1399 info = compose->fwdinfo;
1400 } else if (compose->targetinfo) {
1401 info = compose->targetinfo;
1404 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1405 MimeInfo *partinfo = mimeinfo;
1406 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1407 partinfo = procmime_mimeinfo_next(partinfo);
1409 compose->orig_charset =
1410 g_strdup(procmime_mimeinfo_get_parameter(
1411 partinfo, "charset"));
1413 procmime_mimeinfo_free_all(mimeinfo);
1417 #define SIGNAL_BLOCK(buffer) { \
1418 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1419 G_CALLBACK(compose_changed_cb), \
1421 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1422 G_CALLBACK(text_inserted), \
1426 #define SIGNAL_UNBLOCK(buffer) { \
1427 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1428 G_CALLBACK(compose_changed_cb), \
1430 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1431 G_CALLBACK(text_inserted), \
1435 static Compose *compose_generic_reply(MsgInfo *msginfo,
1436 ComposeQuoteMode quote_mode,
1437 gboolean to_all, gboolean to_ml,
1439 gboolean followup_and_reply_to,
1443 PrefsAccount *account = NULL;
1444 GtkTextView *textview;
1445 GtkTextBuffer *textbuf;
1446 gboolean quote = FALSE;
1447 const gchar *qmark = NULL;
1448 const gchar *body_fmt = NULL;
1449 gchar *s_system = NULL;
1451 cm_return_val_if_fail(msginfo != NULL, NULL);
1452 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1454 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1456 cm_return_val_if_fail(account != NULL, NULL);
1458 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1460 compose->updating = TRUE;
1462 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1463 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1465 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1466 if (!compose->replyinfo)
1467 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1469 compose_extract_original_charset(compose);
1471 if (msginfo->folder && msginfo->folder->ret_rcpt)
1472 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1474 /* Set save folder */
1475 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1476 gchar *folderidentifier;
1478 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1479 folderidentifier = folder_item_get_identifier(msginfo->folder);
1480 compose_set_save_to(compose, folderidentifier);
1481 g_free(folderidentifier);
1484 if (compose_parse_header(compose, msginfo) < 0) {
1485 compose->updating = FALSE;
1486 compose_destroy(compose);
1490 /* override from name according to folder properties */
1491 if (msginfo->folder && msginfo->folder->prefs &&
1492 msginfo->folder->prefs->reply_with_format &&
1493 msginfo->folder->prefs->reply_override_from_format &&
1494 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1499 /* decode \-escape sequences in the internal representation of the quote format */
1500 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1501 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1504 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1505 compose->gtkaspell);
1507 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1509 quote_fmt_scan_string(tmp);
1512 buf = quote_fmt_get_buffer();
1514 alertpanel_error(_("Message reply From format error."));
1516 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1517 quote_fmt_reset_vartable();
1522 textview = (GTK_TEXT_VIEW(compose->text));
1523 textbuf = gtk_text_view_get_buffer(textview);
1524 compose_create_tags(textview, compose);
1526 undo_block(compose->undostruct);
1528 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1531 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1532 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1533 /* use the reply format of folder (if enabled), or the account's one
1534 (if enabled) or fallback to the global reply format, which is always
1535 enabled (even if empty), and use the relevant quotemark */
1537 if (msginfo->folder && msginfo->folder->prefs &&
1538 msginfo->folder->prefs->reply_with_format) {
1539 qmark = msginfo->folder->prefs->reply_quotemark;
1540 body_fmt = msginfo->folder->prefs->reply_body_format;
1542 } else if (account->reply_with_format) {
1543 qmark = account->reply_quotemark;
1544 body_fmt = account->reply_body_format;
1547 qmark = prefs_common.quotemark;
1548 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1549 body_fmt = gettext(prefs_common.quotefmt);
1556 /* empty quotemark is not allowed */
1557 if (qmark == NULL || *qmark == '\0')
1559 compose_quote_fmt(compose, compose->replyinfo,
1560 body_fmt, qmark, body, FALSE, TRUE,
1561 _("Message reply format error at line %d."));
1562 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1563 quote_fmt_reset_vartable();
1566 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1567 compose_force_encryption(compose, account, FALSE, s_system);
1570 privacy_msginfo_get_signed_state(compose->replyinfo, &s_system);
1571 if (MSG_IS_SIGNED(compose->replyinfo->flags) && account->default_sign_reply) {
1572 compose_force_signing(compose, account, s_system);
1576 SIGNAL_BLOCK(textbuf);
1578 if (account->auto_sig)
1579 compose_insert_sig(compose, FALSE);
1581 compose_wrap_all(compose);
1583 SIGNAL_UNBLOCK(textbuf);
1585 gtk_widget_grab_focus(compose->text);
1587 undo_unblock(compose->undostruct);
1589 if (prefs_common.auto_exteditor)
1590 compose_exec_ext_editor(compose);
1592 compose->modified = FALSE;
1593 compose_set_title(compose);
1595 compose->updating = FALSE;
1596 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1597 SCROLL_TO_CURSOR(compose);
1599 if (compose->deferred_destroy) {
1600 compose_destroy(compose);
1607 #define INSERT_FW_HEADER(var, hdr) \
1608 if (msginfo->var && *msginfo->var) { \
1609 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1610 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1611 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1614 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1615 gboolean as_attach, const gchar *body,
1616 gboolean no_extedit,
1620 GtkTextView *textview;
1621 GtkTextBuffer *textbuf;
1624 cm_return_val_if_fail(msginfo != NULL, NULL);
1625 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
1628 !(account = compose_guess_forward_account_from_msginfo
1630 account = cur_account;
1632 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1634 compose->updating = TRUE;
1635 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1636 if (!compose->fwdinfo)
1637 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1639 compose_extract_original_charset(compose);
1641 if (msginfo->subject && *msginfo->subject) {
1642 gchar *buf, *buf2, *p;
1644 buf = p = g_strdup(msginfo->subject);
1645 p += subject_get_prefix_length(p);
1646 memmove(buf, p, strlen(p) + 1);
1648 buf2 = g_strdup_printf("Fw: %s", buf);
1649 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1655 /* override from name according to folder properties */
1656 if (msginfo->folder && msginfo->folder->prefs &&
1657 msginfo->folder->prefs->forward_with_format &&
1658 msginfo->folder->prefs->forward_override_from_format &&
1659 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1663 MsgInfo *full_msginfo = NULL;
1666 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1668 full_msginfo = procmsg_msginfo_copy(msginfo);
1670 /* decode \-escape sequences in the internal representation of the quote format */
1671 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1672 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1675 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1676 compose->gtkaspell);
1678 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1680 quote_fmt_scan_string(tmp);
1683 buf = quote_fmt_get_buffer();
1685 alertpanel_error(_("Message forward From format error."));
1687 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1688 quote_fmt_reset_vartable();
1691 procmsg_msginfo_free(full_msginfo);
1694 textview = GTK_TEXT_VIEW(compose->text);
1695 textbuf = gtk_text_view_get_buffer(textview);
1696 compose_create_tags(textview, compose);
1698 undo_block(compose->undostruct);
1702 msgfile = procmsg_get_message_file(msginfo);
1703 if (!is_file_exist(msgfile))
1704 g_warning("%s: file not exist\n", msgfile);
1706 compose_attach_append(compose, msgfile, msgfile,
1711 const gchar *qmark = NULL;
1712 const gchar *body_fmt = NULL;
1713 MsgInfo *full_msginfo;
1715 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1716 body_fmt = gettext(prefs_common.fw_quotefmt);
1720 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1722 full_msginfo = procmsg_msginfo_copy(msginfo);
1724 /* use the forward format of folder (if enabled), or the account's one
1725 (if enabled) or fallback to the global forward format, which is always
1726 enabled (even if empty), and use the relevant quotemark */
1727 if (msginfo->folder && msginfo->folder->prefs &&
1728 msginfo->folder->prefs->forward_with_format) {
1729 qmark = msginfo->folder->prefs->forward_quotemark;
1730 body_fmt = msginfo->folder->prefs->forward_body_format;
1732 } else if (account->forward_with_format) {
1733 qmark = account->forward_quotemark;
1734 body_fmt = account->forward_body_format;
1737 qmark = prefs_common.fw_quotemark;
1738 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1739 body_fmt = gettext(prefs_common.fw_quotefmt);
1744 /* empty quotemark is not allowed */
1745 if (qmark == NULL || *qmark == '\0')
1748 compose_quote_fmt(compose, full_msginfo,
1749 body_fmt, qmark, body, FALSE, TRUE,
1750 _("Message forward format error at line %d."));
1751 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1752 quote_fmt_reset_vartable();
1753 compose_attach_parts(compose, msginfo);
1755 procmsg_msginfo_free(full_msginfo);
1758 SIGNAL_BLOCK(textbuf);
1760 if (account->auto_sig)
1761 compose_insert_sig(compose, FALSE);
1763 compose_wrap_all(compose);
1765 SIGNAL_UNBLOCK(textbuf);
1767 gtk_text_buffer_get_start_iter(textbuf, &iter);
1768 gtk_text_buffer_place_cursor(textbuf, &iter);
1770 gtk_widget_grab_focus(compose->header_last->entry);
1772 if (!no_extedit && prefs_common.auto_exteditor)
1773 compose_exec_ext_editor(compose);
1776 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1777 gchar *folderidentifier;
1779 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1780 folderidentifier = folder_item_get_identifier(msginfo->folder);
1781 compose_set_save_to(compose, folderidentifier);
1782 g_free(folderidentifier);
1785 undo_unblock(compose->undostruct);
1787 compose->modified = FALSE;
1788 compose_set_title(compose);
1790 compose->updating = FALSE;
1791 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1792 SCROLL_TO_CURSOR(compose);
1794 if (compose->deferred_destroy) {
1795 compose_destroy(compose);
1802 #undef INSERT_FW_HEADER
1804 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1807 GtkTextView *textview;
1808 GtkTextBuffer *textbuf;
1812 gboolean single_mail = TRUE;
1814 cm_return_val_if_fail(msginfo_list != NULL, NULL);
1816 if (g_slist_length(msginfo_list) > 1)
1817 single_mail = FALSE;
1819 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1820 if (((MsgInfo *)msginfo->data)->folder == NULL)
1823 /* guess account from first selected message */
1825 !(account = compose_guess_forward_account_from_msginfo
1826 (msginfo_list->data)))
1827 account = cur_account;
1829 cm_return_val_if_fail(account != NULL, NULL);
1831 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1832 if (msginfo->data) {
1833 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1834 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1838 if (msginfo_list == NULL || msginfo_list->data == NULL) {
1839 g_warning("no msginfo_list");
1843 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1845 compose->updating = TRUE;
1847 /* override from name according to folder properties */
1848 if (msginfo_list->data) {
1849 MsgInfo *msginfo = msginfo_list->data;
1851 if (msginfo->folder && msginfo->folder->prefs &&
1852 msginfo->folder->prefs->forward_with_format &&
1853 msginfo->folder->prefs->forward_override_from_format &&
1854 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1859 /* decode \-escape sequences in the internal representation of the quote format */
1860 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1861 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1864 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1865 compose->gtkaspell);
1867 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1869 quote_fmt_scan_string(tmp);
1872 buf = quote_fmt_get_buffer();
1874 alertpanel_error(_("Message forward From format error."));
1876 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1877 quote_fmt_reset_vartable();
1883 textview = GTK_TEXT_VIEW(compose->text);
1884 textbuf = gtk_text_view_get_buffer(textview);
1885 compose_create_tags(textview, compose);
1887 undo_block(compose->undostruct);
1888 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1889 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1891 if (!is_file_exist(msgfile))
1892 g_warning("%s: file not exist\n", msgfile);
1894 compose_attach_append(compose, msgfile, msgfile,
1900 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1901 if (info->subject && *info->subject) {
1902 gchar *buf, *buf2, *p;
1904 buf = p = g_strdup(info->subject);
1905 p += subject_get_prefix_length(p);
1906 memmove(buf, p, strlen(p) + 1);
1908 buf2 = g_strdup_printf("Fw: %s", buf);
1909 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1915 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1916 _("Fw: multiple emails"));
1919 SIGNAL_BLOCK(textbuf);
1921 if (account->auto_sig)
1922 compose_insert_sig(compose, FALSE);
1924 compose_wrap_all(compose);
1926 SIGNAL_UNBLOCK(textbuf);
1928 gtk_text_buffer_get_start_iter(textbuf, &iter);
1929 gtk_text_buffer_place_cursor(textbuf, &iter);
1931 gtk_widget_grab_focus(compose->header_last->entry);
1932 undo_unblock(compose->undostruct);
1933 compose->modified = FALSE;
1934 compose_set_title(compose);
1936 compose->updating = FALSE;
1937 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1938 SCROLL_TO_CURSOR(compose);
1940 if (compose->deferred_destroy) {
1941 compose_destroy(compose);
1948 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1950 GtkTextIter start = *iter;
1951 GtkTextIter end_iter;
1952 int start_pos = gtk_text_iter_get_offset(&start);
1954 if (!compose->account->sig_sep)
1957 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1958 start_pos+strlen(compose->account->sig_sep));
1960 /* check sig separator */
1961 str = gtk_text_iter_get_text(&start, &end_iter);
1962 if (!strcmp(str, compose->account->sig_sep)) {
1964 /* check end of line (\n) */
1965 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1966 start_pos+strlen(compose->account->sig_sep));
1967 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1968 start_pos+strlen(compose->account->sig_sep)+1);
1969 tmp = gtk_text_iter_get_text(&start, &end_iter);
1970 if (!strcmp(tmp,"\n")) {
1982 static void compose_colorize_signature(Compose *compose)
1984 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1986 GtkTextIter end_iter;
1987 gtk_text_buffer_get_start_iter(buffer, &iter);
1988 while (gtk_text_iter_forward_line(&iter))
1989 if (compose_is_sig_separator(compose, buffer, &iter)) {
1990 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1991 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1995 #define BLOCK_WRAP() { \
1996 prev_autowrap = compose->autowrap; \
1997 buffer = gtk_text_view_get_buffer( \
1998 GTK_TEXT_VIEW(compose->text)); \
1999 compose->autowrap = FALSE; \
2001 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2002 G_CALLBACK(compose_changed_cb), \
2004 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
2005 G_CALLBACK(text_inserted), \
2008 #define UNBLOCK_WRAP() { \
2009 compose->autowrap = prev_autowrap; \
2010 if (compose->autowrap) { \
2011 gint old = compose->draft_timeout_tag; \
2012 compose->draft_timeout_tag = -2; \
2013 compose_wrap_all(compose); \
2014 compose->draft_timeout_tag = old; \
2017 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2018 G_CALLBACK(compose_changed_cb), \
2020 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
2021 G_CALLBACK(text_inserted), \
2025 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
2027 Compose *compose = NULL;
2028 PrefsAccount *account = NULL;
2029 GtkTextView *textview;
2030 GtkTextBuffer *textbuf;
2034 gchar buf[BUFFSIZE];
2035 gboolean use_signing = FALSE;
2036 gboolean use_encryption = FALSE;
2037 gchar *privacy_system = NULL;
2038 int priority = PRIORITY_NORMAL;
2039 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
2040 gboolean autowrap = prefs_common.autowrap;
2041 gboolean autoindent = prefs_common.auto_indent;
2043 cm_return_val_if_fail(msginfo != NULL, NULL);
2044 cm_return_val_if_fail(msginfo->folder != NULL, NULL);
2046 if (compose_put_existing_to_front(msginfo)) {
2050 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2051 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2052 gchar queueheader_buf[BUFFSIZE];
2055 /* Select Account from queue headers */
2056 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2057 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
2058 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
2059 account = account_find_from_id(id);
2061 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2062 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
2063 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
2064 account = account_find_from_id(id);
2066 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2067 sizeof(queueheader_buf), "NAID:")) {
2068 id = atoi(&queueheader_buf[strlen("NAID:")]);
2069 account = account_find_from_id(id);
2071 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2072 sizeof(queueheader_buf), "MAID:")) {
2073 id = atoi(&queueheader_buf[strlen("MAID:")]);
2074 account = account_find_from_id(id);
2076 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2077 sizeof(queueheader_buf), "S:")) {
2078 account = account_find_from_address(queueheader_buf, FALSE);
2080 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2081 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2082 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2083 use_signing = param;
2086 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2087 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2088 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2089 use_signing = param;
2092 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2093 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2094 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2095 use_encryption = param;
2097 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2098 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2099 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2100 use_encryption = param;
2102 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2103 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2104 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2107 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2108 sizeof(queueheader_buf), "X-Claws-Auto-Indent:")) {
2109 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Indent:")]);
2112 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2113 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2114 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2116 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2117 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2118 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2120 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2121 sizeof(queueheader_buf), "X-Priority: ")) {
2122 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2125 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2126 sizeof(queueheader_buf), "RMID:")) {
2127 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2128 if (tokens[0] && tokens[1] && tokens[2]) {
2129 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2130 if (orig_item != NULL) {
2131 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2136 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2137 sizeof(queueheader_buf), "FMID:")) {
2138 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2139 if (tokens[0] && tokens[1] && tokens[2]) {
2140 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2141 if (orig_item != NULL) {
2142 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2148 account = msginfo->folder->folder->account;
2151 if (!account && prefs_common.reedit_account_autosel) {
2152 gchar from[BUFFSIZE];
2153 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2154 extract_address(from);
2155 account = account_find_from_address(from, FALSE);
2159 account = cur_account;
2161 cm_return_val_if_fail(account != NULL, NULL);
2163 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2165 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2166 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoIndent", autoindent);
2167 compose->autowrap = autowrap;
2168 compose->replyinfo = replyinfo;
2169 compose->fwdinfo = fwdinfo;
2171 compose->updating = TRUE;
2172 compose->priority = priority;
2174 if (privacy_system != NULL) {
2175 compose->privacy_system = privacy_system;
2176 compose_use_signing(compose, use_signing);
2177 compose_use_encryption(compose, use_encryption);
2178 compose_update_privacy_system_menu_item(compose, FALSE);
2180 activate_privacy_system(compose, account, FALSE);
2183 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2185 compose_extract_original_charset(compose);
2187 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2188 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2189 gchar queueheader_buf[BUFFSIZE];
2191 /* Set message save folder */
2192 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2193 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2194 compose_set_save_to(compose, &queueheader_buf[4]);
2196 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2197 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2199 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2204 if (compose_parse_header(compose, msginfo) < 0) {
2205 compose->updating = FALSE;
2206 compose_destroy(compose);
2209 compose_reedit_set_entry(compose, msginfo);
2211 textview = GTK_TEXT_VIEW(compose->text);
2212 textbuf = gtk_text_view_get_buffer(textview);
2213 compose_create_tags(textview, compose);
2215 mark = gtk_text_buffer_get_insert(textbuf);
2216 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2218 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2219 G_CALLBACK(compose_changed_cb),
2222 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2223 fp = procmime_get_first_encrypted_text_content(msginfo);
2225 compose_force_encryption(compose, account, TRUE, NULL);
2228 fp = procmime_get_first_text_content(msginfo);
2231 g_warning("Can't get text part\n");
2235 gboolean prev_autowrap = compose->autowrap;
2236 GtkTextBuffer *buffer = textbuf;
2238 while (fgets(buf, sizeof(buf), fp) != NULL) {
2240 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2246 compose_attach_parts(compose, msginfo);
2248 compose_colorize_signature(compose);
2250 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2251 G_CALLBACK(compose_changed_cb),
2254 gtk_widget_grab_focus(compose->text);
2256 if (prefs_common.auto_exteditor) {
2257 compose_exec_ext_editor(compose);
2259 compose->modified = FALSE;
2260 compose_set_title(compose);
2262 compose->updating = FALSE;
2263 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2264 SCROLL_TO_CURSOR(compose);
2266 if (compose->deferred_destroy) {
2267 compose_destroy(compose);
2271 compose->sig_str = account_get_signature_str(compose->account);
2276 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2283 cm_return_val_if_fail(msginfo != NULL, NULL);
2286 account = account_get_reply_account(msginfo,
2287 prefs_common.reply_account_autosel);
2288 cm_return_val_if_fail(account != NULL, NULL);
2290 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2292 compose->updating = TRUE;
2294 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2295 compose->replyinfo = NULL;
2296 compose->fwdinfo = NULL;
2298 compose_show_first_last_header(compose, TRUE);
2300 gtk_widget_grab_focus(compose->header_last->entry);
2302 filename = procmsg_get_message_file(msginfo);
2304 if (filename == NULL) {
2305 compose->updating = FALSE;
2306 compose_destroy(compose);
2311 compose->redirect_filename = filename;
2313 /* Set save folder */
2314 item = msginfo->folder;
2315 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2316 gchar *folderidentifier;
2318 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2319 folderidentifier = folder_item_get_identifier(item);
2320 compose_set_save_to(compose, folderidentifier);
2321 g_free(folderidentifier);
2324 compose_attach_parts(compose, msginfo);
2326 if (msginfo->subject)
2327 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2329 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2331 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2332 _("Message redirect format error at line %d."));
2333 quote_fmt_reset_vartable();
2334 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2336 compose_colorize_signature(compose);
2339 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2340 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2341 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2343 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2344 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2345 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2346 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2347 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2348 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2349 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2350 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2352 if (compose->toolbar->draft_btn)
2353 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2354 if (compose->toolbar->insert_btn)
2355 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2356 if (compose->toolbar->attach_btn)
2357 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2358 if (compose->toolbar->sig_btn)
2359 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2360 if (compose->toolbar->exteditor_btn)
2361 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2362 if (compose->toolbar->linewrap_current_btn)
2363 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2364 if (compose->toolbar->linewrap_all_btn)
2365 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2367 compose->modified = FALSE;
2368 compose_set_title(compose);
2369 compose->updating = FALSE;
2370 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2371 SCROLL_TO_CURSOR(compose);
2373 if (compose->deferred_destroy) {
2374 compose_destroy(compose);
2381 GList *compose_get_compose_list(void)
2383 return compose_list;
2386 void compose_entry_append(Compose *compose, const gchar *address,
2387 ComposeEntryType type)
2389 const gchar *header;
2391 gboolean in_quote = FALSE;
2392 if (!address || *address == '\0') return;
2399 header = N_("Bcc:");
2401 case COMPOSE_REPLYTO:
2402 header = N_("Reply-To:");
2404 case COMPOSE_NEWSGROUPS:
2405 header = N_("Newsgroups:");
2407 case COMPOSE_FOLLOWUPTO:
2408 header = N_( "Followup-To:");
2415 header = prefs_common_translated_header_name(header);
2417 cur = begin = (gchar *)address;
2419 /* we separate the line by commas, but not if we're inside a quoted
2421 while (*cur != '\0') {
2423 in_quote = !in_quote;
2424 if (*cur == ',' && !in_quote) {
2425 gchar *tmp = g_strdup(begin);
2427 tmp[cur-begin]='\0';
2430 while (*tmp == ' ' || *tmp == '\t')
2432 compose_add_header_entry(compose, header, tmp);
2439 gchar *tmp = g_strdup(begin);
2441 tmp[cur-begin]='\0';
2444 while (*tmp == ' ' || *tmp == '\t')
2446 compose_add_header_entry(compose, header, tmp);
2451 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2453 static GdkColor yellow;
2454 static GdkColor black;
2455 static gboolean yellow_initialised = FALSE;
2459 if (!yellow_initialised) {
2460 gdk_color_parse("#f5f6be", &yellow);
2461 gdk_color_parse("#000000", &black);
2462 yellow_initialised = gdk_colormap_alloc_color(
2463 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2464 yellow_initialised &= gdk_colormap_alloc_color(
2465 gdk_colormap_get_system(), &black, FALSE, TRUE);
2468 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2469 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2470 if (gtk_entry_get_text(entry) &&
2471 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2472 if (yellow_initialised) {
2473 gtk_widget_modify_base(
2474 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2475 GTK_STATE_NORMAL, &yellow);
2476 gtk_widget_modify_text(
2477 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2478 GTK_STATE_NORMAL, &black);
2484 void compose_toolbar_cb(gint action, gpointer data)
2486 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2487 Compose *compose = (Compose*)toolbar_item->parent;
2489 cm_return_if_fail(compose != NULL);
2493 compose_send_cb(NULL, compose);
2496 compose_send_later_cb(NULL, compose);
2499 compose_draft(compose, COMPOSE_QUIT_EDITING);
2502 compose_insert_file_cb(NULL, compose);
2505 compose_attach_cb(NULL, compose);
2508 compose_insert_sig(compose, FALSE);
2511 compose_ext_editor_cb(NULL, compose);
2513 case A_LINEWRAP_CURRENT:
2514 compose_beautify_paragraph(compose, NULL, TRUE);
2516 case A_LINEWRAP_ALL:
2517 compose_wrap_all_full(compose, TRUE);
2520 compose_address_cb(NULL, compose);
2523 case A_CHECK_SPELLING:
2524 compose_check_all(NULL, compose);
2532 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2537 gchar *subject = NULL;
2541 gchar **attach = NULL;
2543 /* get mailto parts but skip from */
2544 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2547 compose_entry_append(compose, to, to_type);
2549 compose_entry_append(compose, cc, COMPOSE_CC);
2551 compose_entry_append(compose, bcc, COMPOSE_BCC);
2553 if (!g_utf8_validate (subject, -1, NULL)) {
2554 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2555 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2558 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2562 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2563 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2566 gboolean prev_autowrap = compose->autowrap;
2568 compose->autowrap = FALSE;
2570 mark = gtk_text_buffer_get_insert(buffer);
2571 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2573 if (!g_utf8_validate (body, -1, NULL)) {
2574 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2575 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2578 gtk_text_buffer_insert(buffer, &iter, body, -1);
2580 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2582 compose->autowrap = prev_autowrap;
2583 if (compose->autowrap)
2584 compose_wrap_all(compose);
2588 gint i = 0, att = 0;
2589 gchar *warn_files = NULL;
2590 while (attach[i] != NULL) {
2591 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2592 if (utf8_filename) {
2593 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2594 gchar *tmp = g_strdup_printf("%s%s\n",
2595 warn_files?warn_files:"",
2601 g_free(utf8_filename);
2603 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2608 alertpanel_notice(ngettext(
2609 "The following file has been attached: \n%s",
2610 "The following files have been attached: \n%s", att), warn_files);
2622 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2624 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2625 {"Cc:", NULL, TRUE},
2626 {"References:", NULL, FALSE},
2627 {"Bcc:", NULL, TRUE},
2628 {"Newsgroups:", NULL, TRUE},
2629 {"Followup-To:", NULL, TRUE},
2630 {"List-Post:", NULL, FALSE},
2631 {"X-Priority:", NULL, FALSE},
2632 {NULL, NULL, FALSE}};
2648 cm_return_val_if_fail(msginfo != NULL, -1);
2650 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2651 procheader_get_header_fields(fp, hentry);
2654 if (hentry[H_REPLY_TO].body != NULL) {
2655 if (hentry[H_REPLY_TO].body[0] != '\0') {
2657 conv_unmime_header(hentry[H_REPLY_TO].body,
2660 g_free(hentry[H_REPLY_TO].body);
2661 hentry[H_REPLY_TO].body = NULL;
2663 if (hentry[H_CC].body != NULL) {
2664 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2665 g_free(hentry[H_CC].body);
2666 hentry[H_CC].body = NULL;
2668 if (hentry[H_REFERENCES].body != NULL) {
2669 if (compose->mode == COMPOSE_REEDIT)
2670 compose->references = hentry[H_REFERENCES].body;
2672 compose->references = compose_parse_references
2673 (hentry[H_REFERENCES].body, msginfo->msgid);
2674 g_free(hentry[H_REFERENCES].body);
2676 hentry[H_REFERENCES].body = NULL;
2678 if (hentry[H_BCC].body != NULL) {
2679 if (compose->mode == COMPOSE_REEDIT)
2681 conv_unmime_header(hentry[H_BCC].body, NULL);
2682 g_free(hentry[H_BCC].body);
2683 hentry[H_BCC].body = NULL;
2685 if (hentry[H_NEWSGROUPS].body != NULL) {
2686 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2687 hentry[H_NEWSGROUPS].body = NULL;
2689 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2690 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2691 compose->followup_to =
2692 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2695 g_free(hentry[H_FOLLOWUP_TO].body);
2696 hentry[H_FOLLOWUP_TO].body = NULL;
2698 if (hentry[H_LIST_POST].body != NULL) {
2701 extract_address(hentry[H_LIST_POST].body);
2702 if (hentry[H_LIST_POST].body[0] != '\0') {
2703 scan_mailto_url(hentry[H_LIST_POST].body,
2704 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2706 g_free(compose->ml_post);
2707 compose->ml_post = to;
2710 g_free(hentry[H_LIST_POST].body);
2711 hentry[H_LIST_POST].body = NULL;
2714 /* CLAWS - X-Priority */
2715 if (compose->mode == COMPOSE_REEDIT)
2716 if (hentry[H_X_PRIORITY].body != NULL) {
2719 priority = atoi(hentry[H_X_PRIORITY].body);
2720 g_free(hentry[H_X_PRIORITY].body);
2722 hentry[H_X_PRIORITY].body = NULL;
2724 if (priority < PRIORITY_HIGHEST ||
2725 priority > PRIORITY_LOWEST)
2726 priority = PRIORITY_NORMAL;
2728 compose->priority = priority;
2731 if (compose->mode == COMPOSE_REEDIT) {
2732 if (msginfo->inreplyto && *msginfo->inreplyto)
2733 compose->inreplyto = g_strdup(msginfo->inreplyto);
2737 if (msginfo->msgid && *msginfo->msgid)
2738 compose->inreplyto = g_strdup(msginfo->msgid);
2740 if (!compose->references) {
2741 if (msginfo->msgid && *msginfo->msgid) {
2742 if (msginfo->inreplyto && *msginfo->inreplyto)
2743 compose->references =
2744 g_strdup_printf("<%s>\n\t<%s>",
2748 compose->references =
2749 g_strconcat("<", msginfo->msgid, ">",
2751 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2752 compose->references =
2753 g_strconcat("<", msginfo->inreplyto, ">",
2761 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2763 GSList *ref_id_list, *cur;
2767 ref_id_list = references_list_append(NULL, ref);
2768 if (!ref_id_list) return NULL;
2769 if (msgid && *msgid)
2770 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2775 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2776 /* "<" + Message-ID + ">" + CR+LF+TAB */
2777 len += strlen((gchar *)cur->data) + 5;
2779 if (len > MAX_REFERENCES_LEN) {
2780 /* remove second message-ID */
2781 if (ref_id_list && ref_id_list->next &&
2782 ref_id_list->next->next) {
2783 g_free(ref_id_list->next->data);
2784 ref_id_list = g_slist_remove
2785 (ref_id_list, ref_id_list->next->data);
2787 slist_free_strings(ref_id_list);
2788 g_slist_free(ref_id_list);
2795 new_ref = g_string_new("");
2796 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2797 if (new_ref->len > 0)
2798 g_string_append(new_ref, "\n\t");
2799 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2802 slist_free_strings(ref_id_list);
2803 g_slist_free(ref_id_list);
2805 new_ref_str = new_ref->str;
2806 g_string_free(new_ref, FALSE);
2811 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2812 const gchar *fmt, const gchar *qmark,
2813 const gchar *body, gboolean rewrap,
2814 gboolean need_unescape,
2815 const gchar *err_msg)
2817 MsgInfo* dummyinfo = NULL;
2818 gchar *quote_str = NULL;
2820 gboolean prev_autowrap;
2821 const gchar *trimmed_body = body;
2822 gint cursor_pos = -1;
2823 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2824 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2829 SIGNAL_BLOCK(buffer);
2832 dummyinfo = compose_msginfo_new_from_compose(compose);
2833 msginfo = dummyinfo;
2836 if (qmark != NULL) {
2838 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2839 compose->gtkaspell);
2841 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2843 quote_fmt_scan_string(qmark);
2846 buf = quote_fmt_get_buffer();
2848 alertpanel_error(_("Quote mark format error."));
2850 Xstrdup_a(quote_str, buf, goto error)
2853 if (fmt && *fmt != '\0') {
2856 while (*trimmed_body == '\n')
2860 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2861 compose->gtkaspell);
2863 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2865 if (need_unescape) {
2868 /* decode \-escape sequences in the internal representation of the quote format */
2869 tmp = malloc(strlen(fmt)+1);
2870 pref_get_unescaped_pref(tmp, fmt);
2871 quote_fmt_scan_string(tmp);
2875 quote_fmt_scan_string(fmt);
2879 buf = quote_fmt_get_buffer();
2881 gint line = quote_fmt_get_line();
2882 alertpanel_error(err_msg, line);
2888 prev_autowrap = compose->autowrap;
2889 compose->autowrap = FALSE;
2891 mark = gtk_text_buffer_get_insert(buffer);
2892 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2893 if (g_utf8_validate(buf, -1, NULL)) {
2894 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2896 gchar *tmpout = NULL;
2897 tmpout = conv_codeset_strdup
2898 (buf, conv_get_locale_charset_str_no_utf8(),
2900 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2902 tmpout = g_malloc(strlen(buf)*2+1);
2903 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2905 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2909 cursor_pos = quote_fmt_get_cursor_pos();
2910 if (cursor_pos == -1)
2911 cursor_pos = gtk_text_iter_get_offset(&iter);
2912 compose->set_cursor_pos = cursor_pos;
2914 gtk_text_buffer_get_start_iter(buffer, &iter);
2915 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2916 gtk_text_buffer_place_cursor(buffer, &iter);
2918 compose->autowrap = prev_autowrap;
2919 if (compose->autowrap && rewrap)
2920 compose_wrap_all(compose);
2927 SIGNAL_UNBLOCK(buffer);
2929 procmsg_msginfo_free( dummyinfo );
2934 /* if ml_post is of type addr@host and from is of type
2935 * addr-anything@host, return TRUE
2937 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2939 gchar *left_ml = NULL;
2940 gchar *right_ml = NULL;
2941 gchar *left_from = NULL;
2942 gchar *right_from = NULL;
2943 gboolean result = FALSE;
2945 if (!ml_post || !from)
2948 left_ml = g_strdup(ml_post);
2949 if (strstr(left_ml, "@")) {
2950 right_ml = strstr(left_ml, "@")+1;
2951 *(strstr(left_ml, "@")) = '\0';
2954 left_from = g_strdup(from);
2955 if (strstr(left_from, "@")) {
2956 right_from = strstr(left_from, "@")+1;
2957 *(strstr(left_from, "@")) = '\0';
2960 if (left_ml && left_from && right_ml && right_from
2961 && !strncmp(left_from, left_ml, strlen(left_ml))
2962 && !strcmp(right_from, right_ml)) {
2971 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2973 gchar *my_addr1, *my_addr2;
2975 if (!addr1 || !addr2)
2978 Xstrdup_a(my_addr1, addr1, return FALSE);
2979 Xstrdup_a(my_addr2, addr2, return FALSE);
2981 extract_address(my_addr1);
2982 extract_address(my_addr2);
2984 return !strcasecmp(my_addr1, my_addr2);
2987 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2988 gboolean to_all, gboolean to_ml,
2990 gboolean followup_and_reply_to)
2992 GSList *cc_list = NULL;
2995 gchar *replyto = NULL;
2996 GHashTable *to_table;
2998 gboolean reply_to_ml = FALSE;
2999 gboolean default_reply_to = FALSE;
3001 cm_return_if_fail(compose->account != NULL);
3002 cm_return_if_fail(msginfo != NULL);
3004 reply_to_ml = to_ml && compose->ml_post;
3006 default_reply_to = msginfo->folder &&
3007 msginfo->folder->prefs->enable_default_reply_to;
3009 if (compose->account->protocol != A_NNTP) {
3010 if (msginfo && msginfo->folder && msginfo->folder->prefs) {
3011 if (msginfo->folder->prefs->enable_default_replyto) {
3012 compose_entry_append(compose, msginfo->folder->prefs->default_replyto, COMPOSE_REPLYTO);
3014 if (msginfo->folder->prefs->enable_default_bcc) {
3015 compose_entry_append(compose, msginfo->folder->prefs->default_bcc, COMPOSE_BCC);
3017 if (msginfo->folder->prefs->enable_default_cc) {
3018 compose_entry_append(compose, msginfo->folder->prefs->default_cc, COMPOSE_CC);
3021 if (reply_to_ml && !default_reply_to) {
3023 gboolean is_subscr = is_subscription(compose->ml_post,
3026 /* normal answer to ml post with a reply-to */
3027 compose_entry_append(compose,
3030 if (compose->replyto
3031 && !same_address(compose->ml_post, compose->replyto))
3032 compose_entry_append(compose,
3036 /* answer to subscription confirmation */
3037 if (compose->replyto)
3038 compose_entry_append(compose,
3041 else if (msginfo->from)
3042 compose_entry_append(compose,
3047 else if (!(to_all || to_sender) && default_reply_to) {
3048 compose_entry_append(compose,
3049 msginfo->folder->prefs->default_reply_to,
3051 compose_entry_mark_default_to(compose,
3052 msginfo->folder->prefs->default_reply_to);
3057 Xstrdup_a(tmp1, msginfo->from, return);
3058 extract_address(tmp1);
3059 if (to_all || to_sender ||
3060 !account_find_from_address(tmp1, FALSE))
3061 compose_entry_append(compose,
3062 (compose->replyto && !to_sender)
3063 ? compose->replyto :
3064 msginfo->from ? msginfo->from : "",
3066 else if (!to_all && !to_sender) {
3067 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
3068 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
3069 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
3070 if (compose->replyto) {
3071 compose_entry_append(compose,
3075 compose_entry_append(compose,
3076 msginfo->from ? msginfo->from : "",
3080 /* replying to own mail, use original recp */
3081 compose_entry_append(compose,
3082 msginfo->to ? msginfo->to : "",
3084 compose_entry_append(compose,
3085 msginfo->cc ? msginfo->cc : "",
3091 if (to_sender || (compose->followup_to &&
3092 !strncmp(compose->followup_to, "poster", 6)))
3093 compose_entry_append
3095 (compose->replyto ? compose->replyto :
3096 msginfo->from ? msginfo->from : ""),
3099 else if (followup_and_reply_to || to_all) {
3100 compose_entry_append
3102 (compose->replyto ? compose->replyto :
3103 msginfo->from ? msginfo->from : ""),
3106 compose_entry_append
3108 compose->followup_to ? compose->followup_to :
3109 compose->newsgroups ? compose->newsgroups : "",
3110 COMPOSE_NEWSGROUPS);
3113 compose_entry_append
3115 compose->followup_to ? compose->followup_to :
3116 compose->newsgroups ? compose->newsgroups : "",
3117 COMPOSE_NEWSGROUPS);
3120 if (msginfo->subject && *msginfo->subject) {
3124 buf = p = g_strdup(msginfo->subject);
3125 p += subject_get_prefix_length(p);
3126 memmove(buf, p, strlen(p) + 1);
3128 buf2 = g_strdup_printf("Re: %s", buf);
3129 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3134 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3136 if (to_ml && compose->ml_post) return;
3137 if (!to_all || compose->account->protocol == A_NNTP) return;
3139 if (compose->replyto) {
3140 Xstrdup_a(replyto, compose->replyto, return);
3141 extract_address(replyto);
3143 if (msginfo->from) {
3144 Xstrdup_a(from, msginfo->from, return);
3145 extract_address(from);
3148 if (replyto && from)
3149 cc_list = address_list_append_with_comments(cc_list, from);
3150 if (to_all && msginfo->folder &&
3151 msginfo->folder->prefs->enable_default_reply_to)
3152 cc_list = address_list_append_with_comments(cc_list,
3153 msginfo->folder->prefs->default_reply_to);
3154 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3155 cc_list = address_list_append_with_comments(cc_list, compose->cc);