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