1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU Lesser General Public License as published by
6 * the Free Software Foundation.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * You should have received a copy of the GNU Lesser General Public License
14 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18 * Jeffrey Stedfast <fejj@ximian.com>
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
24 #include "evolution-config.h"
28 #include <glib/gi18n.h>
30 #include <e-util/e-util.h>
32 #include <libemail-engine/libemail-engine.h>
34 #include <em-format/e-mail-parser.h>
35 #include <em-format/e-mail-part-utils.h>
36 #include <em-format/e-mail-formatter-quote.h>
38 #include <shell/e-shell.h>
40 #include <composer/e-msg-composer.h>
41 #include <composer/e-composer-actions.h>
42 #include <composer/e-composer-post-header.h>
44 #include "e-mail-printer.h"
45 #include "e-mail-ui-session.h"
46 #include "e-mail-templates.h"
47 #include "e-mail-templates-store.h"
49 #include "em-composer-utils.h"
50 #include "em-folder-selector.h"
51 #include "em-folder-tree.h"
53 #include "mail-send-recv.h"
63 /* The gmtime() and localtime() in Microsoft's C library are MT-safe */
64 #define gmtime_r(tp,tmp) (gmtime(tp)?(*(tmp)=*gmtime(tp),(tmp)):0)
65 #define localtime_r(tp,tmp) (localtime(tp)?(*(tmp)=*localtime(tp),(tmp)):0)
68 typedef struct _AsyncContext AsyncContext
;
69 typedef struct _ForwardData ForwardData
;
71 struct _AsyncContext
{
72 CamelMimeMessage
*message
;
73 EMailSession
*session
;
74 EMsgComposer
*composer
;
78 gulong num_loading_handler_id
;
79 gulong cancelled_handler_id
;
86 EMailForwardStyle style
;
90 async_context_free (AsyncContext
*async_context
)
92 if (async_context
->cancelled_handler_id
) {
93 GCancellable
*cancellable
;
95 cancellable
= e_activity_get_cancellable (async_context
->activity
);
96 /* Cannot use g_cancellable_disconnect(), because when this is called
97 from inside the cancelled handler, then the GCancellable deadlocks. */
98 g_signal_handler_disconnect (cancellable
, async_context
->cancelled_handler_id
);
99 async_context
->cancelled_handler_id
= 0;
102 if (async_context
->num_loading_handler_id
) {
103 EAttachmentView
*view
;
104 EAttachmentStore
*store
;
106 view
= e_msg_composer_get_attachment_view (async_context
->composer
);
107 store
= e_attachment_view_get_store (view
);
109 e_signal_disconnect_notify_handler (store
, &async_context
->num_loading_handler_id
);
112 g_clear_object (&async_context
->message
);
113 g_clear_object (&async_context
->session
);
114 g_clear_object (&async_context
->composer
);
115 g_clear_object (&async_context
->activity
);
117 g_free (async_context
->folder_uri
);
118 g_free (async_context
->message_uid
);
120 g_slice_free (AsyncContext
, async_context
);
124 forward_data_free (ForwardData
*data
)
126 if (data
->shell
!= NULL
)
127 g_object_unref (data
->shell
);
129 if (data
->folder
!= NULL
)
130 g_object_unref (data
->folder
);
132 if (data
->uids
!= NULL
)
133 g_ptr_array_unref (data
->uids
);
135 g_slice_free (ForwardData
, data
);
139 ask_confirm_for_unwanted_html_mail (EMsgComposer
*composer
,
140 EDestination
**recipients
)
146 str
= g_string_new ("");
147 for (i
= 0; recipients
[i
] != NULL
; ++i
) {
148 if (!e_destination_get_html_mail_pref (recipients
[i
])) {
151 name
= e_destination_get_textrep (recipients
[i
], FALSE
);
153 g_string_append_printf (str
, " %s\n", name
);
158 res
= e_util_prompt_user (
159 GTK_WINDOW (composer
),
160 "org.gnome.evolution.mail", "prompt-on-unwanted-html",
161 "mail:ask-send-html", str
->str
, NULL
);
165 g_string_free (str
, TRUE
);
171 ask_confirm_for_empty_subject (EMsgComposer
*composer
)
173 return e_util_prompt_user (
174 GTK_WINDOW (composer
),
175 "org.gnome.evolution.mail",
176 "prompt-on-empty-subject",
177 "mail:ask-send-no-subject", NULL
);
181 ask_confirm_for_only_bcc (EMsgComposer
*composer
,
182 gboolean hidden_list_case
)
184 /* If the user is mailing a hidden contact list, it is possible for
185 * them to create a message with only Bcc recipients without really
186 * realizing it. To try to avoid being totally confusing, I've changed
187 * this dialog to provide slightly different text in that case, to
188 * better explain what the hell is going on. */
190 return e_util_prompt_user (
191 GTK_WINDOW (composer
),
192 "org.gnome.evolution.mail",
193 "prompt-on-only-bcc",
195 "mail:ask-send-only-bcc-contact" :
196 "mail:ask-send-only-bcc", NULL
);
200 is_group_definition (const gchar
*str
)
207 colon
= strchr (str
, ':');
208 return colon
> str
&& strchr (str
, ';') > colon
;
212 composer_presend_check_recipients (EMsgComposer
*composer
,
213 EMailSession
*session
)
215 EDestination
**recipients
;
216 EDestination
**recipients_bcc
;
217 CamelInternetAddress
*cia
;
218 EComposerHeaderTable
*table
;
219 EComposerHeader
*post_to_header
;
220 GString
*invalid_addrs
= NULL
;
222 gboolean check_passed
= FALSE
;
231 /* We should do all of the validity checks based on the composer,
232 * and not on the created message, as extra interaction may occur
233 * when we get the message (e.g. passphrase to sign a message). */
235 table
= e_msg_composer_get_header_table (composer
);
237 recipients
= e_composer_header_table_get_destinations_to (table
);
239 for (ii
= 0; recipients
[ii
] != NULL
; ii
++) {
242 addr
= e_destination_get_address (recipients
[ii
]);
243 if (addr
== NULL
|| *addr
== '\0')
249 e_destination_freev (recipients
);
252 recipients
= e_composer_header_table_get_destinations_cc (table
);
254 for (ii
= 0; recipients
[ii
] != NULL
; ii
++) {
257 addr
= e_destination_get_address (recipients
[ii
]);
258 if (addr
== NULL
|| *addr
== '\0')
264 e_destination_freev (recipients
);
267 recipients
= e_composer_header_table_get_destinations (table
);
269 cia
= camel_internet_address_new ();
271 /* See which ones are visible, present, etc. */
272 for (ii
= 0; recipients
!= NULL
&& recipients
[ii
] != NULL
; ii
++) {
276 addr
= e_destination_get_address (recipients
[ii
]);
277 if (addr
== NULL
|| *addr
== '\0')
280 camel_address_decode (CAMEL_ADDRESS (cia
), addr
);
281 len
= camel_address_length (CAMEL_ADDRESS (cia
));
284 if (!e_destination_is_evolution_list (recipients
[ii
])) {
285 for (j
= 0; j
< len
; j
++) {
286 const gchar
*name
= NULL
, *eml
= NULL
;
288 if (!camel_internet_address_get (cia
, j
, &name
, &eml
) ||
290 strchr (eml
, '@') <= eml
) {
292 invalid_addrs
= g_string_new ("");
294 g_string_append (invalid_addrs
, ", ");
297 g_string_append (invalid_addrs
, name
);
299 g_string_append (invalid_addrs
, name
? " <" : "");
300 g_string_append (invalid_addrs
, eml
);
301 g_string_append (invalid_addrs
, name
? ">" : "");
307 camel_address_remove (CAMEL_ADDRESS (cia
), -1);
309 if (e_destination_is_evolution_list (recipients
[ii
])
310 && !e_destination_list_show_addresses (recipients
[ii
])) {
315 } else if (is_group_definition (addr
)) {
316 /* like an address, it will not claim on only-bcc */
319 } else if (!invalid_addrs
) {
320 invalid_addrs
= g_string_new (addr
);
322 g_string_append (invalid_addrs
, ", ");
323 g_string_append (invalid_addrs
, addr
);
327 recipients_bcc
= e_composer_header_table_get_destinations_bcc (table
);
328 if (recipients_bcc
) {
329 for (ii
= 0; recipients_bcc
[ii
] != NULL
; ii
++) {
332 addr
= e_destination_get_address (recipients_bcc
[ii
]);
333 if (addr
== NULL
|| *addr
== '\0')
336 camel_address_decode (CAMEL_ADDRESS (cia
), addr
);
337 if (camel_address_length (CAMEL_ADDRESS (cia
)) > 0) {
338 camel_address_remove (CAMEL_ADDRESS (cia
), -1);
343 e_destination_freev (recipients_bcc
);
346 g_object_unref (cia
);
348 post_to_header
= e_composer_header_table_get_header (
349 table
, E_COMPOSER_HEADER_POST_TO
);
350 if (e_composer_header_get_visible (post_to_header
)) {
353 postlist
= e_composer_header_table_get_post_to (table
);
354 num_post
= g_list_length (postlist
);
355 g_list_foreach (postlist
, (GFunc
) g_free
, NULL
);
356 g_list_free (postlist
);
359 /* I'm sensing a lack of love, er, I mean recipients. */
360 if (num
== 0 && num_post
== 0) {
363 editor
= e_msg_composer_get_editor (composer
);
364 e_alert_submit (E_ALERT_SINK (editor
), "mail:send-no-recipients", NULL
);
370 if (!e_util_prompt_user (
371 GTK_WINDOW (composer
),
372 "org.gnome.evolution.mail",
373 "prompt-on-invalid-recip",
374 strstr (invalid_addrs
->str
, ", ") ?
375 "mail:ask-send-invalid-recip-multi" :
376 "mail:ask-send-invalid-recip-one",
377 invalid_addrs
->str
, NULL
)) {
378 g_string_free (invalid_addrs
, TRUE
);
382 g_string_free (invalid_addrs
, TRUE
);
385 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
386 if (num_to_cc
> 1 && num_to_cc
>= g_settings_get_int (settings
, "composer-many-to-cc-recips-num")) {
390 g_clear_object (&settings
);
392 head
= g_strdup_printf (ngettext (
393 /* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
394 "Are you sure you want to send a message with %d To and CC recipients?",
395 "Are you sure you want to send a message with %d To and CC recipients?",
396 num_to_cc
), num_to_cc
);
398 msg
= g_strdup_printf (ngettext (
399 /* Translators: The %d is replaced with the actual count of recipients, which is always more than one. */
400 "You are trying to send a message to %d recipients in To and CC fields."
401 " This would result in all recipients seeing the email addresses of each"
402 " other. In some cases this behaviour is undesired, especially if they"
403 " do not know each other or if privacy is a concern. Consider adding"
404 " recipients to the BCC field instead.",
405 "You are trying to send a message to %d recipients in To and CC fields."
406 " This would result in all recipients seeing the email addresses of each"
407 " other. In some cases this behaviour is undesired, especially if they"
408 " do not know each other or if privacy is a concern. Consider adding"
409 " recipients to the BCC field instead.",
410 num_to_cc
), num_to_cc
);
412 if (!e_util_prompt_user (
413 GTK_WINDOW (composer
),
414 "org.gnome.evolution.mail",
415 "prompt-on-many-to-cc-recips",
416 "mail:ask-many-to-cc-recips",
423 action
= E_COMPOSER_ACTION_VIEW_BCC (composer
);
424 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), TRUE
);
432 g_clear_object (&settings
);
434 if (num
> 0 && (num
== num_bcc
|| shown
== 0)) {
435 /* this means that the only recipients are Bcc's */
436 if (!ask_confirm_for_only_bcc (composer
, shown
== 0))
443 if (recipients
!= NULL
)
444 e_destination_freev (recipients
);
450 composer_presend_check_identity (EMsgComposer
*composer
,
451 EMailSession
*session
)
453 EComposerHeaderTable
*table
;
454 ESource
*source
= NULL
;
456 gboolean success
= TRUE
;
458 table
= e_msg_composer_get_header_table (composer
);
460 uid
= e_composer_header_table_dup_identity_uid (table
, NULL
, NULL
);
462 source
= e_composer_header_table_ref_source (table
, uid
);
466 EClientCache
*client_cache
;
467 ESourceRegistry
*registry
;
469 client_cache
= e_composer_header_table_ref_client_cache (table
);
470 registry
= e_client_cache_ref_registry (client_cache
);
472 success
= e_source_registry_check_enabled (registry
, source
);
475 E_ALERT_SINK (e_msg_composer_get_editor (composer
)),
476 "mail:send-no-account-enabled", NULL
);
479 g_object_unref (client_cache
);
480 g_object_unref (registry
);
484 E_ALERT_SINK (e_msg_composer_get_editor (composer
)),
485 "mail:send-no-account", NULL
);
489 g_clear_object (&source
);
495 composer_presend_check_plugins (EMsgComposer
*composer
,
496 EMailSession
*session
)
499 EMEventTargetComposer
*target
;
502 /** @Event: composer.presendchecks
503 * @Title: Composer PreSend Checks
504 * @Target: EMEventTargetMessage
506 * composer.presendchecks is emitted during pre-checks for the
507 * message just before sending. Since the e-plugin framework
508 * doesn't provide a way to return a value from the plugin,
509 * use 'presend_check_status' to set whether the check passed.
511 eme
= em_event_peek ();
512 target
= em_event_target_new_composer (eme
, composer
, 0);
515 (EEvent
*) eme
, "composer.presendchecks",
516 (EEventTarget
*) target
);
518 /* A non-NULL value for this key means the check failed. */
519 data
= g_object_get_data (G_OBJECT (composer
), "presend_check_status");
521 /* Clear the value in case we have to run these checks again. */
522 g_object_set_data (G_OBJECT (composer
), "presend_check_status", NULL
);
524 return (data
== NULL
);
528 composer_presend_check_subject (EMsgComposer
*composer
,
529 EMailSession
*session
)
531 EComposerHeaderTable
*table
;
532 const gchar
*subject
;
533 gboolean check_passed
= TRUE
;
535 table
= e_msg_composer_get_header_table (composer
);
536 subject
= e_composer_header_table_get_subject (table
);
538 if (subject
== NULL
|| subject
[0] == '\0') {
539 if (!ask_confirm_for_empty_subject (composer
))
540 check_passed
= FALSE
;
547 composer_presend_check_unwanted_html (EMsgComposer
*composer
,
548 EMailSession
*session
)
550 EDestination
**recipients
;
552 EContentEditor
*cnt_editor
;
553 EComposerHeaderTable
*table
;
555 gboolean check_passed
= TRUE
;
558 gboolean confirm_html
;
561 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
563 editor
= e_msg_composer_get_editor (composer
);
564 cnt_editor
= e_html_editor_get_content_editor (editor
);
565 html_mode
= e_content_editor_get_html_mode (cnt_editor
);
567 table
= e_msg_composer_get_header_table (composer
);
568 recipients
= e_composer_header_table_get_destinations (table
);
570 send_html
= g_settings_get_boolean (settings
, "composer-send-html");
571 confirm_html
= g_settings_get_boolean (settings
, "prompt-on-unwanted-html");
573 /* Only show this warning if our default is to send html. If it
574 * isn't, we've manually switched into html mode in the composer
575 * and (presumably) had a good reason for doing this. */
576 if (html_mode
&& send_html
&& confirm_html
&& recipients
!= NULL
) {
577 gboolean html_problem
= FALSE
;
579 for (ii
= 0; recipients
[ii
] != NULL
; ii
++) {
580 if (!e_destination_get_html_mail_pref (recipients
[ii
])) {
587 if (!ask_confirm_for_unwanted_html_mail (
588 composer
, recipients
))
589 check_passed
= FALSE
;
593 if (recipients
!= NULL
)
594 e_destination_freev (recipients
);
596 g_object_unref (settings
);
602 composer_send_completed (GObject
*source_object
,
603 GAsyncResult
*result
,
607 gboolean service_unavailable
;
608 gboolean set_changed
= FALSE
;
609 AsyncContext
*async_context
;
610 GError
*local_error
= NULL
;
612 async_context
= (AsyncContext
*) user_data
;
614 activity
= async_context
->activity
;
616 e_mail_session_send_to_finish (
617 E_MAIL_SESSION (source_object
), result
, &local_error
);
619 if (e_activity_handle_cancellation (activity
, local_error
)) {
624 /* Check for error codes which may indicate we're offline
625 * or name resolution failed or connection attempt failed. */
626 service_unavailable
=
628 local_error
, CAMEL_SERVICE_ERROR
,
629 CAMEL_SERVICE_ERROR_UNAVAILABLE
) ||
630 /* name resolution failed */
632 local_error
, G_RESOLVER_ERROR
,
633 G_RESOLVER_ERROR_NOT_FOUND
) ||
635 local_error
, G_RESOLVER_ERROR
,
636 G_RESOLVER_ERROR_TEMPORARY_FAILURE
) ||
637 /* something internal to Camel failed */
639 local_error
, CAMEL_SERVICE_ERROR
,
640 CAMEL_SERVICE_ERROR_URL_INVALID
);
641 if (service_unavailable
) {
642 /* Inform the user. */
643 e_alert_run_dialog_for_args (
644 GTK_WINDOW (async_context
->composer
),
645 "mail-composer:saving-to-outbox", NULL
);
646 e_msg_composer_save_to_outbox (async_context
->composer
);
650 /* Post-processing errors are shown in the shell window. */
651 if (g_error_matches (
652 local_error
, E_MAIL_ERROR
,
653 E_MAIL_ERROR_POST_PROCESSING
)) {
657 shell
= e_msg_composer_get_shell (async_context
->composer
);
659 alert
= e_alert_new (
660 "mail-composer:send-post-processing-error",
661 local_error
->message
, NULL
);
662 e_shell_submit_alert (shell
, alert
);
663 g_object_unref (alert
);
665 /* All other errors are shown in the composer window. */
666 } else if (local_error
!= NULL
) {
669 /* Clear the activity bar before
670 * presenting the error dialog. */
671 g_clear_object (&async_context
->activity
);
672 activity
= async_context
->activity
;
674 response
= e_alert_run_dialog_for_args (
675 GTK_WINDOW (async_context
->composer
),
676 "mail-composer:send-error",
677 local_error
->message
, NULL
);
678 if (response
== GTK_RESPONSE_OK
) /* Try Again */
679 e_msg_composer_send (async_context
->composer
);
680 if (response
== GTK_RESPONSE_ACCEPT
) /* Save to Outbox */
681 e_msg_composer_save_to_outbox (async_context
->composer
);
686 e_activity_set_state (activity
, E_ACTIVITY_COMPLETED
);
688 /* Wait for the EActivity's completion message to
689 * time out and then destroy the composer window. */
691 G_OBJECT (activity
), (GWeakNotify
)
692 gtk_widget_destroy
, async_context
->composer
);
695 g_clear_error (&local_error
);
699 EContentEditor
*cnt_editor
;
701 editor
= e_msg_composer_get_editor (async_context
->composer
);
702 cnt_editor
= e_html_editor_get_content_editor (editor
);
704 e_content_editor_set_changed (cnt_editor
, TRUE
);
706 gtk_window_present (GTK_WINDOW (async_context
->composer
));
709 async_context_free (async_context
);
713 em_utils_composer_real_send (EMsgComposer
*composer
,
714 CamelMimeMessage
*message
,
716 EMailSession
*session
)
718 AsyncContext
*async_context
;
719 GCancellable
*cancellable
;
722 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
723 if (g_settings_get_boolean (settings
, "composer-use-outbox")) {
724 e_msg_composer_save_to_outbox (composer
);
725 g_object_unref (settings
);
729 g_object_unref (settings
);
731 if (!camel_session_get_online (CAMEL_SESSION (session
))) {
732 e_alert_run_dialog_for_args (
733 GTK_WINDOW (composer
),
734 "mail-composer:saving-to-outbox", NULL
);
735 e_msg_composer_save_to_outbox (composer
);
739 async_context
= g_slice_new0 (AsyncContext
);
740 async_context
->message
= g_object_ref (message
);
741 async_context
->composer
= g_object_ref (composer
);
742 async_context
->activity
= g_object_ref (activity
);
744 cancellable
= e_activity_get_cancellable (activity
);
746 e_mail_session_send_to (
749 cancellable
, NULL
, NULL
,
750 composer_send_completed
,
755 composer_num_loading_notify_cb (EAttachmentStore
*store
,
757 AsyncContext
*async_context
)
759 if (e_attachment_store_get_num_loading (store
) > 0)
762 em_utils_composer_real_send (
763 async_context
->composer
,
764 async_context
->message
,
765 async_context
->activity
,
766 async_context
->session
);
768 async_context_free (async_context
);
772 composer_wait_for_attachment_load_cancelled_cb (GCancellable
*cancellable
,
773 AsyncContext
*async_context
)
775 async_context_free (async_context
);
779 em_utils_composer_send_cb (EMsgComposer
*composer
,
780 CamelMimeMessage
*message
,
782 EMailSession
*session
)
784 AsyncContext
*async_context
;
785 GCancellable
*cancellable
;
786 EAttachmentView
*view
;
787 EAttachmentStore
*store
;
789 view
= e_msg_composer_get_attachment_view (composer
);
790 store
= e_attachment_view_get_store (view
);
792 if (e_attachment_store_get_num_loading (store
) <= 0) {
793 em_utils_composer_real_send (composer
, message
, activity
, session
);
797 async_context
= g_slice_new0 (AsyncContext
);
798 async_context
->session
= g_object_ref (session
);
799 async_context
->message
= g_object_ref (message
);
800 async_context
->composer
= g_object_ref (composer
);
801 async_context
->activity
= g_object_ref (activity
);
803 cancellable
= e_activity_get_cancellable (activity
);
804 /* This message is never removed from the camel operation, otherwise the GtkInfoBar
805 hides itself and the user sees no feedback. */
806 camel_operation_push_message (cancellable
, "%s", _("Waiting for attachments to load..."));
808 async_context
->num_loading_handler_id
= e_signal_connect_notify (store
, "notify::num-loading",
809 G_CALLBACK (composer_num_loading_notify_cb
), async_context
);
810 /* Cannot use g_cancellable_connect() here, see async_context_free() */
811 async_context
->cancelled_handler_id
= g_signal_connect (cancellable
, "cancelled",
812 G_CALLBACK (composer_wait_for_attachment_load_cancelled_cb
), async_context
);
816 composer_set_no_change (EMsgComposer
*composer
)
819 EContentEditor
*cnt_editor
;
821 g_return_if_fail (composer
!= NULL
);
823 editor
= e_msg_composer_get_editor (composer
);
824 cnt_editor
= e_html_editor_get_content_editor (editor
);
826 e_content_editor_set_changed (cnt_editor
, FALSE
);
829 /* delete original messages from Outbox folder */
831 manage_x_evolution_replace_outbox (EMsgComposer
*composer
,
832 EMailSession
*session
,
833 CamelMimeMessage
*message
,
834 GCancellable
*cancellable
)
836 const gchar
*message_uid
;
840 g_return_if_fail (composer
!= NULL
);
841 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
843 header
= "X-Evolution-Replace-Outbox-UID";
844 message_uid
= camel_medium_get_header (CAMEL_MEDIUM (message
), header
);
845 e_msg_composer_remove_header (composer
, header
);
850 outbox
= e_mail_session_get_local_folder (
851 session
, E_MAIL_LOCAL_FOLDER_OUTBOX
);
852 g_return_if_fail (outbox
!= NULL
);
854 camel_folder_set_message_flags (
856 CAMEL_MESSAGE_DELETED
| CAMEL_MESSAGE_SEEN
,
857 CAMEL_MESSAGE_DELETED
| CAMEL_MESSAGE_SEEN
);
859 /* ignore errors here */
860 camel_folder_synchronize_message_sync (
861 outbox
, message_uid
, cancellable
, NULL
);
865 composer_save_to_drafts_complete (GObject
*source_object
,
866 GAsyncResult
*result
,
870 AsyncContext
*async_context
;
872 EContentEditor
*cnt_editor
;
873 GError
*local_error
= NULL
;
875 async_context
= (AsyncContext
*) user_data
;
877 editor
= e_msg_composer_get_editor (async_context
->composer
);
878 cnt_editor
= e_html_editor_get_content_editor (editor
);
880 /* We don't really care if this failed. If something other than
881 * cancellation happened, emit a runtime warning so the error is
882 * not completely lost. */
883 e_mail_session_handle_draft_headers_finish (
884 E_MAIL_SESSION (source_object
), result
, &local_error
);
886 activity
= async_context
->activity
;
888 if (e_activity_handle_cancellation (activity
, local_error
)) {
889 e_content_editor_set_changed (cnt_editor
, TRUE
);
890 g_error_free (local_error
);
892 } else if (local_error
!= NULL
) {
893 e_content_editor_set_changed (cnt_editor
, TRUE
);
894 g_warning ("%s", local_error
->message
);
895 g_error_free (local_error
);
897 e_activity_set_state (activity
, E_ACTIVITY_COMPLETED
);
899 /* Encode the draft message we just saved into the EMsgComposer
900 * as X-Evolution-Draft headers. The message will be marked for
901 * deletion if the user saves a newer draft message or sends the
902 * composed message. */
903 e_msg_composer_set_draft_headers (
904 async_context
->composer
,
905 async_context
->folder_uri
,
906 async_context
->message_uid
);
908 e_content_editor_set_changed (cnt_editor
, FALSE
);
910 async_context_free (async_context
);
914 composer_save_to_drafts_append_mail (AsyncContext
*async_context
,
915 CamelFolder
*drafts_folder
);
918 composer_save_to_drafts_cleanup (GObject
*source_object
,
919 GAsyncResult
*result
,
922 CamelSession
*session
;
924 EAlertSink
*alert_sink
;
925 GCancellable
*cancellable
;
927 EContentEditor
*cnt_editor
;
928 AsyncContext
*async_context
;
929 GError
*local_error
= NULL
;
931 async_context
= (AsyncContext
*) user_data
;
933 editor
= e_msg_composer_get_editor (async_context
->composer
);
934 cnt_editor
= e_html_editor_get_content_editor (editor
);
936 activity
= async_context
->activity
;
937 alert_sink
= e_activity_get_alert_sink (activity
);
938 cancellable
= e_activity_get_cancellable (activity
);
940 e_mail_folder_append_message_finish (
941 CAMEL_FOLDER (source_object
), result
,
942 &async_context
->message_uid
, &local_error
);
944 if (e_activity_handle_cancellation (activity
, local_error
)) {
945 g_warn_if_fail (async_context
->message_uid
== NULL
);
946 e_content_editor_set_changed (cnt_editor
, TRUE
);
947 async_context_free (async_context
);
948 g_error_free (local_error
);
951 } else if (local_error
!= NULL
) {
952 g_warn_if_fail (async_context
->message_uid
== NULL
);
954 if (e_msg_composer_is_exiting (async_context
->composer
)) {
957 /* If we can't retrieve the Drafts folder for the
958 * selected account, ask the user if he wants to
959 * save to the local Drafts folder instead. */
960 response
= e_alert_run_dialog_for_args (
961 GTK_WINDOW (async_context
->composer
),
962 "mail:ask-default-drafts", local_error
->message
, NULL
);
963 if (response
!= GTK_RESPONSE_YES
) {
964 e_content_editor_set_changed (cnt_editor
, TRUE
);
965 async_context_free (async_context
);
967 composer_save_to_drafts_append_mail (async_context
, NULL
);
970 g_error_free (local_error
);
976 "mail-composer:save-to-drafts-error",
977 local_error
->message
, NULL
);
978 e_content_editor_set_changed (cnt_editor
, TRUE
);
979 async_context_free (async_context
);
980 g_error_free (local_error
);
984 session
= e_msg_composer_ref_session (async_context
->composer
);
986 /* Mark the previously saved draft message for deletion.
987 * Note: This is just a nice-to-have; ignore failures. */
988 e_mail_session_handle_draft_headers (
989 E_MAIL_SESSION (session
),
990 async_context
->message
,
991 G_PRIORITY_DEFAULT
, cancellable
,
992 composer_save_to_drafts_complete
,
995 g_object_unref (session
);
999 composer_save_to_drafts_append_mail (AsyncContext
*async_context
,
1000 CamelFolder
*drafts_folder
)
1002 CamelFolder
*local_drafts_folder
;
1003 GCancellable
*cancellable
;
1004 CamelMessageInfo
*info
;
1006 local_drafts_folder
=
1007 e_mail_session_get_local_folder (
1008 async_context
->session
, E_MAIL_LOCAL_FOLDER_DRAFTS
);
1010 if (drafts_folder
== NULL
)
1011 drafts_folder
= g_object_ref (local_drafts_folder
);
1013 cancellable
= e_activity_get_cancellable (async_context
->activity
);
1015 info
= camel_message_info_new (NULL
);
1017 camel_message_info_set_flags (info
, CAMEL_MESSAGE_DRAFT
| CAMEL_MESSAGE_SEEN
|
1018 (camel_mime_message_has_attachment (async_context
->message
) ? CAMEL_MESSAGE_ATTACHMENTS
: 0), ~0);
1020 camel_medium_remove_header (
1021 CAMEL_MEDIUM (async_context
->message
),
1022 "X-Evolution-Replace-Outbox-UID");
1024 e_mail_folder_append_message (
1025 drafts_folder
, async_context
->message
,
1026 info
, G_PRIORITY_DEFAULT
, cancellable
,
1027 composer_save_to_drafts_cleanup
,
1030 g_clear_object (&info
);
1032 g_object_unref (drafts_folder
);
1036 composer_save_to_drafts_got_folder (GObject
*source_object
,
1037 GAsyncResult
*result
,
1040 EActivity
*activity
;
1041 CamelFolder
*drafts_folder
;
1042 EHTMLEditor
*editor
;
1043 EContentEditor
*cnt_editor
;
1044 AsyncContext
*async_context
;
1045 GError
*local_error
= NULL
;
1047 async_context
= (AsyncContext
*) user_data
;
1049 activity
= async_context
->activity
;
1051 editor
= e_msg_composer_get_editor (async_context
->composer
);
1052 cnt_editor
= e_html_editor_get_content_editor (editor
);
1054 drafts_folder
= e_mail_session_uri_to_folder_finish (
1055 E_MAIL_SESSION (source_object
), result
, &local_error
);
1059 ((drafts_folder
!= NULL
) && (local_error
== NULL
)) ||
1060 ((drafts_folder
== NULL
) && (local_error
!= NULL
)));
1062 if (e_activity_handle_cancellation (activity
, local_error
)) {
1063 e_content_editor_set_changed (cnt_editor
, TRUE
);
1064 async_context_free (async_context
);
1065 g_error_free (local_error
);
1068 } else if (local_error
!= NULL
) {
1071 /* If we can't retrieve the Drafts folder for the
1072 * selected account, ask the user if he wants to
1073 * save to the local Drafts folder instead. */
1074 response
= e_alert_run_dialog_for_args (
1075 GTK_WINDOW (async_context
->composer
),
1076 "mail:ask-default-drafts", local_error
->message
, NULL
);
1078 g_error_free (local_error
);
1080 if (response
!= GTK_RESPONSE_YES
) {
1081 e_content_editor_set_changed (cnt_editor
, TRUE
);
1082 async_context_free (async_context
);
1087 composer_save_to_drafts_append_mail (async_context
, drafts_folder
);
1091 em_utils_composer_save_to_drafts_cb (EMsgComposer
*composer
,
1092 CamelMimeMessage
*message
,
1093 EActivity
*activity
,
1094 EMailSession
*session
)
1096 AsyncContext
*async_context
;
1097 EComposerHeaderTable
*table
;
1099 const gchar
*local_drafts_folder_uri
;
1100 gchar
*identity_uid
;
1101 gchar
*drafts_folder_uri
= NULL
;
1103 async_context
= g_slice_new0 (AsyncContext
);
1104 async_context
->message
= g_object_ref (message
);
1105 async_context
->session
= g_object_ref (session
);
1106 async_context
->composer
= g_object_ref (composer
);
1107 async_context
->activity
= g_object_ref (activity
);
1109 table
= e_msg_composer_get_header_table (composer
);
1111 identity_uid
= e_composer_header_table_dup_identity_uid (table
, NULL
, NULL
);
1112 source
= e_composer_header_table_ref_source (table
, identity_uid
);
1114 /* Get the selected identity's preferred Drafts folder. */
1115 if (source
!= NULL
) {
1116 ESourceMailComposition
*extension
;
1117 const gchar
*extension_name
;
1120 extension_name
= E_SOURCE_EXTENSION_MAIL_COMPOSITION
;
1121 extension
= e_source_get_extension (source
, extension_name
);
1122 uri
= e_source_mail_composition_dup_drafts_folder (extension
);
1124 drafts_folder_uri
= uri
;
1126 g_object_unref (source
);
1129 local_drafts_folder_uri
=
1130 e_mail_session_get_local_folder_uri (
1131 session
, E_MAIL_LOCAL_FOLDER_DRAFTS
);
1133 if (drafts_folder_uri
== NULL
) {
1134 async_context
->folder_uri
= g_strdup (local_drafts_folder_uri
);
1135 composer_save_to_drafts_append_mail (async_context
, NULL
);
1137 GCancellable
*cancellable
;
1139 cancellable
= e_activity_get_cancellable (activity
);
1140 async_context
->folder_uri
= g_strdup (drafts_folder_uri
);
1142 e_mail_session_uri_to_folder (
1143 session
, drafts_folder_uri
, 0,
1144 G_PRIORITY_DEFAULT
, cancellable
,
1145 composer_save_to_drafts_got_folder
,
1148 g_free (drafts_folder_uri
);
1151 g_free (identity_uid
);
1155 composer_save_to_outbox_completed (GObject
*source_object
,
1156 GAsyncResult
*result
,
1159 EMailSession
*session
;
1160 EActivity
*activity
;
1161 EAlertSink
*alert_sink
;
1162 GCancellable
*cancellable
;
1163 AsyncContext
*async_context
;
1164 GSettings
*settings
;
1165 GError
*local_error
= NULL
;
1167 session
= E_MAIL_SESSION (source_object
);
1168 async_context
= (AsyncContext
*) user_data
;
1170 activity
= async_context
->activity
;
1171 alert_sink
= e_activity_get_alert_sink (activity
);
1172 cancellable
= e_activity_get_cancellable (activity
);
1174 e_mail_session_append_to_local_folder_finish (
1175 session
, result
, NULL
, &local_error
);
1177 if (e_activity_handle_cancellation (activity
, local_error
)) {
1178 g_error_free (local_error
);
1181 } else if (local_error
!= NULL
) {
1184 "mail-composer:append-to-outbox-error",
1185 local_error
->message
, NULL
);
1186 g_error_free (local_error
);
1190 /* special processing for Outbox folder */
1191 manage_x_evolution_replace_outbox (
1192 async_context
->composer
,
1194 async_context
->message
,
1197 e_activity_set_state (activity
, E_ACTIVITY_COMPLETED
);
1199 /* Wait for the EActivity's completion message to
1200 * time out and then destroy the composer window. */
1202 G_OBJECT (activity
), (GWeakNotify
)
1203 gtk_widget_destroy
, async_context
->composer
);
1205 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
1206 if (g_settings_get_boolean (settings
, "composer-use-outbox")) {
1207 gint delay_flush
= g_settings_get_int (settings
, "composer-delay-outbox-flush");
1209 if (delay_flush
== 0) {
1210 e_mail_session_flush_outbox (session
);
1211 } else if (delay_flush
> 0) {
1212 e_mail_session_schedule_outbox_flush (session
, delay_flush
);
1215 g_object_unref (settings
);
1218 async_context_free (async_context
);
1222 em_utils_composer_save_to_outbox_cb (EMsgComposer
*composer
,
1223 CamelMimeMessage
*message
,
1224 EActivity
*activity
,
1225 EMailSession
*session
)
1227 AsyncContext
*async_context
;
1228 CamelMessageInfo
*info
;
1229 GCancellable
*cancellable
;
1231 async_context
= g_slice_new0 (AsyncContext
);
1232 async_context
->message
= g_object_ref (message
);
1233 async_context
->composer
= g_object_ref (composer
);
1234 async_context
->activity
= g_object_ref (activity
);
1236 cancellable
= e_activity_get_cancellable (activity
);
1238 info
= camel_message_info_new (NULL
);
1239 camel_message_info_set_flags (info
, CAMEL_MESSAGE_SEEN
, ~0);
1241 e_mail_session_append_to_local_folder (
1242 session
, E_MAIL_LOCAL_FOLDER_OUTBOX
,
1243 message
, info
, G_PRIORITY_DEFAULT
, cancellable
,
1244 composer_save_to_outbox_completed
,
1247 g_clear_object (&info
);
1250 typedef struct _PrintAsyncContext
{
1251 GMainLoop
*main_loop
;
1253 } PrintAsyncContext
;
1256 em_composer_utils_print_done_cb (GObject
*source_object
,
1257 GAsyncResult
*result
,
1260 PrintAsyncContext
*async_context
= user_data
;
1262 g_return_if_fail (E_IS_MAIL_PRINTER (source_object
));
1263 g_return_if_fail (async_context
!= NULL
);
1264 g_return_if_fail (async_context
->main_loop
!= NULL
);
1266 e_mail_printer_print_finish (E_MAIL_PRINTER (source_object
), result
, &(async_context
->error
));
1268 g_main_loop_quit (async_context
->main_loop
);
1272 em_utils_composer_print_cb (EMsgComposer
*composer
,
1273 GtkPrintOperationAction action
,
1274 CamelMimeMessage
*message
,
1275 EActivity
*activity
,
1276 EMailSession
*session
)
1278 EMailParser
*parser
;
1279 EMailPartList
*parts
, *reserved_parts
;
1280 EMailPrinter
*printer
;
1281 EMailBackend
*mail_backend
;
1282 const gchar
*message_id
;
1283 GCancellable
*cancellable
;
1284 CamelObjectBag
*parts_registry
;
1286 PrintAsyncContext async_context
;
1288 mail_backend
= E_MAIL_BACKEND (e_shell_get_backend_by_name (e_msg_composer_get_shell (composer
), "mail"));
1289 g_return_if_fail (mail_backend
!= NULL
);
1291 cancellable
= e_activity_get_cancellable (activity
);
1292 parser
= e_mail_parser_new (CAMEL_SESSION (session
));
1294 message_id
= camel_mime_message_get_message_id (message
);
1295 parts
= e_mail_parser_parse_sync (parser
, NULL
, message_id
, message
, cancellable
);
1297 g_clear_object (&parser
);
1301 parts_registry
= e_mail_part_list_get_registry ();
1303 mail_uri
= e_mail_part_build_uri (NULL
, message_id
, NULL
, NULL
);
1304 reserved_parts
= camel_object_bag_reserve (parts_registry
, mail_uri
);
1305 g_clear_object (&reserved_parts
);
1307 camel_object_bag_add (parts_registry
, mail_uri
, parts
);
1309 printer
= e_mail_printer_new (parts
, e_mail_backend_get_remote_content (mail_backend
));
1311 async_context
.error
= NULL
;
1312 async_context
.main_loop
= g_main_loop_new (NULL
, FALSE
);
1314 /* Cannot use EAsyncClosure here, it blocks the main context, which is not good here. */
1315 e_mail_printer_print (printer
, action
, NULL
, cancellable
, em_composer_utils_print_done_cb
, &async_context
);
1317 g_main_loop_run (async_context
.main_loop
);
1319 camel_object_bag_remove (parts_registry
, parts
);
1320 g_main_loop_unref (async_context
.main_loop
);
1321 g_object_unref (printer
);
1322 g_object_unref (parts
);
1325 if (e_activity_handle_cancellation (activity
, async_context
.error
)) {
1326 g_error_free (async_context
.error
);
1327 } else if (async_context
.error
!= NULL
) {
1329 e_activity_get_alert_sink (activity
),
1330 "mail-composer:no-build-message",
1331 async_context
.error
->message
, NULL
);
1332 g_error_free (async_context
.error
);
1337 compare_sources_with_uids_order_cb (gconstpointer a
,
1341 ESource
*asource
= (ESource
*) a
;
1342 ESource
*bsource
= (ESource
*) b
;
1343 GHashTable
*uids_order
= user_data
;
1344 gint aindex
, bindex
;
1346 aindex
= GPOINTER_TO_INT (g_hash_table_lookup (uids_order
, e_source_get_uid (asource
)));
1347 bindex
= GPOINTER_TO_INT (g_hash_table_lookup (uids_order
, e_source_get_uid (bsource
)));
1350 aindex
= g_hash_table_size (uids_order
);
1352 bindex
= g_hash_table_size (uids_order
);
1354 return aindex
- bindex
;
1358 sort_sources_by_ui (GList
**psources
,
1361 EShell
*shell
= user_data
;
1362 EShellBackend
*shell_backend
;
1363 EMailSession
*mail_session
;
1364 EMailAccountStore
*account_store
;
1365 GtkTreeModel
*model
;
1367 GHashTable
*uids_order
;
1370 g_return_if_fail (psources
!= NULL
);
1371 g_return_if_fail (E_IS_SHELL (shell
));
1373 /* nothing to sort */
1374 if (!*psources
|| !g_list_next (*psources
))
1377 shell_backend
= e_shell_get_backend_by_name (shell
, "mail");
1378 g_return_if_fail (shell_backend
!= NULL
);
1380 mail_session
= e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend
));
1381 g_return_if_fail (mail_session
!= NULL
);
1383 account_store
= e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session
));
1384 g_return_if_fail (account_store
!= NULL
);
1386 model
= GTK_TREE_MODEL (account_store
);
1387 if (!gtk_tree_model_get_iter_first (model
, &iter
))
1390 uids_order
= g_hash_table_new_full (g_str_hash
, g_str_equal
, g_free
, NULL
);
1393 CamelService
*service
= NULL
;
1395 gtk_tree_model_get (model
, &iter
, E_MAIL_ACCOUNT_STORE_COLUMN_SERVICE
, &service
, -1);
1399 g_hash_table_insert (uids_order
, g_strdup (camel_service_get_uid (service
)), GINT_TO_POINTER (index
));
1400 g_object_unref (service
);
1402 } while (gtk_tree_model_iter_next (model
, &iter
));
1404 *psources
= g_list_sort_with_data (*psources
, compare_sources_with_uids_order_cb
, uids_order
);
1406 g_hash_table_destroy (uids_order
);
1409 /* Composing messages... */
1411 static CamelMimeMessage
*em_utils_get_composer_recipients_as_message (EMsgComposer
*composer
);
1414 set_up_new_composer (EMsgComposer
*composer
,
1415 const gchar
*subject
,
1416 CamelFolder
*folder
,
1417 CamelMimeMessage
*message
,
1418 const gchar
*message_uid
)
1420 EClientCache
*client_cache
;
1421 ESourceRegistry
*registry
;
1422 EComposerHeaderTable
*table
;
1423 ESource
*source
= NULL
;
1424 gchar
*identity
= NULL
, *identity_name
= NULL
, *identity_address
= NULL
;
1426 table
= e_msg_composer_get_header_table (composer
);
1428 client_cache
= e_composer_header_table_ref_client_cache (table
);
1429 registry
= e_client_cache_ref_registry (client_cache
);
1431 if (folder
!= NULL
) {
1436 g_object_ref (message
);
1437 } else if (message_uid
) {
1438 message
= em_utils_get_composer_recipients_as_message (composer
);
1444 shell
= e_msg_composer_get_shell (composer
);
1446 /* Check send account override for the passed-in folder */
1447 source
= em_utils_check_send_account_override (shell
, message
, folder
, &identity_name
, &identity_address
);
1449 /* If not set and it's a search folder, then check the original folder */
1450 if (!source
&& message_uid
&& CAMEL_IS_VEE_FOLDER (folder
)) {
1451 CamelMessageInfo
*mi
= camel_folder_get_message_info (folder
, message_uid
);
1453 CamelFolder
*location
;
1455 location
= camel_vee_folder_get_location (CAMEL_VEE_FOLDER (folder
), (CamelVeeMessageInfo
*) mi
, NULL
);
1457 source
= em_utils_check_send_account_override (shell
, message
, location
, &identity_name
, &identity_address
);
1458 g_clear_object (&mi
);
1462 /* If no send account override, then guess */
1464 source
= em_utils_guess_mail_identity_with_recipients_and_sort (
1465 registry
, message
, folder
, message_uid
, &identity_name
, &identity_address
, sort_sources_by_ui
, shell
);
1469 /* In case of search folder, try to guess the store from
1470 the internal folders of it. If they are all from the same
1471 store, then use that store. */
1472 if (!source
&& CAMEL_IS_VEE_FOLDER (folder
)) {
1473 GHashTable
*stores
, *done_folders
;
1476 stores
= g_hash_table_new_full (g_direct_hash
, g_direct_equal
, g_object_unref
, NULL
);
1477 done_folders
= g_hash_table_new (g_direct_hash
, g_direct_equal
);
1479 todo
= g_slist_prepend (NULL
, g_object_ref (folder
));
1482 CamelVeeFolder
*vfolder
= todo
->data
;
1484 todo
= g_slist_remove (todo
, vfolder
);
1485 if (!g_hash_table_contains (done_folders
, vfolder
)) {
1486 GList
*folders
, *llink
;
1488 g_hash_table_insert (done_folders
, vfolder
, NULL
);
1490 folders
= camel_vee_folder_ref_folders (vfolder
);
1491 for (llink
= folders
; llink
; llink
= g_list_next (llink
)) {
1492 CamelFolder
*subfolder
= llink
->data
;
1494 if (!g_hash_table_contains (done_folders
, subfolder
)) {
1495 if (CAMEL_IS_VEE_FOLDER (subfolder
)) {
1496 todo
= g_slist_prepend (todo
, g_object_ref (subfolder
));
1498 CamelStore
*store
= camel_folder_get_parent_store (subfolder
);
1500 g_hash_table_insert (done_folders
, subfolder
, NULL
);
1503 g_hash_table_insert (stores
, g_object_ref (store
), NULL
);
1505 if (g_hash_table_size (stores
) > 1) {
1506 g_slist_free_full (todo
, g_object_unref
);
1515 g_list_free_full (folders
, g_object_unref
);
1518 g_object_unref (vfolder
);
1521 if (g_hash_table_size (stores
) == 1) {
1522 GHashTableIter iter
;
1525 g_hash_table_iter_init (&iter
, stores
);
1526 if (g_hash_table_iter_next (&iter
, &store
, NULL
) && store
)
1527 source
= em_utils_ref_mail_identity_for_store (registry
, store
);
1530 g_slist_free_full (todo
, g_object_unref
);
1531 g_hash_table_destroy (done_folders
);
1532 g_hash_table_destroy (stores
);
1535 g_clear_object (&message
);
1540 store
= camel_folder_get_parent_store (folder
);
1541 source
= em_utils_ref_mail_identity_for_store (registry
, store
);
1544 folder_uri
= e_mail_folder_uri_from_folder (folder
);
1546 list
= g_list_prepend (NULL
, folder_uri
);
1547 e_composer_header_table_set_post_to_list (table
, list
);
1550 g_free (folder_uri
);
1553 if (source
!= NULL
) {
1554 identity
= e_source_dup_uid (source
);
1555 g_object_unref (source
);
1559 e_composer_header_table_set_subject (table
, subject
);
1560 e_composer_header_table_set_identity_uid (table
, identity
, identity_name
, identity_address
);
1562 em_utils_apply_send_account_override_to_composer (composer
, folder
);
1565 g_free (identity_name
);
1566 g_free (identity_address
);
1568 g_object_unref (client_cache
);
1569 g_object_unref (registry
);
1573 * em_utils_compose_new_message:
1574 * @composer: an #EMsgComposer
1575 * @folder: (nullable): a #CamelFolder, or %NULL
1577 * Sets up a new @composer window.
1579 * See: em_utils_compose_new_message_with_selection()
1584 em_utils_compose_new_message (EMsgComposer
*composer
,
1585 CamelFolder
*folder
)
1587 em_utils_compose_new_message_with_selection (composer
, folder
, NULL
);
1591 * em_utils_compose_new_message_with_selection:
1592 * @composer: an #EMsgComposer
1593 * @folder: (nullable): a #CamelFolder, or %NULL
1594 * @message_uid: (nullable): a UID of the selected message, or %NULL
1596 * Sets up a new @composer window, similar to em_utils_compose_new_message(),
1597 * but also tries to identify From account more precisely, when the @folder
1598 * is a search folder.
1603 em_utils_compose_new_message_with_selection (EMsgComposer
*composer
,
1604 CamelFolder
*folder
,
1605 const gchar
*message_uid
)
1607 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
1610 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
1612 set_up_new_composer (composer
, "", folder
, NULL
, message_uid
);
1613 composer_set_no_change (composer
);
1615 gtk_widget_show (GTK_WIDGET (composer
));
1618 static CamelMimeMessage
*
1619 em_utils_get_composer_recipients_as_message (EMsgComposer
*composer
)
1621 CamelMimeMessage
*message
;
1622 EComposerHeaderTable
*table
;
1623 EComposerHeader
*header
;
1624 EDestination
**destv
;
1625 CamelInternetAddress
*to_addr
, *cc_addr
, *bcc_addr
, *dest_addr
;
1626 const gchar
*text_addr
;
1629 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), NULL
);
1631 table
= e_msg_composer_get_header_table (composer
);
1632 header
= e_composer_header_table_get_header (table
, E_COMPOSER_HEADER_TO
);
1634 if (!e_composer_header_get_visible (header
))
1637 message
= camel_mime_message_new ();
1639 to_addr
= camel_internet_address_new ();
1640 cc_addr
= camel_internet_address_new ();
1641 bcc_addr
= camel_internet_address_new ();
1644 dest_addr
= to_addr
;
1645 destv
= e_composer_header_table_get_destinations_to (table
);
1646 for (ii
= 0; destv
!= NULL
&& destv
[ii
] != NULL
; ii
++) {
1647 text_addr
= e_destination_get_address (destv
[ii
]);
1648 if (text_addr
&& *text_addr
) {
1649 if (camel_address_decode (CAMEL_ADDRESS (dest_addr
), text_addr
) <= 0)
1650 camel_internet_address_add (dest_addr
, "", text_addr
);
1653 e_destination_freev (destv
);
1656 dest_addr
= cc_addr
;
1657 destv
= e_composer_header_table_get_destinations_cc (table
);
1658 for (ii
= 0; destv
!= NULL
&& destv
[ii
] != NULL
; ii
++) {
1659 text_addr
= e_destination_get_address (destv
[ii
]);
1660 if (text_addr
&& *text_addr
) {
1661 if (camel_address_decode (CAMEL_ADDRESS (dest_addr
), text_addr
) <= 0)
1662 camel_internet_address_add (dest_addr
, "", text_addr
);
1665 e_destination_freev (destv
);
1668 dest_addr
= bcc_addr
;
1669 destv
= e_composer_header_table_get_destinations_bcc (table
);
1670 for (ii
= 0; destv
!= NULL
&& destv
[ii
] != NULL
; ii
++) {
1671 text_addr
= e_destination_get_address (destv
[ii
]);
1672 if (text_addr
&& *text_addr
) {
1673 if (camel_address_decode (CAMEL_ADDRESS (dest_addr
), text_addr
) <= 0)
1674 camel_internet_address_add (dest_addr
, "", text_addr
);
1677 e_destination_freev (destv
);
1679 if (camel_address_length (CAMEL_ADDRESS (to_addr
)) > 0)
1680 camel_mime_message_set_recipients (message
, CAMEL_RECIPIENT_TYPE_TO
, to_addr
);
1682 if (camel_address_length (CAMEL_ADDRESS (cc_addr
)) > 0)
1683 camel_mime_message_set_recipients (message
, CAMEL_RECIPIENT_TYPE_CC
, cc_addr
);
1685 if (camel_address_length (CAMEL_ADDRESS (bcc_addr
)) > 0)
1686 camel_mime_message_set_recipients (message
, CAMEL_RECIPIENT_TYPE_BCC
, bcc_addr
);
1688 g_object_unref (to_addr
);
1689 g_object_unref (cc_addr
);
1690 g_object_unref (bcc_addr
);
1695 typedef struct _CreateComposerData
{
1696 CamelFolder
*folder
;
1697 const gchar
*message_uid
; /* In the Camel string pool */
1699 } CreateComposerData
;
1702 create_composer_data_free (gpointer ptr
)
1704 CreateComposerData
*ccd
= ptr
;
1707 g_clear_object (&ccd
->folder
);
1708 camel_pstring_free (ccd
->message_uid
);
1709 g_free (ccd
->mailto
);
1715 msg_composer_created_with_mailto_cb (GObject
*source_object
,
1716 GAsyncResult
*result
,
1719 CreateComposerData
*ccd
= user_data
;
1720 EMsgComposer
*composer
;
1721 EComposerHeaderTable
*table
;
1722 EClientCache
*client_cache
;
1723 ESourceRegistry
*registry
;
1724 GError
*error
= NULL
;
1726 g_return_if_fail (ccd
!= NULL
);
1728 composer
= e_msg_composer_new_finish (result
, &error
);
1730 g_warning ("%s: Failed to create msg composer: %s", G_STRFUNC
, error
->message
);
1731 g_clear_error (&error
);
1732 create_composer_data_free (ccd
);
1738 e_msg_composer_setup_from_url (composer
, ccd
->mailto
);
1740 set_up_new_composer (composer
, NULL
, ccd
->folder
, NULL
, ccd
->message_uid
);
1742 table
= e_msg_composer_get_header_table (composer
);
1744 client_cache
= e_composer_header_table_ref_client_cache (table
);
1745 registry
= e_client_cache_ref_registry (client_cache
);
1747 composer_set_no_change (composer
);
1749 /* If a CamelFolder was given, we need to backtrack and find
1750 * the corresponding ESource with a Mail Identity extension. */
1756 store
= camel_folder_get_parent_store (ccd
->folder
);
1757 source
= em_utils_ref_mail_identity_for_store (registry
, store
);
1759 if (source
!= NULL
) {
1760 const gchar
*uid
= e_source_get_uid (source
);
1761 e_composer_header_table_set_identity_uid (table
, uid
, NULL
, NULL
);
1762 g_object_unref (source
);
1766 g_object_unref (client_cache
);
1767 g_object_unref (registry
);
1769 gtk_window_present (GTK_WINDOW (composer
));
1771 create_composer_data_free (ccd
);
1775 * em_utils_compose_new_message_with_mailto:
1776 * @shell: an #EShell
1777 * @mailto: a mailto URL
1778 * @folder: a #CamelFolder, or %NULL
1780 * Opens a new composer window as a child window of @parent's toplevel
1781 * window. If @mailto is non-NULL, the composer fields will be filled in
1782 * according to the values in the mailto URL.
1784 * See: em_utils_compose_new_message_with_mailto_and_selection()
1787 em_utils_compose_new_message_with_mailto (EShell
*shell
,
1788 const gchar
*mailto
,
1789 CamelFolder
*folder
)
1791 em_utils_compose_new_message_with_mailto_and_selection (shell
, mailto
, folder
, NULL
);
1795 * em_utils_compose_new_message_with_mailto_and_selection:
1796 * @shell: an #EShell
1797 * @mailto: a mailto URL
1798 * @folder: a #CamelFolder, or %NULL
1799 * @message_uid: (nullable): a UID of the selected message, or %NULL
1801 * similarly to em_utils_compose_new_message_with_mailto(), opens a new composer
1802 * window as a child window of @parent's toplevel window. If @mailto is non-NULL,
1803 * the composer fields will be filled in according to the values in the mailto URL.
1804 * It also tries to identify From account more precisely, when the @folder
1805 * is a search folder.
1810 em_utils_compose_new_message_with_mailto_and_selection (EShell
*shell
,
1811 const gchar
*mailto
,
1812 CamelFolder
*folder
,
1813 const gchar
*message_uid
)
1815 CreateComposerData
*ccd
;
1817 g_return_if_fail (E_IS_SHELL (shell
));
1820 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
1822 ccd
= g_new0 (CreateComposerData
, 1);
1823 ccd
->folder
= folder
? g_object_ref (folder
) : NULL
;
1824 ccd
->message_uid
= camel_pstring_strdup (message_uid
);
1825 ccd
->mailto
= g_strdup (mailto
);
1827 e_msg_composer_new (shell
, msg_composer_created_with_mailto_cb
, ccd
);
1831 replace_variables (GSList
*clues
,
1832 CamelMimeMessage
*message
,
1836 gboolean string_changed
= FALSE
, count1
= FALSE
;
1839 g_return_val_if_fail (pstr
!= NULL
, FALSE
);
1840 g_return_val_if_fail (*pstr
!= NULL
, FALSE
);
1841 g_return_val_if_fail (message
!= NULL
, FALSE
);
1845 for (i
= 0; i
< strlen (str
); i
++) {
1846 const gchar
*cur
= str
+ i
;
1847 if (!g_ascii_strncasecmp (cur
, "$", 1)) {
1848 const gchar
*end
= cur
+ 1;
1853 while (*end
&& (g_unichar_isalnum (*end
) || *end
== '_'))
1856 out
= g_strndup ((const gchar
*) cur
, end
- cur
);
1858 temp_str
= g_strsplit (str
, out
, 2);
1860 for (list
= clues
; list
; list
= g_slist_next (list
)) {
1861 gchar
**temp
= g_strsplit (list
->data
, "=", 2);
1862 if (!g_ascii_strcasecmp (temp
[0], out
+ 1)) {
1864 str
= g_strconcat (temp_str
[0], temp
[1], temp_str
[1], NULL
);
1866 string_changed
= TRUE
;
1873 if (getenv (out
+ 1)) {
1880 string_changed
= TRUE
;
1886 CamelInternetAddress
*to
;
1887 const gchar
*name
, *addr
;
1889 to
= camel_mime_message_get_recipients (
1890 message
, CAMEL_RECIPIENT_TYPE_TO
);
1891 if (!camel_internet_address_get (to
, 0, &name
, &addr
))
1894 if (name
&& g_ascii_strcasecmp ("sender_name", out
+ 1) == 0) {
1896 str
= g_strconcat (temp_str
[0], name
, temp_str
[1], NULL
);
1898 string_changed
= TRUE
;
1899 } else if (addr
&& g_ascii_strcasecmp ("sender_email", out
+ 1) == 0) {
1901 str
= g_strconcat (temp_str
[0], addr
, temp_str
[1], NULL
);
1903 string_changed
= TRUE
;
1907 g_strfreev (temp_str
);
1914 return string_changed
;
1918 traverse_parts (GSList
*clues
,
1919 CamelMimeMessage
*message
,
1920 CamelDataWrapper
*content
)
1922 g_return_if_fail (message
!= NULL
);
1927 if (CAMEL_IS_MULTIPART (content
)) {
1929 CamelMultipart
*multipart
= CAMEL_MULTIPART (content
);
1930 CamelMimePart
*part
;
1932 n
= camel_multipart_get_number (multipart
);
1933 for (i
= 0; i
< n
; i
++) {
1934 part
= camel_multipart_get_part (multipart
, i
);
1938 traverse_parts (clues
, message
, CAMEL_DATA_WRAPPER (part
));
1940 } else if (CAMEL_IS_MIME_PART (content
)) {
1941 CamelMimePart
*part
= CAMEL_MIME_PART (content
);
1942 CamelContentType
*type
;
1943 CamelStream
*stream
;
1944 GByteArray
*byte_array
;
1947 content
= camel_medium_get_content (CAMEL_MEDIUM (part
));
1951 if (CAMEL_IS_MULTIPART (content
)) {
1952 traverse_parts (clues
, message
, CAMEL_DATA_WRAPPER (content
));
1956 type
= camel_mime_part_get_content_type (part
);
1957 if (!camel_content_type_is (type
, "text", "*"))
1960 byte_array
= g_byte_array_new ();
1961 stream
= camel_stream_mem_new_with_byte_array (byte_array
);
1962 camel_data_wrapper_decode_to_stream_sync (
1963 content
, stream
, NULL
, NULL
);
1965 str
= g_strndup ((gchar
*) byte_array
->data
, byte_array
->len
);
1966 g_object_unref (stream
);
1968 if (replace_variables (clues
, message
, &str
)) {
1969 stream
= camel_stream_mem_new_with_buffer (str
, strlen (str
));
1970 camel_data_wrapper_construct_from_stream_sync (
1971 content
, stream
, NULL
, NULL
);
1972 g_object_unref (stream
);
1979 /* Editing messages... */
1982 QUOTING_ATTRIBUTION
,
1988 const gchar
* conf_key
;
1989 const gchar
* message
;
1990 } conf_messages
[] = {
1991 [QUOTING_ATTRIBUTION
] =
1992 { "composer-message-attribution",
1993 /* Note to translators: this is the attribution string used
1994 * when quoting messages. Each ${Variable} gets replaced
1995 * with a value. To see a full list of available variables,
1996 * see mail/em-composer-utils.c:attribvars array. */
1997 N_("On ${AbbrevWeekdayName}, ${Year}-${Month}-${Day} at "
1998 "${24Hour}:${Minute} ${TimeZone}, ${Sender} wrote:")
2002 { "composer-message-forward",
2003 N_("-------- Forwarded Message --------")
2006 [QUOTING_ORIGINAL
] =
2007 { "composer-message-original",
2008 N_("-----Original Message-----")
2013 quoting_text (QuotingTextEnum type
)
2015 GSettings
*settings
;
2018 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2019 text
= g_settings_get_string (settings
, conf_messages
[type
].conf_key
);
2020 g_object_unref (settings
);
2027 return g_strdup (_(conf_messages
[type
].message
));
2031 * em_composer_utils_get_forward_marker:
2033 * Returns: (transfer full): a text marker which is used for inline forwarded messages.
2034 * Free returned pointer with g_free(), when no longer needed.
2039 em_composer_utils_get_forward_marker (void)
2041 return quoting_text (QUOTING_FORWARD
);
2045 * em_composer_utils_get_original_marker:
2047 * Returns: (transfer full): a text marker which is used for inline message replies.
2048 * Free returned pointer with g_free(), when no longer needed.
2053 em_composer_utils_get_original_marker (void)
2055 return quoting_text (QUOTING_ORIGINAL
);
2059 emcu_message_references_existing_account (CamelMimeMessage
*message
,
2060 EMsgComposer
*composer
)
2063 gchar
*identity_uid
;
2064 gboolean res
= FALSE
;
2066 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message
), FALSE
);
2067 g_return_val_if_fail (E_IS_MSG_COMPOSER (composer
), FALSE
);
2069 identity_uid
= (gchar
*) camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Evolution-Identity");
2070 if (!identity_uid
) {
2071 /* for backward compatibility */
2072 identity_uid
= (gchar
*) camel_medium_get_header (CAMEL_MEDIUM (message
), "X-Evolution-Account");
2078 identity_uid
= g_strstrip (g_strdup (identity_uid
));
2079 source
= e_composer_header_table_ref_source (e_msg_composer_get_header_table (composer
), identity_uid
);
2081 res
= source
!= NULL
;
2083 g_clear_object (&source
);
2084 g_free (identity_uid
);
2090 * em_utils_edit_message:
2091 * @composer: an #EMsgComposer
2092 * @folder: a #CamelFolder
2093 * @message: a #CamelMimeMessage
2094 * @message_uid: UID of @message, or %NULL
2096 * Sets up the @composer with the headers/mime-parts/etc of the @message.
2101 em_utils_edit_message (EMsgComposer
*composer
,
2102 CamelFolder
*folder
,
2103 CamelMimeMessage
*message
,
2104 const gchar
*message_uid
,
2105 gboolean keep_signature
)
2107 ESourceRegistry
*registry
;
2109 gboolean folder_is_sent
;
2110 gboolean folder_is_drafts
;
2111 gboolean folder_is_outbox
;
2112 gboolean folder_is_templates
;
2113 gchar
*override_identity_uid
= NULL
, *override_alias_name
= NULL
, *override_alias_address
= NULL
;
2115 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2116 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
2118 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
2120 registry
= e_shell_get_registry (e_msg_composer_get_shell (composer
));
2123 folder_is_sent
= em_utils_folder_is_sent (registry
, folder
);
2124 folder_is_drafts
= em_utils_folder_is_drafts (registry
, folder
);
2125 folder_is_outbox
= em_utils_folder_is_outbox (registry
, folder
);
2126 folder_is_templates
= em_utils_folder_is_templates (registry
, folder
);
2128 folder_is_sent
= FALSE
;
2129 folder_is_drafts
= FALSE
;
2130 folder_is_outbox
= FALSE
;
2131 folder_is_templates
= FALSE
;
2134 /* Template specific code follows. */
2135 if (folder_is_templates
) {
2136 CamelDataWrapper
*content
;
2137 GSettings
*settings
;
2140 GSList
*clue_list
= NULL
;
2142 settings
= e_util_ref_settings ("org.gnome.evolution.plugin.templates");
2144 /* Get the list from GSettings */
2145 strv
= g_settings_get_strv (settings
, "template-placeholders");
2146 for (i
= 0; strv
[i
] != NULL
; i
++)
2147 clue_list
= g_slist_append (clue_list
, g_strdup (strv
[i
]));
2148 g_object_unref (settings
);
2151 content
= camel_medium_get_content (CAMEL_MEDIUM (message
));
2152 traverse_parts (clue_list
, message
, content
);
2154 g_slist_foreach (clue_list
, (GFunc
) g_free
, NULL
);
2155 g_slist_free (clue_list
);
2159 if ((!folder_is_sent
&& !folder_is_drafts
&& !folder_is_outbox
&& !folder_is_templates
) ||
2160 (!folder_is_outbox
&& !folder_is_templates
&& !emcu_message_references_existing_account (message
, composer
))) {
2163 store
= camel_folder_get_parent_store (folder
);
2164 source
= em_utils_ref_mail_identity_for_store (registry
, store
);
2167 g_free (override_identity_uid
);
2168 override_identity_uid
= e_source_dup_uid (source
);
2169 g_object_unref (source
);
2173 source
= em_utils_check_send_account_override (e_msg_composer_get_shell (composer
), message
, folder
, &override_alias_name
, &override_alias_address
);
2175 g_free (override_identity_uid
);
2176 override_identity_uid
= e_source_dup_uid (source
);
2177 g_object_unref (source
);
2181 e_msg_composer_setup_with_message (composer
, message
, keep_signature
, override_identity_uid
, override_alias_name
, override_alias_address
, NULL
);
2183 g_free (override_identity_uid
);
2184 g_free (override_alias_name
);
2185 g_free (override_alias_address
);
2187 /* Override PostTo header only if the folder is a regular folder */
2188 if (folder
&& !folder_is_sent
&& !folder_is_drafts
&& !folder_is_outbox
&& !folder_is_templates
) {
2189 EComposerHeaderTable
*table
;
2193 table
= e_msg_composer_get_header_table (composer
);
2195 folder_uri
= e_mail_folder_uri_from_folder (folder
);
2197 list
= g_list_prepend (NULL
, folder_uri
);
2198 e_composer_header_table_set_post_to_list (table
, list
);
2201 g_free (folder_uri
);
2204 e_msg_composer_remove_header (
2205 composer
, "X-Evolution-Replace-Outbox-UID");
2207 if (message_uid
!= NULL
&& folder_is_drafts
&& folder
) {
2210 folder_uri
= e_mail_folder_uri_from_folder (folder
);
2212 e_msg_composer_set_draft_headers (
2213 composer
, folder_uri
, message_uid
);
2215 g_free (folder_uri
);
2217 } else if (message_uid
!= NULL
&& folder_is_outbox
) {
2218 e_msg_composer_set_header (
2219 composer
, "X-Evolution-Replace-Outbox-UID",
2223 composer_set_no_change (composer
);
2225 gtk_widget_show (GTK_WIDGET (composer
));
2229 emu_update_composers_security (EMsgComposer
*composer
,
2230 guint32 validity_found
)
2233 GSettings
*settings
;
2234 gboolean sign_by_default
;
2236 g_return_if_fail (composer
!= NULL
);
2238 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2241 (validity_found
& E_MAIL_PART_VALIDITY_SIGNED
) != 0 &&
2242 /* FIXME This should be an EMsgComposer property. */
2243 g_settings_get_boolean (
2244 settings
, "composer-sign-reply-if-signed");
2246 g_object_unref (settings
);
2248 /* Pre-set only for encrypted messages, not for signed */
2249 if (sign_by_default
) {
2252 if (validity_found
& E_MAIL_PART_VALIDITY_SMIME
) {
2253 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer
))) &&
2254 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer
))))
2255 action
= E_COMPOSER_ACTION_SMIME_SIGN (composer
);
2257 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer
))) &&
2258 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer
))))
2259 action
= E_COMPOSER_ACTION_PGP_SIGN (composer
);
2263 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), TRUE
);
2266 if (validity_found
& E_MAIL_PART_VALIDITY_ENCRYPTED
) {
2269 if (validity_found
& E_MAIL_PART_VALIDITY_SMIME
) {
2270 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_SIGN (composer
))) &&
2271 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_PGP_ENCRYPT (composer
))))
2272 action
= E_COMPOSER_ACTION_SMIME_ENCRYPT (composer
);
2274 if (!gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_SIGN (composer
))) &&
2275 !gtk_toggle_action_get_active (GTK_TOGGLE_ACTION (E_COMPOSER_ACTION_SMIME_ENCRYPT (composer
))))
2276 action
= E_COMPOSER_ACTION_PGP_ENCRYPT (composer
);
2280 gtk_toggle_action_set_active (GTK_TOGGLE_ACTION (action
), TRUE
);
2285 em_utils_get_real_folder_uri_and_message_uid (CamelFolder
*folder
,
2288 gchar
**message_uid
)
2290 g_return_if_fail (folder
!= NULL
);
2291 g_return_if_fail (uid
!= NULL
);
2292 g_return_if_fail (folder_uri
!= NULL
);
2293 g_return_if_fail (message_uid
!= NULL
);
2295 em_utils_get_real_folder_and_message_uid (folder
, uid
, NULL
, folder_uri
, message_uid
);
2299 emu_add_composer_references_from_message (EMsgComposer
*composer
,
2300 CamelMimeMessage
*message
)
2302 const gchar
*message_id_header
;
2304 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2305 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
2307 message_id_header
= camel_mime_message_get_message_id (message
);
2308 if (message_id_header
&& *message_id_header
) {
2309 GString
*references
= g_string_new ("");
2314 while (value
= e_msg_composer_get_header (composer
, "References", ii
), value
) {
2317 if (references
->len
)
2318 g_string_append_c (references
, ' ');
2319 g_string_append (references
, value
);
2322 if (references
->len
)
2323 g_string_append_c (references
, ' ');
2325 if (*message_id_header
!= '<')
2326 g_string_append_c (references
, '<');
2328 g_string_append (references
, message_id_header
);
2330 if (*message_id_header
!= '<')
2331 g_string_append_c (references
, '>');
2333 unfolded
= camel_header_unfold (references
->str
);
2335 e_msg_composer_set_header (composer
, "References", unfolded
);
2337 g_string_free (references
, TRUE
);
2343 real_update_forwarded_flag (gpointer uid
,
2347 camel_folder_set_message_flags (
2348 folder
, uid
, CAMEL_MESSAGE_FORWARDED
,
2349 CAMEL_MESSAGE_FORWARDED
);
2353 update_forwarded_flags_cb (EMsgComposer
*composer
,
2356 if (data
&& data
->uids
&& data
->folder
)
2357 g_ptr_array_foreach (
2358 data
->uids
, real_update_forwarded_flag
, data
->folder
);
2362 setup_forward_attached_callbacks (EMsgComposer
*composer
,
2363 CamelFolder
*folder
,
2368 if (!composer
|| !folder
|| !uids
|| !uids
->len
)
2371 g_object_ref (folder
);
2373 data
= g_slice_new0 (ForwardData
);
2374 data
->folder
= g_object_ref (folder
);
2375 data
->uids
= g_ptr_array_ref (uids
);
2379 G_CALLBACK (update_forwarded_flags_cb
), data
);
2381 composer
, "save-to-drafts",
2382 G_CALLBACK (update_forwarded_flags_cb
), data
);
2384 g_object_set_data_full (
2385 G_OBJECT (composer
), "forward-data", data
,
2386 (GDestroyNotify
) forward_data_free
);
2390 forward_non_attached (EMsgComposer
*composer
,
2391 CamelFolder
*folder
,
2393 CamelMimeMessage
*message
,
2394 EMailForwardStyle style
)
2396 CamelSession
*session
;
2397 gchar
*text
, *forward
;
2398 guint32 validity_found
= 0;
2401 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2403 session
= e_msg_composer_ref_session (composer
);
2405 flags
= E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS
|
2406 E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG
;
2407 if (style
== E_MAIL_FORWARD_STYLE_QUOTED
)
2408 flags
|= E_MAIL_FORMATTER_QUOTE_FLAG_CITE
;
2410 forward
= quoting_text (QUOTING_FORWARD
);
2411 text
= em_utils_message_to_html (session
, message
, forward
, flags
, NULL
, NULL
, NULL
, &validity_found
);
2414 CamelDataWrapper
*content
;
2417 subject
= mail_tool_generate_forward_subject (message
);
2418 set_up_new_composer (composer
, subject
, folder
, message
, uid
);
2421 content
= camel_medium_get_content (CAMEL_MEDIUM (message
));
2423 if (CAMEL_IS_MULTIPART (content
))
2424 e_msg_composer_add_message_attachments (
2425 composer
, message
, FALSE
);
2427 e_msg_composer_set_body_text (composer
, text
, TRUE
);
2429 emu_add_composer_references_from_message (composer
, message
);
2432 gchar
*folder_uri
= NULL
, *tmp_message_uid
= NULL
;
2434 em_utils_get_real_folder_uri_and_message_uid (
2435 folder
, uid
, &folder_uri
, &tmp_message_uid
);
2437 e_msg_composer_set_source_headers (
2438 composer
, folder_uri
, tmp_message_uid
,
2439 CAMEL_MESSAGE_FORWARDED
);
2441 g_free (folder_uri
);
2442 g_free (tmp_message_uid
);
2445 emu_update_composers_security (composer
, validity_found
);
2446 composer_set_no_change (composer
);
2447 gtk_widget_show (GTK_WIDGET (composer
));
2452 g_clear_object (&session
);
2457 * em_utils_forward_message:
2458 * @composer: an #EMsgComposer
2459 * @message: a #CamelMimeMessage to forward
2460 * @style: the forward style to use
2461 * @folder: (nullable): a #CamelFolder, or %NULL
2462 * @uid: (nullable): the UID of %message, or %NULL
2464 * Forwards @message in the given @style.
2466 * If @style is #E_MAIL_FORWARD_STYLE_ATTACHED, the new message is
2467 * created as follows. If there is more than a single message in @uids,
2468 * a multipart/digest will be constructed and attached to a new composer
2469 * window preset with the appropriate header defaults for forwarding the
2470 * first message in the list. If only one message is to be forwarded,
2471 * it is forwarded as a simple message/rfc822 attachment.
2473 * If @style is #E_MAIL_FORWARD_STYLE_INLINE, each message is forwarded
2474 * in its own composer window in 'inline' form.
2476 * If @style is #E_MAIL_FORWARD_STYLE_QUOTED, each message is forwarded
2477 * in its own composer window in 'quoted' form (each line starting with
2481 em_utils_forward_message (EMsgComposer
*composer
,
2482 CamelMimeMessage
*message
,
2483 EMailForwardStyle style
,
2484 CamelFolder
*folder
,
2487 CamelMimePart
*part
;
2490 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2491 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
2493 e_msg_composer_set_is_reply_or_forward (composer
, TRUE
);
2496 case E_MAIL_FORWARD_STYLE_ATTACHED
:
2498 part
= mail_tool_make_message_attachment (message
);
2499 subject
= mail_tool_generate_forward_subject (message
);
2501 em_utils_forward_attachment (composer
, part
, subject
, NULL
, NULL
);
2503 g_object_unref (part
);
2507 case E_MAIL_FORWARD_STYLE_INLINE
:
2508 case E_MAIL_FORWARD_STYLE_QUOTED
:
2509 forward_non_attached (composer
, folder
, uid
, message
, style
);
2515 em_utils_forward_attachment (EMsgComposer
*composer
,
2516 CamelMimePart
*part
,
2517 const gchar
*subject
,
2518 CamelFolder
*folder
,
2521 CamelDataWrapper
*content
;
2523 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2524 g_return_if_fail (CAMEL_IS_MIME_PART (part
));
2527 g_return_if_fail (CAMEL_IS_FOLDER (folder
));
2529 e_msg_composer_set_is_reply_or_forward (composer
, TRUE
);
2531 set_up_new_composer (composer
, subject
, folder
, NULL
, NULL
);
2533 e_msg_composer_attach (composer
, part
);
2535 content
= camel_medium_get_content (CAMEL_MEDIUM (part
));
2536 if (CAMEL_IS_MIME_MESSAGE (content
)) {
2537 emu_add_composer_references_from_message (composer
, CAMEL_MIME_MESSAGE (content
));
2538 } else if (CAMEL_IS_MULTIPART (content
)) {
2539 const gchar
*mime_type
;
2541 mime_type
= camel_data_wrapper_get_mime_type (content
);
2542 if (mime_type
&& g_ascii_strcasecmp (mime_type
, "multipart/digest") == 0) {
2543 /* This is the way evolution forwards multiple messages as attachment */
2544 CamelMultipart
*multipart
;
2547 multipart
= CAMEL_MULTIPART (content
);
2548 nparts
= camel_multipart_get_number (multipart
);
2550 for (ii
= 0; ii
< nparts
; ii
++) {
2551 CamelMimePart
*mpart
;
2553 mpart
= camel_multipart_get_part (multipart
, ii
);
2554 mime_type
= camel_data_wrapper_get_mime_type (CAMEL_DATA_WRAPPER (mpart
));
2556 if (mime_type
&& g_ascii_strcasecmp (mime_type
, "message/rfc822") == 0) {
2557 content
= camel_medium_get_content (CAMEL_MEDIUM (mpart
));
2559 if (CAMEL_IS_MIME_MESSAGE (content
))
2560 emu_add_composer_references_from_message (composer
, CAMEL_MIME_MESSAGE (content
));
2567 setup_forward_attached_callbacks (composer
, folder
, uids
);
2569 composer_set_no_change (composer
);
2571 gtk_widget_show (GTK_WIDGET (composer
));
2575 * em_utils_redirect_message:
2576 * @composer: an #EMsgComposer
2577 * @message: message to redirect
2579 * Sets up the @composer to redirect @message (Note: only headers will be
2580 * editable). Adds Resent-From/Resent-To/etc headers.
2585 em_utils_redirect_message (EMsgComposer
*composer
,
2586 CamelMimeMessage
*message
)
2588 ESourceRegistry
*registry
;
2591 CamelMedium
*medium
;
2592 gchar
*identity_uid
= NULL
, *alias_name
= NULL
, *alias_address
= NULL
;
2594 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2595 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
2597 shell
= e_msg_composer_get_shell (composer
);
2598 medium
= CAMEL_MEDIUM (message
);
2600 /* QMail will refuse to send a message if it finds one of
2601 * it's Delivered-To headers in the message, so remove all
2602 * Delivered-To headers. Fixes bug #23635. */
2603 while (camel_medium_get_header (medium
, "Delivered-To"))
2604 camel_medium_remove_header (medium
, "Delivered-To");
2606 while (camel_medium_get_header (medium
, "Bcc"))
2607 camel_medium_remove_header (medium
, "Bcc");
2609 while (camel_medium_get_header (medium
, "Resent-Bcc"))
2610 camel_medium_remove_header (medium
, "Resent-Bcc");
2612 registry
= e_shell_get_registry (shell
);
2614 /* This returns a new ESource reference. */
2615 source
= em_utils_check_send_account_override (shell
, message
, NULL
, &alias_name
, &alias_address
);
2617 source
= em_utils_guess_mail_identity_with_recipients_and_sort (
2618 registry
, message
, NULL
, NULL
, &alias_name
, &alias_address
, sort_sources_by_ui
, shell
);
2620 if (source
!= NULL
) {
2621 identity_uid
= e_source_dup_uid (source
);
2622 g_object_unref (source
);
2625 e_msg_composer_setup_redirect (composer
, message
, identity_uid
, alias_name
, alias_address
, NULL
);
2627 g_free (identity_uid
);
2628 g_free (alias_name
);
2629 g_free (alias_address
);
2631 gtk_widget_show (GTK_WIDGET (composer
));
2633 composer_set_no_change (composer
);
2636 /* Replying to messages... */
2639 em_utils_camel_address_to_destination (CamelInternetAddress
*iaddr
)
2641 EDestination
*dest
, **destv
;
2647 if ((n
= camel_address_length ((CamelAddress
*) iaddr
)) == 0)
2650 destv
= g_malloc (sizeof (EDestination
*) * (n
+ 1));
2651 for (i
= 0, j
= 0; i
< n
; i
++) {
2652 const gchar
*name
, *addr
;
2654 if (camel_internet_address_get (iaddr
, i
, &name
, &addr
)) {
2655 dest
= e_destination_new ();
2656 e_destination_set_name (dest
, name
);
2657 e_destination_set_email (dest
, addr
);
2674 emcu_construct_reply_subject (const gchar
*source_subject
)
2678 if (source_subject
) {
2679 GSettings
*settings
;
2680 gboolean skip_len
= -1;
2682 if (em_utils_is_re_in_subject (source_subject
, &skip_len
, NULL
, NULL
) && skip_len
> 0)
2683 source_subject
= source_subject
+ skip_len
;
2685 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2686 if (g_settings_get_boolean (settings
, "composer-use-localized-fwd-re")) {
2687 /* Translators: This is a reply attribution in the message reply subject. The %s is replaced with the subject of the original message. Both 'Re'-s in the 'reply-attribution' translation context should translate into the same string, the same as the ':' separator. */
2688 res
= g_strdup_printf (C_("reply-attribution", "Re: %s"), source_subject
);
2690 /* Do not localize this string */
2691 res
= g_strdup_printf ("Re: %s", source_subject
);
2693 g_clear_object (&settings
);
2695 res
= g_strdup ("");
2702 reply_setup_composer_recipients (EMsgComposer
*composer
,
2703 CamelInternetAddress
*to
,
2704 CamelInternetAddress
*cc
,
2705 CamelFolder
*folder
,
2706 const gchar
*message_uid
,
2707 CamelNNTPAddress
*postto
)
2709 EComposerHeaderTable
*table
;
2710 EDestination
**tov
, **ccv
;
2712 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2714 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to
));
2717 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc
));
2719 /* Construct the tov/ccv */
2720 tov
= em_utils_camel_address_to_destination (to
);
2721 ccv
= em_utils_camel_address_to_destination (cc
);
2723 table
= e_msg_composer_get_header_table (composer
);
2724 e_composer_header_table_set_destinations_to (table
, tov
);
2726 /* Add destinations instead of setting, so we don't remove
2727 * automatic CC addresses that have already been added. */
2728 e_composer_header_table_add_destinations_cc (table
, ccv
);
2730 e_destination_freev (tov
);
2731 e_destination_freev (ccv
);
2733 /* Add post-to, if necessary */
2734 if (postto
&& camel_address_length ((CamelAddress
*) postto
)) {
2735 CamelFolder
*use_folder
= folder
, *temp_folder
= NULL
;
2736 gchar
*store_url
= NULL
;
2739 if (use_folder
&& CAMEL_IS_VEE_FOLDER (use_folder
) && message_uid
) {
2740 em_utils_get_real_folder_and_message_uid (use_folder
, message_uid
, &temp_folder
, NULL
, NULL
);
2743 use_folder
= temp_folder
;
2747 CamelStore
*parent_store
;
2748 CamelService
*service
;
2751 parent_store
= camel_folder_get_parent_store (use_folder
);
2753 service
= CAMEL_SERVICE (parent_store
);
2754 url
= camel_service_new_camel_url (service
);
2756 store_url
= camel_url_to_string (
2757 url
, CAMEL_URL_HIDE_ALL
);
2758 if (store_url
[strlen (store_url
) - 1] == '/')
2759 store_url
[strlen (store_url
) - 1] = '\0';
2761 camel_url_free (url
);
2764 post
= camel_address_encode ((CamelAddress
*) postto
);
2765 e_composer_header_table_set_post_to_base (
2766 table
, store_url
? store_url
: "", post
);
2769 g_clear_object (&temp_folder
);
2774 reply_setup_composer (EMsgComposer
*composer
,
2775 CamelMimeMessage
*message
,
2776 const gchar
*identity_uid
,
2777 const gchar
*identity_name
,
2778 const gchar
*identity_address
,
2779 CamelInternetAddress
*to
,
2780 CamelInternetAddress
*cc
,
2781 CamelFolder
*folder
,
2782 const gchar
*message_uid
,
2783 CamelNNTPAddress
*postto
)
2785 gchar
*message_id
, *references
;
2786 EComposerHeaderTable
*table
;
2787 CamelMedium
*medium
;
2790 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
2791 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
2793 e_msg_composer_set_is_reply_or_forward (composer
, TRUE
);
2796 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to
));
2799 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc
));
2801 reply_setup_composer_recipients (composer
, to
, cc
, folder
, message_uid
, postto
);
2803 /* Set the subject of the new message. */
2804 subject
= emcu_construct_reply_subject (camel_mime_message_get_subject (message
));
2806 table
= e_msg_composer_get_header_table (composer
);
2807 e_composer_header_table_set_subject (table
, subject
);
2808 e_composer_header_table_set_identity_uid (table
, identity_uid
, identity_name
, identity_address
);
2812 /* Add In-Reply-To and References. */
2814 medium
= CAMEL_MEDIUM (message
);
2815 message_id
= camel_header_unfold (camel_medium_get_header (medium
, "Message-ID"));
2816 references
= camel_header_unfold (camel_medium_get_header (medium
, "References"));
2818 if (message_id
!= NULL
) {
2821 e_msg_composer_add_header (
2822 composer
, "In-Reply-To", message_id
);
2825 reply_refs
= g_strdup_printf (
2826 "%s %s", references
, message_id
);
2828 reply_refs
= g_strdup (message_id
);
2830 e_msg_composer_add_header (
2831 composer
, "References", reply_refs
);
2832 g_free (reply_refs
);
2834 } else if (references
!= NULL
) {
2835 e_msg_composer_add_header (
2836 composer
, "References", references
);
2839 g_free (message_id
);
2840 g_free (references
);
2844 get_reply_list (CamelMimeMessage
*message
,
2845 CamelInternetAddress
*to
)
2847 const gchar
*header
, *p
;
2852 * List-Post: <mailto:list@host.com>
2853 * List-Post: <mailto:moderator@host.com?subject=list%20posting>
2854 * List-Post: NO (posting not allowed on this list)
2856 if (!(header
= camel_medium_get_header ((CamelMedium
*) message
, "List-Post")))
2859 while (*header
== ' ' || *header
== '\t')
2863 if (!g_ascii_strncasecmp (header
, "NO", 2))
2866 /* Search for the first mailto angle-bracket enclosed URL.
2867 * (See rfc2369, Section 2, paragraph 3 for details) */
2868 if (!(header
= camel_strstrcase (header
, "<mailto:")))
2874 while (*p
&& !strchr ("?>", *p
))
2877 addr
= g_strndup (header
, p
- header
);
2878 camel_internet_address_add (to
, NULL
, addr
);
2885 em_utils_is_munged_list_message (CamelMimeMessage
*message
)
2887 CamelInternetAddress
*reply_to
, *list
;
2888 gboolean result
= FALSE
;
2890 reply_to
= camel_mime_message_get_reply_to (message
);
2892 list
= camel_internet_address_new ();
2894 if (get_reply_list (message
, list
) &&
2895 camel_address_length (CAMEL_ADDRESS (list
)) ==
2896 camel_address_length (CAMEL_ADDRESS (reply_to
))) {
2898 const gchar
*r_name
, *r_addr
;
2899 const gchar
*l_name
, *l_addr
;
2901 for (i
= 0; i
< camel_address_length (CAMEL_ADDRESS (list
)); i
++) {
2902 if (!camel_internet_address_get (reply_to
, i
, &r_name
, &r_addr
))
2904 if (!camel_internet_address_get (list
, i
, &l_name
, &l_addr
))
2906 if (strcmp (l_addr
, r_addr
))
2909 if (i
== camel_address_length (CAMEL_ADDRESS (list
)))
2912 g_object_unref (list
);
2917 static CamelInternetAddress
*
2918 get_reply_to (CamelMimeMessage
*message
)
2920 CamelInternetAddress
*reply_to
;
2922 reply_to
= camel_mime_message_get_reply_to (message
);
2924 GSettings
*settings
;
2925 gboolean ignore_list_reply_to
;
2927 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
2928 ignore_list_reply_to
= g_settings_get_boolean (
2929 settings
, "composer-ignore-list-reply-to");
2930 g_object_unref (settings
);
2932 if (ignore_list_reply_to
&& em_utils_is_munged_list_message (message
))
2936 reply_to
= camel_mime_message_get_from (message
);
2942 get_reply_sender (CamelMimeMessage
*message
,
2943 CamelInternetAddress
*to
,
2944 CamelNNTPAddress
*postto
)
2946 CamelInternetAddress
*reply_to
;
2947 CamelMedium
*medium
;
2948 const gchar
*posthdr
= NULL
;
2950 medium
= CAMEL_MEDIUM (message
);
2952 /* check whether there is a 'Newsgroups: ' header in there */
2953 if (postto
!= NULL
&& posthdr
== NULL
)
2954 posthdr
= camel_medium_get_header (medium
, "Followup-To");
2956 if (postto
!= NULL
&& posthdr
== NULL
)
2957 posthdr
= camel_medium_get_header (medium
, "Newsgroups");
2959 if (postto
!= NULL
&& posthdr
!= NULL
) {
2960 camel_address_decode (CAMEL_ADDRESS (postto
), posthdr
);
2964 reply_to
= get_reply_to (message
);
2966 if (reply_to
!= NULL
) {
2971 while (camel_internet_address_get (reply_to
, ii
++, &name
, &addr
))
2972 camel_internet_address_add (to
, name
, addr
);
2977 em_utils_get_reply_sender (CamelMimeMessage
*message
,
2978 CamelInternetAddress
*to
,
2979 CamelNNTPAddress
*postto
)
2981 get_reply_sender (message
, to
, postto
);
2985 get_reply_from (CamelMimeMessage
*message
,
2986 CamelInternetAddress
*to
,
2987 CamelNNTPAddress
*postto
)
2989 CamelInternetAddress
*from
;
2990 CamelMedium
*medium
;
2991 const gchar
*name
, *addr
;
2992 const gchar
*posthdr
= NULL
;
2994 medium
= CAMEL_MEDIUM (message
);
2996 /* check whether there is a 'Newsgroups: ' header in there */
2997 if (postto
!= NULL
&& posthdr
== NULL
)
2998 posthdr
= camel_medium_get_header (medium
, "Followup-To");
3000 if (postto
!= NULL
&& posthdr
== NULL
)
3001 posthdr
= camel_medium_get_header (medium
, "Newsgroups");
3003 if (postto
!= NULL
&& posthdr
!= NULL
) {
3004 camel_address_decode (CAMEL_ADDRESS (postto
), posthdr
);
3008 from
= camel_mime_message_get_from (message
);
3013 while (camel_internet_address_get (from
, ii
++, &name
, &addr
))
3014 camel_internet_address_add (to
, name
, addr
);
3019 get_reply_recipient (CamelMimeMessage
*message
,
3020 CamelInternetAddress
*to
,
3021 CamelNNTPAddress
*postto
,
3022 CamelInternetAddress
*address
)
3024 CamelMedium
*medium
;
3025 const gchar
*posthdr
= NULL
;
3027 medium
= CAMEL_MEDIUM (message
);
3029 /* check whether there is a 'Newsgroups: ' header in there */
3030 if (postto
!= NULL
&& posthdr
== NULL
)
3031 posthdr
= camel_medium_get_header (medium
, "Followup-To");
3033 if (postto
!= NULL
&& posthdr
== NULL
)
3034 posthdr
= camel_medium_get_header (medium
, "Newsgroups");
3036 if (postto
!= NULL
&& posthdr
!= NULL
) {
3037 camel_address_decode (CAMEL_ADDRESS (postto
), posthdr
);
3041 if (address
!= NULL
) {
3046 while (camel_internet_address_get (address
, ii
++, &name
, &addr
))
3047 camel_internet_address_add (to
, name
, addr
);
3053 concat_unique_addrs (CamelInternetAddress
*dest
,
3054 CamelInternetAddress
*src
,
3055 GHashTable
*rcpt_hash
)
3057 const gchar
*name
, *addr
;
3060 for (i
= 0; camel_internet_address_get (src
, i
, &name
, &addr
); i
++) {
3061 if (!g_hash_table_contains (rcpt_hash
, addr
)) {
3062 camel_internet_address_add (dest
, name
, addr
);
3063 g_hash_table_insert (rcpt_hash
, g_strdup (addr
), NULL
);
3069 add_source_to_recipient_hash (ESourceRegistry
*registry
,
3070 GHashTable
*rcpt_hash
,
3071 const gchar
*address
,
3073 gboolean source_is_default
)
3075 ESource
*cached_source
;
3076 gboolean insert_source
;
3078 g_return_if_fail (rcpt_hash
!= NULL
);
3079 g_return_if_fail (E_IS_SOURCE (source
));
3081 if (!address
|| !*address
)
3084 cached_source
= g_hash_table_lookup (rcpt_hash
, address
);
3086 insert_source
= source_is_default
|| !cached_source
;
3089 g_hash_table_insert (rcpt_hash
, g_strdup (address
), g_object_ref (source
));
3093 unref_nonull_object (gpointer ptr
)
3096 g_object_unref (ptr
);
3100 generate_recipient_hash (ESourceRegistry
*registry
)
3102 GHashTable
*rcpt_hash
;
3103 ESource
*default_source
;
3105 const gchar
*extension_name
;
3107 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), NULL
);
3109 rcpt_hash
= g_hash_table_new_full (
3111 camel_strcase_equal
,
3112 g_free
, unref_nonull_object
);
3114 default_source
= e_source_registry_ref_default_mail_identity (registry
);
3116 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
3117 list
= e_source_registry_list_sources (registry
, extension_name
);
3119 for (link
= list
; link
!= NULL
; link
= g_list_next (link
)) {
3120 ESource
*source
= E_SOURCE (link
->data
);
3121 ESourceMailIdentity
*extension
;
3122 GHashTable
*aliases
;
3123 const gchar
*address
;
3124 gboolean source_is_default
;
3126 /* No default mail identity implies there are no mail
3127 * identities at all and so we should never get here. */
3128 g_warn_if_fail (default_source
!= NULL
);
3130 if (!e_source_registry_check_enabled (registry
, source
))
3133 source_is_default
= e_source_equal (source
, default_source
);
3135 extension_name
= E_SOURCE_EXTENSION_MAIL_IDENTITY
;
3136 extension
= e_source_get_extension (source
, extension_name
);
3138 address
= e_source_mail_identity_get_address (extension
);
3140 add_source_to_recipient_hash (registry
, rcpt_hash
, address
, source
, source_is_default
);
3142 aliases
= e_source_mail_identity_get_aliases_as_hash_table (extension
);
3144 GHashTableIter iter
;
3147 g_hash_table_iter_init (&iter
, aliases
);
3148 while (g_hash_table_iter_next (&iter
, &key
, NULL
)) {
3151 add_source_to_recipient_hash (registry
, rcpt_hash
, address
, source
, source_is_default
);
3154 g_hash_table_destroy (aliases
);
3158 g_list_free_full (list
, (GDestroyNotify
) g_object_unref
);
3160 if (default_source
!= NULL
)
3161 g_object_unref (default_source
);
3167 em_utils_get_reply_all (ESourceRegistry
*registry
,
3168 CamelMimeMessage
*message
,
3169 CamelInternetAddress
*to
,
3170 CamelInternetAddress
*cc
,
3171 CamelNNTPAddress
*postto
)
3173 CamelInternetAddress
*reply_to
;
3174 CamelInternetAddress
*to_addrs
;
3175 CamelInternetAddress
*cc_addrs
;
3176 CamelMedium
*medium
;
3177 const gchar
*name
, *addr
;
3178 const gchar
*posthdr
= NULL
;
3179 GHashTable
*rcpt_hash
;
3181 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry
));
3182 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
3183 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (to
));
3184 g_return_if_fail (CAMEL_IS_INTERNET_ADDRESS (cc
));
3186 medium
= CAMEL_MEDIUM (message
);
3188 /* check whether there is a 'Newsgroups: ' header in there */
3189 if (postto
!= NULL
&& posthdr
== NULL
)
3190 posthdr
= camel_medium_get_header (medium
, "Followup-To");
3192 if (postto
!= NULL
&& posthdr
== NULL
)
3193 posthdr
= camel_medium_get_header (medium
, "Newsgroups");
3195 if (postto
!= NULL
&& posthdr
!= NULL
)
3196 camel_address_decode (CAMEL_ADDRESS (postto
), posthdr
);
3198 rcpt_hash
= generate_recipient_hash (registry
);
3200 reply_to
= get_reply_to (message
);
3201 to_addrs
= camel_mime_message_get_recipients (
3202 message
, CAMEL_RECIPIENT_TYPE_TO
);
3203 cc_addrs
= camel_mime_message_get_recipients (
3204 message
, CAMEL_RECIPIENT_TYPE_CC
);
3206 if (reply_to
!= NULL
) {
3209 while (camel_internet_address_get (reply_to
, ii
++, &name
, &addr
)) {
3210 /* Ignore references to the Reply-To address
3211 * in the To and Cc lists. */
3212 if (addr
&& !g_hash_table_contains (rcpt_hash
, addr
)) {
3213 /* In the case we are doing a Reply-To-All,
3214 * we do not want to include the user's email
3215 * address because replying to oneself is
3217 camel_internet_address_add (to
, name
, addr
);
3218 g_hash_table_insert (rcpt_hash
, g_strdup (addr
), NULL
);
3223 concat_unique_addrs (to
, to_addrs
, rcpt_hash
);
3224 concat_unique_addrs (cc
, cc_addrs
, rcpt_hash
);
3226 /* Promote the first Cc: address to To: if To: is empty. */
3227 if (camel_address_length ((CamelAddress
*) to
) == 0 &&
3228 camel_address_length ((CamelAddress
*) cc
) > 0) {
3229 camel_internet_address_get (cc
, 0, &name
, &addr
);
3230 camel_internet_address_add (to
, name
, addr
);
3231 camel_address_remove ((CamelAddress
*) cc
, 0);
3234 /* If To: is still empty, may we removed duplicates (i.e. ourself),
3235 * so add the original To if it was set. */
3236 if (camel_address_length ((CamelAddress
*) to
) == 0
3237 && (camel_internet_address_get (to_addrs
, 0, &name
, &addr
)
3238 || camel_internet_address_get (cc_addrs
, 0, &name
, &addr
))) {
3239 camel_internet_address_add (to
, name
, addr
);
3242 g_hash_table_destroy (rcpt_hash
);
3258 ATTRIB_TM_WDAY
, /* not actually used */
3262 typedef void (*AttribFormatter
) (GString
*str
,
3264 CamelMimeMessage
*message
);
3267 format_sender (GString
*str
,
3269 CamelMimeMessage
*message
)
3271 CamelInternetAddress
*sender
;
3272 const gchar
*name
, *addr
= NULL
;
3275 sender
= camel_mime_message_get_from (message
);
3276 if (sender
!= NULL
&& camel_address_length (CAMEL_ADDRESS (sender
)) > 0) {
3277 camel_internet_address_get (sender
, 0, &name
, &addr
);
3279 if (name
&& !*name
) {
3281 } else if (name
&& *name
== '\"') {
3282 gint len
= strlen (name
);
3286 } else if (len
> 1 && name
[len
- 1] == '\"') {
3290 tmp
= g_strndup (name
+ 1, len
- 2);
3296 name
= _("an unknown sender");
3299 if (name
&& !strcmp (attr
, "{SenderName}")) {
3300 g_string_append (str
, name
);
3301 } else if (addr
&& !strcmp (attr
, "{SenderEMail}")) {
3302 g_string_append (str
, addr
);
3303 } else if (name
&& *name
) {
3304 g_string_append (str
, name
);
3306 g_string_append (str
, addr
);
3316 const gchar
*format
; /* strftime or printf format */
3317 AttribFormatter formatter
; /* custom formatter */
3320 { "{Sender}", ATTRIB_CUSTOM
, { NULL
, format_sender
} },
3321 { "{SenderName}", ATTRIB_CUSTOM
, { NULL
, format_sender
} },
3322 { "{SenderEMail}", ATTRIB_CUSTOM
, { NULL
, format_sender
} },
3323 { "{AbbrevWeekdayName}", ATTRIB_STRFTIME
, { "%a", NULL
} },
3324 { "{WeekdayName}", ATTRIB_STRFTIME
, { "%A", NULL
} },
3325 { "{AbbrevMonthName}", ATTRIB_STRFTIME
, { "%b", NULL
} },
3326 { "{MonthName}", ATTRIB_STRFTIME
, { "%B", NULL
} },
3327 { "{AmPmUpper}", ATTRIB_STRFTIME
, { "%p", NULL
} },
3328 { "{AmPmLower}", ATTRIB_STRFTIME
, { "%P", NULL
} },
3329 { "{Day}", ATTRIB_TM_MDAY
, { "%02d", NULL
} }, /* %d 01-31 */
3330 { "{ Day}", ATTRIB_TM_MDAY
, { "% 2d", NULL
} }, /* %e 1-31 */
3331 { "{24Hour}", ATTRIB_TM_24HOUR
, { "%02d", NULL
} }, /* %H 00-23 */
3332 { "{12Hour}", ATTRIB_TM_12HOUR
, { "%02d", NULL
} }, /* %I 00-12 */
3333 { "{DayOfYear}", ATTRIB_TM_YDAY
, { "%d", NULL
} }, /* %j 1-366 */
3334 { "{Month}", ATTRIB_TM_MON
, { "%02d", NULL
} }, /* %m 01-12 */
3335 { "{Minute}", ATTRIB_TM_MIN
, { "%02d", NULL
} }, /* %M 00-59 */
3336 { "{Seconds}", ATTRIB_TM_SEC
, { "%02d", NULL
} }, /* %S 00-61 */
3337 { "{2DigitYear}", ATTRIB_TM_2YEAR
, { "%02d", NULL
} }, /* %y */
3338 { "{Year}", ATTRIB_TM_YEAR
, { "%04d", NULL
} }, /* %Y */
3339 { "{TimeZone}", ATTRIB_TIMEZONE
, { "%+05d", NULL
} }
3343 attribution_format (CamelMimeMessage
*message
)
3345 register const gchar
*inptr
;
3353 gchar
*format
= quoting_text (QUOTING_ATTRIBUTION
);
3355 str
= g_string_new ("");
3357 date
= camel_mime_message_get_date (message
, &tzone
);
3359 if (date
== CAMEL_MESSAGE_DATE_CURRENT
) {
3360 /* The message has no Date: header, look at Received: */
3361 date
= camel_mime_message_get_date_received (message
, &tzone
);
3363 if (date
== CAMEL_MESSAGE_DATE_CURRENT
) {
3364 /* That didn't work either, use current time */
3367 } else if (tzone
== 0) {
3368 GSettings
*settings
;
3370 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
3372 if (g_settings_get_boolean (settings
, "composer-reply-credits-utc-to-localtime")) {
3373 struct tm gmtm
, lctm
;
3376 gmtime_r (&date
, &gmtm
);
3377 localtime_r (&date
, &lctm
);
3379 gmtt
= mktime (&gmtm
);
3380 lctt
= mktime (&lctm
);
3382 tzone
= (lctt
- gmtt
) * 100 / 3600;
3385 g_clear_object (&settings
);
3388 /* Convert to UTC */
3389 date
+= (tzone
/ 100) * 60 * 60;
3390 date
+= (tzone
% 100) * 60;
3392 gmtime_r (&date
, &tm
);
3395 while (*inptr
!= '\0') {
3397 while (*inptr
&& strncmp (inptr
, "${", 2) != 0)
3400 g_string_append_len (str
, start
, inptr
- start
);
3406 while (*inptr
&& *inptr
!= '}')
3409 if (*inptr
!= '}') {
3410 /* broken translation */
3411 g_string_append_len (str
, "${", 2);
3417 len
= inptr
- start
;
3418 type
= ATTRIB_UNKNOWN
;
3419 for (i
= 0; i
< G_N_ELEMENTS (attribvars
); i
++) {
3420 if (!strncmp (attribvars
[i
].name
, start
, len
)) {
3421 type
= attribvars
[i
].type
;
3428 attribvars
[i
].v
.formatter (
3429 str
, attribvars
[i
].name
, message
);
3431 case ATTRIB_TIMEZONE
:
3432 g_string_append_printf (
3433 str
, attribvars
[i
].v
.format
, tzone
);
3435 case ATTRIB_STRFTIME
:
3436 e_utf8_strftime_match_lc_messages (
3437 buf
, sizeof (buf
), attribvars
[i
].v
.format
, &tm
);
3438 g_string_append (str
, buf
);
3441 g_string_append_printf (
3442 str
, attribvars
[i
].v
.format
, tm
.tm_sec
);
3445 g_string_append_printf (
3446 str
, attribvars
[i
].v
.format
, tm
.tm_min
);
3448 case ATTRIB_TM_24HOUR
:
3449 g_string_append_printf (
3450 str
, attribvars
[i
].v
.format
, tm
.tm_hour
);
3452 case ATTRIB_TM_12HOUR
:
3453 g_string_append_printf (
3454 str
, attribvars
[i
].v
.format
,
3455 (tm
.tm_hour
+ 1) % 13);
3457 case ATTRIB_TM_MDAY
:
3458 g_string_append_printf (
3459 str
, attribvars
[i
].v
.format
, tm
.tm_mday
);
3462 g_string_append_printf (
3463 str
, attribvars
[i
].v
.format
, tm
.tm_mon
+ 1);
3465 case ATTRIB_TM_YEAR
:
3466 g_string_append_printf (
3467 str
, attribvars
[i
].v
.format
, tm
.tm_year
+ 1900);
3469 case ATTRIB_TM_2YEAR
:
3470 g_string_append_printf (
3471 str
, attribvars
[i
].v
.format
, tm
.tm_year
% 100);
3473 case ATTRIB_TM_WDAY
:
3474 /* not actually used */
3475 g_string_append_printf (
3476 str
, attribvars
[i
].v
.format
, tm
.tm_wday
);
3478 case ATTRIB_TM_YDAY
:
3479 g_string_append_printf (
3480 str
, attribvars
[i
].v
.format
, tm
.tm_yday
+ 1);
3483 /* Misspelled variable? Drop the
3484 * format argument and continue. */
3490 g_string_free (str
, FALSE
);
3497 composer_set_body (EMsgComposer
*composer
,
3498 CamelMimeMessage
*message
,
3499 EMailReplyStyle style
,
3500 EMailPartList
*parts_list
)
3502 gchar
*text
, *credits
, *original
;
3503 CamelMimePart
*part
;
3504 CamelSession
*session
;
3505 GSettings
*settings
;
3506 guint32 validity_found
= 0, keep_sig_flag
= 0;
3508 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
3509 if (g_settings_get_boolean (settings
, "composer-reply-keep-signature"))
3510 keep_sig_flag
= E_MAIL_FORMATTER_QUOTE_FLAG_KEEP_SIG
;
3511 g_clear_object (&settings
);
3513 session
= e_msg_composer_ref_session (composer
);
3516 case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE
:
3519 case E_MAIL_REPLY_STYLE_ATTACH
:
3520 /* attach the original message as an attachment */
3521 part
= mail_tool_make_message_attachment (message
);
3522 e_msg_composer_attach (composer
, part
);
3523 g_object_unref (part
);
3525 case E_MAIL_REPLY_STYLE_OUTLOOK
:
3526 original
= quoting_text (QUOTING_ORIGINAL
);
3527 text
= em_utils_message_to_html (
3528 session
, message
, original
, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS
| keep_sig_flag
,
3529 parts_list
, NULL
, NULL
, &validity_found
);
3530 e_msg_composer_set_body_text (composer
, text
, TRUE
);
3533 emu_update_composers_security (composer
, validity_found
);
3536 case E_MAIL_REPLY_STYLE_QUOTED
:
3538 /* do what any sane user would want when replying... */
3539 credits
= attribution_format (message
);
3540 text
= em_utils_message_to_html (
3541 session
, message
, credits
, E_MAIL_FORMATTER_QUOTE_FLAG_CITE
| keep_sig_flag
,
3542 parts_list
, NULL
, NULL
, &validity_found
);
3544 e_msg_composer_set_body_text (composer
, text
, TRUE
);
3546 emu_update_composers_security (composer
, validity_found
);
3550 g_object_unref (session
);
3554 em_utils_construct_composer_text (CamelSession
*session
,
3555 CamelMimeMessage
*message
,
3556 EMailPartList
*parts_list
)
3558 gchar
*text
, *credits
;
3560 g_return_val_if_fail (CAMEL_IS_SESSION (session
), NULL
);
3562 credits
= attribution_format (message
);
3563 text
= em_utils_message_to_html (
3564 session
, message
, credits
, E_MAIL_FORMATTER_QUOTE_FLAG_CITE
,
3565 parts_list
, NULL
, NULL
, NULL
);
3572 emcu_folder_is_inbox (CamelFolder
*folder
)
3574 CamelSession
*session
;
3576 gboolean is_inbox
= FALSE
;
3578 g_return_val_if_fail (CAMEL_IS_FOLDER (folder
), FALSE
);
3580 store
= camel_folder_get_parent_store (folder
);
3584 session
= camel_service_ref_session (CAMEL_SERVICE (store
));
3588 if (E_IS_MAIL_SESSION (session
)) {
3589 MailFolderCache
*folder_cache
;
3590 CamelFolderInfoFlags flags
= 0;
3592 folder_cache
= e_mail_session_get_folder_cache (E_MAIL_SESSION (session
));
3593 if (folder_cache
&& mail_folder_cache_get_folder_info_flags (
3594 folder_cache
, store
, camel_folder_get_full_name (folder
), &flags
)) {
3595 is_inbox
= (flags
& CAMEL_FOLDER_TYPE_MASK
) == CAMEL_FOLDER_TYPE_INBOX
;
3599 g_object_unref (session
);
3604 typedef struct _AltReplyContext
{
3606 EAlertSink
*alert_sink
;
3607 CamelMimeMessage
*source_message
;
3608 CamelFolder
*folder
;
3610 CamelMimeMessage
*new_message
; /* When processed with a template */
3611 EMailPartList
*source
;
3612 EMailReplyType type
;
3613 EMailReplyStyle style
;
3615 gboolean template_preserve_subject
;
3619 alt_reply_context_free (gpointer ptr
)
3621 AltReplyContext
*context
= ptr
;
3624 g_clear_object (&context
->shell
);
3625 g_clear_object (&context
->alert_sink
);
3626 g_clear_object (&context
->source_message
);
3627 g_clear_object (&context
->folder
);
3628 g_clear_object (&context
->source
);
3629 g_clear_object (&context
->new_message
);
3630 g_free (context
->message_uid
);
3636 alt_reply_composer_created_cb (GObject
*source_object
,
3637 GAsyncResult
*result
,
3640 AltReplyContext
*context
= user_data
;
3641 EMsgComposer
*composer
;
3642 GError
*error
= NULL
;
3644 g_return_if_fail (context
!= NULL
);
3646 composer
= e_msg_composer_new_finish (result
, &error
);
3649 EContentEditor
*cnt_editor
;
3651 cnt_editor
= e_html_editor_get_content_editor (e_msg_composer_get_editor (composer
));
3653 if (context
->new_message
) {
3654 CamelInternetAddress
*to
= NULL
, *cc
= NULL
;
3655 CamelNNTPAddress
*postto
= NULL
;
3656 gboolean need_reply_all
= FALSE
;
3658 if ((context
->flags
& (E_MAIL_REPLY_FLAG_FORMAT_PLAIN
| E_MAIL_REPLY_FLAG_FORMAT_HTML
)) != 0) {
3659 e_content_editor_set_html_mode (cnt_editor
, (context
->flags
& E_MAIL_REPLY_FLAG_FORMAT_HTML
) != 0);
3662 em_utils_edit_message (composer
, context
->folder
, context
->new_message
, context
->message_uid
, TRUE
);
3664 if (context
->type
== E_MAIL_REPLY_TO_SENDER
) {
3665 /* Reply to sender */
3666 to
= camel_internet_address_new ();
3667 if (context
->folder
)
3668 postto
= camel_nntp_address_new ();
3669 get_reply_sender (context
->source_message
, to
, postto
);
3670 } else if (context
->type
== E_MAIL_REPLY_TO_LIST
) {
3672 to
= camel_internet_address_new ();
3674 if (!get_reply_list (context
->source_message
, to
)) {
3675 need_reply_all
= TRUE
;
3676 g_clear_object (&to
);
3678 } else if (context
->type
!= E_MAIL_REPLY_TO_ALL
) {
3679 g_warn_if_reached ();
3682 if (context
->type
== E_MAIL_REPLY_TO_ALL
|| need_reply_all
) {
3684 to
= camel_internet_address_new ();
3685 cc
= camel_internet_address_new ();
3687 if (context
->folder
)
3688 postto
= camel_nntp_address_new ();
3690 em_utils_get_reply_all (e_shell_get_registry (context
->shell
), context
->source_message
, to
, cc
, postto
);
3693 reply_setup_composer_recipients (composer
, to
, cc
, context
->folder
, context
->message_uid
, postto
);
3695 composer_set_no_change (composer
);
3697 g_clear_object (&to
);
3698 g_clear_object (&cc
);
3699 g_clear_object (&postto
);
3701 if (context
->folder
&& context
->message_uid
) {
3702 gchar
*source_folder_uri
= NULL
;
3703 gchar
*source_message_uid
= NULL
;
3705 em_utils_get_real_folder_uri_and_message_uid (context
->folder
, context
->message_uid
,
3706 &source_folder_uri
, &source_message_uid
);
3708 if (!source_message_uid
)
3709 source_message_uid
= g_strdup (context
->message_uid
);
3711 if (source_folder_uri
) {
3712 e_msg_composer_set_source_headers (composer
, source_folder_uri
,
3713 source_message_uid
, CAMEL_MESSAGE_ANSWERED
| CAMEL_MESSAGE_SEEN
);
3716 g_free (source_folder_uri
);
3717 g_free (source_message_uid
);
3720 em_utils_reply_to_message (composer
, context
->source_message
,
3721 context
->folder
, context
->message_uid
, context
->type
, context
->style
,
3722 context
->source
, NULL
, context
->flags
);
3725 e_alert_submit (context
->alert_sink
, "mail-composer:failed-create-composer",
3726 error
? error
->message
: _("Unknown error"), NULL
);
3729 alt_reply_context_free (context
);
3730 g_clear_error (&error
);
3734 alt_reply_template_applied_cb (GObject
*source_object
,
3735 GAsyncResult
*result
,
3738 AltReplyContext
*context
= user_data
;
3739 GError
*error
= NULL
;
3741 g_return_if_fail (context
!= NULL
);
3743 context
->new_message
= e_mail_templates_apply_finish (source_object
, result
, &error
);
3745 if (context
->new_message
) {
3746 if (context
->template_preserve_subject
) {
3749 subject
= emcu_construct_reply_subject (camel_mime_message_get_subject (context
->source_message
));
3750 camel_mime_message_set_subject (context
->new_message
, subject
);
3754 e_msg_composer_new (context
->shell
, alt_reply_composer_created_cb
, context
);
3756 e_alert_submit (context
->alert_sink
, "mail:no-retrieve-message",
3757 error
? error
->message
: _("Unknown error"), NULL
);
3758 alt_reply_context_free (context
);
3761 g_clear_error (&error
);
3765 emcu_three_state_toggled_cb (GtkToggleButton
*widget
,
3768 glong
*phandlerid
= user_data
;
3770 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (widget
));
3771 g_return_if_fail (phandlerid
!= NULL
);
3773 g_signal_handler_block (widget
, *phandlerid
);
3775 if (gtk_toggle_button_get_inconsistent (widget
) &&
3776 gtk_toggle_button_get_active (widget
)) {
3777 gtk_toggle_button_set_active (widget
, FALSE
);
3778 gtk_toggle_button_set_inconsistent (widget
, FALSE
);
3779 } else if (!gtk_toggle_button_get_active (widget
)) {
3780 gtk_toggle_button_set_inconsistent (widget
, TRUE
);
3781 gtk_toggle_button_set_active (widget
, FALSE
);
3785 g_signal_handler_unblock (widget
, *phandlerid
);
3789 emcu_connect_three_state_changer (GtkToggleButton
*toggle_button
)
3793 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button
));
3795 phandlerid
= g_new0 (glong
, 1);
3797 *phandlerid
= g_signal_connect_data (toggle_button
, "toggled",
3798 G_CALLBACK (emcu_three_state_toggled_cb
),
3799 phandlerid
, (GClosureNotify
) g_free
, 0);
3803 emcu_three_state_set_value (GtkToggleButton
*toggle_button
,
3806 g_return_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button
));
3808 if (value
== E_THREE_STATE_OFF
) {
3809 gtk_toggle_button_set_active (toggle_button
, FALSE
);
3810 gtk_toggle_button_set_inconsistent (toggle_button
, FALSE
);
3811 } else if (value
== E_THREE_STATE_ON
) {
3812 gtk_toggle_button_set_active (toggle_button
, TRUE
);
3813 gtk_toggle_button_set_inconsistent (toggle_button
, FALSE
);
3815 gtk_toggle_button_set_active (toggle_button
, FALSE
);
3816 gtk_toggle_button_set_inconsistent (toggle_button
, TRUE
);
3821 emcu_three_state_get_value (GtkToggleButton
*toggle_button
)
3823 g_return_val_if_fail (GTK_IS_TOGGLE_BUTTON (toggle_button
), E_THREE_STATE_INCONSISTENT
);
3825 if (gtk_toggle_button_get_inconsistent (toggle_button
))
3826 return E_THREE_STATE_INCONSISTENT
;
3827 else if (gtk_toggle_button_get_active (toggle_button
))
3828 return E_THREE_STATE_ON
;
3830 return E_THREE_STATE_OFF
;
3833 static GtkComboBox
*
3834 emcu_create_templates_combo (EShell
*shell
,
3835 const gchar
*folder_uri
,
3836 const gchar
*message_uid
)
3839 GtkCellRenderer
*renderer
;
3840 EShellBackend
*shell_backend
;
3841 EMailSession
*mail_session
;
3842 EMailTemplatesStore
*templates_store
;
3843 GtkTreeStore
*tree_store
;
3844 GtkTreeIter found_iter
;
3845 gboolean found_message
= FALSE
;
3847 shell_backend
= e_shell_get_backend_by_name (shell
, "mail");
3848 g_return_val_if_fail (E_IS_MAIL_BACKEND (shell_backend
), NULL
);
3850 mail_session
= e_mail_backend_get_session (E_MAIL_BACKEND (shell_backend
));
3851 templates_store
= e_mail_templates_store_ref_default (e_mail_ui_session_get_account_store (E_MAIL_UI_SESSION (mail_session
)));
3853 tree_store
= e_mail_templates_store_build_model (templates_store
, folder_uri
, message_uid
, &found_message
, &found_iter
);
3855 combo
= GTK_COMBO_BOX (gtk_combo_box_new_with_model (GTK_TREE_MODEL (tree_store
)));
3857 renderer
= gtk_cell_renderer_text_new ();
3858 g_object_set (G_OBJECT (renderer
),
3859 "ellipsize", PANGO_ELLIPSIZE_END
,
3860 "max-width-chars", 60,
3863 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo
), renderer
, TRUE
);
3864 gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo
), renderer
,
3865 "text", E_MAIL_TEMPLATES_STORE_COLUMN_DISPLAY_NAME
,
3868 g_clear_object (&templates_store
);
3869 g_clear_object (&tree_store
);
3871 if (found_message
) {
3872 gtk_combo_box_set_active_iter (combo
, &found_iter
);
3874 gtk_widget_set_sensitive (GTK_WIDGET (combo
), FALSE
);
3881 * em_utils_reply_alternative:
3882 * @parent: (nullable): a parent #GtkWindow for the question dialog
3883 * @shell: an #EShell instance used to create #EMsgComposer
3884 * @alert_sink: an #EAlertSink to put any errors to
3885 * @message: a #CamelMimeMessage
3886 * @folder: (nullable): a #CamelFolder, or %NULL
3887 * @message_uid: (nullable): the UID of @message, or %NULL
3888 * @style: the reply style to use
3889 * @source: (nullable): source to inherit view settings from
3891 * This is similar to em_utils_reply_to_message(), except it asks user to
3892 * change some settings before sending. It calls em_utils_reply_to_message()
3893 * at the end for non-templated replies.
3898 em_utils_reply_alternative (GtkWindow
*parent
,
3900 EAlertSink
*alert_sink
,
3901 CamelMimeMessage
*message
,
3902 CamelFolder
*folder
,
3903 const gchar
*message_uid
,
3904 EMailReplyStyle default_style
,
3905 EMailPartList
*source
)
3907 GtkWidget
*dialog
, *widget
, *style_label
;
3908 GtkBox
*hbox
, *vbox
;
3909 GtkRadioButton
*recip_sender
, *recip_list
, *recip_all
;
3910 GtkLabel
*sender_label
, *list_label
, *all_label
;
3911 GtkRadioButton
*style_default
, *style_attach
, *style_inline
, *style_quote
, *style_no_quote
;
3912 GtkToggleButton
*html_format
;
3913 GtkToggleButton
*bottom_posting
;
3914 GtkCheckButton
*apply_template
;
3915 GtkComboBox
*templates
;
3916 GtkCheckButton
*preserve_message_subject
;
3917 PangoAttrList
*attr_list
;
3918 GSettings
*settings
;
3919 gchar
*last_tmpl_folder_uri
, *last_tmpl_message_uid
, *address
, *text
;
3920 gboolean can_reply_list
= FALSE
;
3921 CamelInternetAddress
*to
, *cc
;
3922 CamelNNTPAddress
*postto
= NULL
;
3925 g_return_if_fail (E_IS_SHELL (shell
));
3926 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
3928 settings
= e_util_ref_settings ("org.gnome.evolution.mail");
3930 dialog
= gtk_dialog_new_with_buttons (_("Alternative Reply"), parent
,
3931 GTK_DIALOG_DESTROY_WITH_PARENT
,
3932 _("_Cancel"), GTK_RESPONSE_CANCEL
,
3933 _("_Reply"), GTK_RESPONSE_OK
,
3936 gtk_dialog_set_default_response (GTK_DIALOG (dialog
), GTK_RESPONSE_OK
);
3938 vbox
= GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog
)));
3939 gtk_container_set_border_width (GTK_CONTAINER (vbox
), 12);
3940 gtk_box_set_spacing (vbox
, 2);
3942 #define add_with_indent(x) \
3943 gtk_widget_set_margin_left (GTK_WIDGET (x), 12); \
3944 gtk_box_pack_start (vbox, GTK_WIDGET (x), FALSE, FALSE, 0);
3946 widget
= gtk_label_new (_("Recipients:"));
3947 g_object_set (G_OBJECT (widget
),
3949 "halign", GTK_ALIGN_START
,
3951 gtk_box_pack_start (vbox
, widget
, FALSE
, FALSE
, 0);
3953 attr_list
= pango_attr_list_new ();
3954 pango_attr_list_insert (attr_list
, pango_attr_style_new (PANGO_STYLE_ITALIC
));
3956 #define add_with_label(wgt, lbl) G_STMT_START { \
3957 GtkWidget *divider_label = gtk_label_new (":"); \
3958 gtk_label_set_attributes (GTK_LABEL (lbl), attr_list); \
3959 hbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6)); \
3960 gtk_box_pack_start (hbox, GTK_WIDGET (wgt), FALSE, FALSE, 0); \
3961 gtk_box_pack_start (hbox, GTK_WIDGET (divider_label), FALSE, FALSE, 0); \
3962 gtk_box_pack_start (hbox, GTK_WIDGET (lbl), FALSE, FALSE, 0); \
3963 e_binding_bind_property ( \
3965 divider_label, "visible", \
3966 G_BINDING_SYNC_CREATE); \
3967 e_binding_bind_property ( \
3970 G_BINDING_SYNC_CREATE); \
3971 add_with_indent (hbox); } G_STMT_END
3973 recip_sender
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
3974 NULL
, _("Reply to _Sender")));
3975 sender_label
= GTK_LABEL (gtk_label_new (""));
3976 add_with_label (recip_sender
, sender_label
);
3978 recip_list
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
3979 gtk_radio_button_get_group (recip_sender
), _("Reply to _List")));
3980 list_label
= GTK_LABEL (gtk_label_new (""));
3981 add_with_label (recip_list
, list_label
);
3983 recip_all
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
3984 gtk_radio_button_get_group (recip_sender
), _("Reply to _All")));
3985 all_label
= GTK_LABEL (gtk_label_new (""));
3986 add_with_label (recip_all
, all_label
);
3988 #undef add_with_label
3990 pango_attr_list_unref (attr_list
);
3992 /* One line gap between sections */
3993 widget
= gtk_label_new (" ");
3994 gtk_box_pack_start (vbox
, widget
, FALSE
, FALSE
, 0);
3996 style_label
= gtk_label_new (_("Reply style:"));
3997 g_object_set (G_OBJECT (style_label
),
3999 "halign", GTK_ALIGN_START
,
4001 gtk_box_pack_start (vbox
, style_label
, FALSE
, FALSE
, 0);
4003 style_default
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4004 NULL
, _("_Default")));
4005 add_with_indent (style_default
);
4007 style_attach
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4008 gtk_radio_button_get_group (style_default
), _("Attach_ment")));
4009 add_with_indent (style_attach
);
4011 style_inline
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4012 gtk_radio_button_get_group (style_default
), _("Inline (_Outlook style)")));
4013 add_with_indent (style_inline
);
4015 style_quote
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4016 gtk_radio_button_get_group (style_default
), _("_Quote")));
4017 add_with_indent (style_quote
);
4019 style_no_quote
= GTK_RADIO_BUTTON (gtk_radio_button_new_with_mnemonic (
4020 gtk_radio_button_get_group (style_default
), _("Do _Not Quote")));
4021 add_with_indent (style_no_quote
);
4023 /* One line gap between sections */
4024 widget
= gtk_label_new (" ");
4025 gtk_box_pack_start (vbox
, widget
, FALSE
, FALSE
, 0);
4027 html_format
= GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("_Format message in HTML")));
4028 gtk_box_pack_start (vbox
, GTK_WIDGET (html_format
), FALSE
, FALSE
, 0);
4030 bottom_posting
= GTK_TOGGLE_BUTTON (gtk_check_button_new_with_mnemonic (_("Start _typing at the bottom")));
4031 gtk_box_pack_start (vbox
, GTK_WIDGET (bottom_posting
), FALSE
, FALSE
, 0);
4033 /* One line gap between sections */
4034 widget
= gtk_label_new (" ");
4035 gtk_box_pack_start (vbox
, widget
, FALSE
, FALSE
, 0);
4037 apply_template
= GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Apply t_emplate")));
4038 gtk_box_pack_start (vbox
, GTK_WIDGET (apply_template
), FALSE
, FALSE
, 0);
4040 last_tmpl_folder_uri
= g_settings_get_string (settings
, "alt-reply-template-folder-uri");
4041 last_tmpl_message_uid
= g_settings_get_string (settings
, "alt-reply-template-message-uid");
4043 templates
= emcu_create_templates_combo (shell
, last_tmpl_folder_uri
, last_tmpl_message_uid
);
4044 add_with_indent (templates
);
4046 g_free (last_tmpl_folder_uri
);
4047 g_free (last_tmpl_message_uid
);
4049 preserve_message_subject
= GTK_CHECK_BUTTON (gtk_check_button_new_with_mnemonic (_("Preserve original message S_ubject")));
4050 add_with_indent (preserve_message_subject
);
4052 #undef add_with_indent
4054 gtk_widget_show_all (GTK_WIDGET (vbox
));
4056 #define populate_label_with_text(lbl, txt) \
4057 g_object_set (G_OBJECT (lbl), \
4058 "ellipsize", PANGO_ELLIPSIZE_END, \
4059 "max-width-chars", 50, \
4060 "label", txt ? txt : "", \
4061 "tooltip-text", txt ? txt : "", \
4064 /* Reply to sender */
4065 to
= camel_internet_address_new ();
4067 postto
= camel_nntp_address_new ();
4068 get_reply_sender (message
, to
, postto
);
4070 if (postto
&& camel_address_length (CAMEL_ADDRESS (postto
)) > 0) {
4071 address
= camel_address_format (CAMEL_ADDRESS (postto
));
4073 address
= camel_address_format (CAMEL_ADDRESS (to
));
4076 populate_label_with_text (sender_label
, address
);
4078 g_clear_object (&postto
);
4079 g_clear_object (&to
);
4083 to
= camel_internet_address_new ();
4085 if (!get_reply_list (message
, to
)) {
4086 gtk_widget_set_sensitive (GTK_WIDGET (recip_list
), FALSE
);
4088 can_reply_list
= TRUE
;
4090 address
= camel_address_format (CAMEL_ADDRESS (to
));
4091 populate_label_with_text (list_label
, address
);
4095 g_clear_object (&to
);
4098 to
= camel_internet_address_new ();
4099 cc
= camel_internet_address_new ();
4102 postto
= camel_nntp_address_new ();
4104 em_utils_get_reply_all (e_shell_get_registry (shell
), message
, to
, cc
, postto
);
4106 if (postto
&& camel_address_length (CAMEL_ADDRESS (postto
)) > 0) {
4107 n_addresses
= camel_address_length (CAMEL_ADDRESS (postto
));
4108 address
= camel_address_format (CAMEL_ADDRESS (postto
));
4110 camel_address_cat (CAMEL_ADDRESS (to
), CAMEL_ADDRESS (cc
));
4111 n_addresses
= camel_address_length (CAMEL_ADDRESS (to
));
4112 address
= camel_address_format (CAMEL_ADDRESS (to
));
4115 text
= g_strdup_printf (g_dngettext (GETTEXT_PACKAGE
, "one recipient", "%d recipients", n_addresses
), n_addresses
);
4116 populate_label_with_text (all_label
, text
);
4117 gtk_widget_set_tooltip_text (GTK_WIDGET (all_label
), address
);
4119 g_clear_object (&to
);
4120 g_clear_object (&cc
);
4121 g_clear_object (&postto
);
4125 #undef populate_label_with_text
4127 /* Prefer reply-to-list */
4128 if (g_settings_get_boolean (settings
, "composer-group-reply-to-list")) {
4130 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list
), TRUE
);
4131 else if (n_addresses
> 1)
4132 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all
), TRUE
);
4134 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender
), TRUE
);
4136 /* Prefer reply-to-all */
4138 if (n_addresses
> 1)
4139 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_all
), TRUE
);
4140 else if (can_reply_list
)
4141 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_list
), TRUE
);
4143 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (recip_sender
), TRUE
);
4146 switch (g_settings_get_enum (settings
, "alt-reply-style")) {
4147 case E_MAIL_REPLY_STYLE_UNKNOWN
:
4148 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_default
), TRUE
);
4150 case E_MAIL_REPLY_STYLE_QUOTED
:
4151 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_quote
), TRUE
);
4153 case E_MAIL_REPLY_STYLE_DO_NOT_QUOTE
:
4154 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_no_quote
), TRUE
);
4156 case E_MAIL_REPLY_STYLE_ATTACH
:
4157 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_attach
), TRUE
);
4159 case E_MAIL_REPLY_STYLE_OUTLOOK
:
4160 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (style_inline
), TRUE
);
4164 emcu_three_state_set_value (html_format
, g_settings_get_enum (settings
, "alt-reply-html-format"));
4165 emcu_three_state_set_value (bottom_posting
, g_settings_get_enum (settings
, "alt-reply-start-bottom"));
4166 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template
), g_settings_get_boolean (settings
, "alt-reply-template-apply"));
4167 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (preserve_message_subject
), g_settings_get_boolean (settings
, "alt-reply-template-preserve-subject"));
4169 if (!gtk_widget_get_sensitive (GTK_WIDGET (templates
)))
4170 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (apply_template
), FALSE
);
4172 emcu_connect_three_state_changer (html_format
);
4173 emcu_connect_three_state_changer (bottom_posting
);
4175 e_binding_bind_property (
4176 apply_template
, "active",
4177 templates
, "sensitive",
4178 G_BINDING_SYNC_CREATE
);
4180 e_binding_bind_property (
4181 apply_template
, "active",
4182 preserve_message_subject
, "sensitive",
4183 G_BINDING_SYNC_CREATE
);
4185 /* Enable the 'Reply Style' section only if not using Template */
4186 e_binding_bind_property (
4187 apply_template
, "active",
4188 style_label
, "sensitive",
4189 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4191 e_binding_bind_property (
4192 apply_template
, "active",
4193 style_default
, "sensitive",
4194 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4196 e_binding_bind_property (
4197 apply_template
, "active",
4198 style_attach
, "sensitive",
4199 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4201 e_binding_bind_property (
4202 apply_template
, "active",
4203 style_inline
, "sensitive",
4204 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4206 e_binding_bind_property (
4207 apply_template
, "active",
4208 style_quote
, "sensitive",
4209 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4211 e_binding_bind_property (
4212 apply_template
, "active",
4213 style_no_quote
, "sensitive",
4214 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4216 /* Similarly with bottom posting, which doesn't work when using Templates */
4217 e_binding_bind_property (
4218 apply_template
, "active",
4219 bottom_posting
, "sensitive",
4220 G_BINDING_SYNC_CREATE
| G_BINDING_INVERT_BOOLEAN
);
4222 if (gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_OK
) {
4224 AltReplyContext
*context
;
4225 EThreeState three_state
;
4226 CamelFolder
*template_folder
= NULL
;
4227 gchar
*template_message_uid
= NULL
;
4229 context
= g_new0 (AltReplyContext
, 1);
4230 context
->shell
= g_object_ref (shell
);
4231 context
->alert_sink
= g_object_ref (alert_sink
);
4232 context
->source_message
= g_object_ref (message
);
4233 context
->folder
= folder
? g_object_ref (folder
) : NULL
;
4234 context
->source
= source
? g_object_ref (source
) : NULL
;
4235 context
->message_uid
= g_strdup (message_uid
);
4236 context
->style
= E_MAIL_REPLY_STYLE_UNKNOWN
;
4237 context
->flags
= E_MAIL_REPLY_FLAG_FORCE_STYLE
;
4239 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_quote
)))
4240 context
->style
= E_MAIL_REPLY_STYLE_QUOTED
;
4241 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_no_quote
)))
4242 context
->style
= E_MAIL_REPLY_STYLE_DO_NOT_QUOTE
;
4243 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_attach
)))
4244 context
->style
= E_MAIL_REPLY_STYLE_ATTACH
;
4245 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (style_inline
)))
4246 context
->style
= E_MAIL_REPLY_STYLE_OUTLOOK
;
4248 context
->flags
= context
->flags
& (~E_MAIL_REPLY_FLAG_FORCE_STYLE
);
4250 three_state
= emcu_three_state_get_value (html_format
);
4251 g_settings_set_enum (settings
, "alt-reply-html-format", three_state
);
4253 if (three_state
== E_THREE_STATE_ON
)
4254 context
->flags
|= E_MAIL_REPLY_FLAG_FORMAT_HTML
;
4255 else if (three_state
== E_THREE_STATE_OFF
)
4256 context
->flags
|= E_MAIL_REPLY_FLAG_FORMAT_PLAIN
;
4258 three_state
= emcu_three_state_get_value (bottom_posting
);
4259 g_settings_set_enum (settings
, "alt-reply-start-bottom", three_state
);
4261 if (three_state
== E_THREE_STATE_ON
)
4262 context
->flags
|= E_MAIL_REPLY_FLAG_BOTTOM_POSTING
;
4263 else if (three_state
== E_THREE_STATE_OFF
)
4264 context
->flags
|= E_MAIL_REPLY_FLAG_TOP_POSTING
;
4266 g_settings_set_enum (settings
, "alt-reply-style", context
->style
);
4267 g_settings_set_boolean (settings
, "alt-reply-template-apply", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template
)));
4268 g_settings_set_boolean (settings
, "alt-reply-template-preserve-subject", gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject
)));
4270 if (gtk_combo_box_get_active_iter (templates
, &iter
)) {
4271 gtk_tree_model_get (gtk_combo_box_get_model (templates
), &iter
,
4272 E_MAIL_TEMPLATES_STORE_COLUMN_FOLDER
, &template_folder
,
4273 E_MAIL_TEMPLATES_STORE_COLUMN_MESSAGE_UID
, &template_message_uid
,
4277 if (template_folder
) {
4280 folder_uri
= e_mail_folder_uri_from_folder (template_folder
);
4281 g_settings_set_string (settings
, "alt-reply-template-folder-uri", folder_uri
? folder_uri
: "");
4282 g_free (folder_uri
);
4285 g_settings_set_string (settings
, "alt-reply-template-message-uid", template_message_uid
? template_message_uid
: "");
4287 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_sender
)))
4288 context
->type
= E_MAIL_REPLY_TO_SENDER
;
4289 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_list
)))
4290 context
->type
= E_MAIL_REPLY_TO_LIST
;
4291 else if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (recip_all
)))
4292 context
->type
= E_MAIL_REPLY_TO_ALL
;
4294 g_warn_if_reached ();
4296 if (context
->style
== E_MAIL_REPLY_STYLE_UNKNOWN
)
4297 context
->style
= default_style
;
4299 context
->template_preserve_subject
= gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (preserve_message_subject
));
4301 if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (apply_template
))) {
4302 e_mail_templates_apply (context
->source_message
, template_folder
, template_message_uid
,
4303 NULL
, alt_reply_template_applied_cb
, context
);
4306 e_msg_composer_new (context
->shell
, alt_reply_composer_created_cb
, context
);
4309 g_clear_object (&template_folder
);
4310 g_free (template_message_uid
);
4313 gtk_widget_destroy (dialog
);
4314 g_clear_object (&settings
);
4318 * em_utils_reply_to_message:
4319 * @composer: an #EMsgComposer
4320 * @message: a #CamelMimeMessage
4321 * @folder: a #CamelFolder, or %NULL
4322 * @message_uid: the UID of @message, or %NULL
4323 * @type: the type of reply to create
4324 * @style: the reply style to use
4325 * @source: source to inherit view settings from
4326 * @address: used for E_MAIL_REPLY_TO_RECIPIENT @type
4327 * @reply_flags: bit-or of #EMailReplyFlags
4329 * Creates a new composer ready to reply to @message.
4331 * @folder and @message_uid may be supplied in order to update the message
4332 * flags once it has been replied to.
4335 em_utils_reply_to_message (EMsgComposer
*composer
,
4336 CamelMimeMessage
*message
,
4337 CamelFolder
*folder
,
4338 const gchar
*message_uid
,
4339 EMailReplyType type
,
4340 EMailReplyStyle style
,
4341 EMailPartList
*parts_list
,
4342 CamelInternetAddress
*address
,
4343 EMailReplyFlags reply_flags
)
4345 ESourceRegistry
*registry
;
4346 CamelInternetAddress
*to
, *cc
;
4347 CamelNNTPAddress
*postto
= NULL
;
4349 ESourceMailCompositionReplyStyle prefer_reply_style
= E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT
;
4351 EContentEditor
*cnt_editor
;
4352 gchar
*identity_uid
= NULL
, *identity_name
= NULL
, *identity_address
= NULL
;
4355 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4356 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message
));
4358 cnt_editor
= e_html_editor_get_content_editor (e_msg_composer_get_editor (composer
));
4360 if ((reply_flags
& (E_MAIL_REPLY_FLAG_FORMAT_PLAIN
| E_MAIL_REPLY_FLAG_FORMAT_HTML
)) != 0) {
4361 e_content_editor_set_html_mode (cnt_editor
, (reply_flags
& E_MAIL_REPLY_FLAG_FORMAT_HTML
) != 0);
4364 if ((reply_flags
& (E_MAIL_REPLY_FLAG_TOP_POSTING
| E_MAIL_REPLY_FLAG_BOTTOM_POSTING
)) != 0) {
4365 e_content_editor_set_start_bottom (cnt_editor
,
4366 (reply_flags
& E_MAIL_REPLY_FLAG_TOP_POSTING
) != 0 ?
4367 E_THREE_STATE_OFF
: E_THREE_STATE_ON
);
4370 to
= camel_internet_address_new ();
4371 cc
= camel_internet_address_new ();
4373 shell
= e_msg_composer_get_shell (composer
);
4374 registry
= e_shell_get_registry (shell
);
4376 /* This returns a new ESource reference. */
4377 source
= em_utils_check_send_account_override (shell
, message
, folder
, &identity_name
, &identity_address
);
4379 source
= em_utils_guess_mail_identity_with_recipients_and_sort (
4380 registry
, message
, folder
, message_uid
, &identity_name
, &identity_address
, sort_sources_by_ui
, shell
);
4381 if (source
!= NULL
) {
4382 identity_uid
= e_source_dup_uid (source
);
4383 if (!(reply_flags
& E_MAIL_REPLY_FLAG_FORCE_STYLE
) &&
4384 e_source_has_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
)) {
4385 ESourceMailComposition
*extension
;
4387 extension
= e_source_get_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
);
4388 prefer_reply_style
= e_source_mail_composition_get_reply_style (extension
);
4391 g_object_unref (source
);
4394 flags
= CAMEL_MESSAGE_ANSWERED
| CAMEL_MESSAGE_SEEN
;
4396 if (!address
&& (type
== E_MAIL_REPLY_TO_FROM
|| type
== E_MAIL_REPLY_TO_SENDER
) &&
4397 folder
&& !emcu_folder_is_inbox (folder
) && em_utils_folder_is_sent (registry
, folder
))
4398 type
= E_MAIL_REPLY_TO_ALL
;
4401 case E_MAIL_REPLY_TO_FROM
:
4403 postto
= camel_nntp_address_new ();
4405 get_reply_from (message
, to
, postto
);
4407 case E_MAIL_REPLY_TO_RECIPIENT
:
4409 postto
= camel_nntp_address_new ();
4411 get_reply_recipient (message
, to
, postto
, address
);
4413 case E_MAIL_REPLY_TO_SENDER
:
4415 postto
= camel_nntp_address_new ();
4417 get_reply_sender (message
, to
, postto
);
4419 case E_MAIL_REPLY_TO_LIST
:
4420 flags
|= CAMEL_MESSAGE_ANSWERED_ALL
;
4421 if (get_reply_list (message
, to
))
4424 case E_MAIL_REPLY_TO_ALL
:
4425 flags
|= CAMEL_MESSAGE_ANSWERED_ALL
;
4427 postto
= camel_nntp_address_new ();
4429 em_utils_get_reply_all (registry
, message
, to
, cc
, postto
);
4433 reply_setup_composer (composer
, message
, identity_uid
, identity_name
, identity_address
, to
, cc
, folder
, message_uid
, postto
);
4434 e_msg_composer_add_message_attachments (composer
, message
, TRUE
);
4437 g_object_unref (postto
);
4438 g_object_unref (to
);
4439 g_object_unref (cc
);
4441 /* If there was no send-account override */
4442 if (!identity_uid
) {
4443 EComposerHeaderTable
*header_table
;
4444 gchar
*used_identity_uid
;
4446 header_table
= e_msg_composer_get_header_table (composer
);
4447 used_identity_uid
= e_composer_header_table_dup_identity_uid (header_table
, NULL
, NULL
);
4449 if (used_identity_uid
) {
4450 source
= e_source_registry_ref_source (e_shell_get_registry (shell
), used_identity_uid
);
4452 if (!(reply_flags
& E_MAIL_REPLY_FLAG_FORCE_STYLE
) &&
4453 e_source_has_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
)) {
4454 ESourceMailComposition
*extension
;
4456 extension
= e_source_get_extension (source
, E_SOURCE_EXTENSION_MAIL_COMPOSITION
);
4457 prefer_reply_style
= e_source_mail_composition_get_reply_style (extension
);
4460 g_object_unref (source
);
4464 g_free (used_identity_uid
);
4467 switch (prefer_reply_style
) {
4468 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT
:
4469 /* Do nothing, keep the passed-in reply style. */
4471 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_QUOTED
:
4472 style
= E_MAIL_REPLY_STYLE_QUOTED
;
4474 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DO_NOT_QUOTE
:
4475 style
= E_MAIL_REPLY_STYLE_DO_NOT_QUOTE
;
4477 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_ATTACH
:
4478 style
= E_MAIL_REPLY_STYLE_ATTACH
;
4480 case E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_OUTLOOK
:
4481 style
= E_MAIL_REPLY_STYLE_OUTLOOK
;
4485 composer_set_body (composer
, message
, style
, parts_list
);
4487 if (folder
!= NULL
) {
4488 gchar
*folder_uri
= NULL
, *tmp_message_uid
= NULL
;
4490 em_utils_get_real_folder_uri_and_message_uid (folder
, message_uid
, &folder_uri
, &tmp_message_uid
);
4492 e_msg_composer_set_source_headers (
4493 composer
, folder_uri
, tmp_message_uid
, flags
);
4495 g_free (folder_uri
);
4496 g_free (tmp_message_uid
);
4499 /* because some reply types can change recipients after the composer is populated */
4500 em_utils_apply_send_account_override_to_composer (composer
, folder
);
4502 /* This is required to be done (also) at the end */
4503 if ((reply_flags
& (E_MAIL_REPLY_FLAG_TOP_POSTING
| E_MAIL_REPLY_FLAG_BOTTOM_POSTING
)) != 0) {
4504 e_content_editor_set_start_bottom (cnt_editor
,
4505 (reply_flags
& E_MAIL_REPLY_FLAG_TOP_POSTING
) != 0 ?
4506 E_THREE_STATE_OFF
: E_THREE_STATE_ON
);
4509 composer_set_no_change (composer
);
4511 gtk_widget_show (GTK_WIDGET (composer
));
4513 g_free (identity_uid
);
4514 g_free (identity_name
);
4515 g_free (identity_address
);
4519 post_header_clicked_cb (EComposerPostHeader
*header
,
4520 EMailSession
*session
)
4522 GtkTreeSelection
*selection
;
4523 EMFolderSelector
*selector
;
4524 EMFolderTreeModel
*model
;
4525 EMFolderTree
*folder_tree
;
4528 const gchar
*caption
;
4530 /* FIXME Limit the folder tree to the NNTP account? */
4531 model
= em_folder_tree_model_get_default ();
4533 dialog
= em_folder_selector_new (
4534 /* FIXME GTK_WINDOW (composer) */ NULL
, model
);
4536 gtk_window_set_title (GTK_WINDOW (dialog
), _("Posting destination"));
4538 selector
= EM_FOLDER_SELECTOR (dialog
);
4539 em_folder_selector_set_can_create (selector
, TRUE
);
4541 caption
= _("Choose folders to post the message to.");
4542 em_folder_selector_set_caption (selector
, caption
);
4544 folder_tree
= em_folder_selector_get_folder_tree (selector
);
4546 em_folder_tree_set_excluded (
4548 EMFT_EXCLUDE_NOSELECT
|
4549 EMFT_EXCLUDE_VIRTUAL
|
4550 EMFT_EXCLUDE_VTRASH
);
4552 selection
= gtk_tree_view_get_selection (GTK_TREE_VIEW (folder_tree
));
4553 gtk_tree_selection_set_mode (selection
, GTK_SELECTION_MULTIPLE
);
4555 list
= e_composer_post_header_get_folders (header
);
4556 em_folder_tree_set_selected_list (folder_tree
, list
, FALSE
);
4557 g_list_foreach (list
, (GFunc
) g_free
, NULL
);
4560 if (gtk_dialog_run (GTK_DIALOG (dialog
)) != GTK_RESPONSE_OK
) {
4561 /* Prevent the header's "custom" flag from being reset,
4562 * which is what the default method will do next. */
4563 g_signal_stop_emission_by_name (header
, "clicked");
4567 list
= em_folder_tree_get_selected_uris (folder_tree
);
4568 e_composer_post_header_set_folders (header
, list
);
4569 g_list_foreach (list
, (GFunc
) g_free
, NULL
);
4573 gtk_widget_destroy (dialog
);
4577 * em_configure_new_composer:
4578 * @composer: a newly created #EMsgComposer
4580 * Integrates a newly created #EMsgComposer into the mail backend. The
4581 * composer can't link directly to the mail backend without introducing
4582 * circular library dependencies, so this function finishes configuring
4583 * things the #EMsgComposer instance can't do itself.
4586 em_configure_new_composer (EMsgComposer
*composer
,
4587 EMailSession
*session
)
4589 EComposerHeaderTable
*table
;
4590 EComposerHeaderType header_type
;
4591 EComposerHeader
*header
;
4593 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4594 g_return_if_fail (E_IS_MAIL_SESSION (session
));
4596 header_type
= E_COMPOSER_HEADER_POST_TO
;
4597 table
= e_msg_composer_get_header_table (composer
);
4598 header
= e_composer_header_table_get_header (table
, header_type
);
4601 composer
, "presend",
4602 G_CALLBACK (composer_presend_check_recipients
), session
);
4605 composer
, "presend",
4606 G_CALLBACK (composer_presend_check_identity
), session
);
4609 composer
, "presend",
4610 G_CALLBACK (composer_presend_check_plugins
), session
);
4613 composer
, "presend",
4614 G_CALLBACK (composer_presend_check_subject
), session
);
4617 composer
, "presend",
4618 G_CALLBACK (composer_presend_check_unwanted_html
), session
);
4622 G_CALLBACK (em_utils_composer_send_cb
), session
);
4625 composer
, "save-to-drafts",
4626 G_CALLBACK (em_utils_composer_save_to_drafts_cb
), session
);
4629 composer
, "save-to-outbox",
4630 G_CALLBACK (em_utils_composer_save_to_outbox_cb
), session
);
4634 G_CALLBACK (em_utils_composer_print_cb
), session
);
4636 /* Handle "Post To:" button clicks, which displays a folder tree
4637 * widget. The composer doesn't know about folder tree widgets,
4638 * so it can't handle this itself.
4640 * Note: This is a G_SIGNAL_RUN_LAST signal, which allows us to
4641 * stop the signal emission if the user cancels or closes
4642 * the folder selector dialog. See the handler function. */
4645 G_CALLBACK (post_header_clicked_cb
), session
);
4648 /* free returned pointer with g_object_unref(), if not NULL */
4650 em_utils_check_send_account_override (EShell
*shell
,
4651 CamelMimeMessage
*message
,
4652 CamelFolder
*folder
,
4653 gchar
**out_alias_name
,
4654 gchar
**out_alias_address
)
4656 EMailBackend
*mail_backend
;
4657 EMailSendAccountOverride
*account_override
;
4658 CamelInternetAddress
*to
= NULL
, *cc
= NULL
, *bcc
= NULL
;
4659 gchar
*folder_uri
= NULL
, *account_uid
, *alias_name
= NULL
, *alias_address
= NULL
;
4660 ESource
*account_source
= NULL
;
4661 ESourceRegistry
*source_registry
;
4663 g_return_val_if_fail (E_IS_SHELL (shell
), NULL
);
4665 if (!message
&& !folder
)
4669 to
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_TO
);
4670 cc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_CC
);
4671 bcc
= camel_mime_message_get_recipients (message
, CAMEL_RECIPIENT_TYPE_BCC
);
4674 mail_backend
= E_MAIL_BACKEND (e_shell_get_backend_by_name (shell
, "mail"));
4675 g_return_val_if_fail (mail_backend
!= NULL
, NULL
);
4678 folder_uri
= e_mail_folder_uri_from_folder (folder
);
4680 source_registry
= e_shell_get_registry (shell
);
4681 account_override
= e_mail_backend_get_send_account_override (mail_backend
);
4682 account_uid
= e_mail_send_account_override_get_account_uid (account_override
, folder_uri
, to
, cc
, bcc
, &alias_name
, &alias_address
);
4684 while (account_uid
) {
4685 account_source
= e_source_registry_ref_source (source_registry
, account_uid
);
4689 /* stored send account override settings contain a reference
4690 * to a dropped account, thus cleanup it now */
4691 e_mail_send_account_override_remove_for_account_uid (account_override
, account_uid
, alias_name
, alias_address
);
4693 g_free (account_uid
);
4694 g_free (alias_name
);
4695 g_free (alias_address
);
4698 alias_address
= NULL
;
4700 account_uid
= e_mail_send_account_override_get_account_uid (account_override
, folder_uri
, to
, cc
, bcc
, &alias_name
, &alias_address
);
4704 *out_alias_name
= alias_name
;
4706 g_free (alias_name
);
4708 if (out_alias_address
)
4709 *out_alias_address
= alias_address
;
4711 g_free (alias_address
);
4713 g_free (folder_uri
);
4714 g_free (account_uid
);
4716 return account_source
;
4720 em_utils_apply_send_account_override_to_composer (EMsgComposer
*composer
,
4721 CamelFolder
*folder
)
4723 CamelMimeMessage
*message
;
4724 EComposerHeaderTable
*header_table
;
4727 gchar
*alias_name
= NULL
, *alias_address
= NULL
;
4729 g_return_if_fail (E_IS_MSG_COMPOSER (composer
));
4731 shell
= e_msg_composer_get_shell (composer
);
4732 message
= em_utils_get_composer_recipients_as_message (composer
);
4733 source
= em_utils_check_send_account_override (shell
, message
, folder
, &alias_name
, &alias_address
);
4734 g_clear_object (&message
);
4739 header_table
= e_msg_composer_get_header_table (composer
);
4740 e_composer_header_table_set_identity_uid (header_table
, e_source_get_uid (source
), alias_name
, alias_address
);
4742 g_object_unref (source
);
4743 g_free (alias_name
);
4744 g_free (alias_address
);