1 /*
2 * e-mail-session.c
3 *
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.
7 *
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
11 * for more details.
12 *
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/>.
15 *
16 *
17 * Authors:
18 * Jonathon Jongsma <jonathon.jongsma@collabora.co.uk>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 * Copyright (C) 2009 Intel Corporation
22 *
23 */
24
25 /* mail-session.c: handles the session information and resource manipulation */
26
27 #include "evolution-config.h"
28
29 #include <errno.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #include <glib/gi18n-lib.h>
34 #include <glib/gstdio.h>
35
36 #include <gtk/gtk.h>
37
38 #include <libebackend/libebackend.h>
39 #include <libedataserver/libedataserver.h>
40
41 #include "libemail-engine/mail-mt.h"
42
43 /* This is our hack, not part of libcamel. */
44 #include "camel-null-store.h"
45
46 #include "e-mail-session.h"
47 #include "e-mail-folder-utils.h"
48 #include "e-mail-utils.h"
49 #include "mail-config.h"
50 #include "mail-ops.h"
51 #include "mail-tools.h"
52
53 #define E_MAIL_SESSION_GET_PRIVATE(obj) \
54 (G_TYPE_INSTANCE_GET_PRIVATE \
55 ((obj), E_TYPE_MAIL_SESSION, EMailSessionPrivate))
56
57 typedef struct _AsyncContext AsyncContext;
58 typedef struct _ServiceProxyData ServiceProxyData;
59
60 struct _EMailSessionPrivate {
61 MailFolderCache *folder_cache;
62 ESourceRegistry *registry;
63
64 /* ESource UID -> Timeout ID */
65 GHashTable *auto_refresh_table;
66
67 gulong source_added_handler_id;
68 gulong source_removed_handler_id;
69 gulong source_enabled_handler_id;
70 gulong source_disabled_handler_id;
71 gulong default_mail_account_handler_id;
72 gulong outbox_changed_handler_id;
73
74 CamelService *local_store;
75 CamelService *vfolder_store;
76
77 FILE *filter_logfile;
78 GHashTable *junk_filters;
79
80 /* Local folder cache. */
81 GPtrArray *local_folders;
82 GPtrArray *local_folder_uris;
83
84 guint preparing_flush;
85 guint outbox_flush_id;
86 GMutex preparing_flush_lock;
87
88 GMutex used_services_lock;
89 GCond used_services_cond;
90 GHashTable *used_services;
91
92 GMutex archive_folders_hash_lock;
93 GHashTable *archive_folders_hash; /* ESource::uid ~> archive folder URI */
94 };
95
96 struct _AsyncContext {
97 /* arguments */
98 CamelStoreGetFolderFlags flags;
99 gchar *uid;
100 gchar *uri;
101
102 /* results */
103 CamelFolder *folder;
104 };
105
106 struct _ServiceProxyData {
107 ESource *authentication_source;
108 gulong auth_source_changed_handler_id;
109 };
110
111 enum {
112 PROP_0,
113 PROP_FOLDER_CACHE,
114 PROP_LOCAL_STORE,
115 PROP_REGISTRY,
116 PROP_VFOLDER_STORE
117 };
118
119 static const gchar *local_folder_names[E_MAIL_NUM_LOCAL_FOLDERS] = {
120 N_("Inbox"), /* E_MAIL_LOCAL_FOLDER_INBOX */
121 N_("Drafts"), /* E_MAIL_LOCAL_FOLDER_DRAFTS */
122 N_("Outbox"), /* E_MAIL_LOCAL_FOLDER_OUTBOX */
123 N_("Sent"), /* E_MAIL_LOCAL_FOLDER_SENT */
124 N_("Templates"), /* E_MAIL_LOCAL_FOLDER_TEMPLATES */
125 "Inbox" /* E_MAIL_LOCAL_FOLDER_LOCAL_INBOX */
126 };
127
128 enum {
129 FLUSH_OUTBOX,
130 REFRESH_SERVICE,
131 STORE_ADDED,
132 STORE_REMOVED,
133 ALLOW_AUTH_PROMPT,
134 GET_RECIPIENT_CERTIFICATE,
135 ARCHIVE_FOLDER_CHANGED,
136 CONNECT_STORE,
137 LAST_SIGNAL
138 };
139
140 static guint signals[LAST_SIGNAL];
141
142 static gchar *mail_data_dir;
143 static gchar *mail_cache_dir;
144 static gchar *mail_config_dir;
145
G_DEFINE_TYPE_WITH_CODE(EMailSession,e_mail_session,CAMEL_TYPE_SESSION,G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE,NULL))146 G_DEFINE_TYPE_WITH_CODE (
147 EMailSession,
148 e_mail_session,
149 CAMEL_TYPE_SESSION,
150 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
151
152 static gboolean
153 session_forward_to_flush_outbox_cb (gpointer user_data)
154 {
155 EMailSession *session = E_MAIL_SESSION (user_data);
156
157 g_mutex_lock (&session->priv->preparing_flush_lock);
158 session->priv->preparing_flush = 0;
159 g_mutex_unlock (&session->priv->preparing_flush_lock);
160
161 e_mail_session_flush_outbox (session);
162
163 return FALSE;
164 }
165
166 static void
async_context_free(AsyncContext * context)167 async_context_free (AsyncContext *context)
168 {
169 if (context->folder != NULL)
170 g_object_unref (context->folder);
171
172 g_free (context->uid);
173 g_free (context->uri);
174
175 g_slice_free (AsyncContext, context);
176 }
177
178 static void
service_proxy_data_free(ServiceProxyData * proxy_data)179 service_proxy_data_free (ServiceProxyData *proxy_data)
180 {
181 g_signal_handler_disconnect (
182 proxy_data->authentication_source,
183 proxy_data->auth_source_changed_handler_id);
184
185 g_clear_object (&proxy_data->authentication_source);
186
187 g_slice_free (ServiceProxyData, proxy_data);
188 }
189
190 static void
mail_session_update_proxy_resolver(CamelService * service,ESource * authentication_source)191 mail_session_update_proxy_resolver (CamelService *service,
192 ESource *authentication_source)
193 {
194 GProxyResolver *proxy_resolver = NULL;
195 ESourceAuthentication *extension;
196 CamelSession *session;
197 ESource *source = NULL;
198 gchar *uid;
199
200 session = camel_service_ref_session (service);
201
202 extension = e_source_get_extension (
203 authentication_source,
204 E_SOURCE_EXTENSION_AUTHENTICATION);
205
206 uid = e_source_authentication_dup_proxy_uid (extension);
207 if (uid != NULL) {
208 ESourceRegistry *registry;
209 EMailSession *mail_session;
210
211 mail_session = E_MAIL_SESSION (session);
212 registry = e_mail_session_get_registry (mail_session);
213 source = e_source_registry_ref_source (registry, uid);
214 g_free (uid);
215 }
216
217 if (source != NULL) {
218 proxy_resolver = G_PROXY_RESOLVER (source);
219 if (!g_proxy_resolver_is_supported (proxy_resolver))
220 proxy_resolver = NULL;
221 }
222
223 camel_service_set_proxy_resolver (service, proxy_resolver);
224
225 g_clear_object (&session);
226 g_clear_object (&source);
227 }
228
229 static void
mail_session_auth_source_changed_cb(ESource * authentication_source,GWeakRef * service_weak_ref)230 mail_session_auth_source_changed_cb (ESource *authentication_source,
231 GWeakRef *service_weak_ref)
232 {
233 CamelService *service;
234
235 service = g_weak_ref_get (service_weak_ref);
236
237 if (service != NULL) {
238 mail_session_update_proxy_resolver (
239 service, authentication_source);
240 g_object_unref (service);
241 }
242 }
243
244 static void
mail_session_configure_proxy_resolver(ESourceRegistry * registry,CamelService * service)245 mail_session_configure_proxy_resolver (ESourceRegistry *registry,
246 CamelService *service)
247 {
248 ESource *source;
249 ESource *authentication_source;
250 const gchar *uid;
251
252 uid = camel_service_get_uid (service);
253 source = e_source_registry_ref_source (registry, uid);
254 g_return_if_fail (source != NULL);
255
256 authentication_source =
257 e_source_registry_find_extension (
258 registry, source, E_SOURCE_EXTENSION_AUTHENTICATION);
259
260 if (authentication_source != NULL) {
261 ServiceProxyData *proxy_data;
262 gulong handler_id;
263
264 mail_session_update_proxy_resolver (
265 service, authentication_source);
266
267 handler_id = g_signal_connect_data (
268 authentication_source, "changed",
269 G_CALLBACK (mail_session_auth_source_changed_cb),
270 e_weak_ref_new (service),
271 (GClosureNotify) e_weak_ref_free, 0);
272
273 /* This takes ownership of the authentication source. */
274 proxy_data = g_slice_new0 (ServiceProxyData);
275 proxy_data->authentication_source = authentication_source;
276 proxy_data->auth_source_changed_handler_id = handler_id;
277
278 /* Tack the proxy data on to the CamelService,
279 * so it's destroyed along with the CamelService. */
280 g_object_set_data_full (
281 G_OBJECT (service),
282 "proxy-data", proxy_data,
283 (GDestroyNotify) service_proxy_data_free);
284 }
285
286 g_object_unref (source);
287 }
288
289 static gchar *
mail_session_resolve_popb4smtp(ESourceRegistry * registry,CamelService * smtp_service)290 mail_session_resolve_popb4smtp (ESourceRegistry *registry,
291 CamelService *smtp_service)
292 {
293 GList *list, *link;
294 const gchar *extension_name;
295 const gchar *smtp_uid;
296 gchar *pop_uid = NULL;
297
298 /* Find a POP account that uses the given smtp_service as its
299 * transport. XXX This isn't foolproof though, since we don't
300 * check that the POP server is at the same domain as the SMTP
301 * server, which is kind of the point of POPB4SMTP. */
302
303 smtp_uid = camel_service_get_uid (smtp_service);
304 g_return_val_if_fail (smtp_uid != NULL, NULL);
305
306 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
307 list = e_source_registry_list_sources (registry, extension_name);
308
309 for (link = list; link != NULL; link = g_list_next (link)) {
310 ESource *source = E_SOURCE (link->data);
311 ESourceExtension *extension;
312 const gchar *backend_name;
313 gchar *uid;
314
315 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
316 extension = e_source_get_extension (source, extension_name);
317
318 /* We're only interested in POP accounts. */
319
320 backend_name = e_source_backend_get_backend_name (
321 E_SOURCE_BACKEND (extension));
322 if (g_strcmp0 (backend_name, "pop") != 0)
323 continue;
324
325 /* Get the mail account's default mail identity. */
326
327 uid = e_source_mail_account_dup_identity_uid (
328 E_SOURCE_MAIL_ACCOUNT (extension));
329 source = e_source_registry_ref_source (registry, uid);
330 g_free (uid);
331
332 if (source == NULL)
333 continue;
334
335 /* Get the mail identity's default mail transport. */
336
337 extension_name = E_SOURCE_EXTENSION_MAIL_SUBMISSION;
338 extension = e_source_get_extension (source, extension_name);
339
340 uid = e_source_mail_submission_dup_transport_uid (
341 E_SOURCE_MAIL_SUBMISSION (extension));
342
343 g_object_unref (source);
344
345 if (g_strcmp0 (uid, smtp_uid) == 0) {
346 pop_uid = uid;
347 break;
348 }
349
350 g_free (uid);
351 }
352
353 g_list_free_full (list, (GDestroyNotify) g_object_unref);
354
355 return pop_uid;
356 }
357
358 typedef struct _ArchiveFolderChangedData {
359 GWeakRef *session;
360 gchar *service_uid;
361 gchar *old_folder_uri;
362 gchar *new_folder_uri;
363 } ArchiveFolderChangedData;
364
365 static void
archived_folder_changed_data_free(gpointer ptr)366 archived_folder_changed_data_free (gpointer ptr)
367 {
368 ArchiveFolderChangedData *data = ptr;
369
370 if (data) {
371 e_weak_ref_free (data->session);
372 g_free (data->service_uid);
373 g_free (data->old_folder_uri);
374 g_free (data->new_folder_uri);
375 g_slice_free (ArchiveFolderChangedData, data);
376 }
377 }
378
379 static gboolean
mail_session_emit_archive_folder_changed_idle(gpointer user_data)380 mail_session_emit_archive_folder_changed_idle (gpointer user_data)
381 {
382 ArchiveFolderChangedData *data = user_data;
383 EMailSession *session;
384
385 g_return_val_if_fail (data != NULL, FALSE);
386
387 session = g_weak_ref_get (data->session);
388 if (session) {
389 g_signal_emit (session, signals[ARCHIVE_FOLDER_CHANGED], 0,
390 data->service_uid, data->old_folder_uri, data->new_folder_uri);
391
392 g_object_unref (session);
393 }
394
395 return FALSE;
396 }
397
398 static void
mail_session_schedule_archive_folder_changed_locked(EMailSession * session,const gchar * service_uid,const gchar * old_folder_uri,const gchar * new_folder_uri)399 mail_session_schedule_archive_folder_changed_locked (EMailSession *session,
400 const gchar *service_uid,
401 const gchar *old_folder_uri,
402 const gchar *new_folder_uri)
403 {
404 ArchiveFolderChangedData *data;
405
406 data = g_slice_new0 (ArchiveFolderChangedData);
407 data->session = e_weak_ref_new (session);
408 data->service_uid = g_strdup (service_uid);
409 data->old_folder_uri = g_strdup (old_folder_uri);
410 data->new_folder_uri = g_strdup (new_folder_uri);
411
412 g_idle_add_full (G_PRIORITY_LOW, mail_session_emit_archive_folder_changed_idle,
413 data, archived_folder_changed_data_free);
414 }
415
416 static void
mail_session_remember_archive_folder(EMailSession * session,const gchar * uid,const gchar * folder_uri)417 mail_session_remember_archive_folder (EMailSession *session,
418 const gchar *uid,
419 const gchar *folder_uri)
420 {
421 g_return_if_fail (E_IS_MAIL_SESSION (session));
422 g_return_if_fail (uid != NULL);
423
424 g_mutex_lock (&session->priv->archive_folders_hash_lock);
425
426 if (session->priv->archive_folders_hash) {
427 gchar *old_folder_uri;
428
429 old_folder_uri = g_strdup (g_hash_table_lookup (session->priv->archive_folders_hash, uid));
430
431 if (g_strcmp0 (old_folder_uri, folder_uri) != 0) {
432 g_hash_table_insert (session->priv->archive_folders_hash, g_strdup (uid), g_strdup (folder_uri));
433
434 mail_session_schedule_archive_folder_changed_locked (session, uid, old_folder_uri, folder_uri);
435 }
436
437 g_free (old_folder_uri);
438 }
439
440 g_mutex_unlock (&session->priv->archive_folders_hash_lock);
441 }
442
443 static void
mail_session_forget_archive_folder(EMailSession * session,const gchar * uid)444 mail_session_forget_archive_folder (EMailSession *session,
445 const gchar *uid)
446 {
447 g_return_if_fail (E_IS_MAIL_SESSION (session));
448 g_return_if_fail (uid != NULL);
449
450 g_mutex_lock (&session->priv->archive_folders_hash_lock);
451
452 if (session->priv->archive_folders_hash) {
453 gchar *old_folder_uri;
454
455 old_folder_uri = g_strdup (g_hash_table_lookup (session->priv->archive_folders_hash, uid));
456
457 g_hash_table_remove (session->priv->archive_folders_hash, uid);
458
459 if (old_folder_uri && *old_folder_uri)
460 mail_session_schedule_archive_folder_changed_locked (session, uid, old_folder_uri, NULL);
461
462 g_free (old_folder_uri);
463 }
464
465 g_mutex_unlock (&session->priv->archive_folders_hash_lock);
466 }
467
468 static void
mail_session_archive_folder_notify_cb(ESourceExtension * extension,GParamSpec * param,EMailSession * session)469 mail_session_archive_folder_notify_cb (ESourceExtension *extension,
470 GParamSpec *param,
471 EMailSession *session)
472 {
473 ESource *source;
474
475 g_return_if_fail (E_IS_MAIL_SESSION (session));
476
477 source = e_source_extension_ref_source (extension);
478 if (source) {
479 gchar *archive_folder;
480
481 archive_folder = e_source_mail_account_dup_archive_folder (E_SOURCE_MAIL_ACCOUNT (extension));
482
483 mail_session_remember_archive_folder (session, e_source_get_uid (source), archive_folder);
484
485 g_free (archive_folder);
486 g_object_unref (source);
487 }
488 }
489
490 static void
mail_session_local_archive_folder_changed_cb(GSettings * settings,const gchar * key,EMailSession * session)491 mail_session_local_archive_folder_changed_cb (GSettings *settings,
492 const gchar *key,
493 EMailSession *session)
494 {
495 gchar *local_archive_folder;
496
497 g_return_if_fail (E_IS_MAIL_SESSION (session));
498
499 local_archive_folder = g_settings_get_string (settings, "local-archive-folder");
500 mail_session_remember_archive_folder (session, E_MAIL_SESSION_LOCAL_UID, local_archive_folder);
501 g_free (local_archive_folder);
502 }
503
504 static void
mail_session_refresh_cb(ESource * source,EMailSession * session)505 mail_session_refresh_cb (ESource *source,
506 EMailSession *session)
507 {
508 ESourceRegistry *registry;
509 CamelService *service;
510 const gchar *uid;
511
512 registry = e_mail_session_get_registry (session);
513
514 /* Skip the signal emission if the source
515 * or any of its ancestors are disabled. */
516 if (!e_source_registry_check_enabled (registry, source))
517 return;
518
519 uid = e_source_get_uid (source);
520 service = camel_session_ref_service (CAMEL_SESSION (session), uid);
521 g_return_if_fail (service != NULL);
522
523 g_signal_emit (session, signals[REFRESH_SERVICE], 0, service);
524
525 g_object_unref (service);
526 }
527
528 static gboolean
mail_session_check_goa_mail_disabled(EMailSession * session,ESource * source)529 mail_session_check_goa_mail_disabled (EMailSession *session,
530 ESource *source)
531 {
532 ESource *goa_source;
533 ESourceRegistry *registry;
534 gboolean goa_mail_disabled = FALSE;
535
536 registry = e_mail_session_get_registry (session);
537
538 goa_source = e_source_registry_find_extension (
539 registry, source, E_SOURCE_EXTENSION_GOA);
540
541 if (goa_source != NULL) {
542 goa_mail_disabled = !e_source_get_enabled (source);
543 g_object_unref (goa_source);
544 }
545
546 return goa_mail_disabled;
547 }
548
549 static gboolean
mail_session_check_uoa_mail_disabled(EMailSession * session,ESource * source)550 mail_session_check_uoa_mail_disabled (EMailSession *session,
551 ESource *source)
552 {
553 ESource *uoa_source;
554 ESourceRegistry *registry;
555 gboolean uoa_mail_disabled = FALSE;
556
557 registry = e_mail_session_get_registry (session);
558
559 uoa_source = e_source_registry_find_extension (
560 registry, source, E_SOURCE_EXTENSION_UOA);
561
562 if (uoa_source != NULL) {
563 uoa_mail_disabled = !e_source_get_enabled (source);
564 g_object_unref (uoa_source);
565 }
566
567 return uoa_mail_disabled;
568 }
569
570 static void
mail_session_add_from_source(EMailSession * session,CamelProviderType type,ESource * source)571 mail_session_add_from_source (EMailSession *session,
572 CamelProviderType type,
573 ESource *source)
574 {
575 ESourceBackend *extension;
576 CamelService *service;
577 const gchar *uid;
578 const gchar *backend_name;
579 const gchar *display_name;
580 const gchar *extension_name;
581 GError *error = NULL;
582
583 switch (type) {
584 case CAMEL_PROVIDER_STORE:
585 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
586 break;
587 case CAMEL_PROVIDER_TRANSPORT:
588 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
589 break;
590 default:
591 g_return_if_reached ();
592 }
593
594 uid = e_source_get_uid (source);
595 display_name = e_source_get_display_name (source);
596
597 extension = e_source_get_extension (source, extension_name);
598 backend_name = e_source_backend_get_backend_name (extension);
599
600 /* Sanity checks. */
601 g_return_if_fail (uid != NULL);
602 g_return_if_fail (backend_name != NULL);
603
604 /* Collection sources with a [GNOME Online Accounts] extension
605 * require special handling. If the collection's mail-enabled
606 * flag is FALSE, do not add a CamelService. The account must
607 * not appear anywhere, not even in the Mail Accounts list. */
608 if (mail_session_check_goa_mail_disabled (session, source))
609 return;
610
611 /* Same deal for the [Ubuntu Online Accounts] extension. */
612 if (mail_session_check_uoa_mail_disabled (session, source))
613 return;
614
615 service = camel_session_add_service (
616 CAMEL_SESSION (session), uid,
617 backend_name, type, &error);
618
619 if (type == CAMEL_PROVIDER_STORE) {
620 ESourceMailAccount *account_extension;
621 gchar *archive_folder_uri;
622
623 account_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
624 archive_folder_uri = e_source_mail_account_dup_archive_folder (account_extension);
625 mail_session_remember_archive_folder (session, e_source_get_uid (source), archive_folder_uri);
626 g_free (archive_folder_uri);
627
628 g_signal_connect (account_extension, "notify::archive-folder",
629 G_CALLBACK (mail_session_archive_folder_notify_cb), session);
630 }
631
632 /* Our own CamelSession.add_service() method will handle the
633 * new CamelService, so we only need to unreference it here. */
634 if (service != NULL)
635 g_object_unref (service);
636
637 if (error != NULL) {
638 g_warning (
639 "Failed to add service '%s' (%s): %s",
640 display_name, uid, error->message);
641 g_error_free (error);
642 }
643
644 /* Set up auto-refresh. */
645 if (type == CAMEL_PROVIDER_STORE) {
646 guint timeout_id;
647
648 timeout_id = e_source_refresh_add_timeout (
649 source, NULL, (ESourceRefreshFunc)
650 mail_session_refresh_cb, session,
651 (GDestroyNotify) NULL);
652
653 g_hash_table_insert (
654 session->priv->auto_refresh_table,
655 g_strdup (uid),
656 GUINT_TO_POINTER (timeout_id));
657 }
658 }
659
660 static void
mail_session_source_added_cb(ESourceRegistry * registry,ESource * source,EMailSession * session)661 mail_session_source_added_cb (ESourceRegistry *registry,
662 ESource *source,
663 EMailSession *session)
664 {
665 CamelProviderType provider_type;
666 const gchar *extension_name;
667
668 provider_type = CAMEL_PROVIDER_STORE;
669 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
670
671 if (e_source_has_extension (source, extension_name))
672 mail_session_add_from_source (session, provider_type, source);
673
674 provider_type = CAMEL_PROVIDER_TRANSPORT;
675 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
676
677 if (e_source_has_extension (source, extension_name))
678 mail_session_add_from_source (session, provider_type, source);
679 }
680
681 static void
mail_session_source_removed_cb(ESourceRegistry * registry,ESource * source,EMailSession * session)682 mail_session_source_removed_cb (ESourceRegistry *registry,
683 ESource *source,
684 EMailSession *session)
685 {
686 CamelSession *camel_session;
687 CamelService *service;
688 const gchar *uid;
689
690 camel_session = CAMEL_SESSION (session);
691
692 uid = e_source_get_uid (source);
693 service = camel_session_ref_service (camel_session, uid);
694
695 if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
696 ESourceMailAccount *extension;
697
698 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
699
700 g_signal_handlers_disconnect_by_func (extension,
701 G_CALLBACK (mail_session_archive_folder_notify_cb), session);
702
703 mail_session_forget_archive_folder (session, e_source_get_uid (source));
704 }
705
706 if (service != NULL) {
707 camel_session_remove_service (camel_session, service);
708 g_object_unref (service);
709 }
710 }
711
712 static void
mail_session_source_enabled_cb(ESourceRegistry * registry,ESource * source,EMailSession * session)713 mail_session_source_enabled_cb (ESourceRegistry *registry,
714 ESource *source,
715 EMailSession *session)
716 {
717 ESource *goa_source;
718 ESource *uoa_source;
719
720 /* If the source is linked to a GNOME Online Account
721 * or Ubuntu Online Account, enabling the source is
722 * equivalent to adding it. */
723
724 goa_source = e_source_registry_find_extension (
725 registry, source, E_SOURCE_EXTENSION_GOA);
726
727 uoa_source = e_source_registry_find_extension (
728 registry, source, E_SOURCE_EXTENSION_UOA);
729
730 if (goa_source != NULL || uoa_source != NULL)
731 mail_session_source_added_cb (registry, source, session);
732
733 if (goa_source != NULL)
734 g_object_unref (goa_source);
735
736 if (uoa_source != NULL)
737 g_object_unref (uoa_source);
738 }
739
740 static void
mail_session_source_disabled_cb(ESourceRegistry * registry,ESource * source,EMailSession * session)741 mail_session_source_disabled_cb (ESourceRegistry *registry,
742 ESource *source,
743 EMailSession *session)
744 {
745 ESource *goa_source;
746 ESource *uoa_source;
747
748 /* If the source is linked to a GNOME Online Account
749 * or Ubuntu Online Account, disabling the source is
750 * equivalent to removing it. */
751
752 goa_source = e_source_registry_find_extension (
753 registry, source, E_SOURCE_EXTENSION_GOA);
754
755 uoa_source = e_source_registry_find_extension (
756 registry, source, E_SOURCE_EXTENSION_UOA);
757
758 if (goa_source != NULL || uoa_source != NULL)
759 mail_session_source_removed_cb (registry, source, session);
760
761 if (goa_source != NULL)
762 g_object_unref (goa_source);
763
764 if (uoa_source != NULL)
765 g_object_unref (uoa_source);
766 }
767
768 static void
mail_session_default_mail_account_cb(ESourceRegistry * registry,GParamSpec * pspec,EMailSession * session)769 mail_session_default_mail_account_cb (ESourceRegistry *registry,
770 GParamSpec *pspec,
771 EMailSession *session)
772 {
773 ESource *source;
774 ESourceMailAccount *extension;
775 const gchar *extension_name;
776 gchar *uid;
777
778 /* If the default mail account names a valid mail
779 * identity, make it the default mail identity. */
780
781 /* XXX I debated whether to have ESourceRegistry do this
782 * itself but it seems like an Evolution policy to me
783 * right now. I may change my mind in the future, or
784 * decide not to do this synchronization at all. */
785
786 source = e_source_registry_ref_default_mail_account (registry);
787 g_return_if_fail (source != NULL);
788
789 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
790 extension = e_source_get_extension (source, extension_name);
791 uid = e_source_mail_account_dup_identity_uid (extension);
792
793 g_object_unref (source);
794 source = NULL;
795
796 if (uid != NULL) {
797 source = e_source_registry_ref_source (registry, uid);
798 g_free (uid);
799 }
800
801 if (source != NULL) {
802 e_source_registry_set_default_mail_identity (registry, source);
803 g_object_unref (source);
804 }
805 }
806
807 static void
mail_session_outbox_folder_changed_cb(CamelFolder * folder,CamelFolderChangeInfo * changes,EMailSession * session)808 mail_session_outbox_folder_changed_cb (CamelFolder *folder,
809 CamelFolderChangeInfo *changes,
810 EMailSession *session)
811 {
812 g_return_if_fail (CAMEL_IS_FOLDER (folder));
813 g_return_if_fail (changes != NULL);
814 g_return_if_fail (E_IS_MAIL_SESSION (session));
815
816 if (changes->uid_added && changes->uid_added->len) {
817 GSettings *settings;
818
819 settings = e_util_ref_settings ("org.gnome.evolution.mail");
820 if (g_settings_get_boolean (settings, "composer-use-outbox")) {
821 gint delay_flush = g_settings_get_int (settings, "composer-delay-outbox-flush");
822
823 if (delay_flush > 0)
824 e_mail_session_schedule_outbox_flush (session, delay_flush);
825 }
826 g_object_unref (settings);
827 }
828 }
829
830 static void
mail_session_configure_local_store(EMailSession * session)831 mail_session_configure_local_store (EMailSession *session)
832 {
833 CamelLocalSettings *local_settings;
834 CamelSession *camel_session;
835 CamelSettings *settings;
836 CamelService *service;
837 CamelFolder *folder;
838 const gchar *data_dir;
839 const gchar *uid;
840 gchar *path;
841 gint ii;
842
843 camel_session = CAMEL_SESSION (session);
844
845 uid = E_MAIL_SESSION_LOCAL_UID;
846 service = camel_session_ref_service (camel_session, uid);
847 session->priv->local_store = service; /* takes ownership */
848 g_return_if_fail (service != NULL);
849
850 settings = camel_service_ref_settings (service);
851
852 data_dir = camel_session_get_user_data_dir (camel_session);
853 path = g_build_filename (data_dir, E_MAIL_SESSION_LOCAL_UID, NULL);
854
855 local_settings = CAMEL_LOCAL_SETTINGS (settings);
856 camel_local_settings_set_path (local_settings, path);
857
858 g_free (path);
859
860 g_object_unref (settings);
861
862 /* Shouldn't need to worry about other mail applications
863 * altering files in our local mail store. */
864 g_object_set (service, "need-summary-check", FALSE, NULL);
865
866 /* Populate the local folder cache. */
867 for (ii = 0; ii < E_MAIL_NUM_LOCAL_FOLDERS; ii++) {
868 gchar *folder_uri;
869 const gchar *display_name;
870 GError *error = NULL;
871
872 display_name = local_folder_names[ii];
873
874 /* XXX This blocks but should be fast. */
875 if (ii == E_MAIL_LOCAL_FOLDER_LOCAL_INBOX)
876 folder = camel_store_get_inbox_folder_sync (
877 CAMEL_STORE (service), NULL, &error);
878 else
879 folder = camel_store_get_folder_sync (
880 CAMEL_STORE (service), display_name,
881 CAMEL_STORE_FOLDER_CREATE, NULL, &error);
882
883 folder_uri = e_mail_folder_uri_build (
884 CAMEL_STORE (service), display_name);
885
886 /* The arrays take ownership of the items added. */
887 g_ptr_array_add (session->priv->local_folders, folder);
888 g_ptr_array_add (session->priv->local_folder_uris, folder_uri);
889
890 if (error != NULL) {
891 g_critical ("%s: %s", G_STRFUNC, error->message);
892 g_error_free (error);
893 }
894 }
895
896 folder = e_mail_session_get_local_folder (session, E_MAIL_LOCAL_FOLDER_OUTBOX);
897 if (folder) {
898 session->priv->outbox_changed_handler_id = g_signal_connect (folder, "changed",
899 G_CALLBACK (mail_session_outbox_folder_changed_cb), session);
900 }
901 }
902
903 static void
mail_session_configure_vfolder_store(EMailSession * session)904 mail_session_configure_vfolder_store (EMailSession *session)
905 {
906 CamelSession *camel_session;
907 CamelService *service;
908 const gchar *uid;
909
910 camel_session = CAMEL_SESSION (session);
911
912 uid = E_MAIL_SESSION_VFOLDER_UID;
913 service = camel_session_ref_service (camel_session, uid);
914 session->priv->vfolder_store = service; /* takes ownership */
915 g_return_if_fail (service != NULL);
916
917 camel_service_connect_sync (service, NULL, NULL);
918
919 /* XXX There's more configuration to do in vfolder_load_storage()
920 * but it requires an EMailBackend, which we don't have access
921 * to from here, so it has to be called from elsewhere. Kinda
922 * thinking about reworking that... */
923 }
924
925 static void
mail_session_force_refresh(EMailSession * session)926 mail_session_force_refresh (EMailSession *session)
927 {
928 ESourceRegistry *registry;
929 GHashTableIter iter;
930 GSettings *settings;
931 gboolean unconditionally;
932 gpointer key;
933
934 /* Only refresh when the session is online. */
935 if (!camel_session_get_online (CAMEL_SESSION (session)))
936 return;
937
938 /* FIXME EMailSession should define properties for these. */
939 settings = e_util_ref_settings ("org.gnome.evolution.mail");
940 unconditionally =
941 g_settings_get_boolean (settings, "send-recv-on-start") &&
942 g_settings_get_boolean (settings, "send-recv-all-on-start");
943 g_object_unref (settings);
944
945 registry = e_mail_session_get_registry (session);
946 g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
947
948 while (g_hash_table_iter_next (&iter, &key, NULL)) {
949 ESource *source;
950 ESourceRefresh *extension;
951 const gchar *extension_name;
952 gboolean refresh_enabled;
953
954 /* The hash table key is the ESource UID. */
955 source = e_source_registry_ref_source (registry, key);
956
957 if (source == NULL)
958 continue;
959
960 extension_name = E_SOURCE_EXTENSION_REFRESH;
961 extension = e_source_get_extension (source, extension_name);
962 refresh_enabled = e_source_refresh_get_enabled (extension);
963
964 if (refresh_enabled || unconditionally)
965 e_source_refresh_force_timeout (source);
966
967 g_object_unref (source);
968 }
969 }
970
971 static void
mail_session_cancel_refresh(EMailSession * session)972 mail_session_cancel_refresh (EMailSession *session)
973 {
974 ESourceRegistry *registry;
975 GHashTableIter iter;
976 gpointer key, value;
977
978 registry = e_mail_session_get_registry (session);
979 g_hash_table_iter_init (&iter, session->priv->auto_refresh_table);
980
981 while (g_hash_table_iter_next (&iter, &key, &value)) {
982 ESource *source;
983 guint timeout_id;
984
985 /* The hash table key is the ESource UID. */
986 source = e_source_registry_ref_source (registry, key);
987
988 /* The hash table value is the refresh timeout ID. */
989 timeout_id = GPOINTER_TO_UINT (value);
990
991 if (source == NULL)
992 continue;
993
994 e_source_refresh_remove_timeout (source, timeout_id);
995
996 g_object_unref (source);
997 }
998
999 /* All timeouts cancelled so clear the auto-refresh table. */
1000 g_hash_table_remove_all (session->priv->auto_refresh_table);
1001 }
1002
1003 static gboolean
mail_session_idle_refresh_cb(EMailSession * session)1004 mail_session_idle_refresh_cb (EMailSession *session)
1005 {
1006 /* This only runs once at startup (if settings allow). */
1007
1008 if (camel_session_get_online (CAMEL_SESSION (session))) {
1009 mail_session_force_refresh (session);
1010
1011 /* Also flush the Outbox. */
1012 g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
1013 }
1014
1015 /* Listen for network state changes and force a
1016 * mail store refresh when coming back online. */
1017 e_signal_connect_notify (
1018 session, "notify::online",
1019 G_CALLBACK (mail_session_force_refresh), NULL);
1020
1021 return FALSE;
1022 }
1023
1024 static void
mail_session_set_registry(EMailSession * session,ESourceRegistry * registry)1025 mail_session_set_registry (EMailSession *session,
1026 ESourceRegistry *registry)
1027 {
1028 g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
1029 g_return_if_fail (session->priv->registry == NULL);
1030
1031 session->priv->registry = g_object_ref (registry);
1032 }
1033
1034 static void
mail_session_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)1035 mail_session_set_property (GObject *object,
1036 guint property_id,
1037 const GValue *value,
1038 GParamSpec *pspec)
1039 {
1040 switch (property_id) {
1041 case PROP_REGISTRY:
1042 mail_session_set_registry (
1043 E_MAIL_SESSION (object),
1044 g_value_get_object (value));
1045 return;
1046 }
1047
1048 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1049 }
1050
1051 static void
mail_session_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)1052 mail_session_get_property (GObject *object,
1053 guint property_id,
1054 GValue *value,
1055 GParamSpec *pspec)
1056 {
1057 switch (property_id) {
1058 case PROP_FOLDER_CACHE:
1059 g_value_set_object (
1060 value,
1061 e_mail_session_get_folder_cache (
1062 E_MAIL_SESSION (object)));
1063 return;
1064
1065 case PROP_LOCAL_STORE:
1066 g_value_set_object (
1067 value,
1068 e_mail_session_get_local_store (
1069 E_MAIL_SESSION (object)));
1070 return;
1071
1072 case PROP_REGISTRY:
1073 g_value_set_object (
1074 value,
1075 e_mail_session_get_registry (
1076 E_MAIL_SESSION (object)));
1077 return;
1078
1079 case PROP_VFOLDER_STORE:
1080 g_value_set_object (
1081 value,
1082 e_mail_session_get_vfolder_store (
1083 E_MAIL_SESSION (object)));
1084 return;
1085 }
1086
1087 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
1088 }
1089
1090 static void
mail_session_dispose(GObject * object)1091 mail_session_dispose (GObject *object)
1092 {
1093 EMailSessionPrivate *priv;
1094 GSettings *settings;
1095
1096 priv = E_MAIL_SESSION_GET_PRIVATE (object);
1097
1098 if (priv->outbox_changed_handler_id) {
1099 CamelFolder *folder;
1100
1101 folder = e_mail_session_get_local_folder (E_MAIL_SESSION (object), E_MAIL_LOCAL_FOLDER_OUTBOX);
1102 if (folder)
1103 g_signal_handler_disconnect (folder, priv->outbox_changed_handler_id);
1104
1105 priv->outbox_changed_handler_id = 0;
1106 }
1107
1108 g_clear_object (&priv->folder_cache);
1109
1110 g_ptr_array_set_size (priv->local_folders, 0);
1111 g_ptr_array_set_size (priv->local_folder_uris, 0);
1112
1113 g_mutex_lock (&priv->preparing_flush_lock);
1114
1115 if (priv->preparing_flush > 0) {
1116 g_source_remove (priv->preparing_flush);
1117 priv->preparing_flush = 0;
1118 }
1119
1120 if (priv->outbox_flush_id > 0) {
1121 g_source_remove (priv->outbox_flush_id);
1122 priv->outbox_flush_id = 0;
1123 }
1124
1125 g_mutex_unlock (&priv->preparing_flush_lock);
1126
1127 g_clear_object (&priv->local_store);
1128 g_clear_object (&priv->vfolder_store);
1129
1130 g_mutex_lock (&priv->archive_folders_hash_lock);
1131
1132 if (priv->archive_folders_hash) {
1133 if (priv->registry) {
1134 GHashTableIter iter;
1135 gpointer key;
1136
1137 g_hash_table_iter_init (&iter, priv->archive_folders_hash);
1138 while (g_hash_table_iter_next (&iter, &key, NULL)) {
1139 ESource *source;
1140
1141 source = e_source_registry_ref_source (priv->registry, key);
1142 if (source) {
1143 if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
1144 ESourceExtension *extension;
1145
1146 extension = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
1147
1148 g_signal_handlers_disconnect_by_func (extension,
1149 G_CALLBACK (mail_session_archive_folder_notify_cb), object);
1150 }
1151 g_object_unref (source);
1152 }
1153 }
1154 }
1155
1156 g_hash_table_destroy (priv->archive_folders_hash);
1157 priv->archive_folders_hash = NULL;
1158 }
1159
1160 g_mutex_unlock (&priv->archive_folders_hash_lock);
1161
1162 if (priv->registry != NULL) {
1163 g_signal_handler_disconnect (
1164 priv->registry,
1165 priv->source_added_handler_id);
1166 g_signal_handler_disconnect (
1167 priv->registry,
1168 priv->source_removed_handler_id);
1169 g_signal_handler_disconnect (
1170 priv->registry,
1171 priv->source_enabled_handler_id);
1172 g_signal_handler_disconnect (
1173 priv->registry,
1174 priv->source_disabled_handler_id);
1175 g_signal_handler_disconnect (
1176 priv->registry,
1177 priv->default_mail_account_handler_id);
1178
1179 /* This requires the registry. */
1180 mail_session_cancel_refresh (E_MAIL_SESSION (object));
1181
1182 g_object_unref (priv->registry);
1183 priv->registry = NULL;
1184 }
1185
1186 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1187
1188 g_signal_handlers_disconnect_by_func (settings,
1189 G_CALLBACK (mail_session_local_archive_folder_changed_cb), object);
1190
1191 g_object_unref (settings);
1192
1193 /* Chain up to parent's dispose() method. */
1194 G_OBJECT_CLASS (e_mail_session_parent_class)->dispose (object);
1195 }
1196
1197 static void
mail_session_finalize(GObject * object)1198 mail_session_finalize (GObject *object)
1199 {
1200 EMailSessionPrivate *priv;
1201
1202 priv = E_MAIL_SESSION_GET_PRIVATE (object);
1203
1204 g_hash_table_destroy (priv->auto_refresh_table);
1205 g_hash_table_destroy (priv->junk_filters);
1206 g_hash_table_destroy (priv->used_services);
1207
1208 g_ptr_array_free (priv->local_folders, TRUE);
1209 g_ptr_array_free (priv->local_folder_uris, TRUE);
1210
1211 g_mutex_clear (&priv->preparing_flush_lock);
1212 g_mutex_clear (&priv->used_services_lock);
1213 g_mutex_clear (&priv->archive_folders_hash_lock);
1214 g_cond_clear (&priv->used_services_cond);
1215
1216 g_free (mail_data_dir);
1217 g_free (mail_config_dir);
1218
1219 /* Chain up to parent's finalize() method. */
1220 G_OBJECT_CLASS (e_mail_session_parent_class)->finalize (object);
1221 }
1222
1223 static void
mail_session_constructed(GObject * object)1224 mail_session_constructed (GObject *object)
1225 {
1226 EMailSession *session;
1227 EExtensible *extensible;
1228 ESourceRegistry *registry;
1229 GType extension_type;
1230 GList *list, *link;
1231 GSettings *settings;
1232 CamelProviderType provider_type;
1233 const gchar *extension_name;
1234 gchar *local_archive_folder;
1235 gulong handler_id;
1236
1237 session = E_MAIL_SESSION (object);
1238 registry = e_mail_session_get_registry (session);
1239
1240 /* Chain up to parent's constructed() method. */
1241 G_OBJECT_CLASS (e_mail_session_parent_class)->constructed (object);
1242
1243 camel_session_set_network_monitor (CAMEL_SESSION (session), e_network_monitor_get_default ());
1244
1245 /* Add available mail accounts. */
1246
1247 provider_type = CAMEL_PROVIDER_STORE;
1248 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
1249
1250 list = e_source_registry_list_sources (registry, extension_name);
1251
1252 for (link = list; link != NULL; link = g_list_next (link)) {
1253 ESource *source = E_SOURCE (link->data);
1254
1255 mail_session_add_from_source (session, provider_type, source);
1256 }
1257
1258 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1259
1260 /* Add available mail transports. */
1261
1262 provider_type = CAMEL_PROVIDER_TRANSPORT;
1263 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
1264
1265 list = e_source_registry_list_sources (registry, extension_name);
1266
1267 for (link = list; link != NULL; link = g_list_next (link)) {
1268 ESource *source = E_SOURCE (link->data);
1269
1270 mail_session_add_from_source (session, provider_type, source);
1271 }
1272
1273 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1274
1275 /* Built-in stores require extra configuration. */
1276
1277 mail_session_configure_local_store (session);
1278 mail_session_configure_vfolder_store (session);
1279
1280 /* Listen for registry changes. */
1281
1282 handler_id = g_signal_connect (
1283 registry, "source-added",
1284 G_CALLBACK (mail_session_source_added_cb), session);
1285 session->priv->source_added_handler_id = handler_id;
1286
1287 handler_id = g_signal_connect (
1288 registry, "source-removed",
1289 G_CALLBACK (mail_session_source_removed_cb), session);
1290 session->priv->source_removed_handler_id = handler_id;
1291
1292 handler_id = g_signal_connect (
1293 registry, "source-enabled",
1294 G_CALLBACK (mail_session_source_enabled_cb), session);
1295 session->priv->source_enabled_handler_id = handler_id;
1296
1297 handler_id = g_signal_connect (
1298 registry, "source-disabled",
1299 G_CALLBACK (mail_session_source_disabled_cb), session);
1300 session->priv->source_disabled_handler_id = handler_id;
1301
1302 handler_id = e_signal_connect_notify (
1303 registry, "notify::default-mail-account",
1304 G_CALLBACK (mail_session_default_mail_account_cb), session);
1305 session->priv->default_mail_account_handler_id = handler_id;
1306
1307 extensible = E_EXTENSIBLE (object);
1308 e_extensible_load_extensions (extensible);
1309
1310 /* Add junk filter extensions to an internal hash table. */
1311
1312 extension_type = E_TYPE_MAIL_JUNK_FILTER;
1313 list = e_extensible_list_extensions (extensible, extension_type);
1314
1315 for (link = list; link != NULL; link = g_list_next (link)) {
1316 EMailJunkFilter *junk_filter;
1317 EMailJunkFilterClass *class;
1318
1319 junk_filter = E_MAIL_JUNK_FILTER (link->data);
1320 class = E_MAIL_JUNK_FILTER_GET_CLASS (junk_filter);
1321
1322 if (!CAMEL_IS_JUNK_FILTER (junk_filter)) {
1323 g_warning (
1324 "Skipping %s: Does not implement "
1325 "CamelJunkFilterInterface",
1326 G_OBJECT_TYPE_NAME (junk_filter));
1327 continue;
1328 }
1329
1330 if (class->filter_name == NULL) {
1331 g_warning (
1332 "Skipping %s: filter_name unset",
1333 G_OBJECT_TYPE_NAME (junk_filter));
1334 continue;
1335 }
1336
1337 if (class->display_name == NULL) {
1338 g_warning (
1339 "Skipping %s: display_name unset",
1340 G_OBJECT_TYPE_NAME (junk_filter));
1341 continue;
1342 }
1343
1344 /* No need to reference the EMailJunkFilter since
1345 * EMailSession owns the reference to it already. */
1346 g_hash_table_insert (
1347 session->priv->junk_filters,
1348 (gpointer) class->filter_name,
1349 junk_filter);
1350 }
1351
1352 g_list_free (list);
1353
1354 mail_config_reload_junk_headers (session);
1355
1356 /* Initialize the legacy message-passing framework
1357 * before starting the first mail store refresh. */
1358 mail_msg_init ();
1359
1360 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1361
1362 /* The application is not yet fully initialized at this point,
1363 * so run the first mail store refresh from an idle callback. */
1364 if (g_settings_get_boolean (settings, "send-recv-on-start"))
1365 g_idle_add_full (
1366 G_PRIORITY_DEFAULT,
1367 (GSourceFunc) mail_session_idle_refresh_cb,
1368 g_object_ref (session),
1369 (GDestroyNotify) g_object_unref);
1370
1371 g_signal_connect (settings, "changed::local-archive-folder",
1372 G_CALLBACK (mail_session_local_archive_folder_changed_cb), session);
1373
1374 local_archive_folder = g_settings_get_string (settings, "local-archive-folder");
1375 mail_session_remember_archive_folder (session, E_MAIL_SESSION_LOCAL_UID, local_archive_folder);
1376 g_free (local_archive_folder);
1377
1378 g_object_unref (settings);
1379 }
1380
1381 static CamelService *
mail_session_add_service(CamelSession * session,const gchar * uid,const gchar * protocol,CamelProviderType type,GError ** error)1382 mail_session_add_service (CamelSession *session,
1383 const gchar *uid,
1384 const gchar *protocol,
1385 CamelProviderType type,
1386 GError **error)
1387 {
1388 ESourceRegistry *registry;
1389 CamelService *service;
1390 const gchar *extension_name;
1391
1392 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1393 extension_name = e_source_camel_get_extension_name (protocol);
1394
1395 /* Chain up to parents add_service() method. */
1396 service = CAMEL_SESSION_CLASS (e_mail_session_parent_class)->
1397 add_service (session, uid, protocol, type, error);
1398
1399 /* Configure the CamelService from the corresponding ESource. */
1400
1401 if (CAMEL_IS_SERVICE (service)) {
1402 ESource *source;
1403 ESource *tmp_source;
1404
1405 /* Each CamelService has a corresponding ESource. */
1406 source = e_source_registry_ref_source (registry, uid);
1407 g_return_val_if_fail (source != NULL, service);
1408
1409 tmp_source = e_source_registry_find_extension (
1410 registry, source, extension_name);
1411 if (tmp_source != NULL) {
1412 g_object_unref (source);
1413 source = tmp_source;
1414 }
1415
1416 /* This handles all the messy property bindings. */
1417 e_source_camel_configure_service (source, service);
1418
1419 /* Track the proxy resolver for this service. */
1420 mail_session_configure_proxy_resolver (registry, service);
1421
1422 e_binding_bind_property (
1423 source, "display-name",
1424 service, "display-name",
1425 G_BINDING_SYNC_CREATE);
1426
1427 /* Migrate files for this service from its old
1428 * URL-based directory to a UID-based directory
1429 * if necessary. */
1430 camel_service_migrate_files (service);
1431 }
1432
1433 return service;
1434 }
1435
1436 static gchar *
mail_session_get_password(CamelSession * session,CamelService * service,const gchar * prompt,const gchar * item,guint32 flags,GError ** error)1437 mail_session_get_password (CamelSession *session,
1438 CamelService *service,
1439 const gchar *prompt,
1440 const gchar *item,
1441 guint32 flags,
1442 GError **error)
1443 {
1444 ESourceRegistry *registry;
1445 gchar *password = NULL;
1446
1447 /* XXX This method is now only for fringe cases. For normal
1448 * CamelService authentication, use authenticate_sync().
1449 *
1450 * The two known fringe cases that still need this are:
1451 *
1452 * 1) CamelSaslPOPB4SMTP, where the CamelService is an SMTP
1453 * transport and the item name is always "popb4smtp_uid".
1454 * (This is a dirty hack, Camel just needs some way to
1455 * pair up a CamelService and CamelTransport. Not sure
1456 * what that should look like just yet...)
1457 *
1458 * 2) CamelGpgContext, where the CamelService is NULL and
1459 * the item name is a user ID (I think). (Seahorse, or
1460 * one of its dependent libraries, ought to handle this
1461 * transparently once Camel fully transitions to GIO.)
1462 */
1463
1464 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1465
1466 /* Handle the CamelSaslPOPB4SMTP case. */
1467 if (g_strcmp0 (item, "popb4smtp_uid") == 0)
1468 return mail_session_resolve_popb4smtp (registry, service);
1469
1470 /* Otherwise this had better be the CamelGpgContext case. */
1471 g_return_val_if_fail (service == NULL, NULL);
1472
1473 password = e_passwords_get_password (item);
1474
1475 if (password == NULL || (flags & CAMEL_SESSION_PASSWORD_REPROMPT)) {
1476 gboolean remember;
1477 guint eflags = 0;
1478
1479 if (flags & CAMEL_SESSION_PASSWORD_STATIC)
1480 eflags |= E_PASSWORDS_REMEMBER_NEVER;
1481 else
1482 eflags |= E_PASSWORDS_REMEMBER_SESSION;
1483
1484 if (flags & CAMEL_SESSION_PASSWORD_REPROMPT)
1485 eflags |= E_PASSWORDS_REPROMPT;
1486
1487 if (flags & CAMEL_SESSION_PASSWORD_SECRET)
1488 eflags |= E_PASSWORDS_SECRET;
1489
1490 if (flags & CAMEL_SESSION_PASSPHRASE)
1491 eflags |= E_PASSWORDS_PASSPHRASE;
1492
1493 password = e_passwords_ask_password (
1494 "", item, prompt, eflags, &remember, NULL);
1495
1496 if (password == NULL)
1497 e_passwords_forget_password (item);
1498 }
1499
1500 if (password == NULL)
1501 g_set_error (
1502 error, G_IO_ERROR,
1503 G_IO_ERROR_CANCELLED,
1504 _("User cancelled operation"));
1505
1506 return password;
1507 }
1508
1509 static gboolean
mail_session_forget_password(CamelSession * session,CamelService * service,const gchar * item,GError ** error)1510 mail_session_forget_password (CamelSession *session,
1511 CamelService *service,
1512 const gchar *item,
1513 GError **error)
1514 {
1515 /* XXX The only remaining user of this method is CamelGpgContext,
1516 * which does not provide a CamelService. Use 'item' as the
1517 * password key. */
1518
1519 g_return_val_if_fail (service == NULL, FALSE);
1520
1521 e_passwords_forget_password (item);
1522
1523 return TRUE;
1524 }
1525
1526 /* Expects 'forward_with' encoded as: "identity_uid|alias_name|alias_address" with '\\' and '|' being backslash-escaped */
1527 static ESource *
mail_session_decode_forward_with(ESourceRegistry * registry,const gchar * forward_with,gchar ** out_alias_name,gchar ** out_alias_address)1528 mail_session_decode_forward_with (ESourceRegistry *registry,
1529 const gchar *forward_with,
1530 gchar **out_alias_name,
1531 gchar **out_alias_address)
1532 {
1533 ESource *source = NULL;
1534 GString *str;
1535 gint step;
1536 const gchar *ptr;
1537
1538 if (!forward_with || !*forward_with)
1539 return NULL;
1540
1541 str = g_string_sized_new (strlen (forward_with));
1542
1543 for (step = 0, ptr = forward_with; *ptr; ptr++) {
1544 if (*ptr == '\\' && ptr[1]) {
1545 g_string_append_c (str, ptr[1]);
1546 ptr++;
1547 g_string_append_c (str, *ptr);
1548 } else if (*ptr == '|') {
1549 if (step == 0) { /* identity_uid */
1550 source = e_source_registry_ref_source (registry, str->str);
1551 if (!source)
1552 break;
1553 } else if (step == 1) { /* alias_name */
1554 if (str->len)
1555 *out_alias_name = g_strdup (str->str);
1556 } else if (step == 2) { /* alias_address */
1557 if (str->len)
1558 *out_alias_address = g_strdup (str->str);
1559 }
1560
1561 g_string_truncate (str, 0);
1562 step++;
1563
1564 if (step == 3)
1565 break;
1566 } else {
1567 g_string_append_c (str, *ptr);
1568 }
1569 }
1570
1571 /* When the string doesn't end with the '|' */
1572 if (step < 3 && str->len) {
1573 if (step == 0) { /* identity_uid */
1574 source = e_source_registry_ref_source (registry, str->str);
1575 } else if (step == 1) { /* alias_name */
1576 *out_alias_name = g_strdup (str->str);
1577 } else if (step == 2) { /* alias_address */
1578 *out_alias_address = g_strdup (str->str);
1579 }
1580 }
1581
1582 g_string_free (str, TRUE);
1583
1584 return source;
1585 }
1586
1587 static gboolean
mail_session_forward_to_sync(CamelSession * session,CamelFolder * folder,CamelMimeMessage * message,const gchar * address,GCancellable * cancellable,GError ** error)1588 mail_session_forward_to_sync (CamelSession *session,
1589 CamelFolder *folder,
1590 CamelMimeMessage *message,
1591 const gchar *address,
1592 GCancellable *cancellable,
1593 GError **error)
1594 {
1595 EMailSessionPrivate *priv;
1596 ESource *source;
1597 ESourceRegistry *registry;
1598 ESourceMailIdentity *extension;
1599 ESourceMailSubmission *mail_submission;
1600 CamelMimeMessage *forward;
1601 CamelStream *mem;
1602 CamelInternetAddress *addr;
1603 CamelFolder *out_folder;
1604 CamelMessageInfo *info;
1605 CamelMedium *medium;
1606 CamelNameValueArray *orig_headers;
1607 GString *references = NULL;
1608 const gchar *extension_name;
1609 const gchar *from_address;
1610 const gchar *from_name;
1611 const gchar *reply_to, *message_id, *sent_folder = NULL, *transport_uid;
1612 gboolean success;
1613 gchar *subject;
1614 gchar *alias_name = NULL, *alias_address = NULL;
1615 guint ii, len;
1616
1617 g_return_val_if_fail (folder != NULL, FALSE);
1618 g_return_val_if_fail (message != NULL, FALSE);
1619 g_return_val_if_fail (address != NULL, FALSE);
1620
1621 priv = E_MAIL_SESSION_GET_PRIVATE (session);
1622
1623 if (!*address) {
1624 g_set_error (
1625 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1626 _("No destination address provided, forwarding "
1627 "of the message has been cancelled."));
1628 return FALSE;
1629 }
1630
1631 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1632
1633 source = mail_session_decode_forward_with (registry,
1634 camel_medium_get_header (CAMEL_MEDIUM (message), "X-Evolution-Forward-With"),
1635 &alias_name, &alias_address);
1636
1637 if (!source) {
1638 /* This returns a new ESource reference. */
1639 source = em_utils_guess_mail_identity_with_recipients (
1640 registry, message, folder, NULL, &alias_name, &alias_address);
1641 }
1642
1643 if (source == NULL) {
1644 g_set_error (
1645 error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
1646 _("No identity found to use, forwarding "
1647 "of the message has been cancelled."));
1648 return FALSE;
1649 }
1650
1651 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
1652 extension = e_source_get_extension (source, extension_name);
1653 if (alias_address) {
1654 from_name = alias_name;
1655 from_address = alias_address;
1656 } else {
1657 from_name = e_source_mail_identity_get_name (extension);
1658 from_address = e_source_mail_identity_get_address (extension);
1659 }
1660
1661 if (!from_name || !*from_name)
1662 from_name = e_source_mail_identity_get_name (extension);
1663
1664 reply_to = e_source_mail_identity_get_reply_to (extension);
1665
1666 forward = camel_mime_message_new ();
1667
1668 /* make copy of the message, because we are going to modify it */
1669 mem = camel_stream_mem_new ();
1670 camel_data_wrapper_write_to_stream_sync (
1671 CAMEL_DATA_WRAPPER (message), mem, NULL, NULL);
1672 g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
1673 camel_data_wrapper_construct_from_stream_sync (
1674 CAMEL_DATA_WRAPPER (forward), mem, NULL, NULL);
1675 g_object_unref (mem);
1676
1677 medium = CAMEL_MEDIUM (forward);
1678
1679 message_id = camel_mime_message_get_message_id (message);
1680 if (message_id && *message_id) {
1681 references = g_string_sized_new (128);
1682
1683 if (*message_id != '<')
1684 g_string_append_c (references, '<');
1685
1686 g_string_append (references, message_id);
1687
1688 if (*message_id != '<')
1689 g_string_append_c (references, '>');
1690 }
1691
1692 /* Remove all but content describing headers and Subject */
1693 orig_headers = camel_medium_dup_headers (medium);
1694 len = camel_name_value_array_get_length (orig_headers);
1695
1696 for (ii = 0; ii < len; ii++) {
1697 const gchar *header_name = NULL, *header_value = NULL;
1698
1699 if (!camel_name_value_array_get (orig_headers, ii, &header_name, &header_value) || !header_name)
1700 continue;
1701
1702 if (g_ascii_strncasecmp (header_name, "Content-", 8) != 0 &&
1703 g_ascii_strcasecmp (header_name, "Subject") != 0) {
1704 if (g_ascii_strcasecmp (header_name, "References") == 0) {
1705 if (header_value && *header_value) {
1706 if (!references)
1707 references = g_string_sized_new (128);
1708
1709 if (references->len)
1710 g_string_append_c (references, ' ');
1711
1712 g_string_append (references, header_value);
1713 }
1714 }
1715
1716 camel_medium_remove_header (medium, header_name);
1717 }
1718 }
1719
1720 camel_name_value_array_free (orig_headers);
1721
1722 if (references) {
1723 gchar *unfolded;
1724
1725 unfolded = camel_header_unfold (references->str);
1726
1727 if (unfolded && *unfolded)
1728 camel_medium_add_header (medium, "References", unfolded);
1729
1730 g_string_free (references, TRUE);
1731 g_free (unfolded);
1732 }
1733
1734 /* reply-to */
1735 if (reply_to && *reply_to) {
1736 addr = camel_internet_address_new ();
1737 if (camel_address_unformat (CAMEL_ADDRESS (addr), reply_to) > 0)
1738 camel_mime_message_set_reply_to (forward, addr);
1739 g_object_unref (addr);
1740 }
1741
1742 /* from */
1743 addr = camel_internet_address_new ();
1744 camel_internet_address_add (addr, from_name, from_address);
1745 camel_mime_message_set_from (forward, addr);
1746 g_object_unref (addr);
1747
1748 /* to */
1749 addr = camel_internet_address_new ();
1750 camel_address_decode (CAMEL_ADDRESS (addr), address);
1751 camel_mime_message_set_recipients (
1752 forward, CAMEL_RECIPIENT_TYPE_TO, addr);
1753 g_object_unref (addr);
1754
1755 /* subject */
1756 subject = mail_tool_generate_forward_subject (message);
1757 camel_mime_message_set_subject (forward, subject);
1758 g_free (subject);
1759
1760 /* store send account information */
1761 mail_submission = e_source_get_extension (source, E_SOURCE_EXTENSION_MAIL_SUBMISSION);
1762 if (e_source_mail_submission_get_use_sent_folder (mail_submission))
1763 sent_folder = e_source_mail_submission_get_sent_folder (mail_submission);
1764 transport_uid = e_source_mail_submission_get_transport_uid (mail_submission);
1765
1766 camel_medium_set_header (medium, "X-Evolution-Identity", e_source_get_uid (source));
1767 camel_medium_set_header (medium, "X-Evolution-Fcc", sent_folder);
1768 camel_medium_set_header (medium, "X-Evolution-Transport", transport_uid);
1769
1770 /* and send it */
1771 info = camel_message_info_new (NULL);
1772 out_folder = e_mail_session_get_local_folder (
1773 E_MAIL_SESSION (session), E_MAIL_LOCAL_FOLDER_OUTBOX);
1774 camel_message_info_set_flags (
1775 info, CAMEL_MESSAGE_SEEN, CAMEL_MESSAGE_SEEN);
1776
1777 success = e_mail_folder_append_message_sync (
1778 out_folder, forward, info, NULL, cancellable, error);
1779
1780 if (success) {
1781 GSettings *settings;
1782 gboolean flush_outbox;
1783
1784 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1785 flush_outbox = g_settings_get_boolean (settings, "flush-outbox");
1786 g_object_unref (settings);
1787
1788 g_mutex_lock (&priv->preparing_flush_lock);
1789
1790 if (priv->preparing_flush > 0) {
1791 g_source_remove (priv->preparing_flush);
1792 flush_outbox = TRUE;
1793 }
1794
1795 if (flush_outbox) {
1796 GMainContext *main_context;
1797 GSource *timeout_source;
1798
1799 main_context =
1800 camel_session_ref_main_context (session);
1801
1802 timeout_source =
1803 g_timeout_source_new_seconds (60);
1804 g_source_set_callback (
1805 timeout_source,
1806 session_forward_to_flush_outbox_cb,
1807 session, (GDestroyNotify) NULL);
1808 priv->preparing_flush = g_source_attach (
1809 timeout_source, main_context);
1810 g_source_unref (timeout_source);
1811
1812 g_main_context_unref (main_context);
1813 }
1814
1815 g_mutex_unlock (&priv->preparing_flush_lock);
1816 }
1817
1818 g_clear_object (&info);
1819
1820 g_object_unref (source);
1821 g_free (alias_address);
1822 g_free (alias_name);
1823
1824 return success;
1825 }
1826
1827 static gboolean
mail_session_get_oauth2_access_token_sync(CamelSession * session,CamelService * service,gchar ** out_access_token,gint * out_expires_in,GCancellable * cancellable,GError ** error)1828 mail_session_get_oauth2_access_token_sync (CamelSession *session,
1829 CamelService *service,
1830 gchar **out_access_token,
1831 gint *out_expires_in,
1832 GCancellable *cancellable,
1833 GError **error)
1834 {
1835 EMailSession *mail_session;
1836 ESource *source, *cred_source;
1837 GError *local_error = NULL;
1838 gboolean success;
1839
1840 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
1841 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1842
1843 mail_session = E_MAIL_SESSION (session);
1844 source = e_source_registry_ref_source (mail_session->priv->registry, camel_service_get_uid (service));
1845 if (!source) {
1846 g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
1847 _("Corresponding source for service with UID “%s” not found"),
1848 camel_service_get_uid (service));
1849
1850 return FALSE;
1851 }
1852
1853 cred_source = e_source_registry_find_extension (mail_session->priv->registry, source, E_SOURCE_EXTENSION_COLLECTION);
1854 if (cred_source && !e_util_can_use_collection_as_credential_source (cred_source, source)) {
1855 g_clear_object (&cred_source);
1856 }
1857
1858 success = e_source_get_oauth2_access_token_sync (cred_source ? cred_source : source, cancellable, out_access_token, out_expires_in, &local_error);
1859
1860 /* The Connection Refused error can be returned when the OAuth2 token is expired or
1861 when its refresh failed for some reason. In that case change the error domain/code,
1862 thus the other Camel/mail code understands it. */
1863 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_REFUSED) ||
1864 g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1865 local_error->domain = CAMEL_SERVICE_ERROR;
1866 local_error->code = CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE;
1867
1868 e_source_invoke_credentials_required_sync (cred_source ? cred_source : source,
1869 E_SOURCE_CREDENTIALS_REASON_REJECTED, NULL, 0, local_error, cancellable, NULL);
1870 }
1871
1872 if (local_error)
1873 g_propagate_error (error, local_error);
1874
1875 g_clear_object (&cred_source);
1876 g_object_unref (source);
1877
1878 return success;
1879 }
1880
1881 static gboolean
mail_session_is_email_address(const gchar * str)1882 mail_session_is_email_address (const gchar *str)
1883 {
1884 gboolean has_at = FALSE, has_dot_after_at = FALSE;
1885 gint ii;
1886
1887 if (!str)
1888 return FALSE;
1889
1890 for (ii = 0; str[ii]; ii++) {
1891 if (str[ii] == '@') {
1892 if (has_at)
1893 return FALSE;
1894
1895 has_at = TRUE;
1896 } else if (has_at && str[ii] == '.') {
1897 has_dot_after_at = TRUE;
1898 } else if (g_ascii_isspace (str[ii])) {
1899 return FALSE;
1900 } else if (strchr ("<>;,\\\"'|", str[ii])) {
1901 return FALSE;
1902 }
1903 }
1904
1905 return has_at && has_dot_after_at;
1906 }
1907
1908 static gboolean
mail_session_get_recipient_certificates_sync(CamelSession * session,guint32 flags,const GPtrArray * recipients,GSList ** out_certificates,GCancellable * cancellable,GError ** error)1909 mail_session_get_recipient_certificates_sync (CamelSession *session,
1910 guint32 flags, /* bit-or of CamelRecipientCertificateFlags */
1911 const GPtrArray *recipients, /* gchar * */
1912 GSList **out_certificates, /* gchar * */
1913 GCancellable *cancellable,
1914 GError **error)
1915 {
1916 GHashTable *certificates; /* guint index-to-recipients ~> gchar *certificate */
1917 EMailRecipientCertificateLookup lookup_settings;
1918 GSettings *settings;
1919 gboolean success = TRUE;
1920 guint ii;
1921
1922 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
1923 g_return_val_if_fail (recipients != NULL, FALSE);
1924 g_return_val_if_fail (out_certificates != NULL, FALSE);
1925
1926 *out_certificates = NULL;
1927
1928 settings = e_util_ref_settings ("org.gnome.evolution.mail");
1929 lookup_settings = g_settings_get_enum (settings, "lookup-recipient-certificates");
1930 g_object_unref (settings);
1931
1932 if (lookup_settings == E_MAIL_RECIPIENT_CERTIFICATE_LOOKUP_OFF)
1933 return TRUE;
1934
1935 certificates = g_hash_table_new (g_direct_hash, g_direct_equal);
1936
1937 for (ii = 0; ii < recipients->len; ii++) {
1938 gchar *certstr = NULL;
1939
1940 g_signal_emit (session, signals[GET_RECIPIENT_CERTIFICATE], 0, flags, recipients->pdata[ii], &certstr);
1941
1942 if (certstr && *certstr)
1943 g_hash_table_insert (certificates, GUINT_TO_POINTER (ii + 1), certstr);
1944 else
1945 g_free (certstr);
1946 }
1947
1948 if (lookup_settings == E_MAIL_RECIPIENT_CERTIFICATE_LOOKUP_BOOKS &&
1949 g_hash_table_size (certificates) != recipients->len) {
1950 ESourceRegistry *registry;
1951 GPtrArray *todo_recipients;
1952 GSList *found_certificates = NULL;
1953
1954 todo_recipients = g_ptr_array_new ();
1955 for (ii = 0; ii < recipients->len; ii++) {
1956 /* Lookup address books only with email addresses. */
1957 if (!g_hash_table_contains (certificates, GUINT_TO_POINTER (ii + 1)) &&
1958 mail_session_is_email_address (recipients->pdata[ii])) {
1959 g_ptr_array_add (todo_recipients, recipients->pdata[ii]);
1960 }
1961 }
1962
1963 if (todo_recipients->len) {
1964 registry = e_mail_session_get_registry (E_MAIL_SESSION (session));
1965
1966 if ((flags & CAMEL_RECIPIENT_CERTIFICATE_SMIME) != 0)
1967 camel_operation_push_message (cancellable, "%s", _("Looking up recipient S/MIME certificates in address books…"));
1968 else
1969 camel_operation_push_message (cancellable, "%s", _("Looking up recipient PGP keys in address books…"));
1970
1971 success = e_book_utils_get_recipient_certificates_sync (registry, NULL, flags, todo_recipients, &found_certificates, cancellable, error);
1972
1973 camel_operation_pop_message (cancellable);
1974 }
1975
1976 if (success && found_certificates && g_slist_length (found_certificates) == todo_recipients->len) {
1977 GSList *link;
1978
1979 for (link = found_certificates, ii = 0; link && ii < recipients->len; ii++) {
1980 if (!g_hash_table_contains (certificates, GUINT_TO_POINTER (ii + 1))) {
1981 if (link->data) {
1982 g_hash_table_insert (certificates, GUINT_TO_POINTER (ii + 1), link->data);
1983 link->data = NULL;
1984 }
1985
1986 link = g_slist_next (link);
1987 }
1988 }
1989 }
1990
1991 g_slist_free_full (found_certificates, g_free);
1992 g_ptr_array_free (todo_recipients, TRUE);
1993 }
1994
1995 if (success) {
1996 for (ii = 0; ii < recipients->len; ii++) {
1997 *out_certificates = g_slist_prepend (*out_certificates,
1998 g_hash_table_lookup (certificates, GUINT_TO_POINTER (ii + 1)));
1999 }
2000
2001 *out_certificates = g_slist_reverse (*out_certificates);
2002 } else {
2003 GHashTableIter iter;
2004 gpointer value;
2005
2006 /* There is no destructor for the 'value', to be able to easily pass it to
2007 the out_certificates. This code is here to free the values, though it might
2008 not be usually used, because e_book_utils_get_recipient_certificates_sync()
2009 returns TRUE usually. */
2010 g_hash_table_iter_init (&iter, certificates);
2011 while (g_hash_table_iter_next (&iter, NULL, &value)) {
2012 g_free (value);
2013 }
2014 }
2015
2016 g_hash_table_destroy (certificates);
2017
2018 return success;
2019 }
2020
2021 static EMVFolderContext *
mail_session_create_vfolder_context(EMailSession * session)2022 mail_session_create_vfolder_context (EMailSession *session)
2023 {
2024 return em_vfolder_context_new ();
2025 }
2026
2027 static gpointer
mail_session_init_null_provider_once(gpointer unused)2028 mail_session_init_null_provider_once (gpointer unused)
2029 {
2030 camel_null_store_register_provider ();
2031
2032 /* Make sure ESourceCamel picks up the "none" provider. */
2033 e_source_camel_generate_subtype ("none", CAMEL_TYPE_SETTINGS);
2034
2035 return NULL;
2036 }
2037
2038 static void
e_mail_session_class_init(EMailSessionClass * class)2039 e_mail_session_class_init (EMailSessionClass *class)
2040 {
2041 GObjectClass *object_class;
2042 CamelSessionClass *session_class;
2043
2044 g_type_class_add_private (class, sizeof (EMailSessionPrivate));
2045
2046 object_class = G_OBJECT_CLASS (class);
2047 object_class->set_property = mail_session_set_property;
2048 object_class->get_property = mail_session_get_property;
2049 object_class->dispose = mail_session_dispose;
2050 object_class->finalize = mail_session_finalize;
2051 object_class->constructed = mail_session_constructed;
2052
2053 session_class = CAMEL_SESSION_CLASS (class);
2054 session_class->add_service = mail_session_add_service;
2055 session_class->get_password = mail_session_get_password;
2056 session_class->forget_password = mail_session_forget_password;
2057 session_class->forward_to_sync = mail_session_forward_to_sync;
2058 session_class->get_oauth2_access_token_sync = mail_session_get_oauth2_access_token_sync;
2059 session_class->get_recipient_certificates_sync = mail_session_get_recipient_certificates_sync;
2060
2061 class->create_vfolder_context = mail_session_create_vfolder_context;
2062
2063 g_object_class_install_property (
2064 object_class,
2065 PROP_FOLDER_CACHE,
2066 g_param_spec_object (
2067 "folder-cache",
2068 NULL,
2069 NULL,
2070 MAIL_TYPE_FOLDER_CACHE,
2071 G_PARAM_READABLE |
2072 G_PARAM_STATIC_STRINGS));
2073
2074 g_object_class_install_property (
2075 object_class,
2076 PROP_LOCAL_STORE,
2077 g_param_spec_object (
2078 "local-store",
2079 "Local Store",
2080 "Built-in local store",
2081 CAMEL_TYPE_STORE,
2082 G_PARAM_READABLE |
2083 G_PARAM_STATIC_STRINGS));
2084
2085 g_object_class_install_property (
2086 object_class,
2087 PROP_REGISTRY,
2088 g_param_spec_object (
2089 "registry",
2090 "Registry",
2091 "Data source registry",
2092 E_TYPE_SOURCE_REGISTRY,
2093 G_PARAM_READWRITE |
2094 G_PARAM_CONSTRUCT_ONLY |
2095 G_PARAM_STATIC_STRINGS));
2096
2097 g_object_class_install_property (
2098 object_class,
2099 PROP_VFOLDER_STORE,
2100 g_param_spec_object (
2101 "vfolder-store",
2102 "Search Folder Store",
2103 "Built-in search folder store",
2104 CAMEL_TYPE_STORE,
2105 G_PARAM_READABLE |
2106 G_PARAM_STATIC_STRINGS));
2107
2108 /**
2109 * EMailSession::flush-outbox
2110 * @session: the email session
2111 *
2112 * Emitted if the send folder should be flushed.
2113 **/
2114 signals[FLUSH_OUTBOX] = g_signal_new (
2115 "flush-outbox",
2116 G_OBJECT_CLASS_TYPE (object_class),
2117 G_SIGNAL_RUN_FIRST,
2118 G_STRUCT_OFFSET (EMailSessionClass, flush_outbox),
2119 NULL, NULL,
2120 g_cclosure_marshal_VOID__VOID,
2121 G_TYPE_NONE, 0);
2122
2123 /**
2124 * EMailSession::refresh-service
2125 * @session: the #EMailSession that emitted the signal
2126 * @service: a #CamelService
2127 *
2128 * Emitted when @service should be refreshed.
2129 **/
2130 signals[REFRESH_SERVICE] = g_signal_new (
2131 "refresh-service",
2132 G_OBJECT_CLASS_TYPE (object_class),
2133 G_SIGNAL_RUN_LAST,
2134 G_STRUCT_OFFSET (EMailSessionClass, refresh_service),
2135 NULL, NULL,
2136 g_cclosure_marshal_VOID__OBJECT,
2137 G_TYPE_NONE, 1,
2138 CAMEL_TYPE_SERVICE);
2139
2140 /**
2141 * EMailSession::store-added
2142 * @session: the #EMailSession that emitted the signal
2143 * @store: a #CamelStore
2144 *
2145 * Emitted when a store is added
2146 **/
2147 signals[STORE_ADDED] = g_signal_new (
2148 "store-added",
2149 G_OBJECT_CLASS_TYPE (object_class),
2150 G_SIGNAL_RUN_FIRST,
2151 G_STRUCT_OFFSET (EMailSessionClass, store_added),
2152 NULL, NULL,
2153 g_cclosure_marshal_VOID__OBJECT,
2154 G_TYPE_NONE, 1,
2155 CAMEL_TYPE_STORE);
2156
2157 /**
2158 * EMailSession::store-removed
2159 * @session: the #EMailSession that emitted the signal
2160 * @store: a #CamelStore
2161 *
2162 * Emitted when a store is removed
2163 **/
2164 signals[STORE_REMOVED] = g_signal_new (
2165 "store-removed",
2166 G_OBJECT_CLASS_TYPE (object_class),
2167 G_SIGNAL_RUN_FIRST,
2168 G_STRUCT_OFFSET (EMailSessionClass, store_removed),
2169 NULL, NULL,
2170 g_cclosure_marshal_VOID__OBJECT,
2171 G_TYPE_NONE, 1,
2172 CAMEL_TYPE_STORE);
2173
2174 /**
2175 * EMailSession::allow-auth-prompt
2176 * @session: the #EMailSession that emitted the signal
2177 * @source: an #ESource
2178 *
2179 * This signal is emitted with e_mail_session_emit_allow_auth_prompt() to let
2180 * any listeners know to enable credentials prompt for the given @source.
2181 *
2182 * Since: 3.16
2183 **/
2184 signals[ALLOW_AUTH_PROMPT] = g_signal_new (
2185 "allow-auth-prompt",
2186 G_OBJECT_CLASS_TYPE (object_class),
2187 G_SIGNAL_RUN_FIRST,
2188 G_STRUCT_OFFSET (EMailSessionClass, allow_auth_prompt),
2189 NULL, NULL,
2190 g_cclosure_marshal_VOID__OBJECT,
2191 G_TYPE_NONE, 1,
2192 E_TYPE_SOURCE);
2193
2194 /**
2195 * EMailSession::get-recipient-certificate
2196 * @session: the #EMailSession that emitted the signal
2197 * @flags: a bit-or of #CamelRecipientCertificateFlags
2198 * @email_address: recipient's email address
2199 *
2200 * This signal is used to get recipient's S/MIME certificate or
2201 * PGP key for encryption, as part of camel_session_get_recipient_certificates_sync().
2202 * The listener is not supposed to do any expensive look ups, it should only check
2203 * whether it has the certificate available for the given @email_address and
2204 * eventually return it as base64 encoded string.
2205 *
2206 * The caller of the action signal will free returned pointer with g_free(),
2207 * when no longer needed.
2208 *
2209 * Returns: (transfer full) (nullable): %NULL when the certificate not known,
2210 * or a newly allocated base64-encoded string with the certificate.
2211 *
2212 * Since: 3.30
2213 **/
2214 signals[GET_RECIPIENT_CERTIFICATE] = g_signal_new (
2215 "get-recipient-certificate",
2216 G_OBJECT_CLASS_TYPE (object_class),
2217 G_SIGNAL_ACTION,
2218 G_STRUCT_OFFSET (EMailSessionClass, get_recipient_certificate),
2219 NULL, NULL,
2220 NULL,
2221 G_TYPE_STRING, 2,
2222 G_TYPE_UINT,
2223 G_TYPE_STRING);
2224
2225 /**
2226 * EMailSession::archive-folder-changed
2227 * @session: the #EMailSession that emitted the signal
2228 * @service_uid: UID of a #CamelService, whose archive folder setting changed
2229 * @old_folder_uri: (nullable): an old archive folder URI, or %NULL, when none had been set already
2230 * @new_folder_uri: (nullable): a new archive folder URI, or %NULL, when none had been set
2231 *
2232 * Notifies about changes in archive folders setup.
2233 *
2234 * Since: 3.34
2235 **/
2236 signals[ARCHIVE_FOLDER_CHANGED] = g_signal_new (
2237 "archive-folder-changed",
2238 G_OBJECT_CLASS_TYPE (object_class),
2239 G_SIGNAL_RUN_FIRST,
2240 G_STRUCT_OFFSET (EMailSessionClass, archive_folder_changed),
2241 NULL, NULL,
2242 NULL,
2243 G_TYPE_NONE, 3,
2244 G_TYPE_STRING,
2245 G_TYPE_STRING,
2246 G_TYPE_STRING);
2247
2248 /**
2249 * EMailSession::connect-store
2250 * @session: the #EMailSession that emitted the signal
2251 * @store: a #CamelStore
2252 *
2253 * This signal is emitted with e_mail_session_emit_connect_store() to let
2254 * any listeners know to connect the given @store.
2255 *
2256 * Since: 3.34
2257 **/
2258 signals[CONNECT_STORE] = g_signal_new (
2259 "connect-store",
2260 G_OBJECT_CLASS_TYPE (object_class),
2261 G_SIGNAL_RUN_FIRST,
2262 G_STRUCT_OFFSET (EMailSessionClass, connect_store),
2263 NULL, NULL,
2264 g_cclosure_marshal_VOID__OBJECT,
2265 G_TYPE_NONE, 1,
2266 CAMEL_TYPE_STORE);
2267 }
2268
2269 static void
e_mail_session_init(EMailSession * session)2270 e_mail_session_init (EMailSession *session)
2271 {
2272 static GOnce init_null_provider_once = G_ONCE_INIT;
2273 GHashTable *auto_refresh_table;
2274 GHashTable *junk_filters;
2275
2276 /* Do not call this from the class_init(), to avoid a claim from Helgrind about a lock
2277 order violation between CamelProvider's global lock and glib's GType lock. */
2278 g_once (&init_null_provider_once, mail_session_init_null_provider_once, NULL);
2279
2280 auto_refresh_table = g_hash_table_new_full (
2281 (GHashFunc) g_str_hash,
2282 (GEqualFunc) g_str_equal,
2283 (GDestroyNotify) g_free,
2284 (GDestroyNotify) NULL);
2285
2286 junk_filters = g_hash_table_new (
2287 (GHashFunc) g_str_hash,
2288 (GEqualFunc) g_str_equal);
2289
2290 session->priv = E_MAIL_SESSION_GET_PRIVATE (session);
2291 session->priv->folder_cache = mail_folder_cache_new ();
2292 session->priv->auto_refresh_table = auto_refresh_table;
2293 session->priv->junk_filters = junk_filters;
2294
2295 session->priv->local_folders =
2296 g_ptr_array_new_with_free_func (
2297 (GDestroyNotify) g_object_unref);
2298 session->priv->local_folder_uris =
2299 g_ptr_array_new_with_free_func (
2300 (GDestroyNotify) g_free);
2301 session->priv->archive_folders_hash =
2302 g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
2303
2304 g_mutex_init (&session->priv->preparing_flush_lock);
2305 g_mutex_init (&session->priv->used_services_lock);
2306 g_mutex_init (&session->priv->archive_folders_hash_lock);
2307 g_cond_init (&session->priv->used_services_cond);
2308
2309 session->priv->used_services = g_hash_table_new (g_direct_hash, g_direct_equal);
2310 }
2311
2312 EMailSession *
e_mail_session_new(ESourceRegistry * registry)2313 e_mail_session_new (ESourceRegistry *registry)
2314 {
2315 const gchar *user_data_dir;
2316 const gchar *user_cache_dir;
2317
2318 g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
2319
2320 user_data_dir = mail_session_get_data_dir ();
2321 user_cache_dir = mail_session_get_cache_dir ();
2322
2323 return g_object_new (
2324 E_TYPE_MAIL_SESSION,
2325 "user-data-dir", user_data_dir,
2326 "user-cache-dir", user_cache_dir,
2327 "registry", registry,
2328 NULL);
2329 }
2330
2331 ESourceRegistry *
e_mail_session_get_registry(EMailSession * session)2332 e_mail_session_get_registry (EMailSession *session)
2333 {
2334 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2335
2336 return session->priv->registry;
2337 }
2338
2339 MailFolderCache *
e_mail_session_get_folder_cache(EMailSession * session)2340 e_mail_session_get_folder_cache (EMailSession *session)
2341 {
2342 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2343
2344 return session->priv->folder_cache;
2345 }
2346
2347 CamelStore *
e_mail_session_get_local_store(EMailSession * session)2348 e_mail_session_get_local_store (EMailSession *session)
2349 {
2350 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2351
2352 return CAMEL_STORE (session->priv->local_store);
2353 }
2354
2355 CamelFolder *
e_mail_session_get_local_folder(EMailSession * session,EMailLocalFolder type)2356 e_mail_session_get_local_folder (EMailSession *session,
2357 EMailLocalFolder type)
2358 {
2359 GPtrArray *local_folders;
2360 CamelFolder *folder;
2361
2362 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2363
2364 local_folders = session->priv->local_folders;
2365 g_return_val_if_fail (type < local_folders->len, NULL);
2366
2367 folder = g_ptr_array_index (local_folders, type);
2368 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
2369
2370 return folder;
2371 }
2372
2373 const gchar *
e_mail_session_get_local_folder_uri(EMailSession * session,EMailLocalFolder type)2374 e_mail_session_get_local_folder_uri (EMailSession *session,
2375 EMailLocalFolder type)
2376 {
2377 GPtrArray *local_folder_uris;
2378 const gchar *folder_uri;
2379
2380 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2381
2382 local_folder_uris = session->priv->local_folder_uris;
2383 g_return_val_if_fail (type < local_folder_uris->len, NULL);
2384
2385 folder_uri = g_ptr_array_index (local_folder_uris, type);
2386 g_return_val_if_fail (folder_uri != NULL, NULL);
2387
2388 return folder_uri;
2389 }
2390
2391 GList *
e_mail_session_get_available_junk_filters(EMailSession * session)2392 e_mail_session_get_available_junk_filters (EMailSession *session)
2393 {
2394 GList *list, *link;
2395 GQueue trash = G_QUEUE_INIT;
2396
2397 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2398
2399 list = g_hash_table_get_values (session->priv->junk_filters);
2400
2401 /* Discard unavailable junk filters. (e.g. Junk filter
2402 * requires Bogofilter but Bogofilter is not installed,
2403 * hence the junk filter is unavailable.) */
2404
2405 for (link = list; link != NULL; link = g_list_next (link)) {
2406 EMailJunkFilter *junk_filter;
2407
2408 junk_filter = E_MAIL_JUNK_FILTER (link->data);
2409 if (!e_mail_junk_filter_available (junk_filter))
2410 g_queue_push_tail (&trash, link);
2411 }
2412
2413 while ((link = g_queue_pop_head (&trash)) != NULL)
2414 list = g_list_delete_link (list, link);
2415
2416 /* Sort the remaining junk filters by display name. */
2417
2418 return g_list_sort (list, (GCompareFunc) e_mail_junk_filter_compare);
2419 }
2420
2421 /**
2422 * e_mail_session_get_junk_filter_by_name:
2423 * @session: an #EMailSession
2424 * @filter_name: a junk filter name
2425 *
2426 * Looks up an #EMailJunkFilter extension by its filter name, as specified
2427 * in its class structure. If no match is found, the function returns %NULL.
2428 *
2429 * Returns: an #EMailJunkFilter, or %NULL
2430 **/
2431 EMailJunkFilter *
e_mail_session_get_junk_filter_by_name(EMailSession * session,const gchar * filter_name)2432 e_mail_session_get_junk_filter_by_name (EMailSession *session,
2433 const gchar *filter_name)
2434 {
2435 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2436 g_return_val_if_fail (filter_name != NULL, NULL);
2437
2438 return g_hash_table_lookup (session->priv->junk_filters, filter_name);
2439 }
2440
2441 static void
mail_session_get_inbox_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)2442 mail_session_get_inbox_thread (GSimpleAsyncResult *simple,
2443 EMailSession *session,
2444 GCancellable *cancellable)
2445 {
2446 AsyncContext *context;
2447 GError *error = NULL;
2448
2449 context = g_simple_async_result_get_op_res_gpointer (simple);
2450
2451 context->folder = e_mail_session_get_inbox_sync (
2452 session, context->uid, cancellable, &error);
2453
2454 if (error != NULL)
2455 g_simple_async_result_take_error (simple, error);
2456 }
2457
2458 CamelFolder *
e_mail_session_get_inbox_sync(EMailSession * session,const gchar * service_uid,GCancellable * cancellable,GError ** error)2459 e_mail_session_get_inbox_sync (EMailSession *session,
2460 const gchar *service_uid,
2461 GCancellable *cancellable,
2462 GError **error)
2463 {
2464 CamelService *service;
2465 CamelFolder *folder = NULL;
2466
2467 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2468 g_return_val_if_fail (service_uid != NULL, NULL);
2469
2470 service = camel_session_ref_service (
2471 CAMEL_SESSION (session), service_uid);
2472
2473 if (service == NULL)
2474 return NULL;
2475
2476 if (!CAMEL_IS_STORE (service))
2477 goto exit;
2478
2479 if (!camel_service_connect_sync (service, cancellable, error))
2480 goto exit;
2481
2482 folder = camel_store_get_inbox_folder_sync (
2483 CAMEL_STORE (service), cancellable, error);
2484
2485 exit:
2486 g_object_unref (service);
2487
2488 return folder;
2489 }
2490
2491 void
e_mail_session_get_inbox(EMailSession * session,const gchar * service_uid,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2492 e_mail_session_get_inbox (EMailSession *session,
2493 const gchar *service_uid,
2494 gint io_priority,
2495 GCancellable *cancellable,
2496 GAsyncReadyCallback callback,
2497 gpointer user_data)
2498 {
2499 GSimpleAsyncResult *simple;
2500 AsyncContext *context;
2501
2502 g_return_if_fail (E_IS_MAIL_SESSION (session));
2503 g_return_if_fail (service_uid != NULL);
2504
2505 context = g_slice_new0 (AsyncContext);
2506 context->uid = g_strdup (service_uid);
2507
2508 simple = g_simple_async_result_new (
2509 G_OBJECT (session), callback,
2510 user_data, e_mail_session_get_inbox);
2511
2512 g_simple_async_result_set_check_cancellable (simple, cancellable);
2513
2514 g_simple_async_result_set_op_res_gpointer (
2515 simple, context, (GDestroyNotify) async_context_free);
2516
2517 g_simple_async_result_run_in_thread (
2518 simple, (GSimpleAsyncThreadFunc)
2519 mail_session_get_inbox_thread,
2520 io_priority, cancellable);
2521
2522 g_object_unref (simple);
2523 }
2524
2525 CamelFolder *
e_mail_session_get_inbox_finish(EMailSession * session,GAsyncResult * result,GError ** error)2526 e_mail_session_get_inbox_finish (EMailSession *session,
2527 GAsyncResult *result,
2528 GError **error)
2529 {
2530 GSimpleAsyncResult *simple;
2531 AsyncContext *context;
2532
2533 g_return_val_if_fail (
2534 g_simple_async_result_is_valid (
2535 result, G_OBJECT (session),
2536 e_mail_session_get_inbox), NULL);
2537
2538 simple = G_SIMPLE_ASYNC_RESULT (result);
2539 context = g_simple_async_result_get_op_res_gpointer (simple);
2540
2541 if (g_simple_async_result_propagate_error (simple, error))
2542 return NULL;
2543
2544 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2545
2546 return g_object_ref (context->folder);
2547 }
2548
2549 static void
mail_session_get_trash_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)2550 mail_session_get_trash_thread (GSimpleAsyncResult *simple,
2551 EMailSession *session,
2552 GCancellable *cancellable)
2553 {
2554 AsyncContext *context;
2555 GError *error = NULL;
2556
2557 context = g_simple_async_result_get_op_res_gpointer (simple);
2558
2559 context->folder = e_mail_session_get_trash_sync (
2560 session, context->uid, cancellable, &error);
2561
2562 if (error != NULL)
2563 g_simple_async_result_take_error (simple, error);
2564 }
2565
2566 CamelFolder *
e_mail_session_get_trash_sync(EMailSession * session,const gchar * service_uid,GCancellable * cancellable,GError ** error)2567 e_mail_session_get_trash_sync (EMailSession *session,
2568 const gchar *service_uid,
2569 GCancellable *cancellable,
2570 GError **error)
2571 {
2572 CamelService *service;
2573 CamelFolder *folder = NULL;
2574
2575 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2576 g_return_val_if_fail (service_uid != NULL, NULL);
2577
2578 service = camel_session_ref_service (
2579 CAMEL_SESSION (session), service_uid);
2580
2581 if (service == NULL)
2582 return NULL;
2583
2584 if (!CAMEL_IS_STORE (service))
2585 goto exit;
2586
2587 if (!camel_service_connect_sync (service, cancellable, error))
2588 goto exit;
2589
2590 folder = camel_store_get_trash_folder_sync (
2591 CAMEL_STORE (service), cancellable, error);
2592
2593 exit:
2594 g_object_unref (service);
2595
2596 return folder;
2597 }
2598
2599 void
e_mail_session_get_trash(EMailSession * session,const gchar * service_uid,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2600 e_mail_session_get_trash (EMailSession *session,
2601 const gchar *service_uid,
2602 gint io_priority,
2603 GCancellable *cancellable,
2604 GAsyncReadyCallback callback,
2605 gpointer user_data)
2606 {
2607 GSimpleAsyncResult *simple;
2608 AsyncContext *context;
2609
2610 g_return_if_fail (E_IS_MAIL_SESSION (session));
2611 g_return_if_fail (service_uid != NULL);
2612
2613 context = g_slice_new0 (AsyncContext);
2614 context->uid = g_strdup (service_uid);
2615
2616 simple = g_simple_async_result_new (
2617 G_OBJECT (session), callback,
2618 user_data, e_mail_session_get_trash);
2619
2620 g_simple_async_result_set_check_cancellable (simple, cancellable);
2621
2622 g_simple_async_result_set_op_res_gpointer (
2623 simple, context, (GDestroyNotify) async_context_free);
2624
2625 g_simple_async_result_run_in_thread (
2626 simple, (GSimpleAsyncThreadFunc)
2627 mail_session_get_trash_thread,
2628 io_priority, cancellable);
2629
2630 g_object_unref (simple);
2631 }
2632
2633 CamelFolder *
e_mail_session_get_trash_finish(EMailSession * session,GAsyncResult * result,GError ** error)2634 e_mail_session_get_trash_finish (EMailSession *session,
2635 GAsyncResult *result,
2636 GError **error)
2637 {
2638 GSimpleAsyncResult *simple;
2639 AsyncContext *context;
2640
2641 g_return_val_if_fail (
2642 g_simple_async_result_is_valid (
2643 result, G_OBJECT (session),
2644 e_mail_session_get_trash), NULL);
2645
2646 simple = G_SIMPLE_ASYNC_RESULT (result);
2647 context = g_simple_async_result_get_op_res_gpointer (simple);
2648
2649 if (g_simple_async_result_propagate_error (simple, error))
2650 return NULL;
2651
2652 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2653
2654 return g_object_ref (context->folder);
2655 }
2656
2657 static void
mail_session_uri_to_folder_thread(GSimpleAsyncResult * simple,EMailSession * session,GCancellable * cancellable)2658 mail_session_uri_to_folder_thread (GSimpleAsyncResult *simple,
2659 EMailSession *session,
2660 GCancellable *cancellable)
2661 {
2662 AsyncContext *context;
2663 GError *error = NULL;
2664
2665 context = g_simple_async_result_get_op_res_gpointer (simple);
2666
2667 context->folder = e_mail_session_uri_to_folder_sync (
2668 session, context->uri, context->flags,
2669 cancellable, &error);
2670
2671 if (error != NULL)
2672 g_simple_async_result_take_error (simple, error);
2673 }
2674
2675 CamelFolder *
e_mail_session_uri_to_folder_sync(EMailSession * session,const gchar * folder_uri,CamelStoreGetFolderFlags flags,GCancellable * cancellable,GError ** error)2676 e_mail_session_uri_to_folder_sync (EMailSession *session,
2677 const gchar *folder_uri,
2678 CamelStoreGetFolderFlags flags,
2679 GCancellable *cancellable,
2680 GError **error)
2681 {
2682 CamelStore *store;
2683 CamelFolder *folder;
2684 gchar *folder_name;
2685 gboolean success;
2686
2687 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2688 g_return_val_if_fail (folder_uri != NULL, NULL);
2689
2690 success = e_mail_folder_uri_parse (
2691 CAMEL_SESSION (session), folder_uri,
2692 &store, &folder_name, error);
2693
2694 if (!success)
2695 return NULL;
2696
2697 folder = camel_store_get_folder_sync (
2698 store, folder_name, flags, cancellable, error);
2699
2700 if (folder != NULL) {
2701 MailFolderCache *folder_cache;
2702 folder_cache = e_mail_session_get_folder_cache (session);
2703 mail_folder_cache_note_folder (folder_cache, folder);
2704 }
2705
2706 g_free (folder_name);
2707 g_object_unref (store);
2708
2709 return folder;
2710 }
2711
2712 void
e_mail_session_uri_to_folder(EMailSession * session,const gchar * folder_uri,CamelStoreGetFolderFlags flags,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)2713 e_mail_session_uri_to_folder (EMailSession *session,
2714 const gchar *folder_uri,
2715 CamelStoreGetFolderFlags flags,
2716 gint io_priority,
2717 GCancellable *cancellable,
2718 GAsyncReadyCallback callback,
2719 gpointer user_data)
2720 {
2721 GSimpleAsyncResult *simple;
2722 AsyncContext *context;
2723
2724 g_return_if_fail (E_IS_MAIL_SESSION (session));
2725 g_return_if_fail (folder_uri != NULL);
2726
2727 context = g_slice_new0 (AsyncContext);
2728 context->uri = g_strdup (folder_uri);
2729 context->flags = flags;
2730
2731 simple = g_simple_async_result_new (
2732 G_OBJECT (session), callback,
2733 user_data, e_mail_session_uri_to_folder);
2734
2735 g_simple_async_result_set_check_cancellable (simple, cancellable);
2736
2737 g_simple_async_result_set_op_res_gpointer (
2738 simple, context, (GDestroyNotify) async_context_free);
2739
2740 g_simple_async_result_run_in_thread (
2741 simple, (GSimpleAsyncThreadFunc)
2742 mail_session_uri_to_folder_thread,
2743 io_priority, cancellable);
2744
2745 g_object_unref (simple);
2746 }
2747
2748 CamelFolder *
e_mail_session_uri_to_folder_finish(EMailSession * session,GAsyncResult * result,GError ** error)2749 e_mail_session_uri_to_folder_finish (EMailSession *session,
2750 GAsyncResult *result,
2751 GError **error)
2752 {
2753 GSimpleAsyncResult *simple;
2754 AsyncContext *context;
2755
2756 g_return_val_if_fail (
2757 g_simple_async_result_is_valid (
2758 result, G_OBJECT (session),
2759 e_mail_session_uri_to_folder), NULL);
2760
2761 simple = G_SIMPLE_ASYNC_RESULT (result);
2762 context = g_simple_async_result_get_op_res_gpointer (simple);
2763
2764 if (g_simple_async_result_propagate_error (simple, error))
2765 return NULL;
2766
2767 g_return_val_if_fail (CAMEL_IS_FOLDER (context->folder), NULL);
2768
2769 return g_object_ref (context->folder);
2770 }
2771
2772 gboolean
e_binding_transform_service_to_source(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer session)2773 e_binding_transform_service_to_source (GBinding *binding,
2774 const GValue *source_value,
2775 GValue *target_value,
2776 gpointer session)
2777 {
2778 CamelService *service;
2779 ESourceRegistry *registry;
2780 ESource *source;
2781 const gchar *uid;
2782 gboolean success = FALSE;
2783
2784 g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2785 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2786
2787 service = g_value_get_object (source_value);
2788
2789 if (!CAMEL_IS_SERVICE (service))
2790 return FALSE;
2791
2792 uid = camel_service_get_uid (service);
2793 registry = e_mail_session_get_registry (session);
2794 source = e_source_registry_ref_source (registry, uid);
2795
2796 if (source != NULL) {
2797 g_value_take_object (target_value, source);
2798 success = TRUE;
2799 }
2800
2801 return success;
2802 }
2803
2804 gboolean
e_binding_transform_source_to_service(GBinding * binding,const GValue * source_value,GValue * target_value,gpointer session)2805 e_binding_transform_source_to_service (GBinding *binding,
2806 const GValue *source_value,
2807 GValue *target_value,
2808 gpointer session)
2809 {
2810 CamelService *service;
2811 ESource *source;
2812 const gchar *uid;
2813
2814 g_return_val_if_fail (G_IS_BINDING (binding), FALSE);
2815 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2816
2817 source = g_value_get_object (source_value);
2818
2819 if (!E_IS_SOURCE (source))
2820 return FALSE;
2821
2822 uid = e_source_get_uid (source);
2823 service = camel_session_ref_service (session, uid);
2824
2825 if (service == NULL)
2826 return FALSE;
2827
2828 g_value_take_object (target_value, service);
2829
2830 return TRUE;
2831 }
2832
2833 /******************************** Legacy API *********************************/
2834
2835 void
mail_session_flush_filter_log(EMailSession * session)2836 mail_session_flush_filter_log (EMailSession *session)
2837 {
2838 g_return_if_fail (E_IS_MAIL_SESSION (session));
2839
2840 if (session->priv->filter_logfile)
2841 fflush (session->priv->filter_logfile);
2842 }
2843
2844 const gchar *
mail_session_get_data_dir(void)2845 mail_session_get_data_dir (void)
2846 {
2847 if (G_UNLIKELY (mail_data_dir == NULL)) {
2848 mail_data_dir = g_build_filename (e_get_user_data_dir (), "mail", NULL);
2849 g_mkdir_with_parents (mail_data_dir, 0700);
2850 }
2851
2852 return mail_data_dir;
2853 }
2854
2855 const gchar *
mail_session_get_cache_dir(void)2856 mail_session_get_cache_dir (void)
2857 {
2858 if (G_UNLIKELY (mail_cache_dir == NULL)) {
2859 mail_cache_dir = g_build_filename (e_get_user_cache_dir (), "mail", NULL);
2860 g_mkdir_with_parents (mail_cache_dir, 0700);
2861 }
2862
2863 return mail_cache_dir;
2864 }
2865
2866 const gchar *
mail_session_get_config_dir(void)2867 mail_session_get_config_dir (void)
2868 {
2869 if (G_UNLIKELY (mail_config_dir == NULL)) {
2870 mail_config_dir = g_build_filename (e_get_user_config_dir (), "mail", NULL);
2871 g_mkdir_with_parents (mail_config_dir, 0700);
2872 }
2873
2874 return mail_config_dir;
2875 }
2876
2877 CamelStore *
e_mail_session_get_vfolder_store(EMailSession * session)2878 e_mail_session_get_vfolder_store (EMailSession *session)
2879 {
2880 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2881
2882 return CAMEL_STORE (session->priv->vfolder_store);
2883 }
2884
2885 EMVFolderContext *
e_mail_session_create_vfolder_context(EMailSession * session)2886 e_mail_session_create_vfolder_context (EMailSession *session)
2887 {
2888 EMailSessionClass *class;
2889
2890 g_return_val_if_fail (E_IS_MAIL_SESSION (session), NULL);
2891
2892 class = E_MAIL_SESSION_GET_CLASS (session);
2893 g_return_val_if_fail (class != NULL, NULL);
2894 g_return_val_if_fail (class->create_vfolder_context != NULL, NULL);
2895
2896 return class->create_vfolder_context (session);
2897 }
2898
2899 static gboolean
mail_session_flush_outbox_timeout_cb(gpointer user_data)2900 mail_session_flush_outbox_timeout_cb (gpointer user_data)
2901 {
2902 EMailSession *session = user_data;
2903
2904 if (g_source_is_destroyed (g_main_current_source ()))
2905 return FALSE;
2906
2907 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
2908
2909 g_mutex_lock (&session->priv->preparing_flush_lock);
2910 if (session->priv->outbox_flush_id == g_source_get_id (g_main_current_source ()))
2911 session->priv->outbox_flush_id = 0;
2912 g_mutex_unlock (&session->priv->preparing_flush_lock);
2913
2914 e_mail_session_flush_outbox (session);
2915
2916 return FALSE;
2917 }
2918
2919 void
e_mail_session_flush_outbox(EMailSession * session)2920 e_mail_session_flush_outbox (EMailSession *session)
2921 {
2922 g_return_if_fail (E_IS_MAIL_SESSION (session));
2923
2924 g_mutex_lock (&session->priv->preparing_flush_lock);
2925 if (session->priv->outbox_flush_id > 0) {
2926 g_source_remove (session->priv->outbox_flush_id);
2927 session->priv->outbox_flush_id = 0;
2928 }
2929 g_mutex_unlock (&session->priv->preparing_flush_lock);
2930
2931 /* Connect to this and call mail_send in the main email client.*/
2932 g_signal_emit (session, signals[FLUSH_OUTBOX], 0);
2933 }
2934
2935 void
e_mail_session_schedule_outbox_flush(EMailSession * session,gint delay_minutes)2936 e_mail_session_schedule_outbox_flush (EMailSession *session,
2937 gint delay_minutes)
2938 {
2939 g_return_if_fail (E_IS_MAIL_SESSION (session));
2940 g_return_if_fail (delay_minutes >= 0);
2941
2942 if (delay_minutes == 0) {
2943 e_mail_session_flush_outbox (session);
2944 return;
2945 }
2946
2947 g_mutex_lock (&session->priv->preparing_flush_lock);
2948 if (!session->priv->outbox_flush_id) {
2949 /* Do not reschedule the timer, it will be rescheduled
2950 when needed after the flush attempt */
2951 session->priv->outbox_flush_id = e_named_timeout_add_seconds (60 * delay_minutes, mail_session_flush_outbox_timeout_cb, session);
2952 }
2953 g_mutex_unlock (&session->priv->preparing_flush_lock);
2954 }
2955
2956 void
e_mail_session_cancel_scheduled_outbox_flush(EMailSession * session)2957 e_mail_session_cancel_scheduled_outbox_flush (EMailSession *session)
2958 {
2959 g_return_if_fail (E_IS_MAIL_SESSION (session));
2960
2961 g_mutex_lock (&session->priv->preparing_flush_lock);
2962 if (session->priv->outbox_flush_id > 0) {
2963 g_source_remove (session->priv->outbox_flush_id);
2964 session->priv->outbox_flush_id = 0;
2965 }
2966 g_mutex_unlock (&session->priv->preparing_flush_lock);
2967 }
2968
2969 static void
mail_session_wakeup_used_services_cond(GCancellable * cancenllable,EMailSession * session)2970 mail_session_wakeup_used_services_cond (GCancellable *cancenllable,
2971 EMailSession *session)
2972 {
2973 g_return_if_fail (E_IS_MAIL_SESSION (session));
2974
2975 /* Use broadcast here, because it's not known which operation had been
2976 cancelled, thus rather wake up all of them to retest. */
2977 g_cond_broadcast (&session->priv->used_services_cond);
2978 }
2979
2980 /**
2981 * e_mail_session_mark_service_used_sync:
2982 * @session: an #EMailSession
2983 * @service: a #CamelService
2984 * @cancellable: (allow none): a #GCancellable, or NULL
2985 *
2986 * Marks the @service as being used. If it is already in use, then waits
2987 * for its release. The only reasons for a failure are either invalid
2988 * parameters being passed in the function or the wait being cancelled.
2989 * Use e_mail_session_unmark_service_used() to notice the @session that
2990 * that the @service is no longer being used by the caller.
2991 *
2992 * Returns: Whether successfully waited for the @service.
2993 *
2994 * Since: 3.16
2995 **/
2996 gboolean
e_mail_session_mark_service_used_sync(EMailSession * session,CamelService * service,GCancellable * cancellable)2997 e_mail_session_mark_service_used_sync (EMailSession *session,
2998 CamelService *service,
2999 GCancellable *cancellable)
3000 {
3001 gulong cancelled_id = 0;
3002 gboolean message_pushed = FALSE;
3003
3004 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
3005 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
3006
3007 g_mutex_lock (&session->priv->used_services_lock);
3008
3009 if (cancellable)
3010 cancelled_id = g_cancellable_connect (cancellable, G_CALLBACK (mail_session_wakeup_used_services_cond), session, NULL);
3011
3012 while (!g_cancellable_is_cancelled (cancellable) &&
3013 g_hash_table_contains (session->priv->used_services, service)) {
3014
3015 if (!message_pushed) {
3016 camel_operation_push_message (cancellable, _("Waiting for “%s”"), camel_service_get_display_name (service));
3017 message_pushed = TRUE;
3018 }
3019
3020 g_cond_wait (&session->priv->used_services_cond, &session->priv->used_services_lock);
3021 }
3022
3023 if (message_pushed)
3024 camel_operation_pop_message (cancellable);
3025
3026 if (cancelled_id)
3027 g_cancellable_disconnect (cancellable, cancelled_id);
3028
3029 if (!g_cancellable_is_cancelled (cancellable))
3030 g_hash_table_insert (session->priv->used_services, service, GINT_TO_POINTER (1));
3031
3032 g_mutex_unlock (&session->priv->used_services_lock);
3033
3034 return !g_cancellable_is_cancelled (cancellable);
3035 }
3036
3037 /**
3038 * e_mail_session_unmark_service_used:
3039 * @session: an #EMailSession
3040 * @service: a #CamelService
3041 *
3042 * Frees a "use lock" on the @service, thus it can be used by others. If anything
3043 * is waiting for it in e_mail_session_mark_service_used_sync(), then it is woken up.
3044 *
3045 * Since: 3.16
3046 **/
3047 void
e_mail_session_unmark_service_used(EMailSession * session,CamelService * service)3048 e_mail_session_unmark_service_used (EMailSession *session,
3049 CamelService *service)
3050 {
3051 g_return_if_fail (E_IS_MAIL_SESSION (session));
3052 g_return_if_fail (CAMEL_IS_SERVICE (service));
3053
3054 g_mutex_lock (&session->priv->used_services_lock);
3055
3056 if (g_hash_table_remove (session->priv->used_services, service)) {
3057 g_cond_signal (&session->priv->used_services_cond);
3058 }
3059
3060 g_mutex_unlock (&session->priv->used_services_lock);
3061 }
3062
3063 /**
3064 * e_mail_session_emit_allow_auth_prompt:
3065 * @session: an #EMailSession
3066 * @source: an #ESource
3067 *
3068 * Emits 'allow-auth-prompt' on @session for @source. This lets
3069 * any listeners know to enable credentials prompt for this @source.
3070 *
3071 * Since: 3.16
3072 **/
3073 void
e_mail_session_emit_allow_auth_prompt(EMailSession * session,ESource * source)3074 e_mail_session_emit_allow_auth_prompt (EMailSession *session,
3075 ESource *source)
3076 {
3077 g_return_if_fail (E_IS_MAIL_SESSION (session));
3078 g_return_if_fail (E_IS_SOURCE (source));
3079
3080 g_signal_emit (session, signals[ALLOW_AUTH_PROMPT], 0, source);
3081 }
3082
3083 /**
3084 * e_mail_session_is_archive_folder:
3085 * @session: an #EMailSession
3086 * @folder_uri: a folder URI
3087 *
3088 * Returns: whether the @folder_uri is one of configured archive folders
3089 *
3090 * Since: 3.34
3091 **/
3092 gboolean
e_mail_session_is_archive_folder(EMailSession * session,const gchar * folder_uri)3093 e_mail_session_is_archive_folder (EMailSession *session,
3094 const gchar *folder_uri)
3095 {
3096 CamelSession *camel_session;
3097 GHashTableIter iter;
3098 gpointer value;
3099 gboolean is_archive_folder = FALSE;
3100
3101 g_return_val_if_fail (E_IS_MAIL_SESSION (session), FALSE);
3102
3103 if (!folder_uri || !*folder_uri)
3104 return FALSE;
3105
3106 camel_session = CAMEL_SESSION (session);
3107
3108 g_mutex_lock (&session->priv->archive_folders_hash_lock);
3109
3110 g_hash_table_iter_init (&iter, session->priv->archive_folders_hash);
3111 while (!is_archive_folder && g_hash_table_iter_next (&iter, NULL, &value)) {
3112 const gchar *uri = value;
3113
3114 if (uri && *uri)
3115 is_archive_folder = e_mail_folder_uri_equal (camel_session, folder_uri, uri);
3116 }
3117
3118 g_mutex_unlock (&session->priv->archive_folders_hash_lock);
3119
3120 return is_archive_folder;
3121 }
3122
3123 /**
3124 * e_mail_session_emit_connect_store:
3125 * @session: an #EMailSession
3126 * @store: a #CamelStore
3127 *
3128 * Emits 'connect-store' on @session for @store. This lets
3129 * any listeners know to connect the @store.
3130 *
3131 * Since: 3.34
3132 **/
3133 void
e_mail_session_emit_connect_store(EMailSession * session,CamelStore * store)3134 e_mail_session_emit_connect_store (EMailSession *session,
3135 CamelStore *store)
3136 {
3137 g_return_if_fail (E_IS_MAIL_SESSION (session));
3138 g_return_if_fail (CAMEL_IS_STORE (store));
3139
3140 g_signal_emit (session, signals[CONNECT_STORE], 0, store);
3141 }
3142