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