4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) version 3.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with the program; if not, see <http://www.gnu.org/licenses/>
19 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
21 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
22 * Copyright (C) 2009 Intel Corporation
26 /* mail-session.c: handles the session information and resource manipulation */
36 #include <glib/gi18n.h>
37 #include <glib/gstdio.h>
42 #include <canberra-gtk.h>
45 #include <libebackend/libebackend.h>
47 #include "e-mail-account-store.h"
49 #include "e-util/e-util.h"
50 #include "e-util/e-util-private.h"
52 #include "shell/e-shell.h"
53 #include "shell/e-shell-view.h"
54 #include "shell/e-shell-content.h"
55 #include "shell/e-shell-window.h"
57 #include "e-mail-ui-session.h"
58 #include "em-composer-utils.h"
59 #include "em-filter-context.h"
60 #include "em-vfolder-editor-context.h"
61 #include "em-filter-rule.h"
63 #include "mail-send-recv.h"
65 #define E_MAIL_UI_SESSION_GET_PRIVATE(obj) \
66 (G_TYPE_INSTANCE_GET_PRIVATE \
67 ((obj), E_TYPE_MAIL_UI_SESSION, EMailUISessionPrivate))
69 typedef struct _SourceContext SourceContext
;
71 struct _EMailUISessionPrivate
{
73 ESourceRegistry
*registry
;
74 EMailAccountStore
*account_store
;
75 EMailLabelListStore
*label_store
;
76 EPhotoCache
*photo_cache
;
93 static guint signals
[LAST_SIGNAL
];
95 G_DEFINE_TYPE_WITH_CODE (
99 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE
, NULL
))
101 struct _SourceContext
{
102 EMailUISession
*session
;
103 CamelService
*service
;
106 /* Support for CamelSession.get_filter_driver () *****************************/
109 get_folder (CamelFilterDriver
*d
,
114 EMailSession
*session
= E_MAIL_SESSION (user_data
);
116 /* FIXME Not passing a GCancellable here. */
117 /* FIXME Need a camel_filter_driver_get_session(). */
118 return e_mail_session_uri_to_folder_sync (
119 session
, uri
, 0, NULL
, error
);
123 session_play_sound_cb (const gchar
*filename
)
126 if (filename
!= NULL
&& *filename
!= '\0')
128 ca_gtk_context_get (), 0,
129 CA_PROP_MEDIA_FILENAME
, filename
,
139 session_play_sound (CamelFilterDriver
*driver
,
140 const gchar
*filename
,
144 G_PRIORITY_DEFAULT_IDLE
,
145 (GSourceFunc
) session_play_sound_cb
,
146 g_strdup (filename
), (GDestroyNotify
) g_free
);
150 session_system_beep (CamelFilterDriver
*driver
,
153 g_idle_add ((GSourceFunc
) session_play_sound_cb
, NULL
);
156 static CamelFilterDriver
*
157 main_get_filter_driver (CamelSession
*session
,
161 EMailSession
*ms
= E_MAIL_SESSION (session
);
162 CamelFilterDriver
*driver
;
163 EFilterRule
*rule
= NULL
;
164 const gchar
*config_dir
;
165 gchar
*user
, *system
;
168 EMailUISessionPrivate
*priv
;
169 gboolean add_junk_test
;
171 priv
= E_MAIL_UI_SESSION_GET_PRIVATE (session
);
173 settings
= g_settings_new ("org.gnome.evolution.mail");
175 config_dir
= mail_session_get_config_dir ();
176 user
= g_build_filename (config_dir
, "filters.xml", NULL
);
177 system
= g_build_filename (EVOLUTION_PRIVDATADIR
, "filtertypes.xml", NULL
);
178 fc
= (ERuleContext
*) em_filter_context_new (ms
);
179 e_rule_context_load (fc
, system
, user
);
183 driver
= camel_filter_driver_new (session
);
184 camel_filter_driver_set_folder_func (driver
, get_folder
, session
);
186 if (g_settings_get_boolean (settings
, "filters-log-actions")) {
187 if (priv
->filter_logfile
== NULL
) {
190 filename
= g_settings_get_string (settings
, "filters-log-file");
192 priv
->filter_logfile
= g_fopen (filename
, "a+");
197 if (priv
->filter_logfile
)
198 camel_filter_driver_set_logfile (driver
, priv
->filter_logfile
);
201 camel_filter_driver_set_shell_func (driver
, mail_execute_shell_command
, NULL
);
202 camel_filter_driver_set_play_sound_func (driver
, session_play_sound
, NULL
);
203 camel_filter_driver_set_system_beep_func (driver
, session_system_beep
, NULL
);
206 priv
->check_junk
&& (
207 g_str_equal (type
, E_FILTER_SOURCE_INCOMING
) ||
208 g_str_equal (type
, E_FILTER_SOURCE_JUNKTEST
));
211 /* implicit junk check as 1st rule */
212 camel_filter_driver_add_rule (
213 driver
, "Junk check", "(junk-test)",
214 "(begin (set-system-flag \"junk\"))");
217 if (strcmp (type
, E_FILTER_SOURCE_JUNKTEST
) != 0) {
218 GString
*fsearch
, *faction
;
220 fsearch
= g_string_new ("");
221 faction
= g_string_new ("");
223 if (!strcmp (type
, E_FILTER_SOURCE_DEMAND
))
224 type
= E_FILTER_SOURCE_INCOMING
;
226 /* add the user-defined rules next */
227 while ((rule
= e_rule_context_next_rule (fc
, rule
, type
))) {
228 g_string_truncate (fsearch
, 0);
229 g_string_truncate (faction
, 0);
231 /* skip disabled rules */
235 e_filter_rule_build_code (rule
, fsearch
);
236 em_filter_rule_build_action (
237 EM_FILTER_RULE (rule
), faction
);
238 camel_filter_driver_add_rule (
240 fsearch
->str
, faction
->str
);
243 g_string_free (fsearch
, TRUE
);
244 g_string_free (faction
, TRUE
);
249 g_object_unref (settings
);
255 source_context_free (SourceContext
*context
)
257 if (context
->session
!= NULL
)
258 g_object_unref (context
->session
);
260 if (context
->service
!= NULL
)
261 g_object_unref (context
->service
);
263 g_slice_free (SourceContext
, context
);
267 mail_ui_session_add_service_cb (SourceContext
*context
)
269 EMailAccountStore
*store
;
271 /* The CamelService should be fully initialized by now. */
272 store
= e_mail_ui_session_get_account_store (context
->session
);
273 e_mail_account_store_add_service (store
, context
->service
);
279 mail_ui_session_set_property (GObject
*object
,
284 switch (property_id
) {
285 case PROP_CHECK_JUNK
:
286 e_mail_ui_session_set_check_junk (
287 E_MAIL_UI_SESSION (object
),
288 g_value_get_boolean (value
));
292 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
296 mail_ui_session_get_property (GObject
*object
,
301 switch (property_id
) {
302 case PROP_ACCOUNT_STORE
:
305 e_mail_ui_session_get_account_store (
306 E_MAIL_UI_SESSION (object
)));
309 case PROP_CHECK_JUNK
:
310 g_value_set_boolean (
312 e_mail_ui_session_get_check_junk (
313 E_MAIL_UI_SESSION (object
)));
316 case PROP_LABEL_STORE
:
319 e_mail_ui_session_get_label_store (
320 E_MAIL_UI_SESSION (object
)));
323 case PROP_PHOTO_CACHE
:
326 e_mail_ui_session_get_photo_cache (
327 E_MAIL_UI_SESSION (object
)));
331 G_OBJECT_WARN_INVALID_PROPERTY_ID (object
, property_id
, pspec
);
335 mail_ui_session_dispose (GObject
*object
)
337 EMailUISessionPrivate
*priv
;
339 priv
= E_MAIL_UI_SESSION_GET_PRIVATE (object
);
341 if (priv
->registry
!= NULL
) {
342 g_object_unref (priv
->registry
);
343 priv
->registry
= NULL
;
346 if (priv
->account_store
!= NULL
) {
347 e_mail_account_store_clear (priv
->account_store
);
348 g_object_unref (priv
->account_store
);
349 priv
->account_store
= NULL
;
352 if (priv
->label_store
!= NULL
) {
353 g_object_unref (priv
->label_store
);
354 priv
->label_store
= NULL
;
357 if (priv
->photo_cache
!= NULL
) {
358 g_object_unref (priv
->photo_cache
);
359 priv
->photo_cache
= NULL
;
362 /* Chain up to parent's dispose() method. */
363 G_OBJECT_CLASS (e_mail_ui_session_parent_class
)->dispose (object
);
367 mail_ui_session_constructed (GObject
*object
)
369 EMailUISessionPrivate
*priv
;
370 EMFolderTreeModel
*folder_tree_model
;
371 ESourceRegistry
*registry
;
372 EClientCache
*client_cache
;
373 EMailSession
*session
;
376 session
= E_MAIL_SESSION (object
);
377 shell
= e_shell_get_default ();
379 /* synchronize online state first, before any CamelService is created */
380 g_object_bind_property (
383 G_BINDING_SYNC_CREATE
);
385 priv
= E_MAIL_UI_SESSION_GET_PRIVATE (object
);
386 priv
->account_store
= e_mail_account_store_new (session
);
388 /* Keep our own reference to the ESourceRegistry so we
389 * can easily disconnect signal handlers in dispose(). */
390 registry
= e_mail_session_get_registry (session
);
391 priv
->registry
= g_object_ref (registry
);
393 client_cache
= e_shell_get_client_cache (shell
);
394 priv
->photo_cache
= e_photo_cache_new (client_cache
);
396 /* XXX Make sure the folder tree model is created before we
397 * add built-in CamelStores so it gets signals from the
400 * XXX This is creating a circular reference. Perhaps the
401 * model should only hold a weak pointer to EMailSession?
403 * FIXME EMailSession should just own the default instance.
405 folder_tree_model
= em_folder_tree_model_get_default ();
406 em_folder_tree_model_set_session (folder_tree_model
, session
);
408 /* Chain up to parent's constructed() method. */
409 G_OBJECT_CLASS (e_mail_ui_session_parent_class
)->constructed (object
);
412 static CamelService
*
413 mail_ui_session_add_service (CamelSession
*session
,
415 const gchar
*protocol
,
416 CamelProviderType type
,
419 CamelService
*service
;
421 /* Chain up to parent's constructed() method. */
422 service
= CAMEL_SESSION_CLASS (e_mail_ui_session_parent_class
)->
423 add_service (session
, uid
, protocol
, type
, error
);
425 /* Inform the EMailAccountStore of the new CamelService
426 * from an idle callback so the service has a chance to
427 * fully initialize first. */
428 if (CAMEL_IS_STORE (service
)) {
429 SourceContext
*context
;
431 context
= g_slice_new0 (SourceContext
);
432 context
->session
= g_object_ref (session
);
433 context
->service
= g_object_ref (service
);
435 /* Prioritize ahead of GTK+ redraws. */
437 G_PRIORITY_HIGH_IDLE
,
438 (GSourceFunc
) mail_ui_session_add_service_cb
,
439 context
, (GDestroyNotify
) source_context_free
);
446 mail_ui_session_remove_service (CamelSession
*session
,
447 CamelService
*service
)
449 EMailAccountStore
*store
;
450 EMailUISession
*ui_session
;
452 /* Passing a NULL parent window skips confirmation prompts. */
453 ui_session
= E_MAIL_UI_SESSION (session
);
454 store
= e_mail_ui_session_get_account_store (ui_session
);
455 e_mail_account_store_remove_service (store
, NULL
, service
);
459 e_mail_ui_session_trust_prompt (CamelSession
*session
,
460 CamelService
*service
,
461 GTlsCertificate
*certificate
,
462 GTlsCertificateFlags errors
)
464 g_type_ensure (E_TYPE_MAIL_UI_SESSION
);
466 return CAMEL_SESSION_CLASS (e_mail_ui_session_parent_class
)->
467 trust_prompt (session
, service
, certificate
, errors
);
470 static CamelFilterDriver
*
471 mail_ui_session_get_filter_driver (CamelSession
*session
,
475 return (CamelFilterDriver
*) mail_call_main (
476 MAIL_CALL_p_ppp
, (MailMainFunc
) main_get_filter_driver
,
477 session
, type
, error
);
481 mail_ui_session_lookup_addressbook (CamelSession
*session
,
484 CamelInternetAddress
*cia
;
485 gboolean known_address
= FALSE
;
487 /* FIXME CamelSession's lookup_addressbook() method needs redone.
488 * No GCancellable provided, no means of reporting an error. */
490 if (!mail_config_get_lookup_book ())
493 cia
= camel_internet_address_new ();
495 if (camel_address_decode (CAMEL_ADDRESS (cia
), name
) > 0) {
496 GError
*error
= NULL
;
498 e_mail_ui_session_check_known_address_sync (
499 E_MAIL_UI_SESSION (session
), cia
,
500 mail_config_get_lookup_book_local_only (),
501 NULL
, &known_address
, &error
);
504 g_warning ("%s: %s", G_STRFUNC
, error
->message
);
505 g_error_free (error
);
509 "%s: Failed to decode internet "
510 "address '%s'", G_STRFUNC
, name
);
513 g_object_unref (cia
);
515 return known_address
;
519 mail_ui_session_user_alert (CamelSession
*session
,
520 CamelService
*service
,
521 CamelSessionAlertType type
,
522 const gchar
*alert_message
)
526 const gchar
*alert_tag
;
529 shell
= e_shell_get_default ();
532 case CAMEL_SESSION_ALERT_INFO
:
533 alert_tag
= "mail:user-alert-info";
535 case CAMEL_SESSION_ALERT_WARNING
:
536 alert_tag
= "mail:user-alert-warning";
538 case CAMEL_SESSION_ALERT_ERROR
:
539 alert_tag
= "mail:user-alert-error";
542 g_return_if_reached ();
545 display_name
= camel_service_dup_display_name (service
);
547 /* Just submit the alert to the EShell rather than hunting for
548 * a suitable window. This will post it to all shell windows in
549 * all views, but if it's coming from the server then it must be
550 * important... right? */
551 alert
= e_alert_new (alert_tag
, display_name
, alert_message
, NULL
);
552 e_shell_submit_alert (shell
, alert
);
553 g_object_unref (alert
);
555 g_free (display_name
);
559 mail_ui_session_refresh_service (EMailSession
*session
,
560 CamelService
*service
)
562 if (camel_session_get_online (CAMEL_SESSION (session
)))
563 mail_receive_service (service
);
566 static EMVFolderContext
*
567 mail_ui_session_create_vfolder_context (EMailSession
*session
)
569 return (EMVFolderContext
*) em_vfolder_editor_context_new (session
);
573 e_mail_ui_session_class_init (EMailUISessionClass
*class)
575 GObjectClass
*object_class
;
576 CamelSessionClass
*session_class
;
577 EMailSessionClass
*mail_session_class
;
579 g_type_class_add_private (class, sizeof (EMailUISessionPrivate
));
581 object_class
= G_OBJECT_CLASS (class);
582 object_class
->set_property
= mail_ui_session_set_property
;
583 object_class
->get_property
= mail_ui_session_get_property
;
584 object_class
->dispose
= mail_ui_session_dispose
;
585 object_class
->constructed
= mail_ui_session_constructed
;
587 session_class
= CAMEL_SESSION_CLASS (class);
588 session_class
->add_service
= mail_ui_session_add_service
;
589 session_class
->remove_service
= mail_ui_session_remove_service
;
590 session_class
->get_filter_driver
= mail_ui_session_get_filter_driver
;
591 session_class
->lookup_addressbook
= mail_ui_session_lookup_addressbook
;
592 session_class
->user_alert
= mail_ui_session_user_alert
;
594 mail_session_class
= E_MAIL_SESSION_CLASS (class);
595 mail_session_class
->create_vfolder_context
= mail_ui_session_create_vfolder_context
;
596 mail_session_class
->refresh_service
= mail_ui_session_refresh_service
;
598 g_object_class_install_property (
601 g_param_spec_boolean (
604 "Check if incoming messages are junk",
608 G_PARAM_STATIC_STRINGS
));
610 g_object_class_install_property (
613 g_param_spec_object (
617 E_TYPE_MAIL_LABEL_LIST_STORE
,
619 G_PARAM_STATIC_STRINGS
));
621 g_object_class_install_property (
624 g_param_spec_object (
627 "Contact photo cache",
630 G_PARAM_STATIC_STRINGS
));
632 signals
[ACTIVITY_ADDED
] = g_signal_new (
634 G_OBJECT_CLASS_TYPE (class),
636 G_STRUCT_OFFSET (EMailUISessionClass
, activity_added
),
638 g_cclosure_marshal_VOID__OBJECT
,
644 e_mail_ui_session_init (EMailUISession
*session
)
646 session
->priv
= E_MAIL_UI_SESSION_GET_PRIVATE (session
);
647 session
->priv
->label_store
= e_mail_label_list_store_new ();
651 e_mail_ui_session_new (ESourceRegistry
*registry
)
653 const gchar
*user_data_dir
;
654 const gchar
*user_cache_dir
;
656 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry
), NULL
);
658 user_data_dir
= mail_session_get_data_dir ();
659 user_cache_dir
= mail_session_get_cache_dir ();
661 return g_object_new (
662 E_TYPE_MAIL_UI_SESSION
,
663 "registry", registry
,
664 "user-data-dir", user_data_dir
,
665 "user-cache-dir", user_cache_dir
,
670 e_mail_ui_session_get_account_store (EMailUISession
*session
)
672 g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session
), NULL
);
674 return session
->priv
->account_store
;
678 * e_mail_ui_session_get_check_junk:
679 * @session: an #EMailUISession
681 * Returns whether to automatically check incoming messages for junk content.
683 * Returns: whether to check for junk messages
686 e_mail_ui_session_get_check_junk (EMailUISession
*session
)
688 g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session
), FALSE
);
690 return session
->priv
->check_junk
;
694 * e_mail_ui_session_set_check_junk:
695 * @session: an #EMailUISession
696 * @check_junk: whether to check for junk messages
698 * Sets whether to automatically check incoming messages for junk content.
701 e_mail_ui_session_set_check_junk (EMailUISession
*session
,
704 g_return_if_fail (E_IS_MAIL_UI_SESSION (session
));
706 if (check_junk
== session
->priv
->check_junk
)
709 session
->priv
->check_junk
= check_junk
;
711 g_object_notify (G_OBJECT (session
), "check-junk");
714 EMailLabelListStore
*
715 e_mail_ui_session_get_label_store (EMailUISession
*session
)
717 g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session
), NULL
);
719 return session
->priv
->label_store
;
723 e_mail_ui_session_get_photo_cache (EMailUISession
*session
)
725 g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session
), NULL
);
727 return session
->priv
->photo_cache
;
731 e_mail_ui_session_add_activity (EMailUISession
*session
,
734 g_return_if_fail (E_IS_MAIL_UI_SESSION (session
));
735 g_return_if_fail (E_IS_ACTIVITY (activity
));
737 g_signal_emit (session
, signals
[ACTIVITY_ADDED
], 0, activity
);
741 * e_mail_ui_session_check_known_address_sync:
742 * @session: an #EMailUISession
743 * @addr: a #CamelInternetAddress
744 * @check_local_only: only check the builtin address book
745 * @cancellable: optional #GCancellable object, or %NULL
746 * @out_known_address: return location for the determination of
747 * whether @addr is a known address
748 * @error: return location for a #GError, or %NULL
750 * Determines whether @addr is a known email address by querying address
751 * books for contacts with a matching email address. If @check_local_only
752 * is %TRUE then only the builtin address book is checked, otherwise all
753 * enabled address books are checked.
755 * The result of the query is returned through the @out_known_address
756 * boolean pointer, not through the return value. The return value only
757 * indicates whether the address book queries were completed successfully.
758 * If an error occurred, the function sets @error and returns %FALSE.
760 * Returns: whether address books were successfully queried
763 e_mail_ui_session_check_known_address_sync (EMailUISession
*session
,
764 CamelInternetAddress
*addr
,
765 gboolean check_local_only
,
766 GCancellable
*cancellable
,
767 gboolean
*out_known_address
,
770 EPhotoCache
*photo_cache
;
771 EClientCache
*client_cache
;
772 ESourceRegistry
*registry
;
773 EBookQuery
*book_query
;
775 const gchar
*email_address
= NULL
;
776 gchar
*book_query_string
;
777 gboolean known_address
= FALSE
;
778 gboolean success
= TRUE
;
780 g_return_val_if_fail (E_IS_MAIL_UI_SESSION (session
), FALSE
);
781 g_return_val_if_fail (CAMEL_IS_INTERNET_ADDRESS (addr
), FALSE
);
783 camel_internet_address_get (addr
, 0, NULL
, &email_address
);
784 g_return_val_if_fail (email_address
!= NULL
, FALSE
);
786 /* XXX EPhotoCache holds a reference on EClientCache, which
787 * we need. EMailUISession should probably hold its own
788 * EClientCache reference, but this will do for now. */
789 photo_cache
= e_mail_ui_session_get_photo_cache (session
);
790 client_cache
= e_photo_cache_ref_client_cache (photo_cache
);
791 registry
= e_client_cache_ref_registry (client_cache
);
793 book_query
= e_book_query_field_test (
794 E_CONTACT_EMAIL
, E_BOOK_QUERY_IS
, email_address
);
795 book_query_string
= e_book_query_to_string (book_query
);
796 e_book_query_unref (book_query
);
798 if (check_local_only
) {
801 source
= e_source_registry_ref_builtin_address_book (registry
);
802 list
= g_list_prepend (NULL
, g_object_ref (source
));
803 g_object_unref (source
);
805 list
= e_source_registry_list_sources (
806 registry
, E_SOURCE_EXTENSION_ADDRESS_BOOK
);
809 for (link
= list
; link
!= NULL
; link
= g_list_next (link
)) {
810 ESource
*source
= E_SOURCE (link
->data
);
814 /* Skip disabled sources. */
815 if (!e_source_get_enabled (source
))
818 client
= e_client_cache_get_client_sync (
819 client_cache
, source
,
820 E_SOURCE_EXTENSION_ADDRESS_BOOK
,
823 if (client
== NULL
) {
828 success
= e_book_client_get_contacts_uids_sync (
829 E_BOOK_CLIENT (client
), book_query_string
,
830 &uids
, cancellable
, error
);
832 g_object_unref (client
);
835 g_warn_if_fail (uids
== NULL
);
840 g_slist_free_full (uids
, (GDestroyNotify
) g_free
);
841 known_address
= TRUE
;
846 g_list_free_full (list
, (GDestroyNotify
) g_object_unref
);
848 g_free (book_query_string
);
850 g_object_unref (registry
);
851 g_object_unref (client_cache
);
853 if (success
&& out_known_address
!= NULL
)
854 *out_known_address
= known_address
;