1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-session.c : Abstract class for an email session
3  *
4  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
5  *
6  * This library is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13  * for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public License
16  * along with this library. If not, see <http://www.gnu.org/licenses/>.
17  *
18  * Authors: Dan Winship <danw@ximian.com>
19  *          Jeffrey Stedfast <fejj@ximian.com>
20  *          Bertrand Guiheneuf <bertrand@helixcode.com>
21  */
22 
23 #include "evolution-data-server-config.h"
24 
25 #include <errno.h>
26 #include <stdio.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <sys/stat.h>
30 
31 #include <glib/gi18n-lib.h>
32 #include <glib/gstdio.h>
33 
34 #include "camel-debug.h"
35 #include "camel-enumtypes.h"
36 #include "camel-file-utils.h"
37 #include "camel-folder.h"
38 #include "camel-mime-message.h"
39 #include "camel-sasl.h"
40 #include "camel-session.h"
41 #include "camel-store.h"
42 #include "camel-string-utils.h"
43 #include "camel-transport.h"
44 #include "camel-url.h"
45 
46 /* Prioritize ahead of GTK+ redraws. */
47 #define JOB_PRIORITY G_PRIORITY_HIGH_IDLE
48 
49 #define d(x)
50 
51 typedef struct _AsyncContext AsyncContext;
52 typedef struct _SignalClosure SignalClosure;
53 typedef struct _JobData JobData;
54 
55 struct _CamelSessionPrivate {
56 	gchar *user_data_dir;
57 	gchar *user_cache_dir;
58 
59 	GHashTable *services;
60 	GMutex services_lock;
61 
62 	GHashTable *junk_headers;
63 	CamelJunkFilter *junk_filter;
64 
65 	GMainContext *main_context;
66 
67 	GMutex property_lock;
68 	GNetworkMonitor *network_monitor;
69 
70 	guint online : 1;
71 };
72 
73 struct _AsyncContext {
74 	CamelFolder *folder;
75 	CamelMimeMessage *message;
76 	CamelService *service;
77 	gchar *address;
78 	gchar *auth_mechanism;
79 };
80 
81 struct _SignalClosure {
82 	GWeakRef session;
83 	CamelService *service;
84 	CamelSessionAlertType alert_type;
85 	gchar *alert_message;
86 };
87 
88 struct _JobData {
89 	CamelSession *session;
90 	GCancellable *cancellable;
91 	CamelSessionCallback callback;
92 	gpointer user_data;
93 	GDestroyNotify notify;
94 	GMainContext *main_context;
95 	GError *error;
96 };
97 
98 enum {
99 	PROP_0,
100 	PROP_JUNK_FILTER,
101 	PROP_MAIN_CONTEXT,
102 	PROP_NETWORK_MONITOR,
103 	PROP_ONLINE,
104 	PROP_USER_DATA_DIR,
105 	PROP_USER_CACHE_DIR
106 };
107 
108 enum {
109 	JOB_STARTED,
110 	JOB_FINISHED,
111 	USER_ALERT,
112 	LAST_SIGNAL
113 };
114 
115 static guint signals[LAST_SIGNAL];
116 
G_DEFINE_TYPE_WITH_PRIVATE(CamelSession,camel_session,G_TYPE_OBJECT)117 G_DEFINE_TYPE_WITH_PRIVATE (CamelSession, camel_session, G_TYPE_OBJECT)
118 
119 static void
120 async_context_free (AsyncContext *async_context)
121 {
122 	if (async_context->folder != NULL)
123 		g_object_unref (async_context->folder);
124 
125 	if (async_context->message != NULL)
126 		g_object_unref (async_context->message);
127 
128 	if (async_context->service != NULL)
129 		g_object_unref (async_context->service);
130 
131 	g_free (async_context->address);
132 	g_free (async_context->auth_mechanism);
133 
134 	g_slice_free (AsyncContext, async_context);
135 }
136 
137 static void
signal_closure_free(SignalClosure * signal_closure)138 signal_closure_free (SignalClosure *signal_closure)
139 {
140 	g_weak_ref_clear (&signal_closure->session);
141 
142 	if (signal_closure->service != NULL)
143 		g_object_unref (signal_closure->service);
144 
145 	g_free (signal_closure->alert_message);
146 
147 	g_slice_free (SignalClosure, signal_closure);
148 }
149 
150 static void
job_data_free(JobData * job_data)151 job_data_free (JobData *job_data)
152 {
153 	camel_operation_pop_message (job_data->cancellable);
154 
155 	g_object_unref (job_data->session);
156 	g_object_unref (job_data->cancellable);
157 	g_clear_error (&job_data->error);
158 
159 	if (job_data->main_context)
160 		g_main_context_unref (job_data->main_context);
161 
162 	if (job_data->notify != NULL)
163 		job_data->notify (job_data->user_data);
164 
165 	g_slice_free (JobData, job_data);
166 }
167 
168 static gboolean
session_finish_job_cb(gpointer user_data)169 session_finish_job_cb (gpointer user_data)
170 {
171 	JobData *job_data = (JobData *) user_data;
172 
173 	g_return_val_if_fail (job_data != NULL, FALSE);
174 
175 	g_signal_emit (
176 		job_data->session,
177 		signals[JOB_FINISHED], 0,
178 		job_data->cancellable, job_data->error);
179 
180 	return FALSE;
181 }
182 
183 static void
session_job_thread(gpointer data,gpointer user_data)184 session_job_thread (gpointer data,
185 		    gpointer user_data)
186 {
187 	JobData *job_data = (JobData *) data;
188 	GSource *source;
189 
190 	g_return_if_fail (job_data != NULL);
191 
192 	job_data->callback (
193 		job_data->session,
194 		job_data->cancellable,
195 		job_data->user_data,
196 		&job_data->error);
197 
198 	source = g_idle_source_new ();
199 	g_source_set_priority (source, G_PRIORITY_DEFAULT);
200 	g_source_set_callback (source, session_finish_job_cb, job_data, (GDestroyNotify) job_data_free);
201 	g_source_attach (source, job_data->main_context);
202 	g_source_unref (source);
203 }
204 
205 static gboolean
session_start_job_cb(gpointer user_data)206 session_start_job_cb (gpointer user_data)
207 {
208 	static GThreadPool *job_pool = NULL;
209 	static GMutex job_pool_mutex;
210 	JobData *job_data = user_data;
211 
212 	g_signal_emit (
213 		job_data->session,
214 		signals[JOB_STARTED], 0,
215 		job_data->cancellable);
216 
217 	g_mutex_lock (&job_pool_mutex);
218 
219 	if (!job_pool)
220 		job_pool = g_thread_pool_new (session_job_thread, NULL, 20, FALSE, NULL);
221 
222 	job_data->main_context = g_main_context_ref_thread_default ();
223 
224 	g_thread_pool_push (job_pool, job_data, NULL);
225 
226 	g_mutex_unlock (&job_pool_mutex);
227 
228 	return FALSE;
229 }
230 
231 static gboolean
session_emit_user_alert_cb(gpointer user_data)232 session_emit_user_alert_cb (gpointer user_data)
233 {
234 	SignalClosure *signal_closure = user_data;
235 	CamelSession *session;
236 
237 	session = g_weak_ref_get (&signal_closure->session);
238 
239 	if (session != NULL) {
240 		g_signal_emit (
241 			session,
242 			signals[USER_ALERT], 0,
243 			signal_closure->service,
244 			signal_closure->alert_type,
245 			signal_closure->alert_message);
246 		g_object_unref (session);
247 	}
248 
249 	return G_SOURCE_REMOVE;
250 }
251 
252 static void
session_set_user_data_dir(CamelSession * session,const gchar * user_data_dir)253 session_set_user_data_dir (CamelSession *session,
254                            const gchar *user_data_dir)
255 {
256 	g_return_if_fail (user_data_dir != NULL);
257 	g_return_if_fail (session->priv->user_data_dir == NULL);
258 
259 	session->priv->user_data_dir = g_strdup (user_data_dir);
260 }
261 
262 static void
session_set_user_cache_dir(CamelSession * session,const gchar * user_cache_dir)263 session_set_user_cache_dir (CamelSession *session,
264                             const gchar *user_cache_dir)
265 {
266 	g_return_if_fail (user_cache_dir != NULL);
267 	g_return_if_fail (session->priv->user_cache_dir == NULL);
268 
269 	session->priv->user_cache_dir = g_strdup (user_cache_dir);
270 }
271 
272 static void
session_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)273 session_set_property (GObject *object,
274                       guint property_id,
275                       const GValue *value,
276                       GParamSpec *pspec)
277 {
278 	switch (property_id) {
279 		case PROP_JUNK_FILTER:
280 			camel_session_set_junk_filter (
281 				CAMEL_SESSION (object),
282 				g_value_get_object (value));
283 			return;
284 
285 		case PROP_NETWORK_MONITOR:
286 			camel_session_set_network_monitor (
287 				CAMEL_SESSION (object),
288 				g_value_get_object (value));
289 			return;
290 
291 		case PROP_ONLINE:
292 			camel_session_set_online (
293 				CAMEL_SESSION (object),
294 				g_value_get_boolean (value));
295 			return;
296 
297 		case PROP_USER_DATA_DIR:
298 			session_set_user_data_dir (
299 				CAMEL_SESSION (object),
300 				g_value_get_string (value));
301 			return;
302 
303 		case PROP_USER_CACHE_DIR:
304 			session_set_user_cache_dir (
305 				CAMEL_SESSION (object),
306 				g_value_get_string (value));
307 			return;
308 	}
309 
310 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
311 }
312 
313 static void
session_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)314 session_get_property (GObject *object,
315                       guint property_id,
316                       GValue *value,
317                       GParamSpec *pspec)
318 {
319 	switch (property_id) {
320 		case PROP_JUNK_FILTER:
321 			g_value_set_object (
322 				value, camel_session_get_junk_filter (
323 				CAMEL_SESSION (object)));
324 			return;
325 
326 		case PROP_MAIN_CONTEXT:
327 			g_value_take_boxed (
328 				value, camel_session_ref_main_context (
329 				CAMEL_SESSION (object)));
330 			return;
331 
332 		case PROP_NETWORK_MONITOR:
333 			g_value_take_object (
334 				value, camel_session_ref_network_monitor (
335 				CAMEL_SESSION (object)));
336 			return;
337 
338 		case PROP_ONLINE:
339 			g_value_set_boolean (
340 				value, camel_session_get_online (
341 				CAMEL_SESSION (object)));
342 			return;
343 
344 		case PROP_USER_DATA_DIR:
345 			g_value_set_string (
346 				value, camel_session_get_user_data_dir (
347 				CAMEL_SESSION (object)));
348 			return;
349 
350 		case PROP_USER_CACHE_DIR:
351 			g_value_set_string (
352 				value, camel_session_get_user_cache_dir (
353 				CAMEL_SESSION (object)));
354 			return;
355 	}
356 
357 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
358 }
359 
360 static void
session_dispose(GObject * object)361 session_dispose (GObject *object)
362 {
363 	CamelSessionPrivate *priv;
364 
365 	priv = CAMEL_SESSION (object)->priv;
366 
367 	g_hash_table_remove_all (priv->services);
368 
369 	g_clear_object (&priv->junk_filter);
370 	g_clear_object (&priv->network_monitor);
371 
372 	/* Chain up to parent's dispose() method. */
373 	G_OBJECT_CLASS (camel_session_parent_class)->dispose (object);
374 }
375 
376 static void
session_finalize(GObject * object)377 session_finalize (GObject *object)
378 {
379 	CamelSessionPrivate *priv;
380 
381 	priv = CAMEL_SESSION (object)->priv;
382 
383 	g_free (priv->user_data_dir);
384 	g_free (priv->user_cache_dir);
385 
386 	g_hash_table_destroy (priv->services);
387 
388 	if (priv->main_context != NULL)
389 		g_main_context_unref (priv->main_context);
390 
391 	g_mutex_clear (&priv->services_lock);
392 	g_mutex_clear (&priv->property_lock);
393 
394 	if (priv->junk_headers) {
395 		g_hash_table_remove_all (priv->junk_headers);
396 		g_hash_table_destroy (priv->junk_headers);
397 	}
398 
399 	/* Chain up to parent's finalize() method. */
400 	G_OBJECT_CLASS (camel_session_parent_class)->finalize (object);
401 }
402 
403 static CamelService *
session_add_service(CamelSession * session,const gchar * uid,const gchar * protocol,CamelProviderType type,GError ** error)404 session_add_service (CamelSession *session,
405                      const gchar *uid,
406                      const gchar *protocol,
407                      CamelProviderType type,
408                      GError **error)
409 {
410 	CamelService *service;
411 	CamelProvider *provider;
412 	GType service_type = G_TYPE_INVALID;
413 
414 	service = camel_session_ref_service (session, uid);
415 	if (CAMEL_IS_SERVICE (service))
416 		return service;
417 
418 	/* Try to find a suitable CamelService subclass. */
419 	provider = camel_provider_get (protocol, error);
420 	if (provider != NULL)
421 		service_type = provider->object_types[type];
422 
423 	if (error && *error)
424 		return NULL;
425 
426 	if (service_type == G_TYPE_INVALID) {
427 		g_set_error (
428 			error, CAMEL_SERVICE_ERROR,
429 			CAMEL_SERVICE_ERROR_URL_INVALID,
430 			_("No provider available for protocol “%s”"),
431 			protocol);
432 		return NULL;
433 	}
434 
435 	if (!g_type_is_a (service_type, CAMEL_TYPE_SERVICE)) {
436 		g_set_error (
437 			error, CAMEL_SERVICE_ERROR,
438 			CAMEL_SERVICE_ERROR_INVALID,
439 			_("Invalid GType registered for protocol “%s”"),
440 			protocol);
441 		return NULL;
442 	}
443 
444 	service = g_initable_new (
445 		service_type, NULL, error,
446 		"provider", provider, "session",
447 		session, "uid", uid, NULL);
448 
449 	if (service != NULL) {
450 		g_mutex_lock (&session->priv->services_lock);
451 
452 		g_hash_table_insert (
453 			session->priv->services,
454 			g_strdup (uid),
455 			g_object_ref (service));
456 
457 		g_mutex_unlock (&session->priv->services_lock);
458 	}
459 
460 	return service;
461 }
462 
463 static void
session_remove_service(CamelSession * session,CamelService * service)464 session_remove_service (CamelSession *session,
465                         CamelService *service)
466 {
467 	const gchar *uid;
468 
469 	g_mutex_lock (&session->priv->services_lock);
470 
471 	uid = camel_service_get_uid (service);
472 	g_hash_table_remove (session->priv->services, uid);
473 
474 	g_mutex_unlock (&session->priv->services_lock);
475 }
476 
477 static gboolean
session_authenticate_sync(CamelSession * session,CamelService * service,const gchar * mechanism,GCancellable * cancellable,GError ** error)478 session_authenticate_sync (CamelSession *session,
479                            CamelService *service,
480                            const gchar *mechanism,
481                            GCancellable *cancellable,
482                            GError **error)
483 {
484 	CamelServiceAuthType *authtype = NULL;
485 	CamelAuthenticationResult result;
486 	GError *local_error = NULL;
487 
488 	/* XXX This authenticate_sync() implementation serves only as
489 	 *     a rough example and is not intended to be used as is.
490 	 *
491 	 *     Any CamelSession subclass should override this method
492 	 *     and implement a more complete authentication loop that
493 	 *     handles user prompts and password storage.
494 	 */
495 
496 	g_warning (
497 		"The default CamelSession.authenticate_sync() "
498 		"method is not intended for production use.");
499 
500 	/* If a SASL mechanism was given and we can't find
501 	 * a CamelServiceAuthType for it, fail immediately. */
502 	if (mechanism != NULL) {
503 		authtype = camel_sasl_authtype (mechanism);
504 		if (authtype == NULL) {
505 			g_set_error (
506 				error, CAMEL_SERVICE_ERROR,
507 				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
508 				_("No support for %s authentication"),
509 				mechanism);
510 			return FALSE;
511 		}
512 	}
513 
514 	/* If the SASL mechanism does not involve a user
515 	 * password, then it gets one shot to authenticate. */
516 	if (authtype != NULL && !authtype->need_password) {
517 		result = camel_service_authenticate_sync (
518 			service, mechanism, cancellable, error);
519 		if (result == CAMEL_AUTHENTICATION_REJECTED)
520 			g_set_error (
521 				error, CAMEL_SERVICE_ERROR,
522 				CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
523 				_("%s authentication failed"), mechanism);
524 		return (result == CAMEL_AUTHENTICATION_ACCEPTED);
525 	}
526 
527 	/* Some SASL mechanisms can attempt to authenticate without a
528 	 * user password being provided (e.g. single-sign-on credentials),
529 	 * but can fall back to a user password.  Handle that case next. */
530 	if (mechanism != NULL) {
531 		CamelProvider *provider;
532 		CamelSasl *sasl;
533 		const gchar *service_name;
534 		gboolean success = FALSE;
535 
536 		provider = camel_service_get_provider (service);
537 		service_name = provider->protocol;
538 
539 		/* XXX Would be nice if camel_sasl_try_empty_password_sync()
540 		 *     returned CamelAuthenticationResult so it's easier to
541 		 *     detect errors. */
542 		sasl = camel_sasl_new (service_name, mechanism, service);
543 		if (sasl != NULL) {
544 			success = camel_sasl_try_empty_password_sync (
545 				sasl, cancellable, &local_error);
546 			g_object_unref (sasl);
547 		}
548 
549 		if (success)
550 			return TRUE;
551 	}
552 
553 	/* Abort authentication if we got cancelled.
554 	 * Otherwise clear any errors and press on. */
555 	if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
556 		return FALSE;
557 
558 	g_clear_error (&local_error);
559 
560 retry:
561 	/* XXX This is where things get bogus.  In a real implementation you
562 	 *     would want to fetch a stored password or prompt the user here.
563 	 *     Password should be stashed using camel_service_set_password()
564 	 *     before calling camel_service_authenticate_sync(). */
565 
566 	result = camel_service_authenticate_sync (
567 		service, mechanism, cancellable, error);
568 
569 	if (result == CAMEL_AUTHENTICATION_REJECTED) {
570 		/* XXX Request a different password here. */
571 		goto retry;
572 	}
573 
574 	if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
575 		/* XXX Possibly store the password here using
576 		 *     GNOME Keyring or something equivalent. */
577 	}
578 
579 	return (result == CAMEL_AUTHENTICATION_ACCEPTED);
580 }
581 
582 static gboolean
session_forward_to_sync(CamelSession * session,CamelFolder * folder,CamelMimeMessage * message,const gchar * address,GCancellable * cancellable,GError ** error)583 session_forward_to_sync (CamelSession *session,
584                          CamelFolder *folder,
585                          CamelMimeMessage *message,
586                          const gchar *address,
587                          GCancellable *cancellable,
588                          GError **error)
589 {
590 	g_set_error_literal (
591 		error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
592 		_("Forwarding messages is not supported"));
593 
594 	return FALSE;
595 }
596 
597 static void
camel_session_class_init(CamelSessionClass * class)598 camel_session_class_init (CamelSessionClass *class)
599 {
600 	GObjectClass *object_class;
601 
602 	object_class = G_OBJECT_CLASS (class);
603 	object_class->set_property = session_set_property;
604 	object_class->get_property = session_get_property;
605 	object_class->dispose = session_dispose;
606 	object_class->finalize = session_finalize;
607 
608 	class->add_service = session_add_service;
609 	class->remove_service = session_remove_service;
610 
611 	class->authenticate_sync = session_authenticate_sync;
612 	class->forward_to_sync = session_forward_to_sync;
613 
614 	g_object_class_install_property (
615 		object_class,
616 		PROP_JUNK_FILTER,
617 		g_param_spec_object (
618 			"junk-filter",
619 			"Junk Filter",
620 			"Classifies messages as junk or not junk",
621 			CAMEL_TYPE_JUNK_FILTER,
622 			G_PARAM_READWRITE |
623 			G_PARAM_EXPLICIT_NOTIFY |
624 			G_PARAM_STATIC_STRINGS));
625 
626 	g_object_class_install_property (
627 		object_class,
628 		PROP_MAIN_CONTEXT,
629 		g_param_spec_boxed (
630 			"main-context",
631 			"Main Context",
632 			"The main loop context on "
633 			"which to attach event sources",
634 			G_TYPE_MAIN_CONTEXT,
635 			G_PARAM_READABLE |
636 			G_PARAM_STATIC_STRINGS));
637 
638 	g_object_class_install_property (
639 		object_class,
640 		PROP_NETWORK_MONITOR,
641 		g_param_spec_object (
642 			"network-monitor",
643 			"Network Monitor",
644 			NULL,
645 			G_TYPE_NETWORK_MONITOR,
646 			G_PARAM_READWRITE |
647 			G_PARAM_EXPLICIT_NOTIFY |
648 			G_PARAM_STATIC_STRINGS));
649 
650 	g_object_class_install_property (
651 		object_class,
652 		PROP_ONLINE,
653 		g_param_spec_boolean (
654 			"online",
655 			"Online",
656 			"Whether the shell is online",
657 			TRUE,
658 			G_PARAM_READWRITE |
659 			G_PARAM_CONSTRUCT |
660 			G_PARAM_EXPLICIT_NOTIFY |
661 			G_PARAM_STATIC_STRINGS));
662 
663 	g_object_class_install_property (
664 		object_class,
665 		PROP_USER_DATA_DIR,
666 		g_param_spec_string (
667 			"user-data-dir",
668 			"User Data Directory",
669 			"User-specific base directory for mail data",
670 			NULL,
671 			G_PARAM_READWRITE |
672 			G_PARAM_CONSTRUCT |
673 			G_PARAM_EXPLICIT_NOTIFY |
674 			G_PARAM_STATIC_STRINGS));
675 
676 	g_object_class_install_property (
677 		object_class,
678 		PROP_USER_CACHE_DIR,
679 		g_param_spec_string (
680 			"user-cache-dir",
681 			"User Cache Directory",
682 			"User-specific base directory for mail cache",
683 			NULL,
684 			G_PARAM_READWRITE |
685 			G_PARAM_CONSTRUCT |
686 			G_PARAM_EXPLICIT_NOTIFY |
687 			G_PARAM_STATIC_STRINGS));
688 
689 	signals[JOB_STARTED] = g_signal_new (
690 		"job-started",
691 		G_OBJECT_CLASS_TYPE (class),
692 		G_SIGNAL_RUN_LAST,
693 		G_STRUCT_OFFSET (CamelSessionClass, job_started),
694 		NULL, NULL, NULL,
695 		G_TYPE_NONE, 1,
696 		G_TYPE_CANCELLABLE);
697 
698 	signals[JOB_FINISHED] = g_signal_new (
699 		"job-finished",
700 		G_OBJECT_CLASS_TYPE (class),
701 		G_SIGNAL_RUN_LAST,
702 		G_STRUCT_OFFSET (CamelSessionClass, job_finished),
703 		NULL, NULL, NULL,
704 		G_TYPE_NONE, 2,
705 		G_TYPE_CANCELLABLE,
706 		G_TYPE_ERROR);
707 
708 	/**
709 	 * CamelSession::user-alert:
710 	 * @session: the #CamelSession that received the signal
711 	 * @service: the #CamelService issuing the alert
712 	 * @type: the #CamelSessionAlertType
713 	 * @message: the alert message
714 	 *
715 	 * This purpose of this signal is to propagate a server-issued alert
716 	 * message from @service to a user interface.  The @type hints at the
717 	 * severity of the alert message.
718 	 **/
719 	signals[USER_ALERT] = g_signal_new (
720 		"user-alert",
721 		G_OBJECT_CLASS_TYPE (class),
722 		G_SIGNAL_RUN_LAST,
723 		G_STRUCT_OFFSET (CamelSessionClass, user_alert),
724 		NULL, NULL, NULL,
725 		G_TYPE_NONE, 3,
726 		CAMEL_TYPE_SERVICE,
727 		CAMEL_TYPE_SESSION_ALERT_TYPE,
728 		G_TYPE_STRING);
729 }
730 
731 static void
camel_session_init(CamelSession * session)732 camel_session_init (CamelSession *session)
733 {
734 	GHashTable *services;
735 
736 	services = g_hash_table_new_full (
737 		(GHashFunc) g_str_hash,
738 		(GEqualFunc) g_str_equal,
739 		(GDestroyNotify) g_free,
740 		(GDestroyNotify) g_object_unref);
741 
742 	session->priv = camel_session_get_instance_private (session);
743 
744 	session->priv->services = services;
745 	g_mutex_init (&session->priv->services_lock);
746 	g_mutex_init (&session->priv->property_lock);
747 	session->priv->junk_headers = NULL;
748 
749 	session->priv->main_context = g_main_context_ref_thread_default ();
750 }
751 
752 /**
753  * camel_session_ref_main_context:
754  * @session: a #CamelSession
755  *
756  * Returns the #GMainContext on which event sources for @session are to
757  * be attached.
758  *
759  * Returns: a #GMainContext
760  *
761  * Since: 3.8
762  **/
763 GMainContext *
camel_session_ref_main_context(CamelSession * session)764 camel_session_ref_main_context (CamelSession *session)
765 {
766 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
767 
768 	return g_main_context_ref (session->priv->main_context);
769 }
770 
771 /**
772  * camel_session_get_user_data_dir:
773  * @session: a #CamelSession
774  *
775  * Returns the base directory under which to store user-specific mail data.
776  *
777  * Returns: the base directory for mail data
778  *
779  * Since: 3.2
780  **/
781 const gchar *
camel_session_get_user_data_dir(CamelSession * session)782 camel_session_get_user_data_dir (CamelSession *session)
783 {
784 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
785 
786 	return session->priv->user_data_dir;
787 }
788 
789 /**
790  * camel_session_get_user_cache_dir:
791  * @session: a #CamelSession
792  *
793  * Returns the base directory under which to store user-specific mail cache.
794  *
795  * Returns: the base directory for mail cache
796  *
797  * Since: 3.4
798  **/
799 const gchar *
camel_session_get_user_cache_dir(CamelSession * session)800 camel_session_get_user_cache_dir (CamelSession *session)
801 {
802 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
803 
804 	return session->priv->user_cache_dir;
805 }
806 
807 /**
808  * camel_session_set_network_monitor:
809  * @session: a #CamelSession
810  * @network_monitor: (nullable): a #GNetworkMonitor or %NULL
811  *
812  * Sets a network monitor instance for the @session. This can be used
813  * to override which #GNetworkMonitor should be used to check network
814  * availability and whether a server is reachable.
815  *
816  * Since: 3.22
817  **/
818 void
camel_session_set_network_monitor(CamelSession * session,GNetworkMonitor * network_monitor)819 camel_session_set_network_monitor (CamelSession *session,
820 				   GNetworkMonitor *network_monitor)
821 {
822 	gboolean changed = FALSE;
823 
824 	g_return_if_fail (CAMEL_IS_SESSION (session));
825 	if (network_monitor)
826 		g_return_if_fail (G_IS_NETWORK_MONITOR (network_monitor));
827 
828 	g_mutex_lock (&session->priv->property_lock);
829 
830 	if (network_monitor != session->priv->network_monitor) {
831 		g_clear_object (&session->priv->network_monitor);
832 		session->priv->network_monitor = network_monitor ? g_object_ref (network_monitor) : NULL;
833 
834 		changed = TRUE;
835 	}
836 
837 	g_mutex_unlock (&session->priv->property_lock);
838 
839 	if (changed)
840 		g_object_notify (G_OBJECT (session), "network-monitor");
841 }
842 
843 /**
844  * camel_session_ref_network_monitor:
845  * @session: a #CamelSession
846  *
847  * References a #GNetworkMonitor instance, which had been previously set
848  * by camel_session_set_network_monitor(). If none is set, then the default
849  * #GNetworkMonitor is returned, as provided by g_network_monitor_get_default().
850  * The returned pointer is referenced for thread safety, unref it with
851  * g_object_unref() when no longer needed.
852  *
853  * Returns: (transfer full): A referenced #GNetworkMonitor instance to use
854  *   for network availability tests.
855  *
856  * Since:3.22
857  **/
858 GNetworkMonitor *
camel_session_ref_network_monitor(CamelSession * session)859 camel_session_ref_network_monitor (CamelSession *session)
860 {
861 	GNetworkMonitor *network_monitor;
862 
863 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
864 
865 	g_mutex_lock (&session->priv->property_lock);
866 
867 	network_monitor = g_object_ref (session->priv->network_monitor ?
868 		session->priv->network_monitor : g_network_monitor_get_default ());
869 
870 	g_mutex_unlock (&session->priv->property_lock);
871 
872 	return network_monitor;
873 }
874 
875 /**
876  * camel_session_add_service:
877  * @session: a #CamelSession
878  * @uid: a unique identifier string
879  * @protocol: the service protocol
880  * @type: the service type
881  * @error: return location for a #GError, or %NULL
882  *
883  * Instantiates a new #CamelService for @session.  The @uid identifies the
884  * service for future lookup.  The @protocol indicates which #CamelProvider
885  * holds the #GType of the #CamelService subclass to instantiate.  The @type
886  * explicitly designates the service as a #CamelStore or #CamelTransport.
887  *
888  * If the given @uid has already been added, the existing #CamelService
889  * with that @uid is returned regardless of whether it agrees with the
890  * given @protocol and @type.
891  *
892  * If no #CamelProvider is available to handle the given @protocol, or
893  * if the #CamelProvider does not specify a valid #GType for @type, the
894  * function sets @error and returns %NULL.
895  *
896  * The returned #CamelService is referenced for thread-safety and must be
897  * unreferenced with g_object_unref() when finished with it.
898  *
899  * Returns: (transfer full): a #CamelService instance, or %NULL
900  *
901  * Since: 3.2
902  **/
903 CamelService *
camel_session_add_service(CamelSession * session,const gchar * uid,const gchar * protocol,CamelProviderType type,GError ** error)904 camel_session_add_service (CamelSession *session,
905                            const gchar *uid,
906                            const gchar *protocol,
907                            CamelProviderType type,
908                            GError **error)
909 {
910 	CamelSessionClass *class;
911 	CamelService *service;
912 
913 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
914 	g_return_val_if_fail (uid != NULL, NULL);
915 	g_return_val_if_fail (protocol != NULL, NULL);
916 
917 	class = CAMEL_SESSION_GET_CLASS (session);
918 	g_return_val_if_fail (class != NULL, NULL);
919 	g_return_val_if_fail (class->add_service != NULL, NULL);
920 
921 	service = class->add_service (session, uid, protocol, type, error);
922 	CAMEL_CHECK_GERROR (session, add_service, service != NULL, error);
923 
924 	return service;
925 }
926 
927 /**
928  * camel_session_remove_service:
929  * @session: a #CamelSession
930  * @service: the #CamelService to remove
931  *
932  * Removes a #CamelService previously added by camel_session_add_service().
933  *
934  * Since: 3.2
935  **/
936 void
camel_session_remove_service(CamelSession * session,CamelService * service)937 camel_session_remove_service (CamelSession *session,
938                               CamelService *service)
939 {
940 	CamelSessionClass *class;
941 
942 	g_return_if_fail (CAMEL_IS_SESSION (session));
943 	g_return_if_fail (CAMEL_IS_SERVICE (service));
944 
945 	class = CAMEL_SESSION_GET_CLASS (session);
946 	g_return_if_fail (class != NULL);
947 	g_return_if_fail (class->remove_service != NULL);
948 
949 	class->remove_service (session, service);
950 }
951 
952 /**
953  * camel_session_ref_service:
954  * @session: a #CamelSession
955  * @uid: a unique identifier string
956  *
957  * Looks up a #CamelService by its unique identifier string.  The service
958  * must have been previously added using camel_session_add_service().
959  *
960  * The returned #CamelService is referenced for thread-safety and must be
961  * unreferenced with g_object_unref() when finished with it.
962  *
963  * Returns: (transfer full): a #CamelService instance, or %NULL
964  *
965  * Since: 3.6
966  **/
967 CamelService *
camel_session_ref_service(CamelSession * session,const gchar * uid)968 camel_session_ref_service (CamelSession *session,
969                            const gchar *uid)
970 {
971 	CamelService *service;
972 
973 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
974 	g_return_val_if_fail (uid != NULL, NULL);
975 
976 	g_mutex_lock (&session->priv->services_lock);
977 
978 	service = g_hash_table_lookup (session->priv->services, uid);
979 
980 	if (service != NULL)
981 		g_object_ref (service);
982 
983 	g_mutex_unlock (&session->priv->services_lock);
984 
985 	return service;
986 }
987 
988 /**
989  * camel_session_ref_service_by_url:
990  * @session: a #CamelSession
991  * @url: a #CamelURL
992  * @type: a #CamelProviderType
993  *
994  * Looks up a #CamelService by trying to match its #CamelURL against the
995  * given @url and then checking that the object is of the desired @type.
996  * The service must have been previously added using
997  * camel_session_add_service().
998  *
999  * The returned #CamelService is referenced for thread-safety and must be
1000  * unreferenced with g_object_unref() when finished with it.
1001  *
1002  * Note this function is significantly slower than camel_session_ref_service().
1003  *
1004  * Returns: (transfer full): a #CamelService instance, or %NULL
1005  *
1006  * Since: 3.6
1007  **/
1008 CamelService *
camel_session_ref_service_by_url(CamelSession * session,CamelURL * url,CamelProviderType type)1009 camel_session_ref_service_by_url (CamelSession *session,
1010                                   CamelURL *url,
1011                                   CamelProviderType type)
1012 {
1013 	CamelService *match = NULL;
1014 	GList *list, *iter;
1015 
1016 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1017 	g_return_val_if_fail (url != NULL, NULL);
1018 
1019 	list = camel_session_list_services (session);
1020 
1021 	for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1022 		CamelProvider *provider;
1023 		CamelService *service;
1024 		CamelURL *service_url;
1025 		gboolean url_equal;
1026 
1027 		service = CAMEL_SERVICE (iter->data);
1028 		provider = camel_service_get_provider (service);
1029 
1030 		if (provider == NULL)
1031 			continue;
1032 
1033 		if (provider->url_equal == NULL)
1034 			continue;
1035 
1036 		service_url = camel_service_new_camel_url (service);
1037 		url_equal = provider->url_equal (url, service_url);
1038 		camel_url_free (service_url);
1039 
1040 		if (!url_equal)
1041 			continue;
1042 
1043 		switch (type) {
1044 			case CAMEL_PROVIDER_STORE:
1045 				if (CAMEL_IS_STORE (service))
1046 					match = g_object_ref (service);
1047 				break;
1048 			case CAMEL_PROVIDER_TRANSPORT:
1049 				if (CAMEL_IS_TRANSPORT (service))
1050 					match = g_object_ref (service);
1051 				break;
1052 			default:
1053 				g_warn_if_reached ();
1054 				break;
1055 		}
1056 
1057 		if (match != NULL)
1058 			break;
1059 	}
1060 
1061 	g_list_free_full (list, (GDestroyNotify) g_object_unref);
1062 
1063 	return match;
1064 }
1065 
1066 /**
1067  * camel_session_list_services:
1068  * @session: a #CamelSession
1069  *
1070  * Returns a list of all #CamelService objects previously added using
1071  * camel_session_add_service().
1072  *
1073  * The services returned in the list are referenced for thread-safety.
1074  * They must each be unreferenced with g_object_unref() when finished
1075  * with them.  Free the returned list itself with g_list_free().
1076  *
1077  * An easy way to free the list property in one step is as follows:
1078  *
1079  * |[
1080  *   g_list_free_full (list, g_object_unref);
1081  * ]|
1082  *
1083  * Returns: (element-type CamelService) (transfer full): an unsorted list of #CamelService objects
1084  *
1085  * Since: 3.2
1086  **/
1087 GList *
camel_session_list_services(CamelSession * session)1088 camel_session_list_services (CamelSession *session)
1089 {
1090 	GList *list;
1091 
1092 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1093 
1094 	g_mutex_lock (&session->priv->services_lock);
1095 
1096 	list = g_hash_table_get_values (session->priv->services);
1097 
1098 	g_list_foreach (list, (GFunc) g_object_ref, NULL);
1099 
1100 	g_mutex_unlock (&session->priv->services_lock);
1101 
1102 	return list;
1103 }
1104 
1105 /**
1106  * camel_session_remove_services:
1107  * @session: a #CamelSession
1108  *
1109  * Removes all #CamelService instances added by camel_session_add_service().
1110  *
1111  * This can be useful during application shutdown to ensure all #CamelService
1112  * instances are freed properly, especially since #CamelSession instances are
1113  * prone to reference cycles.
1114  *
1115  * Since: 3.2
1116  **/
1117 void
camel_session_remove_services(CamelSession * session)1118 camel_session_remove_services (CamelSession *session)
1119 {
1120 	g_return_if_fail (CAMEL_IS_SESSION (session));
1121 
1122 	g_mutex_lock (&session->priv->services_lock);
1123 
1124 	g_hash_table_remove_all (session->priv->services);
1125 
1126 	g_mutex_unlock (&session->priv->services_lock);
1127 }
1128 
1129 /**
1130  * camel_session_get_password:
1131  * @session: a #CamelSession
1132  * @service: the #CamelService this query is being made by
1133  * @prompt: prompt to provide to user
1134  * @item: an identifier, unique within this service, for the information
1135  * @flags: %CAMEL_SESSION_PASSWORD_REPROMPT, the prompt should force a reprompt
1136  * %CAMEL_SESSION_PASSWORD_SECRET, whether the password is secret
1137  * %CAMEL_SESSION_PASSWORD_STATIC, the password is remembered externally
1138  * @error: return location for a #GError, or %NULL
1139  *
1140  * This function is used by a #CamelService to ask the application and
1141  * the user for a password or other authentication data.
1142  *
1143  * @service and @item together uniquely identify the piece of data the
1144  * caller is concerned with.
1145  *
1146  * @prompt is a question to ask the user (if the application doesn't
1147  * already have the answer cached). If %CAMEL_SESSION_PASSWORD_SECRET
1148  * is set, the user's input will not be echoed back.
1149  *
1150  * If %CAMEL_SESSION_PASSWORD_STATIC is set, it means the password returned
1151  * will be stored statically by the caller automatically, for the current
1152  * session.
1153  *
1154  * The authenticator should set @error to %G_IO_ERROR_CANCELLED if
1155  * the user did not provide the information. The caller must g_free()
1156  * the information returned when it is done with it.
1157  *
1158  * Returns: the authentication information or %NULL
1159  **/
1160 gchar *
camel_session_get_password(CamelSession * session,CamelService * service,const gchar * prompt,const gchar * item,guint32 flags,GError ** error)1161 camel_session_get_password (CamelSession *session,
1162                             CamelService *service,
1163                             const gchar *prompt,
1164                             const gchar *item,
1165                             guint32 flags,
1166                             GError **error)
1167 {
1168 	CamelSessionClass *class;
1169 	gchar *password;
1170 
1171 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1172 	g_return_val_if_fail (prompt != NULL, NULL);
1173 	g_return_val_if_fail (item != NULL, NULL);
1174 
1175 	class = CAMEL_SESSION_GET_CLASS (session);
1176 	g_return_val_if_fail (class != NULL, NULL);
1177 	g_return_val_if_fail (class->get_password != NULL, NULL);
1178 
1179 	password = class->get_password (
1180 		session, service, prompt, item, flags, error);
1181 	CAMEL_CHECK_GERROR (session, get_password, password != NULL, error);
1182 
1183 	return password;
1184 }
1185 
1186 /**
1187  * camel_session_forget_password:
1188  * @session: a #CamelSession
1189  * @service: the #CamelService rejecting the password
1190  * @item: an identifier, unique within this service, for the information
1191  * @error: return location for a #GError, or %NULL
1192  *
1193  * This function is used by a #CamelService to tell the application
1194  * that the authentication information it provided via
1195  * camel_session_get_password() was rejected by the service. If the
1196  * application was caching this information, it should stop,
1197  * and if the service asks for it again, it should ask the user.
1198  *
1199  * @service and @item identify the rejected authentication information,
1200  * as with camel_session_get_password().
1201  *
1202  * Returns: %TRUE on success, %FALSE on failure
1203  **/
1204 gboolean
camel_session_forget_password(CamelSession * session,CamelService * service,const gchar * item,GError ** error)1205 camel_session_forget_password (CamelSession *session,
1206                                CamelService *service,
1207                                const gchar *item,
1208                                GError **error)
1209 {
1210 	CamelSessionClass *class;
1211 	gboolean success;
1212 
1213 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1214 	g_return_val_if_fail (item != NULL, FALSE);
1215 
1216 	class = CAMEL_SESSION_GET_CLASS (session);
1217 	g_return_val_if_fail (class, FALSE);
1218 	g_return_val_if_fail (class->forget_password, FALSE);
1219 
1220 	success = class->forget_password (session, service, item, error);
1221 	CAMEL_CHECK_GERROR (session, forget_password, success, error);
1222 
1223 	return success;
1224 }
1225 
1226 /**
1227  * camel_session_trust_prompt:
1228  * @session: a #CamelSession
1229  * @service: a #CamelService
1230  * @certificate: the peer's #GTlsCertificate
1231  * @errors: the problems with @certificate
1232  *
1233  * Prompts the user whether to accept @certificate for @service.  The
1234  * set of flags given in @errors indicate why the @certificate failed
1235  * validation.
1236  *
1237  * If an error occurs during prompting or if the user declines to respond,
1238  * the function returns #CAMEL_CERT_TRUST_UNKNOWN and the certificate will
1239  * be rejected.
1240  *
1241  * Returns: the user's trust level for @certificate
1242  *
1243  * Since: 3.8
1244  **/
1245 CamelCertTrust
camel_session_trust_prompt(CamelSession * session,CamelService * service,GTlsCertificate * certificate,GTlsCertificateFlags errors)1246 camel_session_trust_prompt (CamelSession *session,
1247                             CamelService *service,
1248                             GTlsCertificate *certificate,
1249                             GTlsCertificateFlags errors)
1250 {
1251 	CamelSessionClass *class;
1252 
1253 	g_return_val_if_fail (CAMEL_IS_SESSION (session), CAMEL_CERT_TRUST_UNKNOWN);
1254 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), CAMEL_CERT_TRUST_UNKNOWN);
1255 	g_return_val_if_fail (G_IS_TLS_CERTIFICATE (certificate), CAMEL_CERT_TRUST_UNKNOWN);
1256 
1257 	class = CAMEL_SESSION_GET_CLASS (session);
1258 	g_return_val_if_fail (class != NULL, CAMEL_CERT_TRUST_UNKNOWN);
1259 	g_return_val_if_fail (class->trust_prompt != NULL, CAMEL_CERT_TRUST_UNKNOWN);
1260 
1261 	return class->trust_prompt (session, service, certificate, errors);
1262 }
1263 
1264 /**
1265  * camel_session_user_alert:
1266  * @session: a #CamelSession
1267  * @service: a #CamelService
1268  * @type: a #CamelSessionAlertType
1269  * @message: the message for the user
1270  *
1271  * Emits a #CamelSession:user_alert signal from an idle source on the main
1272  * loop.  The idle source's priority is #G_PRIORITY_LOW.
1273  *
1274  * The purpose of the signal is to propagate a server-issued alert message
1275  * from @service to a user interface.  The @type hints at the nature of the
1276  * alert message.
1277  *
1278  * Since: 3.12
1279  */
1280 void
camel_session_user_alert(CamelSession * session,CamelService * service,CamelSessionAlertType type,const gchar * message)1281 camel_session_user_alert (CamelSession *session,
1282                           CamelService *service,
1283                           CamelSessionAlertType type,
1284                           const gchar *message)
1285 {
1286 	SignalClosure *signal_closure;
1287 
1288 	g_return_if_fail (CAMEL_IS_SESSION (session));
1289 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1290 	g_return_if_fail (message != NULL);
1291 
1292 	signal_closure = g_slice_new0 (SignalClosure);
1293 	g_weak_ref_init (&signal_closure->session, session);
1294 	signal_closure->service = g_object_ref (service);
1295 	signal_closure->alert_type = type;
1296 	signal_closure->alert_message = g_strdup (message);
1297 
1298 	camel_session_idle_add (
1299 		session, G_PRIORITY_LOW,
1300 		session_emit_user_alert_cb,
1301 		signal_closure,
1302 		(GDestroyNotify) signal_closure_free);
1303 }
1304 
1305 /**
1306  * camel_session_lookup_addressbook:
1307  * @session: a #CamelSession
1308  * @name: a name/address to lookup for
1309  *
1310  * Looks up for the @name in address books.
1311  *
1312  * Returns: whether found the @name in any address book.
1313  *
1314  * Since: 2.22
1315  **/
1316 gboolean
camel_session_lookup_addressbook(CamelSession * session,const gchar * name)1317 camel_session_lookup_addressbook (CamelSession *session,
1318                                   const gchar *name)
1319 {
1320 	CamelSessionClass *class;
1321 
1322 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1323 	g_return_val_if_fail (name != NULL, FALSE);
1324 
1325 	class = CAMEL_SESSION_GET_CLASS (session);
1326 	g_return_val_if_fail (class != NULL, FALSE);
1327 	g_return_val_if_fail (class->lookup_addressbook != NULL, FALSE);
1328 
1329 	return class->lookup_addressbook (session, name);
1330 }
1331 
1332 /**
1333  * camel_session_get_online:
1334  * @session: a #CamelSession
1335  *
1336  * Returns: whether or not @session is online
1337  **/
1338 gboolean
camel_session_get_online(CamelSession * session)1339 camel_session_get_online (CamelSession *session)
1340 {
1341 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1342 
1343 	return session->priv->online;
1344 }
1345 
1346 /**
1347  * camel_session_set_online:
1348  * @session: a #CamelSession
1349  * @online: whether or not the session should be online
1350  *
1351  * Sets the online status of @session to @online.
1352  **/
1353 void
camel_session_set_online(CamelSession * session,gboolean online)1354 camel_session_set_online (CamelSession *session,
1355                           gboolean online)
1356 {
1357 	g_return_if_fail (CAMEL_IS_SESSION (session));
1358 
1359 	if (online == session->priv->online)
1360 		return;
1361 
1362 	session->priv->online = online;
1363 
1364 	g_object_notify (G_OBJECT (session), "online");
1365 }
1366 
1367 /**
1368  * camel_session_get_filter_driver:
1369  * @session: a #CamelSession
1370  * @type: the type of filter (eg, "incoming")
1371  * @for_folder: (nullable): an optional #CamelFolder, for which the filter driver will run, or %NULL
1372  * @error: return location for a #GError, or %NULL
1373  *
1374  * The optional @for_folder can be used to determine which filters
1375  * to add and which not.
1376  *
1377  * Returns: (transfer none): a filter driver, loaded with applicable rules
1378  **/
1379 CamelFilterDriver *
camel_session_get_filter_driver(CamelSession * session,const gchar * type,CamelFolder * for_folder,GError ** error)1380 camel_session_get_filter_driver (CamelSession *session,
1381 				 const gchar *type,
1382 				 CamelFolder *for_folder,
1383 				 GError **error)
1384 {
1385 	CamelSessionClass *class;
1386 	CamelFilterDriver *driver;
1387 
1388 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1389 	g_return_val_if_fail (type != NULL, NULL);
1390 
1391 	class = CAMEL_SESSION_GET_CLASS (session);
1392 	g_return_val_if_fail (class != NULL, NULL);
1393 	g_return_val_if_fail (class->get_filter_driver != NULL, NULL);
1394 
1395 	driver = class->get_filter_driver (session, type, for_folder, error);
1396 	CAMEL_CHECK_GERROR (session, get_filter_driver, driver != NULL, error);
1397 
1398 	return driver;
1399 }
1400 
1401 /**
1402  * camel_session_get_junk_filter:
1403  * @session: a #CamelSession
1404  *
1405  * Returns the #CamelJunkFilter instance used to classify messages as
1406  * junk or not junk during filtering.
1407  *
1408  * Note that #CamelJunkFilter itself is just an interface.  The application
1409  * must implement the interface and install a #CamelJunkFilter instance for
1410  * junk filtering to take place.
1411  *
1412  * Returns: (transfer none): a #CamelJunkFilter, or %NULL
1413  *
1414  * Since: 3.2
1415  **/
1416 CamelJunkFilter *
camel_session_get_junk_filter(CamelSession * session)1417 camel_session_get_junk_filter (CamelSession *session)
1418 {
1419 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1420 
1421 	return session->priv->junk_filter;
1422 }
1423 
1424 /**
1425  * camel_session_set_junk_filter:
1426  * @session: a #CamelSession
1427  * @junk_filter: a #CamelJunkFilter, or %NULL
1428  *
1429  * Installs the #CamelJunkFilter instance used to classify messages as
1430  * junk or not junk during filtering.
1431  *
1432  * Note that #CamelJunkFilter itself is just an interface.  The application
1433  * must implement the interface and install a #CamelJunkFilter instance for
1434  * junk filtering to take place.
1435  *
1436  * Since: 3.2
1437  **/
1438 void
camel_session_set_junk_filter(CamelSession * session,CamelJunkFilter * junk_filter)1439 camel_session_set_junk_filter (CamelSession *session,
1440                                CamelJunkFilter *junk_filter)
1441 {
1442 	g_return_if_fail (CAMEL_IS_SESSION (session));
1443 
1444 	if (junk_filter != NULL) {
1445 		g_return_if_fail (CAMEL_IS_JUNK_FILTER (junk_filter));
1446 		g_object_ref (junk_filter);
1447 	}
1448 
1449 	if (session->priv->junk_filter != NULL)
1450 		g_object_unref (session->priv->junk_filter);
1451 
1452 	session->priv->junk_filter = junk_filter;
1453 
1454 	g_object_notify (G_OBJECT (session), "junk-filter");
1455 }
1456 
1457 /**
1458  * camel_session_idle_add:
1459  * @session: a #CamelSession
1460  * @priority: the priority of the idle source
1461  * @function: a function to call
1462  * @data: data to pass to @function
1463  * @notify: function to call when the idle is removed, or %NULL
1464  *
1465  * Adds a function to be called whenever there are no higher priority events
1466  * pending.  If @function returns %FALSE it is automatically removed from the
1467  * list of event sources and will not be called again.
1468  *
1469  * This internally creates a main loop source using g_idle_source_new()
1470  * and attaches it to @session's own #CamelSession:main-context using
1471  * g_source_attach().
1472  *
1473  * The @priority is typically in the range between %G_PRIORITY_DEFAULT_IDLE
1474  * and %G_PRIORITY_HIGH_IDLE.
1475  *
1476  * Returns: the ID (greater than 0) of the event source
1477  *
1478  * Since: 3.6
1479  **/
1480 guint
camel_session_idle_add(CamelSession * session,gint priority,GSourceFunc function,gpointer data,GDestroyNotify notify)1481 camel_session_idle_add (CamelSession *session,
1482                         gint priority,
1483                         GSourceFunc function,
1484                         gpointer data,
1485                         GDestroyNotify notify)
1486 {
1487 	GMainContext *main_context;
1488 	GSource *source;
1489 	guint source_id;
1490 
1491 	g_return_val_if_fail (CAMEL_IS_SESSION (session), 0);
1492 	g_return_val_if_fail (function != NULL, 0);
1493 
1494 	main_context = camel_session_ref_main_context (session);
1495 
1496 	source = g_idle_source_new ();
1497 
1498 	if (priority != G_PRIORITY_DEFAULT_IDLE)
1499 		g_source_set_priority (source, priority);
1500 
1501 	g_source_set_callback (source, function, data, notify);
1502 
1503 	source_id = g_source_attach (source, main_context);
1504 
1505 	g_source_unref (source);
1506 
1507 	g_main_context_unref (main_context);
1508 
1509 	return source_id;
1510 }
1511 
1512 /**
1513  * camel_session_submit_job:
1514  * @session: a #CamelSession
1515  * @description: human readable description of the job, shown to a user
1516  * @callback: a #CamelSessionCallback
1517  * @user_data: user data passed to the callback
1518  * @notify: a #GDestroyNotify function
1519  *
1520  * This function provides a simple mechanism for providers to initiate
1521  * low-priority background jobs.  Jobs can be submitted from any thread,
1522  * but execution of the jobs is always as follows:
1523  *
1524  * 1) The #CamelSession:job-started signal is emitted from the thread
1525  *    in which @session was created.  This is typically the same thread
1526  *    that hosts the global default #GMainContext, or "main" thread.
1527  *
1528  * 2) The @callback function is invoked from a different thread where
1529  *    it's safe to call synchronous functions.
1530  *
1531  * 3) Once @callback has returned, the #CamelSesson:job-finished signal
1532  *    is emitted from the same thread as #CamelSession:job-started was
1533  *    emitted.
1534  *
1535  * 4) Finally if a @notify function was provided, it is invoked and
1536  *    passed @user_data so that @user_data can be freed.
1537  *
1538  * Since: 3.2
1539  **/
1540 void
camel_session_submit_job(CamelSession * session,const gchar * description,CamelSessionCallback callback,gpointer user_data,GDestroyNotify notify)1541 camel_session_submit_job (CamelSession *session,
1542 			  const gchar *description,
1543                           CamelSessionCallback callback,
1544                           gpointer user_data,
1545                           GDestroyNotify notify)
1546 {
1547 	JobData *job_data;
1548 
1549 	g_return_if_fail (CAMEL_IS_SESSION (session));
1550 	g_return_if_fail (description != NULL);
1551 	g_return_if_fail (callback != NULL);
1552 
1553 	job_data = g_slice_new0 (JobData);
1554 	job_data->session = g_object_ref (session);
1555 	job_data->cancellable = camel_operation_new ();
1556 	job_data->callback = callback;
1557 	job_data->user_data = user_data;
1558 	job_data->notify = notify;
1559 	job_data->main_context = NULL;
1560 	job_data->error = NULL;
1561 
1562 	camel_operation_push_message (job_data->cancellable, "%s", description);
1563 
1564 	camel_session_idle_add (
1565 		session, JOB_PRIORITY,
1566 		session_start_job_cb,
1567 		job_data, (GDestroyNotify) NULL);
1568 }
1569 
1570 /**
1571  * camel_session_set_junk_headers:
1572  * @session: a #CamelSession
1573  * @headers: (array length=len):
1574  * @values: (array):
1575  * @len: the length of the headers and values arrays
1576  *
1577  * Since: 2.22
1578  **/
1579 void
camel_session_set_junk_headers(CamelSession * session,const gchar ** headers,const gchar ** values,gint len)1580 camel_session_set_junk_headers (CamelSession *session,
1581                                 const gchar **headers,
1582                                 const gchar **values,
1583                                 gint len)
1584 {
1585 	gint i;
1586 
1587 	g_return_if_fail (CAMEL_IS_SESSION (session));
1588 
1589 	if (session->priv->junk_headers) {
1590 		g_hash_table_remove_all (session->priv->junk_headers);
1591 		g_hash_table_destroy (session->priv->junk_headers);
1592 	}
1593 
1594 	session->priv->junk_headers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1595 
1596 	for (i = 0; i < len; i++) {
1597 		g_hash_table_insert (session->priv->junk_headers, g_strdup (headers[i]), g_strdup (values[i]));
1598 	}
1599 }
1600 
1601 /**
1602  * camel_session_get_junk_headers:
1603  * @session: a #CamelSession
1604  *
1605  * Returns: (element-type utf8 utf8) (transfer none): Currently used junk
1606  *    headers as a hash table, previously set by camel_session_set_junk_headers().
1607  *
1608  * Since: 2.22
1609  **/
1610 const GHashTable *
camel_session_get_junk_headers(CamelSession * session)1611 camel_session_get_junk_headers (CamelSession *session)
1612 {
1613 	g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1614 
1615 	return session->priv->junk_headers;
1616 }
1617 
1618 /**
1619  * camel_session_authenticate_sync:
1620  * @session: a #CamelSession
1621  * @service: a #CamelService
1622  * @mechanism: (nullable): a SASL mechanism name, or %NULL
1623  * @cancellable: optional #GCancellable object, or %NULL
1624  * @error: return location for a #GError, or %NULL
1625  *
1626  * Authenticates @service, which may involve repeated calls to
1627  * camel_service_authenticate() or camel_service_authenticate_sync().
1628  * A #CamelSession subclass is largely responsible for implementing this,
1629  * and should handle things like user prompts and secure password storage.
1630  * These issues are out-of-scope for Camel.
1631  *
1632  * If an error occurs, or if authentication is aborted, the function sets
1633  * @error and returns %FALSE.
1634  *
1635  * Returns: %TRUE on success, %FALSE on failure
1636  *
1637  * Since: 3.4
1638  **/
1639 gboolean
camel_session_authenticate_sync(CamelSession * session,CamelService * service,const gchar * mechanism,GCancellable * cancellable,GError ** error)1640 camel_session_authenticate_sync (CamelSession *session,
1641                                  CamelService *service,
1642                                  const gchar *mechanism,
1643                                  GCancellable *cancellable,
1644                                  GError **error)
1645 {
1646 	CamelSessionClass *class;
1647 	gboolean success;
1648 
1649 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1650 	g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1651 
1652 	class = CAMEL_SESSION_GET_CLASS (session);
1653 	g_return_val_if_fail (class != NULL, FALSE);
1654 	g_return_val_if_fail (class->authenticate_sync != NULL, FALSE);
1655 
1656 	success = class->authenticate_sync (
1657 		session, service, mechanism, cancellable, error);
1658 	CAMEL_CHECK_GERROR (session, authenticate_sync, success, error);
1659 
1660 	return success;
1661 }
1662 
1663 /* Helper for camel_session_authenticate() */
1664 static void
session_authenticate_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1665 session_authenticate_thread (GTask *task,
1666                              gpointer source_object,
1667                              gpointer task_data,
1668                              GCancellable *cancellable)
1669 {
1670 	gboolean success;
1671 	AsyncContext *async_context;
1672 	GError *local_error = NULL;
1673 
1674 	async_context = (AsyncContext *) task_data;
1675 
1676 	success = camel_session_authenticate_sync (
1677 		CAMEL_SESSION (source_object),
1678 		async_context->service,
1679 		async_context->auth_mechanism,
1680 		cancellable, &local_error);
1681 
1682 	if (local_error != NULL) {
1683 		g_task_return_error (task, local_error);
1684 	} else {
1685 		g_task_return_boolean (task, success);
1686 	}
1687 }
1688 
1689 /**
1690  * camel_session_authenticate:
1691  * @session: a #CamelSession
1692  * @service: a #CamelService
1693  * @mechanism: (nullable): a SASL mechanism name, or %NULL
1694  * @io_priority: the I/O priority for the request
1695  * @cancellable: optional #GCancellable object, or %NULL
1696  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1697  * @user_data: data to pass to the callback function
1698  *
1699  * Asynchronously authenticates @service, which may involve repeated calls
1700  * to camel_service_authenticate() or camel_service_authenticate_sync().
1701  * A #CamelSession subclass is largely responsible for implementing this,
1702  * and should handle things like user prompts and secure password storage.
1703  * These issues are out-of-scope for Camel.
1704  *
1705  * When the operation is finished, @callback will be called.  You can
1706  * then call camel_session_authenticate_finish() to get the result of
1707  * the operation.
1708  *
1709  * Since: 3.4
1710  **/
1711 void
camel_session_authenticate(CamelSession * session,CamelService * service,const gchar * mechanism,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1712 camel_session_authenticate (CamelSession *session,
1713                             CamelService *service,
1714                             const gchar *mechanism,
1715                             gint io_priority,
1716                             GCancellable *cancellable,
1717                             GAsyncReadyCallback callback,
1718                             gpointer user_data)
1719 {
1720 	GTask *task;
1721 	AsyncContext *async_context;
1722 
1723 	g_return_if_fail (CAMEL_IS_SESSION (session));
1724 	g_return_if_fail (CAMEL_IS_SERVICE (service));
1725 
1726 	async_context = g_slice_new0 (AsyncContext);
1727 	async_context->service = g_object_ref (service);
1728 	async_context->auth_mechanism = g_strdup (mechanism);
1729 
1730 	task = g_task_new (session, cancellable, callback, user_data);
1731 	g_task_set_source_tag (task, camel_session_authenticate);
1732 	g_task_set_priority (task, io_priority);
1733 
1734 	g_task_set_task_data (
1735 		task, async_context,
1736 		(GDestroyNotify) async_context_free);
1737 
1738 	g_task_run_in_thread (task, session_authenticate_thread);
1739 
1740 	g_object_unref (task);
1741 }
1742 
1743 /**
1744  * camel_session_authenticate_finish:
1745  * @session: a #CamelSession
1746  * @result: a #GAsyncResult
1747  * @error: return location for a #GError, or %NULL
1748  *
1749  * Finishes the operation started with camel_session_authenticate().
1750  *
1751  * If an error occurred, or if authentication was aborted, the function
1752  * sets @error and returns %FALSE.
1753  *
1754  * Returns: %TRUE on success, %FALSE on failure
1755  *
1756  * Since: 3.4
1757  **/
1758 gboolean
camel_session_authenticate_finish(CamelSession * session,GAsyncResult * result,GError ** error)1759 camel_session_authenticate_finish (CamelSession *session,
1760                                    GAsyncResult *result,
1761                                    GError **error)
1762 {
1763 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1764 	g_return_val_if_fail (g_task_is_valid (result, session), FALSE);
1765 
1766 	g_return_val_if_fail (
1767 		g_async_result_is_tagged (
1768 		result, camel_session_authenticate), FALSE);
1769 
1770 	return g_task_propagate_boolean (G_TASK (result), error);
1771 }
1772 
1773 /**
1774  * camel_session_forward_to_sync:
1775  * @session: a #CamelSession
1776  * @folder: the #CamelFolder where @message is located
1777  * @message: the #CamelMimeMessage to forward
1778  * @address: the recipient's email address
1779  * @cancellable: optional #GCancellable object, or %NULL
1780  * @error: return location for a #GError, or %NULL
1781  *
1782  * Forwards @message in @folder to the email address(es) given by @address.
1783  *
1784  * If an error occurs, the function sets @error and returns %FALSE.
1785  *
1786  * Returns: %TRUE on success, %FALSE on failure
1787  *
1788  * Since: 3.6
1789  **/
1790 gboolean
camel_session_forward_to_sync(CamelSession * session,CamelFolder * folder,CamelMimeMessage * message,const gchar * address,GCancellable * cancellable,GError ** error)1791 camel_session_forward_to_sync (CamelSession *session,
1792                                CamelFolder *folder,
1793                                CamelMimeMessage *message,
1794                                const gchar *address,
1795                                GCancellable *cancellable,
1796                                GError **error)
1797 {
1798 	CamelSessionClass *class;
1799 	gboolean success;
1800 
1801 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1802 	g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1803 	g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
1804 	g_return_val_if_fail (address != NULL, FALSE);
1805 
1806 	class = CAMEL_SESSION_GET_CLASS (session);
1807 	g_return_val_if_fail (class != NULL, FALSE);
1808 	g_return_val_if_fail (class->forward_to_sync != NULL, FALSE);
1809 
1810 	success = class->forward_to_sync (
1811 		session, folder, message, address, cancellable, error);
1812 	CAMEL_CHECK_GERROR (session, forward_to_sync, success, error);
1813 
1814 	return success;
1815 }
1816 
1817 /* Helper for camel_session_forward_to() */
1818 static void
session_forward_to_thread(GTask * task,gpointer source_object,gpointer task_data,GCancellable * cancellable)1819 session_forward_to_thread (GTask *task,
1820                            gpointer source_object,
1821                            gpointer task_data,
1822                            GCancellable *cancellable)
1823 {
1824 	gboolean success;
1825 	AsyncContext *async_context;
1826 	GError *local_error = NULL;
1827 
1828 	async_context = (AsyncContext *) task_data;
1829 
1830 	success = camel_session_forward_to_sync (
1831 		CAMEL_SESSION (source_object),
1832 		async_context->folder,
1833 		async_context->message,
1834 		async_context->address,
1835 		cancellable, &local_error);
1836 
1837 	if (local_error != NULL) {
1838 		g_task_return_error (task, local_error);
1839 	} else {
1840 		g_task_return_boolean (task, success);
1841 	}
1842 }
1843 
1844 /**
1845  * camel_session_forward_to:
1846  * @session: a #CamelSession
1847  * @folder: the #CamelFolder where @message is located
1848  * @message: the #CamelMimeMessage to forward
1849  * @address: the recipient's email address
1850  * @io_priority: the I/O priority for the request
1851  * @cancellable: optional #GCancellable object, or %NULL
1852  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1853  * @user_data: data to pass to the callback function
1854  *
1855  * Asynchronously forwards @message in @folder to the email address(s)
1856  * given by @address.
1857  *
1858  * When the operation is finished, @callback will be called.  You can
1859  * then call camel_session_forward_to_finish() to get the result of the
1860  * operation.
1861  *
1862  * Since: 3.6
1863  **/
1864 void
camel_session_forward_to(CamelSession * session,CamelFolder * folder,CamelMimeMessage * message,const gchar * address,gint io_priority,GCancellable * cancellable,GAsyncReadyCallback callback,gpointer user_data)1865 camel_session_forward_to (CamelSession *session,
1866                           CamelFolder *folder,
1867                           CamelMimeMessage *message,
1868                           const gchar *address,
1869                           gint io_priority,
1870                           GCancellable *cancellable,
1871                           GAsyncReadyCallback callback,
1872                           gpointer user_data)
1873 {
1874 	GTask *task;
1875 	AsyncContext *async_context;
1876 
1877 	g_return_if_fail (CAMEL_IS_SESSION (session));
1878 	g_return_if_fail (CAMEL_IS_FOLDER (folder));
1879 	g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1880 	g_return_if_fail (address != NULL);
1881 
1882 	async_context = g_slice_new0 (AsyncContext);
1883 	async_context->folder = g_object_ref (folder);
1884 	async_context->message = g_object_ref (message);
1885 	async_context->address = g_strdup (address);
1886 
1887 	task = g_task_new (session, cancellable, callback, user_data);
1888 	g_task_set_source_tag (task, camel_session_forward_to);
1889 	g_task_set_priority (task, io_priority);
1890 
1891 	g_task_set_task_data (
1892 		task, async_context,
1893 		(GDestroyNotify) async_context_free);
1894 
1895 	g_task_run_in_thread (task, session_forward_to_thread);
1896 
1897 	g_object_unref (task);
1898 }
1899 
1900 /**
1901  * camel_session_forward_to_finish:
1902  * @session: a #CamelSession
1903  * @result: a #GAsyncResult
1904  * @error: return location for a #GError, or %NULL
1905  *
1906  * Finishes the operation started with camel_session_forward_to().
1907  *
1908  * If an error occurred, the function sets @error and returns %FALSE.
1909  *
1910  * Returns: %TRUE on success, %FALSE on failure
1911  *
1912  * Since: 3.6
1913  **/
1914 gboolean
camel_session_forward_to_finish(CamelSession * session,GAsyncResult * result,GError ** error)1915 camel_session_forward_to_finish (CamelSession *session,
1916                                  GAsyncResult *result,
1917                                  GError **error)
1918 {
1919 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1920 	g_return_val_if_fail (g_task_is_valid (result, session), FALSE);
1921 
1922 	g_return_val_if_fail (
1923 		g_async_result_is_tagged (
1924 		result, camel_session_forward_to), FALSE);
1925 
1926 	return g_task_propagate_boolean (G_TASK (result), error);
1927 }
1928 
1929 /**
1930  * camel_session_get_oauth2_access_token_sync:
1931  * @session: a #CamelSession
1932  * @service: a #CamelService
1933  * @out_access_token: (out) (nullable): return location for the access token, or %NULL
1934  * @out_expires_in: (out) (nullable): return location for the token expiry, or %NULL
1935  * @cancellable: optional #GCancellable object, or %NULL
1936  * @error: return location for a #GError, or %NULL
1937  *
1938  * Obtains the OAuth 2.0 access token for @service along with its expiry
1939  * in seconds from the current time (or 0 if unknown).
1940  *
1941  * Free the returned access token with g_free() when no longer needed.
1942  *
1943  * Returns: whether succeeded
1944  *
1945  * Since: 3.28
1946  **/
1947 gboolean
camel_session_get_oauth2_access_token_sync(CamelSession * session,CamelService * service,gchar ** out_access_token,gint * out_expires_in,GCancellable * cancellable,GError ** error)1948 camel_session_get_oauth2_access_token_sync (CamelSession *session,
1949 					    CamelService *service,
1950 					    gchar **out_access_token,
1951 					    gint *out_expires_in,
1952 					    GCancellable *cancellable,
1953 					    GError **error)
1954 {
1955 	CamelSessionClass *klass;
1956 
1957 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1958 
1959 	klass = CAMEL_SESSION_GET_CLASS (session);
1960 	g_return_val_if_fail (klass != NULL, FALSE);
1961 	g_return_val_if_fail (klass->get_oauth2_access_token_sync != NULL, FALSE);
1962 
1963 	return klass->get_oauth2_access_token_sync (session, service, out_access_token, out_expires_in, cancellable, error);
1964 }
1965 
1966 /**
1967  * camel_session_get_recipient_certificates_sync:
1968  * @session: a #CamelSession
1969  * @flags: bit-or of #CamelRecipientCertificateFlags
1970  * @recipients: (element-type utf8): a #GPtrArray of recipients
1971  * @out_certificates: (element-type utf8) (out): a #GSList of gathered certificates
1972  * @cancellable: optional #GCancellable object, or %NULL
1973  * @error: return location for a #GError, or %NULL
1974  *
1975  * Searches for S/MIME certificates or PGP keys for the given @recipients,
1976  * which are returned as base64 encoded strings in @out_certificates.
1977  * This is used when encrypting messages. The @flags influence what
1978  * the @out_certificates will contain. The order of items in @out_certificates
1979  * should match the order of items in @recipients, with %NULL data for those
1980  * which could not be found.
1981  *
1982  * The function should return failure only if some fatal error happened.
1983  * It's not an error when certificates for some, or all, recipients
1984  * could not be found.
1985  *
1986  * This method is optional and the default implementation returns %TRUE
1987  * and sets the @out_certificates to %NULL. It's the only exception
1988  * when the length of @recipients and @out_certificates can differ.
1989  * In all other cases the length of the two should match.
1990  *
1991  * The @out_certificates will be freed with g_slist_free_full (certificates, g_free);
1992  * when done with it.
1993  *
1994  * Returns: Whether succeeded, or better whether no fatal error happened.
1995  *
1996  * Since: 3.30
1997  **/
1998 gboolean
camel_session_get_recipient_certificates_sync(CamelSession * session,guint32 flags,const GPtrArray * recipients,GSList ** out_certificates,GCancellable * cancellable,GError ** error)1999 camel_session_get_recipient_certificates_sync (CamelSession *session,
2000 					       guint32 flags, /* bit-or of CamelRecipientCertificateFlags */
2001 					       const GPtrArray *recipients, /* gchar * */
2002 					       GSList **out_certificates, /* gchar * */
2003 					       GCancellable *cancellable,
2004 					       GError **error)
2005 {
2006 	CamelSessionClass *klass;
2007 
2008 	g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
2009 	g_return_val_if_fail (recipients != NULL, FALSE);
2010 	g_return_val_if_fail (out_certificates != NULL, FALSE);
2011 
2012 	*out_certificates = NULL;
2013 
2014 	klass = CAMEL_SESSION_GET_CLASS (session);
2015 	g_return_val_if_fail (klass != NULL, FALSE);
2016 
2017 	if (!klass->get_recipient_certificates_sync)
2018 		return TRUE;
2019 
2020 	return klass->get_recipient_certificates_sync (session, flags, recipients, out_certificates, cancellable, error);
2021 }
2022