2 * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3 * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail team
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
26 #ifndef PANGO_ENABLE_ENGINE
27 # define PANGO_ENABLE_ENGINE
31 #include <glib/gi18n.h>
32 #include <gdk/gdkkeysyms.h>
35 #include <pango/pango-break.h>
40 #include <sys/types.h>
46 # include <sys/wait.h>
50 #ifndef G_OS_WIN32 /* fixme we should have a configure test. */
54 #if (HAVE_WCTYPE_H && HAVE_WCHAR_H)
61 #include "mainwindow.h"
63 #include "addressbook.h"
64 #include "folderview.h"
67 #include "stock_pixmap.h"
68 #include "send_message.h"
71 #include "customheader.h"
72 #include "prefs_common.h"
73 #include "prefs_account.h"
77 #include "procheader.h"
79 #include "statusbar.h"
82 #include "quoted-printable.h"
87 #include "alertpanel.h"
88 #include "manage_window.h"
89 #include "gtkshruler.h"
91 #include "addr_compl.h"
92 #include "quote_fmt.h"
94 #include "foldersel.h"
97 #include "message_search.h"
102 #include "autofaces.h"
114 #define N_ATTACH_COLS (N_COL_COLUMNS)
118 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BEGINNING_OF_LINE,
119 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_CHARACTER,
120 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_CHARACTER,
121 COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD,
122 COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD,
123 COMPOSE_CALL_ADVANCED_ACTION_MOVE_END_OF_LINE,
124 COMPOSE_CALL_ADVANCED_ACTION_MOVE_NEXT_LINE,
125 COMPOSE_CALL_ADVANCED_ACTION_MOVE_PREVIOUS_LINE,
126 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_CHARACTER,
127 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_CHARACTER,
128 COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD,
129 COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD,
130 COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE,
131 COMPOSE_CALL_ADVANCED_ACTION_DELETE_TO_LINE_END
132 } ComposeCallAdvancedAction;
136 PRIORITY_HIGHEST = 1,
145 COMPOSE_INSERT_SUCCESS,
146 COMPOSE_INSERT_READ_ERROR,
147 COMPOSE_INSERT_INVALID_CHARACTER,
148 COMPOSE_INSERT_NO_FILE
149 } ComposeInsertResult;
153 COMPOSE_WRITE_FOR_SEND,
154 COMPOSE_WRITE_FOR_STORE
159 COMPOSE_QUOTE_FORCED,
164 #define B64_LINE_SIZE 57
165 #define B64_BUFFSIZE 77
167 #define MAX_REFERENCES_LEN 999
169 static GList *compose_list = NULL;
171 static Compose *compose_generic_new (PrefsAccount *account,
174 GPtrArray *attach_files,
175 GList *listAddress );
177 static Compose *compose_create (PrefsAccount *account,
182 static void compose_entry_mark_default_to (Compose *compose,
183 const gchar *address);
184 static Compose *compose_followup_and_reply_to (MsgInfo *msginfo,
185 ComposeQuoteMode quote_mode,
189 static Compose *compose_forward_multiple (PrefsAccount *account,
190 GSList *msginfo_list);
191 static Compose *compose_reply (MsgInfo *msginfo,
192 ComposeQuoteMode quote_mode,
197 static Compose *compose_reply_mode (ComposeMode mode,
198 GSList *msginfo_list,
200 static void compose_template_apply_fields(Compose *compose, Template *tmpl);
201 static void compose_update_privacy_systems_menu(Compose *compose);
203 static GtkWidget *compose_account_option_menu_create
205 static void compose_set_out_encoding (Compose *compose);
206 static void compose_set_template_menu (Compose *compose);
207 static void compose_template_apply (Compose *compose,
210 static void compose_destroy (Compose *compose);
212 static void compose_entries_set (Compose *compose,
214 ComposeEntryType to_type);
215 static gint compose_parse_header (Compose *compose,
217 static gchar *compose_parse_references (const gchar *ref,
220 static gchar *compose_quote_fmt (Compose *compose,
226 gboolean need_unescape,
227 const gchar *err_msg);
229 static void compose_reply_set_entry (Compose *compose,
235 followup_and_reply_to);
236 static void compose_reedit_set_entry (Compose *compose,
239 static void compose_insert_sig (Compose *compose,
241 static gchar *compose_get_signature_str (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 void 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);
338 static void compose_add_field_list ( Compose *compose,
339 GList *listAddress );
341 /* callback functions */
343 static gboolean compose_edit_size_alloc (GtkEditable *widget,
344 GtkAllocation *allocation,
345 GtkSHRuler *shruler);
346 static void account_activated (GtkComboBox *optmenu,
348 static void attach_selected (GtkTreeView *tree_view,
349 GtkTreePath *tree_path,
350 GtkTreeViewColumn *column,
352 static gboolean attach_button_pressed (GtkWidget *widget,
353 GdkEventButton *event,
355 static gboolean attach_key_pressed (GtkWidget *widget,
358 static void compose_send_cb (GtkAction *action, gpointer data);
359 static void compose_send_later_cb (GtkAction *action, gpointer data);
361 static void compose_save_cb (GtkAction *action,
364 static void compose_attach_cb (GtkAction *action,
366 static void compose_insert_file_cb (GtkAction *action,
368 static void compose_insert_sig_cb (GtkAction *action,
371 static void compose_close_cb (GtkAction *action,
374 static void compose_set_encoding_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
376 static void compose_address_cb (GtkAction *action,
378 static void about_show_cb (GtkAction *action,
380 static void compose_template_activate_cb(GtkWidget *widget,
383 static void compose_ext_editor_cb (GtkAction *action,
386 static gint compose_delete_cb (GtkWidget *widget,
390 static void compose_undo_cb (GtkAction *action,
392 static void compose_redo_cb (GtkAction *action,
394 static void compose_cut_cb (GtkAction *action,
396 static void compose_copy_cb (GtkAction *action,
398 static void compose_paste_cb (GtkAction *action,
400 static void compose_paste_as_quote_cb (GtkAction *action,
402 static void compose_paste_no_wrap_cb (GtkAction *action,
404 static void compose_paste_wrap_cb (GtkAction *action,
406 static void compose_allsel_cb (GtkAction *action,
409 static void compose_advanced_action_cb (GtkAction *action,
412 static void compose_grab_focus_cb (GtkWidget *widget,
415 static void compose_changed_cb (GtkTextBuffer *textbuf,
418 static void compose_wrap_cb (GtkAction *action,
420 static void compose_wrap_all_cb (GtkAction *action,
422 static void compose_find_cb (GtkAction *action,
424 static void compose_toggle_autowrap_cb (GtkToggleAction *action,
427 static void compose_toggle_ruler_cb (GtkToggleAction *action,
429 static void compose_toggle_sign_cb (GtkToggleAction *action,
431 static void compose_toggle_encrypt_cb (GtkToggleAction *action,
433 static void compose_set_privacy_system_cb(GtkWidget *widget, gpointer data);
434 static void compose_update_privacy_system_menu_item(Compose * compose, gboolean warn);
435 static void activate_privacy_system (Compose *compose,
436 PrefsAccount *account,
438 static void compose_use_signing(Compose *compose, gboolean use_signing);
439 static void compose_use_encryption(Compose *compose, gboolean use_encryption);
440 static void compose_toggle_return_receipt_cb(GtkToggleAction *action,
442 static void compose_toggle_remove_refs_cb(GtkToggleAction *action,
444 static void compose_set_priority_cb (GtkAction *action, GtkRadioAction *current, gpointer data);
445 static void compose_reply_change_mode (Compose *compose, ComposeMode action);
446 static void compose_reply_change_mode_cb(GtkAction *action, GtkRadioAction *current, gpointer data);
448 static void compose_attach_drag_received_cb (GtkWidget *widget,
449 GdkDragContext *drag_context,
452 GtkSelectionData *data,
456 static void compose_insert_drag_received_cb (GtkWidget *widget,
457 GdkDragContext *drag_context,
460 GtkSelectionData *data,
464 static void compose_header_drag_received_cb (GtkWidget *widget,
465 GdkDragContext *drag_context,
468 GtkSelectionData *data,
473 static gboolean compose_drag_drop (GtkWidget *widget,
474 GdkDragContext *drag_context,
476 guint time, gpointer user_data);
478 static void text_inserted (GtkTextBuffer *buffer,
483 static Compose *compose_generic_reply(MsgInfo *msginfo,
484 ComposeQuoteMode quote_mode,
488 gboolean followup_and_reply_to,
491 static gboolean compose_headerentry_changed_cb (GtkWidget *entry,
492 ComposeHeaderEntry *headerentry);
493 static gboolean compose_headerentry_key_press_event_cb(GtkWidget *entry,
495 ComposeHeaderEntry *headerentry);
497 static void compose_show_first_last_header (Compose *compose, gboolean show_first);
499 static void compose_allow_user_actions (Compose *compose, gboolean allow);
501 static void compose_nothing_cb (GtkAction *action, gpointer data)
507 static void compose_check_all (GtkAction *action, gpointer data);
508 static void compose_highlight_all (GtkAction *action, gpointer data);
509 static void compose_check_backwards (GtkAction *action, gpointer data);
510 static void compose_check_forwards_go (GtkAction *action, gpointer data);
513 static gint compose_defer_auto_save_draft (Compose *compose);
514 static PrefsAccount *compose_guess_forward_account_from_msginfo (MsgInfo *msginfo);
516 static MsgInfo *compose_msginfo_new_from_compose(Compose *compose);
519 static void compose_set_dictionaries_from_folder_prefs(Compose *compose,
520 FolderItem *folder_item);
522 static void compose_attach_update_label(Compose *compose);
524 static void compose_attach_from_list(Compose *compose, GList *file_list, gboolean free_data);
526 static GtkActionEntry compose_popup_entries[] =
528 {"Compose", NULL, "Compose" },
529 {"Compose/Add", NULL, N_("_Add..."), NULL, NULL, G_CALLBACK(compose_attach_cb) },
530 {"Compose/Remove", NULL, N_("_Remove"), NULL, NULL, G_CALLBACK(compose_attach_remove_selected) },
531 {"Compose/---", NULL, "---", NULL, NULL, NULL },
532 {"Compose/Properties", NULL, N_("_Properties..."), NULL, NULL, G_CALLBACK(compose_attach_property) },
535 static GtkActionEntry compose_entries[] =
537 {"Menu", NULL, "Menu" },
539 {"Message", NULL, N_("_Message") },
540 {"Edit", NULL, N_("_Edit") },
542 {"Spelling", NULL, N_("_Spelling") },
544 {"Options", NULL, N_("_Options") },
545 {"Tools", NULL, N_("_Tools") },
546 {"Help", NULL, N_("_Help") },
548 {"Message/Send", NULL, N_("S_end"), "<control>Return", NULL, G_CALLBACK(compose_send_cb) },
549 {"Message/SendLater", NULL, N_("Send _later"), "<shift><control>S", NULL, G_CALLBACK(compose_send_later_cb) },
550 {"Message/---", NULL, "---" },
552 {"Message/AttachFile", NULL, N_("_Attach file"), "<control>M", NULL, G_CALLBACK(compose_attach_cb) },
553 {"Message/InsertFile", NULL, N_("_Insert file"), "<control>I", NULL, G_CALLBACK(compose_insert_file_cb) },
554 {"Message/InsertSig", NULL, N_("Insert si_gnature"), "<control>G", NULL, G_CALLBACK(compose_insert_sig_cb) },
555 /* {"Message/---", NULL, "---" }, */
556 {"Message/Save", NULL, N_("_Save"), "<control>S", NULL, G_CALLBACK(compose_save_cb) }, /*COMPOSE_KEEP_EDITING*/
557 /* {"Message/---", NULL, "---" }, */
558 {"Message/Close", NULL, N_("_Close"), "<control>W", NULL, G_CALLBACK(compose_close_cb) },
561 {"Edit/Undo", NULL, N_("_Undo"), "<control>Z", NULL, G_CALLBACK(compose_undo_cb) },
562 {"Edit/Redo", NULL, N_("_Redo"), "<control>Y", NULL, G_CALLBACK(compose_redo_cb) },
563 {"Edit/---", NULL, "---" },
565 {"Edit/Cut", NULL, N_("Cu_t"), "<control>X", NULL, G_CALLBACK(compose_cut_cb) },
566 {"Edit/Copy", NULL, N_("_Copy"), "<control>C", NULL, G_CALLBACK(compose_copy_cb) },
567 {"Edit/Paste", NULL, N_("_Paste"), "<control>V", NULL, G_CALLBACK(compose_paste_cb) },
569 {"Edit/SpecialPaste", NULL, N_("Special paste") },
570 {"Edit/SpecialPaste/AsQuotation", NULL, N_("as _quotation"), NULL, NULL, G_CALLBACK(compose_paste_as_quote_cb) },
571 {"Edit/SpecialPaste/Wrapped", NULL, N_("_wrapped"), NULL, NULL, G_CALLBACK(compose_paste_wrap_cb) },
572 {"Edit/SpecialPaste/Unwrapped", NULL, N_("_unwrapped"), NULL, NULL, G_CALLBACK(compose_paste_no_wrap_cb) },
574 {"Edit/SelectAll", NULL, N_("Select _all"), "<control>A", NULL, G_CALLBACK(compose_allsel_cb) },
576 {"Edit/Advanced", NULL, N_("A_dvanced") },
577 {"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*/
578 {"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*/
579 {"Edit/Advanced/BackWord", NULL, N_("Move a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_BACKWARD_WORD*/
580 {"Edit/Advanced/ForwWord", NULL, N_("Move a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_MOVE_FORWARD_WORD*/
581 {"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*/
582 {"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*/
583 {"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*/
584 {"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*/
585 {"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*/
586 {"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*/
587 {"Edit/Advanced/DelBackWord", NULL, N_("Delete a word backward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_BACKWARD_WORD*/
588 {"Edit/Advanced/DelForwWord", NULL, N_("Delete a word forward"), NULL, NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_FORWARD_WORD*/
589 {"Edit/Advanced/DelLine", NULL, N_("Delete line"), "<control>U", NULL, G_CALLBACK(compose_advanced_action_cb) }, /*COMPOSE_CALL_ADVANCED_ACTION_DELETE_LINE*/
590 {"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*/
592 /* {"Edit/---", NULL, "---" }, */
593 {"Edit/Find", NULL, N_("_Find"), "<control>F", NULL, G_CALLBACK(compose_find_cb) },
595 /* {"Edit/---", NULL, "---" }, */
596 {"Edit/WrapPara", NULL, N_("_Wrap current paragraph"), "<control>L", NULL, G_CALLBACK(compose_wrap_cb) }, /* 0 */
597 {"Edit/WrapAllLines", NULL, N_("Wrap all long _lines"), "<control><alt>L", NULL, G_CALLBACK(compose_wrap_all_cb) }, /* 1 */
598 /* {"Edit/---", NULL, "---" }, */
599 {"Edit/ExtEditor", NULL, N_("Edit with e_xternal editor"), "<shift><control>X", NULL, G_CALLBACK(compose_ext_editor_cb) },
602 {"Spelling/CheckAllSel", NULL, N_("_Check all or check selection"), NULL, NULL, G_CALLBACK(compose_check_all) },
603 {"Spelling/HighlightAll", NULL, N_("_Highlight all misspelled words"), NULL, NULL, G_CALLBACK(compose_highlight_all) },
604 {"Spelling/CheckBackwards", NULL, N_("Check _backwards misspelled word"), NULL, NULL, G_CALLBACK(compose_check_backwards) },
605 {"Spelling/ForwardNext", NULL, N_("_Forward to next misspelled word"), NULL, NULL, G_CALLBACK(compose_check_forwards_go) },
607 {"Spelling/---", NULL, "---" },
608 {"Spelling/Options", NULL, N_("_Options") },
613 {"Options/ReplyMode", NULL, N_("Reply _mode") },
614 {"Options/---", NULL, "---" },
615 {"Options/PrivacySystem", NULL, N_("Privacy _System") },
616 {"Options/PrivacySystem/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
618 /* {"Options/---", NULL, "---" }, */
620 {"Options/Priority", NULL, N_("_Priority") },
622 {"Options/Encoding", NULL, N_("Character _encoding") },
623 {"Options/Encoding/---", NULL, "---" },
624 #define ENC_ACTION(cs_char,c_char,string) \
625 { "Options/Encoding/" cs_char, NULL, N_(string), NULL, NULL, c_char }
627 {"Options/Encoding/Western", NULL, N_("Western European") },
628 {"Options/Encoding/Baltic", NULL, N_("Baltic") },
629 {"Options/Encoding/Hebrew", NULL, N_("Hebrew") },
630 {"Options/Encoding/Arabic", NULL, N_("Arabic") },
631 {"Options/Encoding/Cyrillic", NULL, N_("Cyrillic") },
632 {"Options/Encoding/Japanese", NULL, N_("Japanese") },
633 {"Options/Encoding/Chinese", NULL, N_("Chinese") },
634 {"Options/Encoding/Korean", NULL, N_("Korean") },
635 {"Options/Encoding/Thai", NULL, N_("Thai") },
638 {"Tools/AddressBook", NULL, N_("_Address book"), NULL, NULL, G_CALLBACK(compose_address_cb) },
640 {"Tools/Template", NULL, N_("_Template") },
641 {"Tools/Template/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
642 {"Tools/Actions", NULL, N_("Actio_ns") },
643 {"Tools/Actions/PlaceHolder", NULL, "Placeholder", NULL, NULL, G_CALLBACK(compose_nothing_cb) },
646 {"Help/About", NULL, N_("_About"), NULL, NULL, G_CALLBACK(about_show_cb) },
649 static GtkToggleActionEntry compose_toggle_entries[] =
651 {"Edit/AutoWrap", NULL, N_("Aut_o wrapping"), "<shift><control>L", NULL, G_CALLBACK(compose_toggle_autowrap_cb) }, /* TOGGLE */
652 {"Options/Sign", NULL, N_("Si_gn"), NULL, NULL, G_CALLBACK(compose_toggle_sign_cb) }, /* Toggle */
653 {"Options/Encrypt", NULL, N_("_Encrypt"), NULL, NULL, G_CALLBACK(compose_toggle_encrypt_cb) }, /* Toggle */
654 {"Options/RequestRetRcpt", NULL, N_("_Request Return Receipt"), NULL, NULL, G_CALLBACK(compose_toggle_return_receipt_cb) }, /* TOGGLE */
655 {"Options/RemoveReferences", NULL, N_("Remo_ve references"), NULL, NULL, G_CALLBACK(compose_toggle_remove_refs_cb) }, /* TOGGLE */
656 {"Tools/ShowRuler", NULL, N_("Show _ruler"), NULL, NULL, G_CALLBACK(compose_toggle_ruler_cb) }, /* Toggle */
659 static GtkRadioActionEntry compose_radio_rm_entries[] =
661 {"Options/ReplyMode/Normal", NULL, N_("_Normal"), NULL, NULL, COMPOSE_REPLY }, /* RADIO compose_reply_change_mode_cb */
662 {"Options/ReplyMode/All", NULL, N_("_All"), NULL, NULL, COMPOSE_REPLY_TO_ALL }, /* RADIO compose_reply_change_mode_cb */
663 {"Options/ReplyMode/Sender", NULL, N_("_Sender"), NULL, NULL, COMPOSE_REPLY_TO_SENDER }, /* RADIO compose_reply_change_mode_cb */
664 {"Options/ReplyMode/List", NULL, N_("_Mailing-list"), NULL, NULL, COMPOSE_REPLY_TO_LIST }, /* RADIO compose_reply_change_mode_cb */
667 static GtkRadioActionEntry compose_radio_prio_entries[] =
669 {"Options/Priority/Highest", NULL, N_("_Highest"), NULL, NULL, PRIORITY_HIGHEST }, /* RADIO compose_set_priority_cb */
670 {"Options/Priority/High", NULL, N_("Hi_gh"), NULL, NULL, PRIORITY_HIGH }, /* RADIO compose_set_priority_cb */
671 {"Options/Priority/Normal", NULL, N_("_Normal"), NULL, NULL, PRIORITY_NORMAL }, /* RADIO compose_set_priority_cb */
672 {"Options/Priority/Low", NULL, N_("Lo_w"), NULL, NULL, PRIORITY_LOW }, /* RADIO compose_set_priority_cb */
673 {"Options/Priority/Lowest", NULL, N_("_Lowest"), NULL, NULL, PRIORITY_LOWEST }, /* RADIO compose_set_priority_cb */
676 static GtkRadioActionEntry compose_radio_enc_entries[] =
678 ENC_ACTION(CS_AUTO, C_AUTO, "_Automatic"), /* RADIO compose_set_encoding_cb */
679 ENC_ACTION(CS_US_ASCII, C_US_ASCII, "7bit ASCII (US-ASC_II)"), /* RADIO compose_set_encoding_cb */
680 ENC_ACTION(CS_UTF_8, C_UTF_8, "Unicode (_UTF-8)"), /* RADIO compose_set_encoding_cb */
681 ENC_ACTION("Western/"CS_ISO_8859_1, C_ISO_8859_1, "ISO-8859-_1"), /* RADIO compose_set_encoding_cb */
682 ENC_ACTION("Western/"CS_ISO_8859_15, C_ISO_8859_15, "ISO-8859-15"), /* RADIO compose_set_encoding_cb */
683 ENC_ACTION("Western/"CS_WINDOWS_1252, C_WINDOWS_1252, "Windows-1252"), /* RADIO compose_set_encoding_cb */
684 ENC_ACTION(CS_ISO_8859_2, C_ISO_8859_2, "Central European (ISO-8859-_2)"), /* RADIO compose_set_encoding_cb */
685 ENC_ACTION("Baltic/"CS_ISO_8859_13, C_ISO_8859_13, "ISO-8859-13"), /* RADIO compose_set_encoding_cb */
686 ENC_ACTION("Baltic/"CS_ISO_8859_4, C_ISO_8859_14, "ISO-8859-_4"), /* RADIO compose_set_encoding_cb */
687 ENC_ACTION(CS_ISO_8859_7, C_ISO_8859_7, "Greek (ISO-8859-_7)"), /* RADIO compose_set_encoding_cb */
688 ENC_ACTION("Hebrew/"CS_ISO_8859_8, C_ISO_8859_8, "ISO-8859-_8"), /* RADIO compose_set_encoding_cb */
689 ENC_ACTION("Hebrew/"CS_WINDOWS_1255, C_WINDOWS_1255, "Windows-1255"), /* RADIO compose_set_encoding_cb */
690 ENC_ACTION("Arabic/"CS_ISO_8859_6, C_ISO_8859_6, "ISO-8859-_6"), /* RADIO compose_set_encoding_cb */
691 ENC_ACTION("Arabic/"CS_WINDOWS_1256, C_WINDOWS_1256, "Windows-1256"), /* RADIO compose_set_encoding_cb */
692 ENC_ACTION(CS_ISO_8859_9, C_ISO_8859_9, "Turkish (ISO-8859-_9)"), /* RADIO compose_set_encoding_cb */
693 ENC_ACTION("Cyrillic/"CS_ISO_8859_5, C_ISO_8859_5, "ISO-8859-_5"), /* RADIO compose_set_encoding_cb */
694 ENC_ACTION("Cyrillic/"CS_KOI8_R, C_KOI8_R, "KOI8-_R"), /* RADIO compose_set_encoding_cb */
695 ENC_ACTION("Cyrillic/"CS_KOI8_U, C_KOI8_U, "KOI8-_U"), /* RADIO compose_set_encoding_cb */
696 ENC_ACTION("Cyrillic/"CS_WINDOWS_1251, C_WINDOWS_1251, "Windows-1251"), /* RADIO compose_set_encoding_cb */
697 ENC_ACTION("Japanese/"CS_ISO_2022_JP, C_ISO_2022_JP, "ISO-2022-_JP"), /* RADIO compose_set_encoding_cb */
698 ENC_ACTION("Japanese/"CS_ISO_2022_JP_2, C_ISO_2022_JP_2, "ISO-2022-JP-_2"), /* RADIO compose_set_encoding_cb */
699 ENC_ACTION("Japanese/"CS_EUC_JP, C_EUC_JP, "_EUC-JP"), /* RADIO compose_set_encoding_cb */
700 ENC_ACTION("Japanese/"CS_SHIFT_JIS, C_SHIFT_JIS, "_Shift-JIS"), /* RADIO compose_set_encoding_cb */
701 ENC_ACTION("Chinese/"CS_GB2312, C_GB2312, "_GB2312"), /* RADIO compose_set_encoding_cb */
702 ENC_ACTION("Chinese/"CS_GBK, C_GBK, "GB_K"), /* RADIO compose_set_encoding_cb */
703 ENC_ACTION("Chinese/"CS_BIG5, C_BIG5, "_Big5-JP"), /* RADIO compose_set_encoding_cb */
704 ENC_ACTION("Chinese/"CS_EUC_TW, C_EUC_TW, "EUC-_TW"), /* RADIO compose_set_encoding_cb */
705 ENC_ACTION("Korean/"CS_EUC_KR, C_EUC_KR, "_EUC-KR"), /* RADIO compose_set_encoding_cb */
706 ENC_ACTION("Korean/"CS_ISO_2022_KR, C_ISO_2022_KR, "_ISO-2022-KR"), /* RADIO compose_set_encoding_cb */
707 ENC_ACTION("Thai/"CS_TIS_620, C_TIS_620, "_TIS-620-KR"), /* RADIO compose_set_encoding_cb */
708 ENC_ACTION("Thai/"CS_WINDOWS_874, C_WINDOWS_874, "_Windows-874"), /* RADIO compose_set_encoding_cb */
711 static GtkTargetEntry compose_mime_types[] =
713 {"text/uri-list", 0, 0},
714 {"UTF8_STRING", 0, 0},
718 static gboolean compose_put_existing_to_front(MsgInfo *info)
720 GList *compose_list = compose_get_compose_list();
724 for (elem = compose_list; elem != NULL && elem->data != NULL;
726 Compose *c = (Compose*)elem->data;
728 if (!c->targetinfo || !c->targetinfo->msgid ||
732 if (!strcmp(c->targetinfo->msgid, info->msgid)) {
733 gtkut_window_popup(c->window);
741 static GdkColor quote_color1 =
742 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
743 static GdkColor quote_color2 =
744 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
745 static GdkColor quote_color3 =
746 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
748 static GdkColor quote_bgcolor1 =
749 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
750 static GdkColor quote_bgcolor2 =
751 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
752 static GdkColor quote_bgcolor3 =
753 {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
755 static GdkColor signature_color = {
762 static GdkColor uri_color = {
769 static void compose_create_tags(GtkTextView *text, Compose *compose)
771 GtkTextBuffer *buffer;
772 GdkColor black = {(gulong)0, (gushort)0, (gushort)0, (gushort)0};
778 buffer = gtk_text_view_get_buffer(text);
780 if (prefs_common.enable_color) {
781 /* grab the quote colors, converting from an int to a GdkColor */
782 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_col,
784 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_col,
786 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_col,
788 gtkut_convert_int_to_gdk_color(prefs_common.quote_level1_bgcol,
790 gtkut_convert_int_to_gdk_color(prefs_common.quote_level2_bgcol,
792 gtkut_convert_int_to_gdk_color(prefs_common.quote_level3_bgcol,
794 gtkut_convert_int_to_gdk_color(prefs_common.signature_col,
796 gtkut_convert_int_to_gdk_color(prefs_common.uri_col,
799 signature_color = quote_color1 = quote_color2 = quote_color3 =
800 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 = uri_color = black;
803 if (prefs_common.enable_color && prefs_common.enable_bgcolor) {
804 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
805 "foreground-gdk", "e_color1,
806 "paragraph-background-gdk", "e_bgcolor1,
808 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
809 "foreground-gdk", "e_color2,
810 "paragraph-background-gdk", "e_bgcolor2,
812 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
813 "foreground-gdk", "e_color3,
814 "paragraph-background-gdk", "e_bgcolor3,
817 compose->quote0_tag = gtk_text_buffer_create_tag(buffer, "quote0",
818 "foreground-gdk", "e_color1,
820 compose->quote1_tag = gtk_text_buffer_create_tag(buffer, "quote1",
821 "foreground-gdk", "e_color2,
823 compose->quote2_tag = gtk_text_buffer_create_tag(buffer, "quote2",
824 "foreground-gdk", "e_color3,
828 compose->signature_tag = gtk_text_buffer_create_tag(buffer, "signature",
829 "foreground-gdk", &signature_color,
832 compose->uri_tag = gtk_text_buffer_create_tag(buffer, "link",
833 "foreground-gdk", &uri_color,
835 compose->no_wrap_tag = gtk_text_buffer_create_tag(buffer, "no_wrap", NULL);
836 compose->no_join_tag = gtk_text_buffer_create_tag(buffer, "no_join", NULL);
838 color[0] = quote_color1;
839 color[1] = quote_color2;
840 color[2] = quote_color3;
841 color[3] = quote_bgcolor1;
842 color[4] = quote_bgcolor2;
843 color[5] = quote_bgcolor3;
844 color[6] = signature_color;
845 color[7] = uri_color;
846 cmap = gdk_drawable_get_colormap(compose->window->window);
847 gdk_colormap_alloc_colors(cmap, color, 8, FALSE, TRUE, success);
849 for (i = 0; i < 8; i++) {
850 if (success[i] == FALSE) {
853 g_warning("Compose: color allocation failed.\n");
854 style = gtk_widget_get_style(GTK_WIDGET(text));
855 quote_color1 = quote_color2 = quote_color3 =
856 quote_bgcolor1 = quote_bgcolor2 = quote_bgcolor3 =
857 signature_color = uri_color = black;
862 Compose *compose_new(PrefsAccount *account, const gchar *mailto,
863 GPtrArray *attach_files)
865 return compose_generic_new(account, mailto, NULL, attach_files, NULL);
868 Compose *compose_new_with_folderitem(PrefsAccount *account, FolderItem *item, const gchar *mailto)
870 return compose_generic_new(account, mailto, item, NULL, NULL);
873 Compose *compose_new_with_list( PrefsAccount *account, GList *listAddress )
875 return compose_generic_new( account, NULL, NULL, NULL, listAddress );
878 #define SCROLL_TO_CURSOR(compose) { \
879 GtkTextMark *cmark = gtk_text_buffer_get_insert( \
880 gtk_text_view_get_buffer( \
881 GTK_TEXT_VIEW(compose->text))); \
882 gtk_text_view_scroll_mark_onscreen( \
883 GTK_TEXT_VIEW(compose->text), \
887 Compose *compose_generic_new(PrefsAccount *account, const gchar *mailto, FolderItem *item,
888 GPtrArray *attach_files, GList *listAddress )
891 GtkTextView *textview;
892 GtkTextBuffer *textbuf;
894 const gchar *subject_format = NULL;
895 const gchar *body_format = NULL;
896 gchar *mailto_from = NULL;
897 PrefsAccount *mailto_account = NULL;
898 MsgInfo* dummyinfo = NULL;
900 /* check if mailto defines a from */
901 if (mailto && *mailto != '\0') {
902 scan_mailto_url(mailto, &mailto_from, NULL, NULL, NULL, NULL, NULL, NULL);
903 /* mailto defines a from, check if we can get account prefs from it,
904 if not, the account prefs will be guessed using other ways, but we'll keep
907 mailto_account = account_find_from_address(mailto_from, TRUE);
909 account = mailto_account;
912 /* if no account prefs set from mailto, set if from folder prefs (if any) */
913 if (!mailto_account && item && item->prefs && item->prefs->enable_default_account)
914 account = account_find_from_id(item->prefs->default_account);
916 /* if no account prefs set, fallback to the current one */
917 if (!account) account = cur_account;
918 g_return_val_if_fail(account != NULL, NULL);
920 compose = compose_create(account, item, COMPOSE_NEW, FALSE);
922 /* override from name if mailto asked for it */
924 gtk_entry_set_text(GTK_ENTRY(compose->from_name), mailto_from);
927 /* override from name according to folder properties */
928 if (item && item->prefs &&
929 item->prefs->compose_with_format &&
930 item->prefs->compose_override_from_format &&
931 *item->prefs->compose_override_from_format != '\0') {
936 dummyinfo = compose_msginfo_new_from_compose(compose);
938 /* decode \-escape sequences in the internal representation of the quote format */
939 tmp = malloc(strlen(item->prefs->compose_override_from_format)+1);
940 pref_get_unescaped_pref(tmp, item->prefs->compose_override_from_format);
943 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE,
946 quote_fmt_init(dummyinfo, NULL, NULL, FALSE, compose->account, FALSE);
948 quote_fmt_scan_string(tmp);
951 buf = quote_fmt_get_buffer();
953 alertpanel_error(_("New message From format error."));
955 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
956 quote_fmt_reset_vartable();
961 compose->replyinfo = NULL;
962 compose->fwdinfo = NULL;
964 textview = GTK_TEXT_VIEW(compose->text);
965 textbuf = gtk_text_view_get_buffer(textview);
966 compose_create_tags(textview, compose);
968 undo_block(compose->undostruct);
970 compose_set_dictionaries_from_folder_prefs(compose, item);
973 if (account->auto_sig)
974 compose_insert_sig(compose, FALSE);
975 gtk_text_buffer_get_start_iter(textbuf, &iter);
976 gtk_text_buffer_place_cursor(textbuf, &iter);
978 if (account->protocol != A_NNTP) {
979 if (mailto && *mailto != '\0') {
980 compose_entries_set(compose, mailto, COMPOSE_TO);
982 } else if (item && item->prefs->enable_default_to) {
983 compose_entry_append(compose, item->prefs->default_to, COMPOSE_TO);
984 compose_entry_mark_default_to(compose, item->prefs->default_to);
986 if (item && item->ret_rcpt) {
987 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
990 if (mailto && *mailto != '\0') {
991 if (!strchr(mailto, '@'))
992 compose_entries_set(compose, mailto, COMPOSE_NEWSGROUPS);
994 compose_entries_set(compose, mailto, COMPOSE_TO);
995 } else if (item && FOLDER_CLASS(item->folder) == news_get_class()) {
996 compose_entry_append(compose, item->path, COMPOSE_NEWSGROUPS);
999 * CLAWS: just don't allow return receipt request, even if the user
1000 * may want to send an email. simple but foolproof.
1002 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", FALSE);
1004 compose_add_field_list( compose, listAddress );
1006 if (item && item->prefs && item->prefs->compose_with_format) {
1007 subject_format = item->prefs->compose_subject_format;
1008 body_format = item->prefs->compose_body_format;
1009 } else if (account->compose_with_format) {
1010 subject_format = account->compose_subject_format;
1011 body_format = account->compose_body_format;
1012 } else if (prefs_common.compose_with_format) {
1013 subject_format = prefs_common.compose_subject_format;
1014 body_format = prefs_common.compose_body_format;
1017 if (subject_format || body_format) {
1020 && *subject_format != '\0' )
1022 gchar *subject = NULL;
1027 dummyinfo = compose_msginfo_new_from_compose(compose);
1029 /* decode \-escape sequences in the internal representation of the quote format */
1030 tmp = malloc(strlen(subject_format)+1);
1031 pref_get_unescaped_pref(tmp, subject_format);
1033 subject = gtk_editable_get_chars(GTK_EDITABLE(compose->subject_entry), 0, -1);
1035 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE,
1036 compose->gtkaspell);
1038 quote_fmt_init(dummyinfo, NULL, subject, FALSE, compose->account, FALSE);
1040 quote_fmt_scan_string(tmp);
1043 buf = quote_fmt_get_buffer();
1045 alertpanel_error(_("New message subject format error."));
1047 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf);
1048 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1049 quote_fmt_reset_vartable();
1056 && *body_format != '\0' )
1059 GtkTextBuffer *buffer;
1060 GtkTextIter start, end;
1064 dummyinfo = compose_msginfo_new_from_compose(compose);
1066 text = GTK_TEXT_VIEW(compose->text);
1067 buffer = gtk_text_view_get_buffer(text);
1068 gtk_text_buffer_get_start_iter(buffer, &start);
1069 gtk_text_buffer_get_iter_at_offset(buffer, &end, -1);
1070 tmp = gtk_text_buffer_get_text(buffer, &start, &end, FALSE);
1072 compose_quote_fmt(compose, dummyinfo,
1074 NULL, tmp, FALSE, TRUE,
1075 _("New message body format error at line %d."));
1076 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1077 quote_fmt_reset_vartable();
1083 procmsg_msginfo_free( dummyinfo );
1089 for (i = 0; i < attach_files->len; i++) {
1090 file = g_ptr_array_index(attach_files, i);
1091 compose_attach_append(compose, file, file, NULL);
1095 compose_show_first_last_header(compose, TRUE);
1097 /* Set save folder */
1098 if (item && item->prefs && item->prefs->save_copy_to_folder) {
1099 gchar *folderidentifier;
1101 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
1102 folderidentifier = folder_item_get_identifier(item);
1103 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1104 g_free(folderidentifier);
1107 gtk_widget_grab_focus(compose->header_last->entry);
1109 undo_unblock(compose->undostruct);
1111 if (prefs_common.auto_exteditor)
1112 compose_exec_ext_editor(compose);
1114 compose->draft_timeout_tag = -1;
1115 SCROLL_TO_CURSOR(compose);
1117 compose->modified = FALSE;
1118 compose_set_title(compose);
1122 static void compose_force_encryption(Compose *compose, PrefsAccount *account,
1123 gboolean override_pref)
1125 gchar *privacy = NULL;
1127 g_return_if_fail(compose != NULL);
1128 g_return_if_fail(account != NULL);
1130 if (override_pref == FALSE && account->default_encrypt_reply == FALSE)
1133 if (account->default_privacy_system
1134 && strlen(account->default_privacy_system)) {
1135 privacy = account->default_privacy_system;
1137 GSList *privacy_avail = privacy_get_system_ids();
1138 if (privacy_avail && g_slist_length(privacy_avail)) {
1139 privacy = (gchar *)(privacy_avail->data);
1142 if (privacy != NULL) {
1143 if (compose->privacy_system == NULL)
1144 compose->privacy_system = g_strdup(privacy);
1145 else if (*(compose->privacy_system) == '\0') {
1146 g_free(compose->privacy_system);
1147 compose->privacy_system = g_strdup(privacy);
1149 compose_update_privacy_system_menu_item(compose, FALSE);
1150 compose_use_encryption(compose, TRUE);
1154 static void compose_force_signing(Compose *compose, PrefsAccount *account)
1156 gchar *privacy = NULL;
1158 if (account->default_privacy_system
1159 && strlen(account->default_privacy_system)) {
1160 privacy = account->default_privacy_system;
1162 GSList *privacy_avail = privacy_get_system_ids();
1163 if (privacy_avail && g_slist_length(privacy_avail)) {
1164 privacy = (gchar *)(privacy_avail->data);
1167 if (privacy != NULL) {
1168 if (compose->privacy_system == NULL)
1169 compose->privacy_system = g_strdup(privacy);
1170 compose_update_privacy_system_menu_item(compose, FALSE);
1171 compose_use_signing(compose, TRUE);
1175 static Compose *compose_reply_mode(ComposeMode mode, GSList *msginfo_list, gchar *body)
1179 Compose *compose = NULL;
1181 g_return_val_if_fail(msginfo_list != NULL, NULL);
1183 msginfo = (MsgInfo*)g_slist_nth_data(msginfo_list, 0);
1184 g_return_val_if_fail(msginfo != NULL, NULL);
1186 list_len = g_slist_length(msginfo_list);
1190 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1191 FALSE, prefs_common.default_reply_list, FALSE, body);
1193 case COMPOSE_REPLY_WITH_QUOTE:
1194 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1195 FALSE, prefs_common.default_reply_list, FALSE, body);
1197 case COMPOSE_REPLY_WITHOUT_QUOTE:
1198 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1199 FALSE, prefs_common.default_reply_list, FALSE, NULL);
1201 case COMPOSE_REPLY_TO_SENDER:
1202 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1203 FALSE, FALSE, TRUE, body);
1205 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1206 compose = compose_followup_and_reply_to(msginfo,
1207 COMPOSE_QUOTE_CHECK,
1208 FALSE, FALSE, body);
1210 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1211 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1212 FALSE, FALSE, TRUE, body);
1214 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1215 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1216 FALSE, FALSE, TRUE, NULL);
1218 case COMPOSE_REPLY_TO_ALL:
1219 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1220 TRUE, FALSE, FALSE, body);
1222 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1223 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1224 TRUE, FALSE, FALSE, body);
1226 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1227 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1228 TRUE, FALSE, FALSE, NULL);
1230 case COMPOSE_REPLY_TO_LIST:
1231 compose = compose_reply(msginfo, COMPOSE_QUOTE_CHECK,
1232 FALSE, TRUE, FALSE, body);
1234 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1235 compose = compose_reply(msginfo, COMPOSE_QUOTE_FORCED,
1236 FALSE, TRUE, FALSE, body);
1238 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1239 compose = compose_reply(msginfo, COMPOSE_QUOTE_SKIP,
1240 FALSE, TRUE, FALSE, NULL);
1242 case COMPOSE_FORWARD:
1243 if (prefs_common.forward_as_attachment) {
1244 compose = compose_reply_mode(COMPOSE_FORWARD_AS_ATTACH, msginfo_list, body);
1247 compose = compose_reply_mode(COMPOSE_FORWARD_INLINE, msginfo_list, body);
1251 case COMPOSE_FORWARD_INLINE:
1252 /* check if we reply to more than one Message */
1253 if (list_len == 1) {
1254 compose = compose_forward(NULL, msginfo, FALSE, body, FALSE, FALSE);
1257 /* more messages FALL THROUGH */
1258 case COMPOSE_FORWARD_AS_ATTACH:
1259 compose = compose_forward_multiple(NULL, msginfo_list);
1261 case COMPOSE_REDIRECT:
1262 compose = compose_redirect(NULL, msginfo, FALSE);
1265 g_warning("compose_reply_mode(): invalid Compose Mode: %d\n", mode);
1268 if (compose == NULL) {
1269 alertpanel_error(_("Unable to reply. The original email probably doesn't exist."));
1273 compose->rmode = mode;
1274 switch (compose->rmode) {
1276 case COMPOSE_REPLY_WITH_QUOTE:
1277 case COMPOSE_REPLY_WITHOUT_QUOTE:
1278 case COMPOSE_FOLLOWUP_AND_REPLY_TO:
1279 debug_print("reply mode Normal\n");
1280 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Normal", TRUE);
1281 compose_reply_change_mode(compose, COMPOSE_REPLY); /* force update */
1283 case COMPOSE_REPLY_TO_SENDER:
1284 case COMPOSE_REPLY_TO_SENDER_WITH_QUOTE:
1285 case COMPOSE_REPLY_TO_SENDER_WITHOUT_QUOTE:
1286 debug_print("reply mode Sender\n");
1287 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/Sender", TRUE);
1289 case COMPOSE_REPLY_TO_ALL:
1290 case COMPOSE_REPLY_TO_ALL_WITH_QUOTE:
1291 case COMPOSE_REPLY_TO_ALL_WITHOUT_QUOTE:
1292 debug_print("reply mode All\n");
1293 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/All", TRUE);
1295 case COMPOSE_REPLY_TO_LIST:
1296 case COMPOSE_REPLY_TO_LIST_WITH_QUOTE:
1297 case COMPOSE_REPLY_TO_LIST_WITHOUT_QUOTE:
1298 debug_print("reply mode List\n");
1299 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/ReplyMode/List", TRUE);
1307 static Compose *compose_reply(MsgInfo *msginfo,
1308 ComposeQuoteMode quote_mode,
1314 return compose_generic_reply(msginfo, quote_mode, to_all, to_ml,
1315 to_sender, FALSE, body);
1318 static Compose *compose_followup_and_reply_to(MsgInfo *msginfo,
1319 ComposeQuoteMode quote_mode,
1324 return compose_generic_reply(msginfo, quote_mode, to_all, FALSE,
1325 to_sender, TRUE, body);
1328 static void compose_extract_original_charset(Compose *compose)
1330 MsgInfo *info = NULL;
1331 if (compose->replyinfo) {
1332 info = compose->replyinfo;
1333 } else if (compose->fwdinfo) {
1334 info = compose->fwdinfo;
1335 } else if (compose->targetinfo) {
1336 info = compose->targetinfo;
1339 MimeInfo *mimeinfo = procmime_scan_message_short(info);
1340 MimeInfo *partinfo = mimeinfo;
1341 while (partinfo && partinfo->type != MIMETYPE_TEXT)
1342 partinfo = procmime_mimeinfo_next(partinfo);
1344 compose->orig_charset =
1345 g_strdup(procmime_mimeinfo_get_parameter(
1346 partinfo, "charset"));
1348 procmime_mimeinfo_free_all(mimeinfo);
1352 #define SIGNAL_BLOCK(buffer) { \
1353 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1354 G_CALLBACK(compose_changed_cb), \
1356 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1357 G_CALLBACK(text_inserted), \
1361 #define SIGNAL_UNBLOCK(buffer) { \
1362 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1363 G_CALLBACK(compose_changed_cb), \
1365 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1366 G_CALLBACK(text_inserted), \
1370 static Compose *compose_generic_reply(MsgInfo *msginfo,
1371 ComposeQuoteMode quote_mode,
1372 gboolean to_all, gboolean to_ml,
1374 gboolean followup_and_reply_to,
1378 PrefsAccount *account = NULL;
1379 GtkTextView *textview;
1380 GtkTextBuffer *textbuf;
1381 gboolean quote = FALSE;
1382 const gchar *qmark = NULL;
1383 const gchar *body_fmt = NULL;
1385 g_return_val_if_fail(msginfo != NULL, NULL);
1386 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1388 account = account_get_reply_account(msginfo, prefs_common.reply_account_autosel);
1390 g_return_val_if_fail(account != NULL, NULL);
1392 compose = compose_create(account, msginfo->folder, COMPOSE_REPLY, FALSE);
1394 compose->updating = TRUE;
1396 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RemoveReferences", FALSE);
1397 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options/RemoveReferences", TRUE);
1399 compose->replyinfo = procmsg_msginfo_get_full_info(msginfo);
1400 if (!compose->replyinfo)
1401 compose->replyinfo = procmsg_msginfo_copy(msginfo);
1403 compose_extract_original_charset(compose);
1405 if (msginfo->folder && msginfo->folder->ret_rcpt)
1406 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
1408 /* Set save folder */
1409 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1410 gchar *folderidentifier;
1412 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1413 folderidentifier = folder_item_get_identifier(msginfo->folder);
1414 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1415 g_free(folderidentifier);
1418 if (compose_parse_header(compose, msginfo) < 0) {
1419 compose->updating = FALSE;
1420 compose_destroy(compose);
1424 /* override from name according to folder properties */
1425 if (msginfo->folder && msginfo->folder->prefs &&
1426 msginfo->folder->prefs->reply_with_format &&
1427 msginfo->folder->prefs->reply_override_from_format &&
1428 *msginfo->folder->prefs->reply_override_from_format != '\0') {
1433 /* decode \-escape sequences in the internal representation of the quote format */
1434 tmp = malloc(strlen(msginfo->folder->prefs->reply_override_from_format)+1);
1435 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->reply_override_from_format);
1438 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE,
1439 compose->gtkaspell);
1441 quote_fmt_init(compose->replyinfo, NULL, NULL, FALSE, compose->account, FALSE);
1443 quote_fmt_scan_string(tmp);
1446 buf = quote_fmt_get_buffer();
1448 alertpanel_error(_("Message reply From format error."));
1450 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1451 quote_fmt_reset_vartable();
1456 textview = (GTK_TEXT_VIEW(compose->text));
1457 textbuf = gtk_text_view_get_buffer(textview);
1458 compose_create_tags(textview, compose);
1460 undo_block(compose->undostruct);
1462 compose_set_dictionaries_from_folder_prefs(compose, msginfo->folder);
1465 if (quote_mode == COMPOSE_QUOTE_FORCED ||
1466 (quote_mode == COMPOSE_QUOTE_CHECK && prefs_common.reply_with_quote)) {
1467 /* use the reply format of folder (if enabled), or the account's one
1468 (if enabled) or fallback to the global reply format, which is always
1469 enabled (even if empty), and use the relevant quotemark */
1471 if (msginfo->folder && msginfo->folder->prefs &&
1472 msginfo->folder->prefs->reply_with_format) {
1473 qmark = msginfo->folder->prefs->reply_quotemark;
1474 body_fmt = msginfo->folder->prefs->reply_body_format;
1476 } else if (account->reply_with_format) {
1477 qmark = account->reply_quotemark;
1478 body_fmt = account->reply_body_format;
1481 qmark = prefs_common.quotemark;
1482 if (prefs_common.quotefmt && *prefs_common.quotefmt)
1483 body_fmt = gettext(prefs_common.quotefmt);
1490 /* empty quotemark is not allowed */
1491 if (qmark == NULL || *qmark == '\0')
1493 compose_quote_fmt(compose, compose->replyinfo,
1494 body_fmt, qmark, body, FALSE, TRUE,
1495 _("Message reply format error at line %d."));
1496 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1497 quote_fmt_reset_vartable();
1500 if (MSG_IS_ENCRYPTED(compose->replyinfo->flags)) {
1501 compose_force_encryption(compose, account, FALSE);
1504 SIGNAL_BLOCK(textbuf);
1506 if (account->auto_sig)
1507 compose_insert_sig(compose, FALSE);
1509 compose_wrap_all(compose);
1511 SIGNAL_UNBLOCK(textbuf);
1513 gtk_widget_grab_focus(compose->text);
1515 undo_unblock(compose->undostruct);
1517 if (prefs_common.auto_exteditor)
1518 compose_exec_ext_editor(compose);
1520 compose->modified = FALSE;
1521 compose_set_title(compose);
1523 compose->updating = FALSE;
1524 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1525 SCROLL_TO_CURSOR(compose);
1527 if (compose->deferred_destroy) {
1528 compose_destroy(compose);
1535 #define INSERT_FW_HEADER(var, hdr) \
1536 if (msginfo->var && *msginfo->var) { \
1537 gtk_stext_insert(text, NULL, NULL, NULL, hdr, -1); \
1538 gtk_stext_insert(text, NULL, NULL, NULL, msginfo->var, -1); \
1539 gtk_stext_insert(text, NULL, NULL, NULL, "\n", 1); \
1542 Compose *compose_forward(PrefsAccount *account, MsgInfo *msginfo,
1543 gboolean as_attach, const gchar *body,
1544 gboolean no_extedit,
1548 GtkTextView *textview;
1549 GtkTextBuffer *textbuf;
1552 g_return_val_if_fail(msginfo != NULL, NULL);
1553 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1556 !(account = compose_guess_forward_account_from_msginfo
1558 account = cur_account;
1560 compose = compose_create(account, msginfo->folder, COMPOSE_FORWARD, batch);
1562 compose->updating = TRUE;
1563 compose->fwdinfo = procmsg_msginfo_get_full_info(msginfo);
1564 if (!compose->fwdinfo)
1565 compose->fwdinfo = procmsg_msginfo_copy(msginfo);
1567 compose_extract_original_charset(compose);
1569 if (msginfo->subject && *msginfo->subject) {
1570 gchar *buf, *buf2, *p;
1572 buf = p = g_strdup(msginfo->subject);
1573 p += subject_get_prefix_length(p);
1574 memmove(buf, p, strlen(p) + 1);
1576 buf2 = g_strdup_printf("Fw: %s", buf);
1577 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1583 /* override from name according to folder properties */
1584 if (msginfo->folder && msginfo->folder->prefs &&
1585 msginfo->folder->prefs->forward_with_format &&
1586 msginfo->folder->prefs->forward_override_from_format &&
1587 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1591 MsgInfo *full_msginfo = NULL;
1594 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1596 full_msginfo = procmsg_msginfo_copy(msginfo);
1598 /* decode \-escape sequences in the internal representation of the quote format */
1599 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1600 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1603 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1604 compose->gtkaspell);
1606 quote_fmt_init(full_msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1608 quote_fmt_scan_string(tmp);
1611 buf = quote_fmt_get_buffer();
1613 alertpanel_error(_("Message forward From format error."));
1615 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1616 quote_fmt_reset_vartable();
1619 procmsg_msginfo_free(full_msginfo);
1622 textview = GTK_TEXT_VIEW(compose->text);
1623 textbuf = gtk_text_view_get_buffer(textview);
1624 compose_create_tags(textview, compose);
1626 undo_block(compose->undostruct);
1630 msgfile = procmsg_get_message_file(msginfo);
1631 if (!is_file_exist(msgfile))
1632 g_warning("%s: file not exist\n", msgfile);
1634 compose_attach_append(compose, msgfile, msgfile,
1639 const gchar *qmark = NULL;
1640 const gchar *body_fmt = NULL;
1641 MsgInfo *full_msginfo;
1643 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1644 body_fmt = gettext(prefs_common.fw_quotefmt);
1648 full_msginfo = procmsg_msginfo_get_full_info(msginfo);
1650 full_msginfo = procmsg_msginfo_copy(msginfo);
1652 /* use the forward format of folder (if enabled), or the account's one
1653 (if enabled) or fallback to the global forward format, which is always
1654 enabled (even if empty), and use the relevant quotemark */
1655 if (msginfo->folder && msginfo->folder->prefs &&
1656 msginfo->folder->prefs->forward_with_format) {
1657 qmark = msginfo->folder->prefs->forward_quotemark;
1658 body_fmt = msginfo->folder->prefs->forward_body_format;
1660 } else if (account->forward_with_format) {
1661 qmark = account->forward_quotemark;
1662 body_fmt = account->forward_body_format;
1665 qmark = prefs_common.fw_quotemark;
1666 if (prefs_common.fw_quotefmt && *prefs_common.fw_quotefmt)
1667 body_fmt = gettext(prefs_common.fw_quotefmt);
1672 /* empty quotemark is not allowed */
1673 if (qmark == NULL || *qmark == '\0')
1676 compose_quote_fmt(compose, full_msginfo,
1677 body_fmt, qmark, body, FALSE, TRUE,
1678 _("Message forward format error at line %d."));
1679 compose_attach_from_list(compose, quote_fmt_get_attachments_list(), FALSE);
1680 quote_fmt_reset_vartable();
1681 compose_attach_parts(compose, msginfo);
1683 procmsg_msginfo_free(full_msginfo);
1686 SIGNAL_BLOCK(textbuf);
1688 if (account->auto_sig)
1689 compose_insert_sig(compose, FALSE);
1691 compose_wrap_all(compose);
1693 SIGNAL_UNBLOCK(textbuf);
1695 gtk_text_buffer_get_start_iter(textbuf, &iter);
1696 gtk_text_buffer_place_cursor(textbuf, &iter);
1698 gtk_widget_grab_focus(compose->header_last->entry);
1700 if (!no_extedit && prefs_common.auto_exteditor)
1701 compose_exec_ext_editor(compose);
1704 if (msginfo->folder && msginfo->folder->prefs && msginfo->folder->prefs->save_copy_to_folder) {
1705 gchar *folderidentifier;
1707 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
1708 folderidentifier = folder_item_get_identifier(msginfo->folder);
1709 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
1710 g_free(folderidentifier);
1713 undo_unblock(compose->undostruct);
1715 compose->modified = FALSE;
1716 compose_set_title(compose);
1718 compose->updating = FALSE;
1719 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1720 SCROLL_TO_CURSOR(compose);
1722 if (compose->deferred_destroy) {
1723 compose_destroy(compose);
1730 #undef INSERT_FW_HEADER
1732 static Compose *compose_forward_multiple(PrefsAccount *account, GSList *msginfo_list)
1735 GtkTextView *textview;
1736 GtkTextBuffer *textbuf;
1740 gboolean single_mail = TRUE;
1742 g_return_val_if_fail(msginfo_list != NULL, NULL);
1744 if (g_slist_length(msginfo_list) > 1)
1745 single_mail = FALSE;
1747 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next)
1748 if (((MsgInfo *)msginfo->data)->folder == NULL)
1751 /* guess account from first selected message */
1753 !(account = compose_guess_forward_account_from_msginfo
1754 (msginfo_list->data)))
1755 account = cur_account;
1757 g_return_val_if_fail(account != NULL, NULL);
1759 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1760 MSG_UNSET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_REPLIED);
1761 MSG_SET_PERM_FLAGS(((MsgInfo *)msginfo->data)->flags, MSG_FORWARDED);
1764 compose = compose_create(account, ((MsgInfo *)msginfo_list->data)->folder, COMPOSE_FORWARD, FALSE);
1766 compose->updating = TRUE;
1768 /* override from name according to folder properties */
1769 if (msginfo_list->data) {
1770 MsgInfo *msginfo = msginfo_list->data;
1772 if (msginfo->folder && msginfo->folder->prefs &&
1773 msginfo->folder->prefs->forward_with_format &&
1774 msginfo->folder->prefs->forward_override_from_format &&
1775 *msginfo->folder->prefs->forward_override_from_format != '\0') {
1780 /* decode \-escape sequences in the internal representation of the quote format */
1781 tmp = malloc(strlen(msginfo->folder->prefs->forward_override_from_format)+1);
1782 pref_get_unescaped_pref(tmp, msginfo->folder->prefs->forward_override_from_format);
1785 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
1786 compose->gtkaspell);
1788 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
1790 quote_fmt_scan_string(tmp);
1793 buf = quote_fmt_get_buffer();
1795 alertpanel_error(_("Message forward From format error."));
1797 gtk_entry_set_text(GTK_ENTRY(compose->from_name), buf);
1798 quote_fmt_reset_vartable();
1804 textview = GTK_TEXT_VIEW(compose->text);
1805 textbuf = gtk_text_view_get_buffer(textview);
1806 compose_create_tags(textview, compose);
1808 undo_block(compose->undostruct);
1809 for (msginfo = msginfo_list; msginfo != NULL; msginfo = msginfo->next) {
1810 msgfile = procmsg_get_message_file((MsgInfo *)msginfo->data);
1812 if (!is_file_exist(msgfile))
1813 g_warning("%s: file not exist\n", msgfile);
1815 compose_attach_append(compose, msgfile, msgfile,
1821 MsgInfo *info = (MsgInfo *)msginfo_list->data;
1822 if (info->subject && *info->subject) {
1823 gchar *buf, *buf2, *p;
1825 buf = p = g_strdup(info->subject);
1826 p += subject_get_prefix_length(p);
1827 memmove(buf, p, strlen(p) + 1);
1829 buf2 = g_strdup_printf("Fw: %s", buf);
1830 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
1836 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
1837 _("Fw: multiple emails"));
1840 SIGNAL_BLOCK(textbuf);
1842 if (account->auto_sig)
1843 compose_insert_sig(compose, FALSE);
1845 compose_wrap_all(compose);
1847 SIGNAL_UNBLOCK(textbuf);
1849 gtk_text_buffer_get_start_iter(textbuf, &iter);
1850 gtk_text_buffer_place_cursor(textbuf, &iter);
1852 gtk_widget_grab_focus(compose->header_last->entry);
1853 undo_unblock(compose->undostruct);
1854 compose->modified = FALSE;
1855 compose_set_title(compose);
1857 compose->updating = FALSE;
1858 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
1859 SCROLL_TO_CURSOR(compose);
1861 if (compose->deferred_destroy) {
1862 compose_destroy(compose);
1869 static gboolean compose_is_sig_separator(Compose *compose, GtkTextBuffer *textbuf, GtkTextIter *iter)
1871 GtkTextIter start = *iter;
1872 GtkTextIter end_iter;
1873 int start_pos = gtk_text_iter_get_offset(&start);
1875 if (!compose->account->sig_sep)
1878 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1879 start_pos+strlen(compose->account->sig_sep));
1881 /* check sig separator */
1882 str = gtk_text_iter_get_text(&start, &end_iter);
1883 if (!strcmp(str, compose->account->sig_sep)) {
1885 /* check end of line (\n) */
1886 gtk_text_buffer_get_iter_at_offset(textbuf, &start,
1887 start_pos+strlen(compose->account->sig_sep));
1888 gtk_text_buffer_get_iter_at_offset(textbuf, &end_iter,
1889 start_pos+strlen(compose->account->sig_sep)+1);
1890 tmp = gtk_text_iter_get_text(&start, &end_iter);
1891 if (!strcmp(tmp,"\n")) {
1903 static void compose_colorize_signature(Compose *compose)
1905 GtkTextBuffer *buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(compose->text));
1907 GtkTextIter end_iter;
1908 gtk_text_buffer_get_start_iter(buffer, &iter);
1909 while (gtk_text_iter_forward_line(&iter))
1910 if (compose_is_sig_separator(compose, buffer, &iter)) {
1911 gtk_text_buffer_get_end_iter(buffer, &end_iter);
1912 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &end_iter);
1916 #define BLOCK_WRAP() { \
1917 prev_autowrap = compose->autowrap; \
1918 buffer = gtk_text_view_get_buffer( \
1919 GTK_TEXT_VIEW(compose->text)); \
1920 compose->autowrap = FALSE; \
1922 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1923 G_CALLBACK(compose_changed_cb), \
1925 g_signal_handlers_block_by_func(G_OBJECT(buffer), \
1926 G_CALLBACK(text_inserted), \
1929 #define UNBLOCK_WRAP() { \
1930 compose->autowrap = prev_autowrap; \
1931 if (compose->autowrap) { \
1932 gint old = compose->draft_timeout_tag; \
1933 compose->draft_timeout_tag = -2; \
1934 compose_wrap_all(compose); \
1935 compose->draft_timeout_tag = old; \
1938 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1939 G_CALLBACK(compose_changed_cb), \
1941 g_signal_handlers_unblock_by_func(G_OBJECT(buffer), \
1942 G_CALLBACK(text_inserted), \
1946 Compose *compose_reedit(MsgInfo *msginfo, gboolean batch)
1948 Compose *compose = NULL;
1949 PrefsAccount *account = NULL;
1950 GtkTextView *textview;
1951 GtkTextBuffer *textbuf;
1955 gchar buf[BUFFSIZE];
1956 gboolean use_signing = FALSE;
1957 gboolean use_encryption = FALSE;
1958 gchar *privacy_system = NULL;
1959 int priority = PRIORITY_NORMAL;
1960 MsgInfo *replyinfo = NULL, *fwdinfo = NULL;
1961 gboolean autowrap = prefs_common.autowrap;
1963 g_return_val_if_fail(msginfo != NULL, NULL);
1964 g_return_val_if_fail(msginfo->folder != NULL, NULL);
1966 if (compose_put_existing_to_front(msginfo)) {
1970 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
1971 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
1972 gchar queueheader_buf[BUFFSIZE];
1975 /* Select Account from queue headers */
1976 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1977 sizeof(queueheader_buf), "X-Claws-Account-Id:")) {
1978 id = atoi(&queueheader_buf[strlen("X-Claws-Account-Id:")]);
1979 account = account_find_from_id(id);
1981 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1982 sizeof(queueheader_buf), "X-Sylpheed-Account-Id:")) {
1983 id = atoi(&queueheader_buf[strlen("X-Sylpheed-Account-Id:")]);
1984 account = account_find_from_id(id);
1986 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1987 sizeof(queueheader_buf), "NAID:")) {
1988 id = atoi(&queueheader_buf[strlen("NAID:")]);
1989 account = account_find_from_id(id);
1991 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1992 sizeof(queueheader_buf), "MAID:")) {
1993 id = atoi(&queueheader_buf[strlen("MAID:")]);
1994 account = account_find_from_id(id);
1996 if (!account && !procheader_get_header_from_msginfo(msginfo, queueheader_buf,
1997 sizeof(queueheader_buf), "S:")) {
1998 account = account_find_from_address(queueheader_buf, FALSE);
2000 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2001 sizeof(queueheader_buf), "X-Claws-Sign:")) {
2002 param = atoi(&queueheader_buf[strlen("X-Claws-Sign:")]);
2003 use_signing = param;
2006 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2007 sizeof(queueheader_buf), "X-Sylpheed-Sign:")) {
2008 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Sign:")]);
2009 use_signing = param;
2012 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2013 sizeof(queueheader_buf), "X-Claws-Encrypt:")) {
2014 param = atoi(&queueheader_buf[strlen("X-Claws-Encrypt:")]);
2015 use_encryption = param;
2017 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2018 sizeof(queueheader_buf), "X-Sylpheed-Encrypt:")) {
2019 param = atoi(&queueheader_buf[strlen("X-Sylpheed-Encrypt:")]);
2020 use_encryption = param;
2022 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2023 sizeof(queueheader_buf), "X-Claws-Auto-Wrapping:")) {
2024 param = atoi(&queueheader_buf[strlen("X-Claws-Auto-Wrapping:")]);
2027 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2028 sizeof(queueheader_buf), "X-Claws-Privacy-System:")) {
2029 privacy_system = g_strdup(&queueheader_buf[strlen("X-Claws-Privacy-System:")]);
2031 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2032 sizeof(queueheader_buf), "X-Sylpheed-Privacy-System:")) {
2033 privacy_system = g_strdup(&queueheader_buf[strlen("X-Sylpheed-Privacy-System:")]);
2035 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2036 sizeof(queueheader_buf), "X-Priority: ")) {
2037 param = atoi(&queueheader_buf[strlen("X-Priority: ")]); /* mind the space */
2040 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2041 sizeof(queueheader_buf), "RMID:")) {
2042 gchar **tokens = g_strsplit(&queueheader_buf[strlen("RMID:")], "\t", 0);
2043 if (tokens[0] && tokens[1] && tokens[2]) {
2044 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2045 if (orig_item != NULL) {
2046 replyinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2051 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf,
2052 sizeof(queueheader_buf), "FMID:")) {
2053 gchar **tokens = g_strsplit(&queueheader_buf[strlen("FMID:")], "\t", 0);
2054 if (tokens[0] && tokens[1] && tokens[2]) {
2055 FolderItem *orig_item = folder_find_item_from_identifier(tokens[0]);
2056 if (orig_item != NULL) {
2057 fwdinfo = folder_item_get_msginfo_by_msgid(orig_item, tokens[2]);
2063 account = msginfo->folder->folder->account;
2066 if (!account && prefs_common.reedit_account_autosel) {
2067 gchar from[BUFFSIZE];
2068 if (!procheader_get_header_from_msginfo(msginfo, from, sizeof(from), "FROM:")) {
2069 extract_address(from);
2070 account = account_find_from_address(from, FALSE);
2074 account = cur_account;
2076 g_return_val_if_fail(account != NULL, NULL);
2078 compose = compose_create(account, msginfo->folder, COMPOSE_REEDIT, batch);
2080 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Edit/AutoWrap", autowrap);
2081 compose->autowrap = autowrap;
2082 compose->replyinfo = replyinfo;
2083 compose->fwdinfo = fwdinfo;
2085 compose->updating = TRUE;
2086 compose->priority = priority;
2088 if (privacy_system != NULL) {
2089 compose->privacy_system = privacy_system;
2090 compose_use_signing(compose, use_signing);
2091 compose_use_encryption(compose, use_encryption);
2092 compose_update_privacy_system_menu_item(compose, FALSE);
2094 activate_privacy_system(compose, account, FALSE);
2097 compose->targetinfo = procmsg_msginfo_copy(msginfo);
2099 compose_extract_original_charset(compose);
2101 if (folder_has_parent_of_type(msginfo->folder, F_QUEUE) ||
2102 folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2103 gchar queueheader_buf[BUFFSIZE];
2105 /* Set message save folder */
2106 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "SCF:")) {
2109 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), TRUE);
2110 gtk_editable_delete_text(GTK_EDITABLE(compose->savemsg_entry), 0, -1);
2111 gtk_editable_insert_text(GTK_EDITABLE(compose->savemsg_entry), &queueheader_buf[4], strlen(&queueheader_buf[4]), &startpos);
2113 if (!procheader_get_header_from_msginfo(msginfo, queueheader_buf, sizeof(queueheader_buf), "RRCPT:")) {
2114 gint active = atoi(&queueheader_buf[strlen("RRCPT:")]);
2116 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/RequestRetRcpt", TRUE);
2121 if (compose_parse_header(compose, msginfo) < 0) {
2122 compose->updating = FALSE;
2123 compose_destroy(compose);
2126 compose_reedit_set_entry(compose, msginfo);
2128 textview = GTK_TEXT_VIEW(compose->text);
2129 textbuf = gtk_text_view_get_buffer(textview);
2130 compose_create_tags(textview, compose);
2132 mark = gtk_text_buffer_get_insert(textbuf);
2133 gtk_text_buffer_get_iter_at_mark(textbuf, &iter, mark);
2135 g_signal_handlers_block_by_func(G_OBJECT(textbuf),
2136 G_CALLBACK(compose_changed_cb),
2139 if (MSG_IS_ENCRYPTED(msginfo->flags)) {
2140 fp = procmime_get_first_encrypted_text_content(msginfo);
2142 compose_force_encryption(compose, account, TRUE);
2145 fp = procmime_get_first_text_content(msginfo);
2148 g_warning("Can't get text part\n");
2152 gboolean prev_autowrap = compose->autowrap;
2153 GtkTextBuffer *buffer = textbuf;
2155 while (fgets(buf, sizeof(buf), fp) != NULL) {
2157 gtk_text_buffer_insert(textbuf, &iter, buf, -1);
2163 compose_attach_parts(compose, msginfo);
2165 compose_colorize_signature(compose);
2167 g_signal_handlers_unblock_by_func(G_OBJECT(textbuf),
2168 G_CALLBACK(compose_changed_cb),
2171 gtk_widget_grab_focus(compose->text);
2173 if (prefs_common.auto_exteditor) {
2174 compose_exec_ext_editor(compose);
2176 compose->modified = FALSE;
2177 compose_set_title(compose);
2179 compose->updating = FALSE;
2180 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2181 SCROLL_TO_CURSOR(compose);
2183 if (compose->deferred_destroy) {
2184 compose_destroy(compose);
2188 compose->sig_str = compose_get_signature_str(compose);
2193 Compose *compose_redirect(PrefsAccount *account, MsgInfo *msginfo,
2200 g_return_val_if_fail(msginfo != NULL, NULL);
2203 account = account_get_reply_account(msginfo,
2204 prefs_common.reply_account_autosel);
2205 g_return_val_if_fail(account != NULL, NULL);
2207 compose = compose_create(account, msginfo->folder, COMPOSE_REDIRECT, batch);
2209 compose->updating = TRUE;
2211 compose_create_tags(GTK_TEXT_VIEW(compose->text), compose);
2212 compose->replyinfo = NULL;
2213 compose->fwdinfo = NULL;
2215 compose_show_first_last_header(compose, TRUE);
2217 gtk_widget_grab_focus(compose->header_last->entry);
2219 filename = procmsg_get_message_file(msginfo);
2221 if (filename == NULL) {
2222 compose->updating = FALSE;
2223 compose_destroy(compose);
2228 compose->redirect_filename = filename;
2230 /* Set save folder */
2231 item = msginfo->folder;
2232 if (item && item->prefs && item->prefs->save_copy_to_folder) {
2233 gchar *folderidentifier;
2235 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(compose->savemsg_checkbtn), prefs_common.savemsg);
2236 folderidentifier = folder_item_get_identifier(item);
2237 gtk_entry_set_text(GTK_ENTRY(compose->savemsg_entry), folderidentifier);
2238 g_free(folderidentifier);
2241 compose_attach_parts(compose, msginfo);
2243 if (msginfo->subject)
2244 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry),
2246 gtk_editable_set_editable(GTK_EDITABLE(compose->subject_entry), FALSE);
2248 compose_quote_fmt(compose, msginfo, "%M", NULL, NULL, FALSE, FALSE,
2249 _("Message redirect format error at line %d."));
2250 quote_fmt_reset_vartable();
2251 gtk_text_view_set_editable(GTK_TEXT_VIEW(compose->text), FALSE);
2253 compose_colorize_signature(compose);
2256 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Add", FALSE);
2257 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Remove", FALSE);
2258 cm_menu_set_sensitive_full(compose->ui_manager, "Popup/Compose/Properties", FALSE);
2260 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/Save", FALSE);
2261 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertFile", FALSE);
2262 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/AttachFile", FALSE);
2263 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Message/InsertSig", FALSE);
2264 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Edit", FALSE);
2265 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Options", FALSE);
2266 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/ShowRuler", FALSE);
2267 cm_menu_set_sensitive_full(compose->ui_manager, "Menu/Tools/Actions", FALSE);
2269 if (compose->toolbar->draft_btn)
2270 gtk_widget_set_sensitive(compose->toolbar->draft_btn, FALSE);
2271 if (compose->toolbar->insert_btn)
2272 gtk_widget_set_sensitive(compose->toolbar->insert_btn, FALSE);
2273 if (compose->toolbar->attach_btn)
2274 gtk_widget_set_sensitive(compose->toolbar->attach_btn, FALSE);
2275 if (compose->toolbar->sig_btn)
2276 gtk_widget_set_sensitive(compose->toolbar->sig_btn, FALSE);
2277 if (compose->toolbar->exteditor_btn)
2278 gtk_widget_set_sensitive(compose->toolbar->exteditor_btn, FALSE);
2279 if (compose->toolbar->linewrap_current_btn)
2280 gtk_widget_set_sensitive(compose->toolbar->linewrap_current_btn, FALSE);
2281 if (compose->toolbar->linewrap_all_btn)
2282 gtk_widget_set_sensitive(compose->toolbar->linewrap_all_btn, FALSE);
2284 compose->modified = FALSE;
2285 compose_set_title(compose);
2286 compose->updating = FALSE;
2287 compose->draft_timeout_tag = -1; /* desinhibit auto-drafting after loading */
2288 SCROLL_TO_CURSOR(compose);
2290 if (compose->deferred_destroy) {
2291 compose_destroy(compose);
2298 GList *compose_get_compose_list(void)
2300 return compose_list;
2303 void compose_entry_append(Compose *compose, const gchar *address,
2304 ComposeEntryType type)
2306 const gchar *header;
2308 gboolean in_quote = FALSE;
2309 if (!address || *address == '\0') return;
2316 header = N_("Bcc:");
2318 case COMPOSE_REPLYTO:
2319 header = N_("Reply-To:");
2321 case COMPOSE_NEWSGROUPS:
2322 header = N_("Newsgroups:");
2324 case COMPOSE_FOLLOWUPTO:
2325 header = N_( "Followup-To:");
2332 header = prefs_common_translated_header_name(header);
2334 cur = begin = (gchar *)address;
2336 /* we separate the line by commas, but not if we're inside a quoted
2338 while (*cur != '\0') {
2340 in_quote = !in_quote;
2341 if (*cur == ',' && !in_quote) {
2342 gchar *tmp = g_strdup(begin);
2344 tmp[cur-begin]='\0';
2347 while (*tmp == ' ' || *tmp == '\t')
2349 compose_add_header_entry(compose, header, tmp);
2356 gchar *tmp = g_strdup(begin);
2358 tmp[cur-begin]='\0';
2361 while (*tmp == ' ' || *tmp == '\t')
2363 compose_add_header_entry(compose, header, tmp);
2368 static void compose_entry_mark_default_to(Compose *compose, const gchar *mailto)
2370 static GdkColor yellow;
2371 static GdkColor black;
2372 static gboolean yellow_initialised = FALSE;
2376 if (!yellow_initialised) {
2377 gdk_color_parse("#f5f6be", &yellow);
2378 gdk_color_parse("#000000", &black);
2379 yellow_initialised = gdk_colormap_alloc_color(
2380 gdk_colormap_get_system(), &yellow, FALSE, TRUE);
2381 yellow_initialised &= gdk_colormap_alloc_color(
2382 gdk_colormap_get_system(), &black, FALSE, TRUE);
2385 for (h_list = compose->header_list; h_list != NULL; h_list = h_list->next) {
2386 entry = GTK_ENTRY(((ComposeHeaderEntry *)h_list->data)->entry);
2387 if (gtk_entry_get_text(entry) &&
2388 !g_utf8_collate(gtk_entry_get_text(entry), mailto)) {
2389 if (yellow_initialised) {
2390 gtk_widget_modify_base(
2391 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2392 GTK_STATE_NORMAL, &yellow);
2393 gtk_widget_modify_text(
2394 GTK_WIDGET(((ComposeHeaderEntry *)h_list->data)->entry),
2395 GTK_STATE_NORMAL, &black);
2401 void compose_toolbar_cb(gint action, gpointer data)
2403 ToolbarItem *toolbar_item = (ToolbarItem*)data;
2404 Compose *compose = (Compose*)toolbar_item->parent;
2406 g_return_if_fail(compose != NULL);
2410 compose_send_cb(NULL, compose);
2413 compose_send_later_cb(NULL, compose);
2416 compose_draft(compose, COMPOSE_QUIT_EDITING);
2419 compose_insert_file_cb(NULL, compose);
2422 compose_attach_cb(NULL, compose);
2425 compose_insert_sig(compose, FALSE);
2428 compose_ext_editor_cb(NULL, compose);
2430 case A_LINEWRAP_CURRENT:
2431 compose_beautify_paragraph(compose, NULL, TRUE);
2433 case A_LINEWRAP_ALL:
2434 compose_wrap_all_full(compose, TRUE);
2437 compose_address_cb(NULL, compose);
2440 case A_CHECK_SPELLING:
2441 compose_check_all(NULL, compose);
2449 static void compose_entries_set(Compose *compose, const gchar *mailto, ComposeEntryType to_type)
2454 gchar *subject = NULL;
2458 gchar **attach = NULL;
2460 /* get mailto parts but skip from */
2461 scan_mailto_url(mailto, NULL, &to, &cc, &bcc, &subject, &body, &attach);
2464 compose_entry_append(compose, to, to_type);
2466 compose_entry_append(compose, cc, COMPOSE_CC);
2468 compose_entry_append(compose, bcc, COMPOSE_BCC);
2470 if (!g_utf8_validate (subject, -1, NULL)) {
2471 temp = g_locale_to_utf8 (subject, -1, NULL, &len, NULL);
2472 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), temp);
2475 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), subject);
2479 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2480 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2483 gboolean prev_autowrap = compose->autowrap;
2485 compose->autowrap = FALSE;
2487 mark = gtk_text_buffer_get_insert(buffer);
2488 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2490 if (!g_utf8_validate (body, -1, NULL)) {
2491 temp = g_locale_to_utf8 (body, -1, NULL, &len, NULL);
2492 gtk_text_buffer_insert(buffer, &iter, temp, -1);
2495 gtk_text_buffer_insert(buffer, &iter, body, -1);
2497 gtk_text_buffer_insert(buffer, &iter, "\n", 1);
2499 compose->autowrap = prev_autowrap;
2500 if (compose->autowrap)
2501 compose_wrap_all(compose);
2505 gint i = 0, att = 0;
2506 gchar *warn_files = NULL;
2507 while (attach[i] != NULL) {
2508 gchar *utf8_filename = conv_filename_to_utf8(attach[i]);
2509 if (utf8_filename) {
2510 if (compose_attach_append(compose, attach[i], utf8_filename, NULL)) {
2511 gchar *tmp = g_strdup_printf("%s%s\n",
2512 warn_files?warn_files:"",
2518 g_free(utf8_filename);
2520 alertpanel_error(_("Couldn't attach a file (charset conversion failed)."));
2525 alertpanel_notice(ngettext(
2526 "The following file has been attached: \n%s",
2527 "The following files have been attached: \n%s", att), warn_files);
2539 static gint compose_parse_header(Compose *compose, MsgInfo *msginfo)
2541 static HeaderEntry hentry[] = {{"Reply-To:", NULL, TRUE},
2542 {"Cc:", NULL, TRUE},
2543 {"References:", NULL, FALSE},
2544 {"Bcc:", NULL, TRUE},
2545 {"Newsgroups:", NULL, TRUE},
2546 {"Followup-To:", NULL, TRUE},
2547 {"List-Post:", NULL, FALSE},
2548 {"X-Priority:", NULL, FALSE},
2549 {NULL, NULL, FALSE}};
2565 g_return_val_if_fail(msginfo != NULL, -1);
2567 if ((fp = procmsg_open_message(msginfo)) == NULL) return -1;
2568 procheader_get_header_fields(fp, hentry);
2571 if (hentry[H_REPLY_TO].body != NULL) {
2572 if (hentry[H_REPLY_TO].body[0] != '\0') {
2574 conv_unmime_header(hentry[H_REPLY_TO].body,
2577 g_free(hentry[H_REPLY_TO].body);
2578 hentry[H_REPLY_TO].body = NULL;
2580 if (hentry[H_CC].body != NULL) {
2581 compose->cc = conv_unmime_header(hentry[H_CC].body, NULL);
2582 g_free(hentry[H_CC].body);
2583 hentry[H_CC].body = NULL;
2585 if (hentry[H_REFERENCES].body != NULL) {
2586 if (compose->mode == COMPOSE_REEDIT)
2587 compose->references = hentry[H_REFERENCES].body;
2589 compose->references = compose_parse_references
2590 (hentry[H_REFERENCES].body, msginfo->msgid);
2591 g_free(hentry[H_REFERENCES].body);
2593 hentry[H_REFERENCES].body = NULL;
2595 if (hentry[H_BCC].body != NULL) {
2596 if (compose->mode == COMPOSE_REEDIT)
2598 conv_unmime_header(hentry[H_BCC].body, NULL);
2599 g_free(hentry[H_BCC].body);
2600 hentry[H_BCC].body = NULL;
2602 if (hentry[H_NEWSGROUPS].body != NULL) {
2603 compose->newsgroups = hentry[H_NEWSGROUPS].body;
2604 hentry[H_NEWSGROUPS].body = NULL;
2606 if (hentry[H_FOLLOWUP_TO].body != NULL) {
2607 if (hentry[H_FOLLOWUP_TO].body[0] != '\0') {
2608 compose->followup_to =
2609 conv_unmime_header(hentry[H_FOLLOWUP_TO].body,
2612 g_free(hentry[H_FOLLOWUP_TO].body);
2613 hentry[H_FOLLOWUP_TO].body = NULL;
2615 if (hentry[H_LIST_POST].body != NULL) {
2618 extract_address(hentry[H_LIST_POST].body);
2619 if (hentry[H_LIST_POST].body[0] != '\0') {
2620 scan_mailto_url(hentry[H_LIST_POST].body,
2621 NULL, &to, NULL, NULL, NULL, NULL, NULL);
2623 g_free(compose->ml_post);
2624 compose->ml_post = to;
2627 g_free(hentry[H_LIST_POST].body);
2628 hentry[H_LIST_POST].body = NULL;
2631 /* CLAWS - X-Priority */
2632 if (compose->mode == COMPOSE_REEDIT)
2633 if (hentry[H_X_PRIORITY].body != NULL) {
2636 priority = atoi(hentry[H_X_PRIORITY].body);
2637 g_free(hentry[H_X_PRIORITY].body);
2639 hentry[H_X_PRIORITY].body = NULL;
2641 if (priority < PRIORITY_HIGHEST ||
2642 priority > PRIORITY_LOWEST)
2643 priority = PRIORITY_NORMAL;
2645 compose->priority = priority;
2648 if (compose->mode == COMPOSE_REEDIT) {
2649 if (msginfo->inreplyto && *msginfo->inreplyto)
2650 compose->inreplyto = g_strdup(msginfo->inreplyto);
2654 if (msginfo->msgid && *msginfo->msgid)
2655 compose->inreplyto = g_strdup(msginfo->msgid);
2657 if (!compose->references) {
2658 if (msginfo->msgid && *msginfo->msgid) {
2659 if (msginfo->inreplyto && *msginfo->inreplyto)
2660 compose->references =
2661 g_strdup_printf("<%s>\n\t<%s>",
2665 compose->references =
2666 g_strconcat("<", msginfo->msgid, ">",
2668 } else if (msginfo->inreplyto && *msginfo->inreplyto) {
2669 compose->references =
2670 g_strconcat("<", msginfo->inreplyto, ">",
2678 static gchar *compose_parse_references(const gchar *ref, const gchar *msgid)
2680 GSList *ref_id_list, *cur;
2684 ref_id_list = references_list_append(NULL, ref);
2685 if (!ref_id_list) return NULL;
2686 if (msgid && *msgid)
2687 ref_id_list = g_slist_append(ref_id_list, g_strdup(msgid));
2692 for (cur = ref_id_list; cur != NULL; cur = cur->next)
2693 /* "<" + Message-ID + ">" + CR+LF+TAB */
2694 len += strlen((gchar *)cur->data) + 5;
2696 if (len > MAX_REFERENCES_LEN) {
2697 /* remove second message-ID */
2698 if (ref_id_list && ref_id_list->next &&
2699 ref_id_list->next->next) {
2700 g_free(ref_id_list->next->data);
2701 ref_id_list = g_slist_remove
2702 (ref_id_list, ref_id_list->next->data);
2704 slist_free_strings(ref_id_list);
2705 g_slist_free(ref_id_list);
2712 new_ref = g_string_new("");
2713 for (cur = ref_id_list; cur != NULL; cur = cur->next) {
2714 if (new_ref->len > 0)
2715 g_string_append(new_ref, "\n\t");
2716 g_string_append_printf(new_ref, "<%s>", (gchar *)cur->data);
2719 slist_free_strings(ref_id_list);
2720 g_slist_free(ref_id_list);
2722 new_ref_str = new_ref->str;
2723 g_string_free(new_ref, FALSE);
2728 static gchar *compose_quote_fmt(Compose *compose, MsgInfo *msginfo,
2729 const gchar *fmt, const gchar *qmark,
2730 const gchar *body, gboolean rewrap,
2731 gboolean need_unescape,
2732 const gchar *err_msg)
2734 MsgInfo* dummyinfo = NULL;
2735 gchar *quote_str = NULL;
2737 gboolean prev_autowrap;
2738 const gchar *trimmed_body = body;
2739 gint cursor_pos = -1;
2740 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
2741 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
2746 SIGNAL_BLOCK(buffer);
2749 dummyinfo = compose_msginfo_new_from_compose(compose);
2750 msginfo = dummyinfo;
2753 if (qmark != NULL) {
2755 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE,
2756 compose->gtkaspell);
2758 quote_fmt_init(msginfo, NULL, NULL, FALSE, compose->account, FALSE);
2760 quote_fmt_scan_string(qmark);
2763 buf = quote_fmt_get_buffer();
2765 alertpanel_error(_("Quote mark format error."));
2767 Xstrdup_a(quote_str, buf, goto error)
2770 if (fmt && *fmt != '\0') {
2773 while (*trimmed_body == '\n')
2777 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE,
2778 compose->gtkaspell);
2780 quote_fmt_init(msginfo, quote_str, trimmed_body, FALSE, compose->account, FALSE);
2782 if (need_unescape) {
2785 /* decode \-escape sequences in the internal representation of the quote format */
2786 tmp = malloc(strlen(fmt)+1);
2787 pref_get_unescaped_pref(tmp, fmt);
2788 quote_fmt_scan_string(tmp);
2792 quote_fmt_scan_string(fmt);
2796 buf = quote_fmt_get_buffer();
2798 gint line = quote_fmt_get_line();
2799 alertpanel_error(err_msg, line);
2805 prev_autowrap = compose->autowrap;
2806 compose->autowrap = FALSE;
2808 mark = gtk_text_buffer_get_insert(buffer);
2809 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
2810 if (g_utf8_validate(buf, -1, NULL)) {
2811 gtk_text_buffer_insert(buffer, &iter, buf, -1);
2813 gchar *tmpout = NULL;
2814 tmpout = conv_codeset_strdup
2815 (buf, conv_get_locale_charset_str_no_utf8(),
2817 if (!tmpout || !g_utf8_validate(tmpout, -1, NULL)) {
2819 tmpout = g_malloc(strlen(buf)*2+1);
2820 conv_localetodisp(tmpout, strlen(buf)*2+1, buf);
2822 gtk_text_buffer_insert(buffer, &iter, tmpout, -1);
2826 cursor_pos = quote_fmt_get_cursor_pos();
2827 if (cursor_pos == -1)
2828 cursor_pos = gtk_text_iter_get_offset(&iter);
2829 compose->set_cursor_pos = cursor_pos;
2831 gtk_text_buffer_get_start_iter(buffer, &iter);
2832 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cursor_pos);
2833 gtk_text_buffer_place_cursor(buffer, &iter);
2835 compose->autowrap = prev_autowrap;
2836 if (compose->autowrap && rewrap)
2837 compose_wrap_all(compose);
2844 SIGNAL_UNBLOCK(buffer);
2846 procmsg_msginfo_free( dummyinfo );
2851 /* if ml_post is of type addr@host and from is of type
2852 * addr-anything@host, return TRUE
2854 static gboolean is_subscription(const gchar *ml_post, const gchar *from)
2856 gchar *left_ml = NULL;
2857 gchar *right_ml = NULL;
2858 gchar *left_from = NULL;
2859 gchar *right_from = NULL;
2860 gboolean result = FALSE;
2862 if (!ml_post || !from)
2865 left_ml = g_strdup(ml_post);
2866 if (strstr(left_ml, "@")) {
2867 right_ml = strstr(left_ml, "@")+1;
2868 *(strstr(left_ml, "@")) = '\0';
2871 left_from = g_strdup(from);
2872 if (strstr(left_from, "@")) {
2873 right_from = strstr(left_from, "@")+1;
2874 *(strstr(left_from, "@")) = '\0';
2877 if (left_ml && left_from && right_ml && right_from
2878 && !strncmp(left_from, left_ml, strlen(left_ml))
2879 && !strcmp(right_from, right_ml)) {
2888 static gboolean same_address(const gchar *addr1, const gchar *addr2)
2890 gchar *my_addr1, *my_addr2;
2892 if (!addr1 || !addr2)
2895 Xstrdup_a(my_addr1, addr1, return FALSE);
2896 Xstrdup_a(my_addr2, addr2, return FALSE);
2898 extract_address(my_addr1);
2899 extract_address(my_addr2);
2901 return !strcasecmp(my_addr1, my_addr2);
2904 static void compose_reply_set_entry(Compose *compose, MsgInfo *msginfo,
2905 gboolean to_all, gboolean to_ml,
2907 gboolean followup_and_reply_to)
2909 GSList *cc_list = NULL;
2912 gchar *replyto = NULL;
2913 GHashTable *to_table;
2915 gboolean reply_to_ml = FALSE;
2916 gboolean default_reply_to = FALSE;
2918 g_return_if_fail(compose->account != NULL);
2919 g_return_if_fail(msginfo != NULL);
2921 reply_to_ml = to_ml && compose->ml_post;
2923 default_reply_to = msginfo->folder &&
2924 msginfo->folder->prefs->enable_default_reply_to;
2926 if (compose->account->protocol != A_NNTP) {
2927 if (reply_to_ml && !default_reply_to) {
2929 gboolean is_subscr = is_subscription(compose->ml_post,
2932 /* normal answer to ml post with a reply-to */
2933 compose_entry_append(compose,
2936 if (compose->replyto
2937 && !same_address(compose->ml_post, compose->replyto))
2938 compose_entry_append(compose,
2942 /* answer to subscription confirmation */
2943 if (compose->replyto)
2944 compose_entry_append(compose,
2947 else if (msginfo->from)
2948 compose_entry_append(compose,
2953 else if (!(to_all || to_sender) && default_reply_to) {
2954 compose_entry_append(compose,
2955 msginfo->folder->prefs->default_reply_to,
2957 compose_entry_mark_default_to(compose,
2958 msginfo->folder->prefs->default_reply_to);
2963 Xstrdup_a(tmp1, msginfo->from, return);
2964 extract_address(tmp1);
2965 if (to_all || to_sender ||
2966 !account_find_from_address(tmp1, FALSE))
2967 compose_entry_append(compose,
2968 (compose->replyto && !to_sender)
2969 ? compose->replyto :
2970 msginfo->from ? msginfo->from : "",
2972 else if (!to_all && !to_sender) {
2973 if (!folder_has_parent_of_type(msginfo->folder, F_QUEUE) &&
2974 !folder_has_parent_of_type(msginfo->folder, F_OUTBOX) &&
2975 !folder_has_parent_of_type(msginfo->folder, F_DRAFT)) {
2976 if (compose->replyto) {
2977 compose_entry_append(compose,
2981 compose_entry_append(compose,
2982 msginfo->from ? msginfo->from : "",
2986 /* replying to own mail, use original recp */
2987 compose_entry_append(compose,
2988 msginfo->to ? msginfo->to : "",
2990 compose_entry_append(compose,
2991 msginfo->cc ? msginfo->cc : "",
2997 if (to_sender || (compose->followup_to &&
2998 !strncmp(compose->followup_to, "poster", 6)))
2999 compose_entry_append
3001 (compose->replyto ? compose->replyto :
3002 msginfo->from ? msginfo->from : ""),
3005 else if (followup_and_reply_to || to_all) {
3006 compose_entry_append
3008 (compose->replyto ? compose->replyto :
3009 msginfo->from ? msginfo->from : ""),
3012 compose_entry_append
3014 compose->followup_to ? compose->followup_to :
3015 compose->newsgroups ? compose->newsgroups : "",
3016 COMPOSE_NEWSGROUPS);
3019 compose_entry_append
3021 compose->followup_to ? compose->followup_to :
3022 compose->newsgroups ? compose->newsgroups : "",
3023 COMPOSE_NEWSGROUPS);
3026 if (msginfo->subject && *msginfo->subject) {
3030 buf = p = g_strdup(msginfo->subject);
3031 p += subject_get_prefix_length(p);
3032 memmove(buf, p, strlen(p) + 1);
3034 buf2 = g_strdup_printf("Re: %s", buf);
3035 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), buf2);
3040 gtk_entry_set_text(GTK_ENTRY(compose->subject_entry), "Re: ");
3042 if (to_ml && compose->ml_post) return;
3043 if (!to_all || compose->account->protocol == A_NNTP) return;
3045 if (compose->replyto) {
3046 Xstrdup_a(replyto, compose->replyto, return);
3047 extract_address(replyto);
3049 if (msginfo->from) {
3050 Xstrdup_a(from, msginfo->from, return);
3051 extract_address(from);
3054 if (replyto && from)
3055 cc_list = address_list_append_with_comments(cc_list, from);
3056 if (to_all && msginfo->folder &&
3057 msginfo->folder->prefs->enable_default_reply_to)
3058 cc_list = address_list_append_with_comments(cc_list,
3059 msginfo->folder->prefs->default_reply_to);
3060 cc_list = address_list_append_with_comments(cc_list, msginfo->to);
3061 cc_list = address_list_append_with_comments(cc_list, compose->cc);
3063 to_table = g_hash_table_new(g_str_hash, g_str_equal);
3065 g_hash_table_insert(to_table, g_utf8_strdown(replyto, -1), GINT_TO_POINTER(1));
3066 if (compose->account) {
3067 g_hash_table_insert(to_table, g_utf8_strdown(compose->account->address, -1),
3068 GINT_TO_POINTER(1));
3070 /* remove address on To: and that of current account */
3071 for (cur = cc_list; cur != NULL; ) {
3072 GSList *next = cur->next;
3075 addr = g_utf8_strdown(cur->data, -1);
3076 extract_address(addr);
3078 if (GPOINTER_TO_INT(g_hash_table_lookup(to_table, addr)) == 1)
3079 cc_list = g_slist_remove(cc_list, cur->data);
3081 g_hash_table_insert(to_table, addr, GINT_TO_POINTER(1));
3085 hash_free_strings(to_table);
3086 g_hash_table_destroy(to_table);
3089 for (cur = cc_list; cur != NULL; cur = cur->next)
3090 compose_entry_append(compose, (gchar *)cur->data,
3092 slist_free_strings(cc_list);
3093 g_slist_free(cc_list);
3098 #define SET_ENTRY(entry, str) \
3101 gtk_entry_set_text(GTK_ENTRY(compose->entry), str); \
3104 #define SET_ADDRESS(type, str) \
3107 compose_entry_append(compose, str, type); \
3110 static void compose_reedit_set_entry(Compose *compose, MsgInfo *msginfo)
3112 g_return_if_fail(msginfo != NULL);
3114 SET_ENTRY(subject_entry, msginfo->subject);
3115 SET_ENTRY(from_name, msginfo->from);
3116 SET_ADDRESS(COMPOSE_TO, msginfo->to);
3117 SET_ADDRESS(COMPOSE_CC, compose->cc);
3118 SET_ADDRESS(COMPOSE_BCC, compose->bcc);
3119 SET_ADDRESS(COMPOSE_REPLYTO, compose->replyto);
3120 SET_ADDRESS(COMPOSE_NEWSGROUPS, compose->newsgroups);
3121 SET_ADDRESS(COMPOSE_FOLLOWUPTO, compose->followup_to);
3123 compose_update_priority_menu_item(compose);
3124 compose_update_privacy_system_menu_item(compose, FALSE);
3125 compose_show_first_last_header(compose, TRUE);
3131 static void compose_insert_sig(Compose *compose, gboolean replace)
3133 GtkTextView *text = GTK_TEXT_VIEW(compose->text);
3134 GtkTextBuffer *buffer = gtk_text_view_get_buffer(text);
3136 GtkTextIter iter, iter_end;
3137 gint cur_pos, ins_pos;
3138 gboolean prev_autowrap;
3139 gboolean found = FALSE;
3140 gboolean exists = FALSE;
3142 g_return_if_fail(compose->account != NULL);
3146 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3147 G_CALLBACK(compose_changed_cb),
3150 mark = gtk_text_buffer_get_insert(buffer);
3151 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3152 cur_pos = gtk_text_iter_get_offset (&iter);
3155 gtk_text_buffer_get_end_iter(buffer, &iter);
3157 exists = (compose->sig_str != NULL);
3160 GtkTextIter first_iter, start_iter, end_iter;
3162 gtk_text_buffer_get_start_iter(buffer, &first_iter);
3164 if (!exists || compose->sig_str[0] == '\0')
3167 found = gtk_text_iter_forward_to_tag_toggle(&first_iter,
3168 compose->signature_tag);
3171 /* include previous \n\n */
3172 gtk_text_iter_backward_chars(&first_iter, 1);
3173 start_iter = first_iter;
3174 end_iter = first_iter;
3176 found = gtk_text_iter_forward_to_tag_toggle(&end_iter,
3177 compose->signature_tag);
3178 found &= gtk_text_iter_forward_to_tag_toggle(&end_iter,
3179 compose->signature_tag);
3181 gtk_text_buffer_delete(buffer, &start_iter, &end_iter);
3187 g_free(compose->sig_str);
3188 compose->sig_str = compose_get_signature_str(compose);
3190 cur_pos = gtk_text_iter_get_offset(&iter);
3192 if (!compose->sig_str || (replace && !compose->account->auto_sig)) {
3193 g_free(compose->sig_str);
3194 compose->sig_str = NULL;
3196 if (compose->sig_inserted == FALSE)
3197 gtk_text_buffer_insert(buffer, &iter, "\n", -1);
3198 compose->sig_inserted = TRUE;
3200 cur_pos = gtk_text_iter_get_offset(&iter);
3201 gtk_text_buffer_insert(buffer, &iter, compose->sig_str, -1);
3203 gtk_text_buffer_get_iter_at_offset(buffer, &iter, cur_pos);
3204 gtk_text_iter_forward_chars(&iter, 1);
3205 gtk_text_buffer_get_end_iter(buffer, &iter_end);
3206 gtk_text_buffer_apply_tag_by_name(buffer,"signature",&iter, &iter_end);
3208 if (cur_pos > gtk_text_buffer_get_char_count (buffer))
3209 cur_pos = gtk_text_buffer_get_char_count (buffer);
3212 /* put the cursor where it should be
3213 * either where the quote_fmt says, either where it was */
3214 if (compose->set_cursor_pos < 0)
3215 gtk_text_buffer_get_iter_at_offset(buffer, &iter, ins_pos);
3217 gtk_text_buffer_get_iter_at_offset(buffer, &iter,
3218 compose->set_cursor_pos);
3220 gtk_text_buffer_place_cursor(buffer, &iter);
3221 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3222 G_CALLBACK(compose_changed_cb),
3228 static gchar *compose_get_signature_str(Compose *compose)
3230 gchar *sig_body = NULL;
3231 gchar *sig_str = NULL;
3232 gchar *utf8_sig_str = NULL;
3234 g_return_val_if_fail(compose->account != NULL, NULL);
3236 if (!compose->account->sig_path)
3239 if (compose->account->sig_type == SIG_FILE) {
3240 if (!is_file_or_fifo_exist(compose->account->sig_path)) {
3241 g_warning("can't open signature file: %s\n",
3242 compose->account->sig_path);
3247 if (compose->account->sig_type == SIG_COMMAND)
3248 sig_body = get_command_output(compose->account->sig_path);
3252 tmp = file_read_to_str(compose->account->sig_path);
3255 sig_body = normalize_newlines(tmp);
3259 if (compose->account->sig_sep) {
3260 sig_str = g_strconcat("\n", compose->account->sig_sep, "\n", sig_body,
3264 sig_str = g_strconcat("\n", sig_body, NULL);
3267 if (g_utf8_validate(sig_str, -1, NULL) == TRUE)
3268 utf8_sig_str = sig_str;
3270 utf8_sig_str = conv_codeset_strdup
3271 (sig_str, conv_get_locale_charset_str_no_utf8(),
3277 return utf8_sig_str;
3280 static ComposeInsertResult compose_insert_file(Compose *compose, const gchar *file)
3283 GtkTextBuffer *buffer;
3286 const gchar *cur_encoding;
3287 gchar buf[BUFFSIZE];
3290 gboolean prev_autowrap;
3291 gboolean badtxt = FALSE;
3293 g_return_val_if_fail(file != NULL, COMPOSE_INSERT_NO_FILE);
3295 if ((fp = g_fopen(file, "rb")) == NULL) {
3296 FILE_OP_ERROR(file, "fopen");
3297 return COMPOSE_INSERT_READ_ERROR;
3300 prev_autowrap = compose->autowrap;
3301 compose->autowrap = FALSE;
3303 text = GTK_TEXT_VIEW(compose->text);
3304 buffer = gtk_text_view_get_buffer(text);
3305 mark = gtk_text_buffer_get_insert(buffer);
3306 gtk_text_buffer_get_iter_at_mark(buffer, &iter, mark);
3308 g_signal_handlers_block_by_func(G_OBJECT(buffer),
3309 G_CALLBACK(text_inserted),
3312 cur_encoding = conv_get_locale_charset_str_no_utf8();
3314 while (fgets(buf, sizeof(buf), fp) != NULL) {
3317 if (g_utf8_validate(buf, -1, NULL) == TRUE)
3318 str = g_strdup(buf);
3320 str = conv_codeset_strdup
3321 (buf, cur_encoding, CS_INTERNAL);
3324 /* strip <CR> if DOS/Windows file,
3325 replace <CR> with <LF> if Macintosh file. */
3328 if (len > 0 && str[len - 1] != '\n') {
3330 if (str[len] == '\r') str[len] = '\n';
3333 gtk_text_buffer_insert(buffer, &iter, str, -1);
3337 g_signal_handlers_unblock_by_func(G_OBJECT(buffer),
3338 G_CALLBACK(text_inserted),
3340 compose->autowrap = prev_autowrap;
3341 if (compose->autowrap)
3342 compose_wrap_all(compose);
3347 return COMPOSE_INSERT_INVALID_CHARACTER;
3349 return COMPOSE_INSERT_SUCCESS;
3352 static gboolean compose_attach_append(Compose *compose, const gchar *file,
3353 const gchar *filename,
3354 const gchar *content_type)
3362 GtkListStore *store;
3364 gboolean has_binary = FALSE;
3366 if (!is_file_exist(file)) {
3367 gchar *file_from_uri = g_filename_from_uri(file, NULL, NULL);
3368 gboolean result = FALSE;
3369 if (file_from_uri && is_file_exist(file_from_uri)) {
3370 result = compose_attach_append(
3371 compose, file_from_uri,
3375 g_free(file_from_uri);
3378 alertpanel_error("File %s doesn't exist\n", filename);
3381 if ((size = get_file_size(file)) < 0) {
3382 alertpanel_error("Can't get file size of %s\n", filename);
3386 alertpanel_error(_("File %s is empty."), filename);
3389 if ((fp = g_fopen(file, "rb")) == NULL) {
3390 alertpanel_error(_("Can't read %s."), filename);
3395 ainfo = g_new0(AttachInfo, 1);
3396 auto_ainfo = g_auto_pointer_new_with_free
3397 (ainfo, (GFreeFunc) compose_attach_info_free);
3398 ainfo->file = g_strdup(file);
3401 ainfo->content_type = g_strdup(content_type);
3402 if (!g_ascii_strcasecmp(content_type, "message/rfc822")) {
3404 MsgFlags flags = {0, 0};
3406 if (procmime_get_encoding_for_text_file(file, &has_binary) == ENC_7BIT)
3407 ainfo->encoding = ENC_7BIT;
3409 ainfo->encoding = ENC_8BIT;
3411 msginfo = procheader_parse_file(file, flags, FALSE, FALSE);
3412 if (msginfo && msginfo->subject)
3413 name = g_strdup(msginfo->subject);
3415 name = g_path_get_basename(filename ? filename : file);
3417 ainfo->name = g_strdup_printf(_("Message: %s"), name);
3419 procmsg_msginfo_free(msginfo);
3421 if (!g_ascii_strncasecmp(content_type, "text", 4))
3422 ainfo->encoding = procmime_get_encoding_for_text_file(file, &has_binary);
3424 ainfo->encoding = ENC_BASE64;
3425 name = g_path_get_basename(filename ? filename : file);
3426 ainfo->name = g_strdup(name);
3430 ainfo->content_type = procmime_get_mime_type(file);
3431 if (!ainfo->content_type) {
3432 ainfo->content_type =
3433 g_strdup("application/octet-stream");
3434 ainfo->encoding = ENC_BASE64;
3435 } else if (!g_ascii_strncasecmp(ainfo->content_type, "text", 4))
3437 procmime_get_encoding_for_text_file(file, &has_binary);
3439 ainfo->encoding = ENC_BASE64;
3440 name = g_path_get_basename(filename ? filename : file);
3441 ainfo->name = g_strdup(name);
3445 if (ainfo->name != NULL
3446 && !strcmp(ainfo->name, ".")) {
3447 g_free(ainfo->name);
3451 if (!strcmp(ainfo->content_type, "unknown") || has_binary) {
3452 g_free(ainfo->content_type);
3453 ainfo->content_type = g_strdup("application/octet-stream");
3456 ainfo->size = (goffset)size;
3457 size_text = to_human_readable((goffset)size);
3459 store = GTK_LIST_STORE(gtk_tree_view_get_model
3460 (GTK_TREE_VIEW(compose->attach_clist)));
3462 gtk_list_store_append(store, &iter);
3463 gtk_list_store_set(store, &iter,
3464 COL_MIMETYPE, ainfo->content_type,
3465 COL_SIZE, size_text,
3466 COL_NAME, ainfo->name,
3468 COL_AUTODATA, auto_ainfo,
3471 g_auto_pointer_free(auto_ainfo);
3472 compose_attach_update_label(compose);
3476 static void compose_use_signing(Compose *compose, gboolean use_signing)
3478 compose->use_signing = use_signing;
3479 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Sign", use_signing);
3482 static void compose_use_encryption(Compose *compose, gboolean use_encryption)
3484 compose->use_encryption = use_encryption;
3485 cm_toggle_menu_set_active_full(compose->ui_manager, "Menu/Options/Encrypt", use_encryption);
3488 #define NEXT_PART_NOT_CHILD(info) \
3490 node = info->node; \
3491 while (node->children) \
3492 node = g_node_last_child(node); \
3493 info = procmime_mimeinfo_next((MimeInfo *)node->data); \
3496 static void compose_attach_parts(Compose *compose, MsgInfo *msginfo)
3500 MimeInfo *firsttext = NULL;
3501 MimeInfo *encrypted = NULL;
3504 const gchar *partname = NULL;
3506 mimeinfo = procmime_scan_message(msginfo);
3507 if (!mimeinfo) return;
3509 if (mimeinfo->node->children == NULL) {
3510 procmime_mimeinfo_free_all(mimeinfo);
3514 /* find first content part */
3515 child = (MimeInfo *) mimeinfo->node->children->data;
3516 while (child && child->node->children && (child->type == MIMETYPE_MULTIPART))
3517 child = (MimeInfo *)child->node->children->data;
3519 if (child->type == MIMETYPE_TEXT) {
3521 debug_print("First text part found\n");
3522 } else if (compose->mode == COMPOSE_REEDIT &&
3523 child->type == MIMETYPE_APPLICATION &&
3524 !g_ascii_strcasecmp(child->subtype, "pgp-encrypted")) {
3525 encrypted = (MimeInfo *)child->node->parent->data;
3528 child = (MimeInfo *) mimeinfo->node->children->data;
3529 while (child != NULL) {
3532 if (child == encrypted) {
3533 /* skip this part of tree */
3534 NEXT_PART_NOT_CHILD(child);
3538 if (child->type == MIMETYPE_MULTIPART) {
3539 /* get the actual content */
3540 child = procmime_mimeinfo_next(child);
3544 if (child == firsttext) {
3545 child = procmime_mimeinfo_next(child);
3549 outfile = procmime_get_tmp_file_name(child);
3550 if ((err = procmime_get_part(outfile, child)) < 0)
3551 g_warning("Can't get the part of multipart message. (%s)", strerror(-err));
3553 gchar *content_type;
3555 content_type = procmime_get_content_type_str(child->type, child->subtype);
3557 /* if we meet a pgp signature, we don't attach it, but
3558 * we force signing. */
3559 if ((strcmp(content_type, "application/pgp-signature") &&
3560 strcmp(content_type, "application/pkcs7-signature") &&
3561 strcmp(content_type, "application/x-pkcs7-signature"))
3562 || compose->mode == COMPOSE_REDIRECT) {
3563 partname = procmime_mimeinfo_get_parameter(child, "filename");
3564 if (partname == NULL)
3565 partname = procmime_mimeinfo_get_parameter(child, "name");
3566 if (partname == NULL)
3568 compose_attach_append(compose, outfile,
3569 partname, content_type);
3571 compose_force_signing(compose, compose->account);
3573 g_free(content_type);
3576 NEXT_PART_NOT_CHILD(child);
3578 procmime_mimeinfo_free_all(mimeinfo);
3581 #undef NEXT_PART_NOT_CHILD
3586 WAIT_FOR_INDENT_CHAR,
3587 WAIT_FOR_INDENT_CHAR_OR_SPACE,
3590 /* return indent length, we allow:
3591 indent characters followed by indent characters or spaces/tabs,
3592 alphabets and numbers immediately followed by indent characters,
3593 and the repeating sequences of the above
3594 If quote ends with multiple spaces, only the first one is included. */
3595 static gchar *compose_get_quote_str(GtkTextBuffer *buffer,
3596 const GtkTextIter *start, gint *len)
3598 GtkTextIter iter = *start;
3602 IndentState state = WAIT_FOR_INDENT_CHAR;
3605 gint alnum_count = 0;
3606 gint space_count = 0;
3609 if (prefs_common.quote_chars == NULL) {
3613 while (!gtk_text_iter_ends_line(&iter)) {
3614 wc = gtk_text_iter_get_char(&iter);
3615 if (g_unichar_iswide(wc))
3617 clen = g_unichar_to_utf8(wc, ch);
3621 is_indent = strchr(prefs_common.quote_chars, ch[0]) ? TRUE : FALSE;
3622 is_space = g_unichar_isspace(wc);
3624 if (state == WAIT_FOR_INDENT_CHAR) {
3625 if (!is_indent && !g_unichar_isalnum(wc))
3628 quote_len += alnum_count + space_count + 1;
3629 alnum_count = space_count = 0;
3630 state = WAIT_FOR_INDENT_CHAR_OR_SPACE;
3633 } else if (state == WAIT_FOR_INDENT_CHAR_OR_SPACE) {
3634 if (!is_indent && !is_space && !g_unichar_isalnum(wc))
3638 else if (is_indent) {
3639 quote_len += alnum_count + space_count + 1;
3640 alnum_count = space_count = 0;
3643 state = WAIT_FOR_INDENT_CHAR;
3647 gtk_text_iter_forward_char(&iter);
3650 if (quote_len > 0 && space_count > 0)
3656 if (quote_len > 0) {
3658 gtk_text_iter_forward_chars(&iter, quote_len);
3659 return gtk_text_buffer_get_text(buffer, start, &iter, FALSE);
3665 /* return TRUE if the line is itemized */
3666 static gboolean compose_is_itemized(GtkTextBuffer *buffer,
3667 const GtkTextIter *start)
3669 GtkTextIter iter = *start;
3674 if (gtk_text_iter_ends_line(&iter))
3678 wc = gtk_text_iter_get_char(&iter);
3679 if (!g_unichar_isspace(wc))
3681 gtk_text_iter_forward_char(&iter);
3682 if (gtk_text_iter_ends_line(&iter))
3686 clen = g_unichar_to_utf8(wc, ch);
3690 if (!strchr("*-+", ch[0]))
3693 gtk_text_iter_forward_char(&iter);
3694 if (gtk_text_iter_ends_line(&iter))