1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-session.c : Abstract class for an email session */
6 * Dan Winship <danw@ximian.com>
7 * Jeffrey Stedfast <fejj@ximian.com>
8 * Bertrand Guiheneuf <bertrand@helixcode.com>
10 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of version 2 of the GNU Lesser General Public
14 * License as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
37 #include <glib/gi18n-lib.h>
38 #include <glib/gstdio.h>
40 #include "camel-debug.h"
41 #include "camel-file-utils.h"
42 #include "camel-folder.h"
43 #include "camel-marshal.h"
44 #include "camel-mime-message.h"
45 #include "camel-sasl.h"
46 #include "camel-session.h"
47 #include "camel-store.h"
48 #include "camel-string-utils.h"
49 #include "camel-transport.h"
50 #include "camel-url.h"
52 #define CAMEL_SESSION_GET_PRIVATE(obj) \
53 (G_TYPE_INSTANCE_GET_PRIVATE \
54 ((obj), CAMEL_TYPE_SESSION, CamelSessionPrivate))
56 #define JOB_PRIORITY G_PRIORITY_LOW
60 typedef struct _AsyncContext AsyncContext;
61 typedef struct _JobData JobData;
63 struct _CamelSessionPrivate {
65 gchar *user_cache_dir;
70 GHashTable *junk_headers;
71 CamelJunkFilter *junk_filter;
73 GMainContext *main_context;
76 guint network_available : 1;
80 struct _AsyncContext {
82 CamelMimeMessage *message;
83 CamelService *service;
85 gchar *auth_mechanism;
89 CamelSession *session;
90 GCancellable *cancellable;
91 CamelSessionCallback callback;
93 GDestroyNotify notify;
101 PROP_NETWORK_AVAILABLE,
113 static guint signals[LAST_SIGNAL];
115 G_DEFINE_TYPE (CamelSession, camel_session, CAMEL_TYPE_OBJECT)
118 async_context_free (AsyncContext *async_context)
120 if (async_context->folder != NULL)
121 g_object_unref (async_context->folder);
123 if (async_context->message != NULL)
124 g_object_unref (async_context->message);
126 if (async_context->service != NULL)
127 g_object_unref (async_context->service);
129 g_free (async_context->address);
130 g_free (async_context->auth_mechanism);
132 g_slice_free (AsyncContext, async_context);
136 job_data_free (JobData *job_data)
138 g_object_unref (job_data->session);
139 g_object_unref (job_data->cancellable);
141 if (job_data->notify != NULL)
142 job_data->notify (job_data->user_data);
144 g_slice_free (JobData, job_data);
148 session_finish_job_cb (CamelSession *session,
149 GSimpleAsyncResult *simple)
152 GError *error = NULL;
154 g_simple_async_result_propagate_error (simple, &error);
155 job_data = g_simple_async_result_get_op_res_gpointer (simple);
159 signals[JOB_FINISHED], 0,
160 job_data->cancellable, error);
162 g_clear_error (&error);
166 session_do_job_cb (GSimpleAsyncResult *simple,
167 CamelSession *session,
168 GCancellable *cancellable)
171 GError *error = NULL;
173 job_data = g_simple_async_result_get_op_res_gpointer (simple);
176 session, cancellable,
177 job_data->user_data, &error);
180 g_simple_async_result_take_error (simple, error);
184 session_start_job_cb (gpointer user_data)
186 JobData *job_data = user_data;
187 GSimpleAsyncResult *simple;
191 signals[JOB_STARTED], 0,
192 job_data->cancellable);
194 simple = g_simple_async_result_new (
195 G_OBJECT (job_data->session),
196 (GAsyncReadyCallback) session_finish_job_cb,
197 NULL, camel_session_submit_job);
199 g_simple_async_result_set_check_cancellable (
200 simple, job_data->cancellable);
202 g_simple_async_result_set_op_res_gpointer (
203 simple, job_data, (GDestroyNotify) job_data_free);
205 g_simple_async_result_run_in_thread (
206 simple, (GSimpleAsyncThreadFunc)
207 session_do_job_cb, JOB_PRIORITY,
208 job_data->cancellable);
210 g_object_unref (simple);
216 session_set_user_data_dir (CamelSession *session,
217 const gchar *user_data_dir)
219 g_return_if_fail (user_data_dir != NULL);
220 g_return_if_fail (session->priv->user_data_dir == NULL);
222 session->priv->user_data_dir = g_strdup (user_data_dir);
226 session_set_user_cache_dir (CamelSession *session,
227 const gchar *user_cache_dir)
229 g_return_if_fail (user_cache_dir != NULL);
230 g_return_if_fail (session->priv->user_cache_dir == NULL);
232 session->priv->user_cache_dir = g_strdup (user_cache_dir);
236 session_set_property (GObject *object,
241 switch (property_id) {
242 case PROP_CHECK_JUNK:
243 camel_session_set_check_junk (
244 CAMEL_SESSION (object),
245 g_value_get_boolean (value));
248 case PROP_JUNK_FILTER:
249 camel_session_set_junk_filter (
250 CAMEL_SESSION (object),
251 g_value_get_object (value));
254 case PROP_NETWORK_AVAILABLE:
255 camel_session_set_network_available (
256 CAMEL_SESSION (object),
257 g_value_get_boolean (value));
261 camel_session_set_online (
262 CAMEL_SESSION (object),
263 g_value_get_boolean (value));
266 case PROP_USER_DATA_DIR:
267 session_set_user_data_dir (
268 CAMEL_SESSION (object),
269 g_value_get_string (value));
272 case PROP_USER_CACHE_DIR:
273 session_set_user_cache_dir (
274 CAMEL_SESSION (object),
275 g_value_get_string (value));
279 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
283 session_get_property (GObject *object,
288 switch (property_id) {
289 case PROP_CHECK_JUNK:
290 g_value_set_boolean (
291 value, camel_session_get_check_junk (
292 CAMEL_SESSION (object)));
295 case PROP_JUNK_FILTER:
297 value, camel_session_get_junk_filter (
298 CAMEL_SESSION (object)));
301 case PROP_MAIN_CONTEXT:
303 value, camel_session_get_main_context (
304 CAMEL_SESSION (object)));
307 case PROP_NETWORK_AVAILABLE:
308 g_value_set_boolean (
309 value, camel_session_get_network_available (
310 CAMEL_SESSION (object)));
314 g_value_set_boolean (
315 value, camel_session_get_online (
316 CAMEL_SESSION (object)));
319 case PROP_USER_DATA_DIR:
321 value, camel_session_get_user_data_dir (
322 CAMEL_SESSION (object)));
325 case PROP_USER_CACHE_DIR:
327 value, camel_session_get_user_cache_dir (
328 CAMEL_SESSION (object)));
332 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336 session_dispose (GObject *object)
338 CamelSessionPrivate *priv;
340 priv = CAMEL_SESSION_GET_PRIVATE (object);
342 g_hash_table_remove_all (priv->services);
344 if (priv->junk_filter != NULL) {
345 g_object_unref (priv->junk_filter);
346 priv->junk_filter = NULL;
349 /* Chain up to parent's dispose() method. */
350 G_OBJECT_CLASS (camel_session_parent_class)->dispose (object);
354 session_finalize (GObject *object)
356 CamelSessionPrivate *priv;
358 priv = CAMEL_SESSION_GET_PRIVATE (object);
360 g_free (priv->user_data_dir);
361 g_free (priv->user_cache_dir);
363 g_hash_table_destroy (priv->services);
365 if (priv->main_context != NULL)
366 g_main_context_unref (priv->main_context);
368 g_mutex_clear (&priv->services_lock);
370 if (priv->junk_headers) {
371 g_hash_table_remove_all (priv->junk_headers);
372 g_hash_table_destroy (priv->junk_headers);
375 /* Chain up to parent's finalize() method. */
376 G_OBJECT_CLASS (camel_session_parent_class)->finalize (object);
379 static CamelService *
380 session_add_service (CamelSession *session,
382 const gchar *protocol,
383 CamelProviderType type,
386 CamelService *service;
387 CamelProvider *provider;
388 GType service_type = G_TYPE_INVALID;
390 service = camel_session_ref_service (session, uid);
391 if (CAMEL_IS_SERVICE (service))
394 /* Try to find a suitable CamelService subclass. */
395 provider = camel_provider_get (protocol, error);
396 if (provider != NULL)
397 service_type = provider->object_types[type];
402 if (service_type == G_TYPE_INVALID) {
404 error, CAMEL_SERVICE_ERROR,
405 CAMEL_SERVICE_ERROR_URL_INVALID,
406 _("No provider available for protocol '%s'"),
411 if (!g_type_is_a (service_type, CAMEL_TYPE_SERVICE)) {
413 error, CAMEL_SERVICE_ERROR,
414 CAMEL_SERVICE_ERROR_INVALID,
415 _("Invalid GType registered for protocol '%s'"),
420 service = g_initable_new (
421 service_type, NULL, error,
422 "provider", provider, "session",
423 session, "uid", uid, NULL);
425 if (service != NULL) {
426 g_mutex_lock (&session->priv->services_lock);
428 g_hash_table_insert (
429 session->priv->services,
431 g_object_ref (service));
433 g_mutex_unlock (&session->priv->services_lock);
440 session_remove_service (CamelSession *session,
441 CamelService *service)
445 g_mutex_lock (&session->priv->services_lock);
447 uid = camel_service_get_uid (service);
448 g_hash_table_remove (session->priv->services, uid);
450 g_mutex_unlock (&session->priv->services_lock);
454 session_get_socks_proxy (CamelSession *session,
455 const gchar *for_host,
464 session_authenticate_sync (CamelSession *session,
465 CamelService *service,
466 const gchar *mechanism,
467 GCancellable *cancellable,
470 CamelServiceAuthType *authtype = NULL;
471 CamelAuthenticationResult result;
472 GError *local_error = NULL;
474 /* XXX This authenticate_sync() implementation serves only as
475 * a rough example and is not intended to be used as is.
477 * Any CamelSession subclass should override this method
478 * and implement a more complete authentication loop that
479 * handles user prompts and password storage.
483 "The default CamelSession.authenticate_sync() "
484 "method is not intended for production use.");
486 /* If a SASL mechanism was given and we can't find
487 * a CamelServiceAuthType for it, fail immediately. */
488 if (mechanism != NULL) {
489 authtype = camel_sasl_authtype (mechanism);
490 if (authtype == NULL) {
492 error, CAMEL_SERVICE_ERROR,
493 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
494 _("No support for %s authentication"),
500 /* If the SASL mechanism does not involve a user
501 * password, then it gets one shot to authenticate. */
502 if (authtype != NULL && !authtype->need_password) {
503 result = camel_service_authenticate_sync (
504 service, mechanism, cancellable, error);
505 if (result == CAMEL_AUTHENTICATION_REJECTED)
507 error, CAMEL_SERVICE_ERROR,
508 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
509 _("%s authentication failed"), mechanism);
510 return (result == CAMEL_AUTHENTICATION_ACCEPTED);
513 /* Some SASL mechanisms can attempt to authenticate without a
514 * user password being provided (e.g. single-sign-on credentials),
515 * but can fall back to a user password. Handle that case next. */
516 if (mechanism != NULL) {
517 CamelProvider *provider;
519 const gchar *service_name;
520 gboolean success = FALSE;
522 provider = camel_service_get_provider (service);
523 service_name = provider->protocol;
525 /* XXX Would be nice if camel_sasl_try_empty_password_sync()
526 * returned CamelAuthenticationResult so it's easier to
528 sasl = camel_sasl_new (service_name, mechanism, service);
530 success = camel_sasl_try_empty_password_sync (
531 sasl, cancellable, &local_error);
532 g_object_unref (sasl);
539 /* Abort authentication if we got cancelled.
540 * Otherwise clear any errors and press on. */
541 if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
544 g_clear_error (&local_error);
547 /* XXX This is where things get bogus. In a real implementation you
548 * would want to fetch a stored password or prompt the user here.
549 * Password should be stashed using camel_service_set_password()
550 * before calling camel_service_authenticate_sync(). */
552 result = camel_service_authenticate_sync (
553 service, mechanism, cancellable, error);
555 if (result == CAMEL_AUTHENTICATION_REJECTED) {
556 /* XXX Request a different password here. */
560 if (result == CAMEL_AUTHENTICATION_ACCEPTED) {
561 /* XXX Possibly store the password here using
562 * GNOME Keyring or something equivalent. */
565 return (result == CAMEL_AUTHENTICATION_ACCEPTED);
569 session_authenticate_thread (GSimpleAsyncResult *simple,
571 GCancellable *cancellable)
573 AsyncContext *async_context;
574 GError *error = NULL;
576 async_context = g_simple_async_result_get_op_res_gpointer (simple);
578 camel_session_authenticate_sync (
579 CAMEL_SESSION (object),
580 async_context->service,
581 async_context->auth_mechanism,
582 cancellable, &error);
585 g_simple_async_result_take_error (simple, error);
589 session_authenticate (CamelSession *session,
590 CamelService *service,
591 const gchar *mechanism,
593 GCancellable *cancellable,
594 GAsyncReadyCallback callback,
597 GSimpleAsyncResult *simple;
598 AsyncContext *async_context;
600 async_context = g_slice_new0 (AsyncContext);
601 async_context->service = g_object_ref (service);
602 async_context->auth_mechanism = g_strdup (mechanism);
604 simple = g_simple_async_result_new (
605 G_OBJECT (session), callback, user_data, session_authenticate);
607 g_simple_async_result_set_check_cancellable (simple, cancellable);
609 g_simple_async_result_set_op_res_gpointer (
610 simple, async_context, (GDestroyNotify) async_context_free);
612 g_simple_async_result_run_in_thread (
613 simple, session_authenticate_thread, io_priority, cancellable);
615 g_object_unref (simple);
619 session_authenticate_finish (CamelSession *session,
620 GAsyncResult *result,
623 GSimpleAsyncResult *simple;
625 g_return_val_if_fail (
626 g_simple_async_result_is_valid (
627 result, G_OBJECT (session), session_authenticate), FALSE);
629 simple = G_SIMPLE_ASYNC_RESULT (result);
631 /* Assume success unless a GError is set. */
632 return !g_simple_async_result_propagate_error (simple, error);
636 session_forward_to_sync (CamelSession *session,
638 CamelMimeMessage *message,
639 const gchar *address,
640 GCancellable *cancellable,
643 g_set_error_literal (
644 error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
645 _("Forwarding messages is not supported"));
651 session_forward_to_thread (GSimpleAsyncResult *simple,
653 GCancellable *cancellable)
655 AsyncContext *async_context;
656 GError *error = NULL;
658 async_context = g_simple_async_result_get_op_res_gpointer (simple);
660 camel_session_forward_to_sync (
661 CAMEL_SESSION (object),
662 async_context->folder,
663 async_context->message,
664 async_context->address,
665 cancellable, &error);
668 g_simple_async_result_take_error (simple, error);
672 session_forward_to (CamelSession *session,
674 CamelMimeMessage *message,
675 const gchar *address,
677 GCancellable *cancellable,
678 GAsyncReadyCallback callback,
681 GSimpleAsyncResult *simple;
682 AsyncContext *async_context;
684 async_context = g_slice_new0 (AsyncContext);
685 async_context->folder = g_object_ref (folder);
686 async_context->message = g_object_ref (message);
687 async_context->address = g_strdup (address);
689 simple = g_simple_async_result_new (
690 G_OBJECT (session), callback, user_data, session_forward_to);
692 g_simple_async_result_set_check_cancellable (simple, cancellable);
694 g_simple_async_result_set_op_res_gpointer (
695 simple, async_context, (GDestroyNotify) async_context_free);
697 g_simple_async_result_run_in_thread (
698 simple, session_forward_to_thread, io_priority, cancellable);
700 g_object_unref (simple);
704 session_forward_to_finish (CamelSession *session,
705 GAsyncResult *result,
708 GSimpleAsyncResult *simple;
710 g_return_val_if_fail (
711 g_simple_async_result_is_valid (
712 result, G_OBJECT (session), session_forward_to), FALSE);
714 simple = G_SIMPLE_ASYNC_RESULT (result);
716 /* Assume success unless a GError is set. */
717 return !g_simple_async_result_propagate_error (simple, error);
721 camel_session_class_init (CamelSessionClass *class)
723 GObjectClass *object_class;
725 g_type_class_add_private (class, sizeof (CamelSessionPrivate));
727 object_class = G_OBJECT_CLASS (class);
728 object_class->set_property = session_set_property;
729 object_class->get_property = session_get_property;
730 object_class->dispose = session_dispose;
731 object_class->finalize = session_finalize;
733 class->add_service = session_add_service;
734 class->remove_service = session_remove_service;
735 class->get_socks_proxy = session_get_socks_proxy;
737 class->authenticate_sync = session_authenticate_sync;
738 class->forward_to_sync = session_forward_to_sync;
740 class->authenticate = session_authenticate;
741 class->authenticate_finish = session_authenticate_finish;
742 class->forward_to = session_forward_to;
743 class->forward_to_finish = session_forward_to_finish;
745 g_object_class_install_property (
748 g_param_spec_boolean (
751 "Check incoming messages for junk",
755 G_PARAM_STATIC_STRINGS));
757 g_object_class_install_property (
760 g_param_spec_object (
763 "Classifies messages as junk or not junk",
764 CAMEL_TYPE_JUNK_FILTER,
766 G_PARAM_STATIC_STRINGS));
768 g_object_class_install_property (
774 "The GMainContext used for signal emissions",
777 G_PARAM_STATIC_STRINGS));
779 g_object_class_install_property (
781 PROP_NETWORK_AVAILABLE,
782 g_param_spec_boolean (
785 "Whether the network is available",
789 G_PARAM_STATIC_STRINGS));
791 g_object_class_install_property (
794 g_param_spec_boolean (
797 "Whether the shell is online",
801 G_PARAM_STATIC_STRINGS));
803 g_object_class_install_property (
806 g_param_spec_string (
808 "User Data Directory",
809 "User-specific base directory for mail data",
813 G_PARAM_STATIC_STRINGS));
815 g_object_class_install_property (
818 g_param_spec_string (
820 "User Cache Directory",
821 "User-specific base directory for mail cache",
825 G_PARAM_STATIC_STRINGS));
827 signals[JOB_STARTED] = g_signal_new (
829 G_OBJECT_CLASS_TYPE (class),
831 G_STRUCT_OFFSET (CamelSessionClass, job_started),
833 g_cclosure_marshal_VOID__OBJECT,
837 signals[JOB_FINISHED] = g_signal_new (
839 G_OBJECT_CLASS_TYPE (class),
841 G_STRUCT_OFFSET (CamelSessionClass, job_finished),
843 camel_marshal_VOID__OBJECT_POINTER,
850 camel_session_init (CamelSession *session)
852 GHashTable *services;
854 services = g_hash_table_new_full (
855 (GHashFunc) g_str_hash,
856 (GEqualFunc) g_str_equal,
857 (GDestroyNotify) g_free,
858 (GDestroyNotify) g_object_unref);
860 session->priv = CAMEL_SESSION_GET_PRIVATE (session);
862 session->priv->services = services;
863 g_mutex_init (&session->priv->services_lock);
864 session->priv->junk_headers = NULL;
866 session->priv->main_context = g_main_context_ref_thread_default ();
870 * camel_session_get_main_context:
871 * @session: a #CamelSession
873 * Returns the #GMainContext from which signals are emitted. This
874 * was the thread-default #GMainContext for the thread where @session
877 * Returns: the #GMainContext for signal emissions
882 camel_session_get_main_context (CamelSession *session)
884 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
886 return session->priv->main_context;
890 * camel_session_get_user_data_dir:
891 * @session: a #CamelSession
893 * Returns the base directory under which to store user-specific mail data.
895 * Returns: the base directory for mail data
900 camel_session_get_user_data_dir (CamelSession *session)
902 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
904 return session->priv->user_data_dir;
908 * camel_session_get_user_cache_dir:
909 * @session: a #CamelSession
911 * Returns the base directory under which to store user-specific mail cache.
913 * Returns: the base directory for mail cache
918 camel_session_get_user_cache_dir (CamelSession *session)
920 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
922 return session->priv->user_cache_dir;
926 * camel_session_add_service:
927 * @session: a #CamelSession
928 * @uid: a unique identifier string
929 * @protocol: the service protocol
930 * @type: the service type
931 * @error: return location for a #GError, or %NULL
933 * Instantiates a new #CamelService for @session. The @uid identifies the
934 * service for future lookup. The @protocol indicates which #CamelProvider
935 * holds the #GType of the #CamelService subclass to instantiate. The @type
936 * explicitly designates the service as a #CamelStore or #CamelTransport.
938 * If the given @uid has already been added, the existing #CamelService
939 * with that @uid is returned regardless of whether it agrees with the
940 * given @protocol and @type.
942 * If no #CamelProvider is available to handle the given @protocol, or
943 * if the #CamelProvider does not specify a valid #GType for @type, the
944 * function sets @error and returns %NULL.
946 * The returned #CamelService is referenced for thread-safety and must be
947 * unreferenced with g_object_unref() when finished with it.
949 * Returns: a #CamelService instance, or %NULL
954 camel_session_add_service (CamelSession *session,
956 const gchar *protocol,
957 CamelProviderType type,
960 CamelSessionClass *class;
961 CamelService *service;
963 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
964 g_return_val_if_fail (uid != NULL, NULL);
965 g_return_val_if_fail (protocol != NULL, NULL);
967 class = CAMEL_SESSION_GET_CLASS (session);
968 g_return_val_if_fail (class->add_service != NULL, NULL);
970 service = class->add_service (session, uid, protocol, type, error);
971 CAMEL_CHECK_GERROR (session, add_service, service != NULL, error);
977 * camel_session_remove_service:
978 * @session: a #CamelSession
979 * @service: the #CamelService to remove
981 * Removes a #CamelService previously added by camel_session_add_service().
986 camel_session_remove_service (CamelSession *session,
987 CamelService *service)
989 CamelSessionClass *class;
991 g_return_if_fail (CAMEL_IS_SESSION (session));
992 g_return_if_fail (CAMEL_IS_SERVICE (service));
994 /* Verify the service belongs to this session. */
995 g_return_if_fail (camel_service_get_session (service) == session);
997 class = CAMEL_SESSION_GET_CLASS (session);
998 g_return_if_fail (class->remove_service != NULL);
1000 class->remove_service (session, service);
1004 * camel_session_ref_service:
1005 * @session: a #CamelSession
1006 * @uid: a unique identifier string
1008 * Looks up a #CamelService by its unique identifier string. The service
1009 * must have been previously added using camel_session_add_service().
1011 * The returned #CamelService is referenced for thread-safety and must be
1012 * unreferenced with g_object_unref() when finished with it.
1014 * Returns: a #CamelService instance, or %NULL
1019 camel_session_ref_service (CamelSession *session,
1022 CamelService *service;
1024 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1025 g_return_val_if_fail (uid != NULL, NULL);
1027 g_mutex_lock (&session->priv->services_lock);
1029 service = g_hash_table_lookup (session->priv->services, uid);
1031 if (service != NULL)
1032 g_object_ref (service);
1034 g_mutex_unlock (&session->priv->services_lock);
1040 * camel_session_ref_service_by_url:
1041 * @session: a #CamelSession
1043 * @type: a #CamelProviderType
1045 * Looks up a #CamelService by trying to match its #CamelURL against the
1046 * given @url and then checking that the object is of the desired @type.
1047 * The service must have been previously added using
1048 * camel_session_add_service().
1050 * The returned #CamelService is referenced for thread-safety and must be
1051 * unreferenced with g_object_unref() when finished with it.
1053 * Note this function is significantly slower than camel_session_ref_service().
1055 * Returns: a #CamelService instance, or %NULL
1060 camel_session_ref_service_by_url (CamelSession *session,
1062 CamelProviderType type)
1064 CamelService *match = NULL;
1067 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1068 g_return_val_if_fail (url != NULL, NULL);
1070 list = camel_session_list_services (session);
1072 for (iter = list; iter != NULL; iter = g_list_next (iter)) {
1073 CamelProvider *provider;
1074 CamelService *service;
1075 CamelURL *service_url;
1078 service = CAMEL_SERVICE (iter->data);
1079 provider = camel_service_get_provider (service);
1081 if (provider == NULL)
1084 if (provider->url_equal == NULL)
1087 service_url = camel_service_new_camel_url (service);
1088 url_equal = provider->url_equal (url, service_url);
1089 camel_url_free (service_url);
1095 case CAMEL_PROVIDER_STORE:
1096 if (CAMEL_IS_STORE (service))
1097 match = g_object_ref (service);
1099 case CAMEL_PROVIDER_TRANSPORT:
1100 if (CAMEL_IS_TRANSPORT (service))
1101 match = g_object_ref (service);
1104 g_warn_if_reached ();
1112 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1118 * camel_session_list_services:
1119 * @session: a #CamelSession
1121 * Returns a list of all #CamelService objects previously added using
1122 * camel_session_add_service().
1124 * The services returned in the list are referenced for thread-safety.
1125 * They must each be unreferenced with g_object_unref() when finished
1126 * with them. Free the returned list itself with g_list_free().
1128 * An easy way to free the list property in one step is as follows:
1131 * g_list_free_full (list, g_object_unref);
1134 * Returns: an unsorted list of #CamelService objects
1139 camel_session_list_services (CamelSession *session)
1143 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1145 g_mutex_lock (&session->priv->services_lock);
1147 list = g_hash_table_get_values (session->priv->services);
1149 g_list_foreach (list, (GFunc) g_object_ref, NULL);
1151 g_mutex_unlock (&session->priv->services_lock);
1157 * camel_session_remove_services:
1158 * @session: a #CamelSession
1160 * Removes all #CamelService instances added by camel_session_add_service().
1162 * This can be useful during application shutdown to ensure all #CamelService
1163 * instances are freed properly, especially since #CamelSession instances are
1164 * prone to reference cycles.
1169 camel_session_remove_services (CamelSession *session)
1171 g_return_if_fail (CAMEL_IS_SESSION (session));
1173 g_mutex_lock (&session->priv->services_lock);
1175 g_hash_table_remove_all (session->priv->services);
1177 g_mutex_unlock (&session->priv->services_lock);
1181 * camel_session_get_password:
1182 * @session: a #CamelSession
1183 * @service: the #CamelService this query is being made by
1184 * @prompt: prompt to provide to user
1185 * @item: an identifier, unique within this service, for the information
1186 * @flags: %CAMEL_SESSION_PASSWORD_REPROMPT, the prompt should force a reprompt
1187 * %CAMEL_SESSION_PASSWORD_SECRET, whether the password is secret
1188 * %CAMEL_SESSION_PASSWORD_STATIC, the password is remembered externally
1189 * @error: return location for a #GError, or %NULL
1191 * This function is used by a #CamelService to ask the application and
1192 * the user for a password or other authentication data.
1194 * @service and @item together uniquely identify the piece of data the
1195 * caller is concerned with.
1197 * @prompt is a question to ask the user (if the application doesn't
1198 * already have the answer cached). If %CAMEL_SESSION_PASSWORD_SECRET
1199 * is set, the user's input will not be echoed back.
1201 * If %CAMEL_SESSION_PASSWORD_STATIC is set, it means the password returned
1202 * will be stored statically by the caller automatically, for the current
1205 * The authenticator should set @error to %G_IO_ERROR_CANCELLED if
1206 * the user did not provide the information. The caller must g_free()
1207 * the information returned when it is done with it.
1209 * Returns: the authentication information or %NULL
1212 camel_session_get_password (CamelSession *session,
1213 CamelService *service,
1214 const gchar *prompt,
1219 CamelSessionClass *class;
1222 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1223 g_return_val_if_fail (prompt != NULL, NULL);
1224 g_return_val_if_fail (item != NULL, NULL);
1226 class = CAMEL_SESSION_GET_CLASS (session);
1227 g_return_val_if_fail (class->get_password != NULL, NULL);
1229 password = class->get_password (
1230 session, service, prompt, item, flags, error);
1231 CAMEL_CHECK_GERROR (session, get_password, password != NULL, error);
1237 * camel_session_forget_password:
1238 * @session: a #CamelSession
1239 * @service: the #CamelService rejecting the password
1240 * @item: an identifier, unique within this service, for the information
1241 * @error: return location for a #GError, or %NULL
1243 * This function is used by a #CamelService to tell the application
1244 * that the authentication information it provided via
1245 * camel_session_get_password() was rejected by the service. If the
1246 * application was caching this information, it should stop,
1247 * and if the service asks for it again, it should ask the user.
1249 * @service and @item identify the rejected authentication information,
1250 * as with camel_session_get_password().
1252 * Returns: %TRUE on success, %FALSE on failure
1255 camel_session_forget_password (CamelSession *session,
1256 CamelService *service,
1260 CamelSessionClass *class;
1263 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1264 g_return_val_if_fail (item != NULL, FALSE);
1266 class = CAMEL_SESSION_GET_CLASS (session);
1267 g_return_val_if_fail (class->forget_password, FALSE);
1269 success = class->forget_password (session, service, item, error);
1270 CAMEL_CHECK_GERROR (session, forget_password, success, error);
1276 * camel_session_alert_user:
1277 * @session: a #CamelSession
1278 * @type: the type of alert (info, warning, or error)
1279 * @prompt: the message for the user
1280 * @button_captions: List of button captions to use. If NULL, only "Dismiss" button is shown.
1282 * Presents the given @prompt to the user, in the style indicated by
1283 * @type. If @cancel is %TRUE, the user will be able to accept or
1284 * cancel. Otherwise, the message is purely informational.
1286 * Returns: Index of pressed button from @button_captions, -1 if NULL.
1289 camel_session_alert_user (CamelSession *session,
1290 CamelSessionAlertType type,
1291 const gchar *prompt,
1292 GSList *button_captions)
1294 CamelSessionClass *class;
1296 g_return_val_if_fail (CAMEL_IS_SESSION (session), -1);
1297 g_return_val_if_fail (prompt != NULL, -1);
1299 class = CAMEL_SESSION_GET_CLASS (session);
1300 g_return_val_if_fail (class->alert_user != NULL, -1);
1302 return class->alert_user (session, type, prompt, button_captions);
1306 * camel_session_lookup_addressbook:
1311 camel_session_lookup_addressbook (CamelSession *session,
1314 CamelSessionClass *class;
1316 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1317 g_return_val_if_fail (name != NULL, FALSE);
1319 class = CAMEL_SESSION_GET_CLASS (session);
1320 g_return_val_if_fail (class->lookup_addressbook != NULL, FALSE);
1322 return class->lookup_addressbook (session, name);
1326 * camel_session_build_password_prompt:
1327 * @type: account type (e.g. "IMAP")
1328 * @user: user name for the account
1329 * @host: host name for the account
1331 * Constructs a localized password prompt from @type, @user and @host,
1332 * suitable for passing to camel_session_get_password(). The resulting
1333 * string contains markup tags. Use g_free() to free it.
1335 * Returns: a newly-allocated password prompt string
1340 camel_session_build_password_prompt (const gchar *type,
1348 g_return_val_if_fail (type != NULL, NULL);
1349 g_return_val_if_fail (user != NULL, NULL);
1350 g_return_val_if_fail (host != NULL, NULL);
1352 /* Add bold tags to the "user" and "host" strings. We use
1353 * separate strings here to avoid putting markup tags in the
1354 * translatable string below. */
1355 user_markup = g_markup_printf_escaped ("<b>%s</b>", user);
1356 host_markup = g_markup_printf_escaped ("<b>%s</b>", host);
1358 /* Translators: The first argument is the account type
1359 * (e.g. "IMAP"), the second is the user name, and the
1360 * third is the host name. */
1361 prompt = g_strdup_printf (
1362 _("Please enter the %s password for %s on host %s."),
1363 type, user_markup, host_markup);
1365 g_free (user_markup);
1366 g_free (host_markup);
1372 * camel_session_get_online:
1373 * @session: a #CamelSession
1375 * Returns: whether or not @session is online
1378 camel_session_get_online (CamelSession *session)
1380 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1382 return session->priv->online;
1386 * camel_session_set_online:
1387 * @session: a #CamelSession
1388 * @online: whether or not the session should be online
1390 * Sets the online status of @session to @online.
1393 camel_session_set_online (CamelSession *session,
1396 g_return_if_fail (CAMEL_IS_SESSION (session));
1398 if (online == session->priv->online)
1401 session->priv->online = online;
1403 g_object_notify (G_OBJECT (session), "online");
1407 * camel_session_get_filter_driver:
1408 * @session: a #CamelSession
1409 * @type: the type of filter (eg, "incoming")
1410 * @error: return location for a #GError, or %NULL
1412 * Returns: a filter driver, loaded with applicable rules
1415 camel_session_get_filter_driver (CamelSession *session,
1419 CamelSessionClass *class;
1420 CamelFilterDriver *driver;
1422 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1423 g_return_val_if_fail (type != NULL, NULL);
1425 class = CAMEL_SESSION_GET_CLASS (session);
1426 g_return_val_if_fail (class->get_filter_driver != NULL, NULL);
1428 driver = class->get_filter_driver (session, type, error);
1429 CAMEL_CHECK_GERROR (session, get_filter_driver, driver != NULL, error);
1435 * camel_session_get_junk_filter:
1436 * @session: a #CamelSession
1438 * Returns the #CamelJunkFilter instance used to classify messages as
1439 * junk or not junk during filtering.
1441 * Note that #CamelJunkFilter itself is just an interface. The application
1442 * must implement the interface and install a #CamelJunkFilter instance for
1443 * junk filtering to take place.
1445 * Returns: a #CamelJunkFilter, or %NULL
1450 camel_session_get_junk_filter (CamelSession *session)
1452 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1454 return session->priv->junk_filter;
1458 * camel_session_set_junk_filter:
1459 * @session: a #CamelSession
1460 * @junk_filter: a #CamelJunkFilter, or %NULL
1462 * Installs the #CamelJunkFilter instance used to classify messages as
1463 * junk or not junk during filtering.
1465 * Note that #CamelJunkFilter itself is just an interface. The application
1466 * must implement the interface and install a #CamelJunkFilter instance for
1467 * junk filtering to take place.
1472 camel_session_set_junk_filter (CamelSession *session,
1473 CamelJunkFilter *junk_filter)
1475 g_return_if_fail (CAMEL_IS_SESSION (session));
1477 if (junk_filter != NULL) {
1478 g_return_if_fail (CAMEL_IS_JUNK_FILTER (junk_filter));
1479 g_object_ref (junk_filter);
1482 if (session->priv->junk_filter != NULL)
1483 g_object_unref (session->priv->junk_filter);
1485 session->priv->junk_filter = junk_filter;
1487 g_object_notify (G_OBJECT (session), "junk-filter");
1491 * camel_session_idle_add:
1492 * @session: a #CamelSession
1493 * @priority: the priority of the idle source
1494 * @function: a function to call
1495 * @data: data to pass to @function
1496 * @notify: function to call when the idle is removed, or %NULL
1498 * Adds a function to be called whenever there are no higher priority events
1499 * pending. If @function returns %FALSE it is automatically removed from the
1500 * list of event sources and will not be called again.
1502 * This internally creates a main loop source using g_idle_source_new()
1503 * and attaches it to @session's own #CamelSession:main-context using
1504 * g_source_attach().
1506 * The @priority is typically in the range between %G_PRIORITY_DEFAULT_IDLE
1507 * and %G_PRIORITY_HIGH_IDLE.
1509 * Returns: the ID (greater than 0) of the event source
1514 camel_session_idle_add (CamelSession *session,
1516 GSourceFunc function,
1518 GDestroyNotify notify)
1520 GMainContext *main_context;
1524 g_return_val_if_fail (CAMEL_IS_SESSION (session), 0);
1525 g_return_val_if_fail (function != NULL, 0);
1527 main_context = camel_session_get_main_context (session);
1529 source = g_idle_source_new ();
1531 if (priority != G_PRIORITY_DEFAULT_IDLE)
1532 g_source_set_priority (source, priority);
1534 g_source_set_callback (source, function, data, notify);
1536 source_id = g_source_attach (source, main_context);
1538 g_source_unref (source);
1544 * camel_session_submit_job:
1545 * @session: a #CamelSession
1546 * @callback: a #CamelSessionCallback
1547 * @user_data: user data passed to the callback
1548 * @notify: a #GDestroyNotify function
1550 * This function provides a simple mechanism for providers to initiate
1551 * low-priority background jobs. Jobs can be submitted from any thread,
1552 * but execution of the jobs is always as follows:
1554 * 1) The #CamelSession:job-started signal is emitted from the thread
1555 * in which @session was created. This is typically the same thread
1556 * that hosts the global default #GMainContext, or "main" thread.
1558 * 2) The @callback function is invoked from a different thread where
1559 * it's safe to call synchronous functions.
1561 * 3) Once @callback has returned, the #CamelSesson:job-finished signal
1562 * is emitted from the same thread as #CamelSession:job-started was
1565 * 4) Finally if a @notify function was provided, it is invoked and
1566 * passed @user_data so that @user_data can be freed.
1571 camel_session_submit_job (CamelSession *session,
1572 CamelSessionCallback callback,
1574 GDestroyNotify notify)
1578 g_return_if_fail (CAMEL_IS_SESSION (session));
1579 g_return_if_fail (callback != NULL);
1581 job_data = g_slice_new0 (JobData);
1582 job_data->session = g_object_ref (session);
1583 job_data->cancellable = camel_operation_new ();
1584 job_data->callback = callback;
1585 job_data->user_data = user_data;
1586 job_data->notify = notify;
1588 camel_session_idle_add (
1589 session, JOB_PRIORITY,
1590 session_start_job_cb,
1591 job_data, (GDestroyNotify) NULL);
1595 * camel_session_get_check_junk:
1596 * @session: a #CamelSession
1598 * Do we have to check incoming messages to be junk?
1600 * Returns: whether or not we are checking incoming messages for junk
1603 camel_session_get_check_junk (CamelSession *session)
1605 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1607 return session->priv->check_junk;
1611 * camel_session_set_check_junk:
1612 * @session: a #CamelSession
1613 * @check_junk: whether to check incoming messages for junk
1615 * Set check_junk flag, if set, incoming mail will be checked for being junk.
1618 camel_session_set_check_junk (CamelSession *session,
1619 gboolean check_junk)
1621 g_return_if_fail (CAMEL_IS_SESSION (session));
1623 session->priv->check_junk = check_junk;
1625 g_object_notify (G_OBJECT (session), "check-junk");
1629 * camel_session_get_network_available:
1630 * @session: a #CamelSession
1635 camel_session_get_network_available (CamelSession *session)
1637 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1639 return session->priv->network_available;
1643 * camel_session_set_network_available:
1644 * @session: a #CamelSession
1645 * @network_available: whether a network is available
1650 camel_session_set_network_available (CamelSession *session,
1651 gboolean network_available)
1653 g_return_if_fail (CAMEL_IS_SESSION (session));
1655 session->priv->network_available = network_available;
1657 g_object_notify (G_OBJECT (session), "network-available");
1661 * camel_session_set_junk_headers:
1666 camel_session_set_junk_headers (CamelSession *session,
1667 const gchar **headers,
1668 const gchar **values,
1673 g_return_if_fail (CAMEL_IS_SESSION (session));
1675 if (session->priv->junk_headers) {
1676 g_hash_table_remove_all (session->priv->junk_headers);
1677 g_hash_table_destroy (session->priv->junk_headers);
1680 session->priv->junk_headers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1682 for (i = 0; i < len; i++) {
1683 g_hash_table_insert (session->priv->junk_headers, g_strdup (headers[i]), g_strdup (values[i]));
1688 * camel_session_get_junk_headers:
1693 camel_session_get_junk_headers (CamelSession *session)
1695 g_return_val_if_fail (CAMEL_IS_SESSION (session), NULL);
1697 return session->priv->junk_headers;
1701 * camel_session_get_socks_proxy:
1702 * @session: A #CamelSession
1703 * @for_host: Host name to which the connection will be requested
1704 * @host_ret: Location to return the SOCKS proxy hostname
1705 * @port_ret: Location to return the SOCKS proxy port
1707 * Queries the SOCKS proxy that is configured for a @session. This will
1708 * put %NULL in @hosts_ret if there is no proxy configured or when
1709 * the @for_host is listed in proxy ignore list.
1714 camel_session_get_socks_proxy (CamelSession *session,
1715 const gchar *for_host,
1719 CamelSessionClass *class;
1721 g_return_if_fail (CAMEL_IS_SESSION (session));
1722 g_return_if_fail (for_host != NULL);
1723 g_return_if_fail (host_ret != NULL);
1724 g_return_if_fail (port_ret != NULL);
1726 class = CAMEL_SESSION_GET_CLASS (session);
1727 g_return_if_fail (class->get_socks_proxy != NULL);
1729 class->get_socks_proxy (session, for_host, host_ret, port_ret);
1733 * camel_session_authenticate_sync:
1734 * @session: a #CamelSession
1735 * @service: a #CamelService
1736 * @mechanism: a SASL mechanism name, or %NULL
1737 * @cancellable: optional #GCancellable object, or %NULL
1738 * @error: return location for a #GError, or %NULL
1740 * Authenticates @service, which may involve repeated calls to
1741 * camel_service_authenticate() or camel_service_authenticate_sync().
1742 * A #CamelSession subclass is largely responsible for implementing this,
1743 * and should handle things like user prompts and secure password storage.
1744 * These issues are out-of-scope for Camel.
1746 * If an error occurs, or if authentication is aborted, the function sets
1747 * @error and returns %FALSE.
1749 * Returns: %TRUE on success, %FALSE on failure
1754 camel_session_authenticate_sync (CamelSession *session,
1755 CamelService *service,
1756 const gchar *mechanism,
1757 GCancellable *cancellable,
1760 CamelSessionClass *class;
1763 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1764 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1766 class = CAMEL_SESSION_GET_CLASS (session);
1767 g_return_val_if_fail (class->authenticate_sync != NULL, FALSE);
1769 success = class->authenticate_sync (
1770 session, service, mechanism, cancellable, error);
1771 CAMEL_CHECK_GERROR (session, authenticate_sync, success, error);
1777 * camel_session_forward_to_sync:
1778 * @session: a #CamelSession
1779 * @folder: the #CamelFolder where @message is located
1780 * @message: the #CamelMimeMessage to forward
1781 * @address: the recipient's email address
1782 * @cancellable: optional #GCancellable object, or %NULL
1783 * @error: return location for a #GError, or %NULL
1785 * Forwards @message in @folder to the email address(es) given by @address.
1787 * If an error occurs, the function sets @error and returns %FALSE.
1789 * Returns: %TRUE on success, %FALSE on failure
1794 camel_session_forward_to_sync (CamelSession *session,
1795 CamelFolder *folder,
1796 CamelMimeMessage *message,
1797 const gchar *address,
1798 GCancellable *cancellable,
1801 CamelSessionClass *class;
1804 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1805 g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE);
1806 g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
1807 g_return_val_if_fail (address != NULL, FALSE);
1809 class = CAMEL_SESSION_GET_CLASS (session);
1810 g_return_val_if_fail (class->forward_to_sync != NULL, FALSE);
1812 success = class->forward_to_sync (
1813 session, folder, message, address, cancellable, error);
1814 CAMEL_CHECK_GERROR (session, forward_to_sync, success, error);
1820 * camel_session_authenticate:
1821 * @session: a #CamelSession
1822 * @service: a #CamelService
1823 * @mechanism: a SASL mechanism name, or %NULL
1824 * @io_priority: the I/O priority for the request
1825 * @cancellable: optional #GCancellable object, or %NULL
1826 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1827 * @user_data: data to pass to the callback function
1829 * Asynchronously authenticates @service, which may involve repeated calls
1830 * to camel_service_authenticate() or camel_service_authenticate_sync().
1831 * A #CamelSession subclass is largely responsible for implementing this,
1832 * and should handle things like user prompts and secure password storage.
1833 * These issues are out-of-scope for Camel.
1835 * When the operation is finished, @callback will be called. You can
1836 * then call camel_session_authenticate_finish() to get the result of
1842 camel_session_authenticate (CamelSession *session,
1843 CamelService *service,
1844 const gchar *mechanism,
1846 GCancellable *cancellable,
1847 GAsyncReadyCallback callback,
1850 CamelSessionClass *class;
1852 g_return_if_fail (CAMEL_IS_SESSION (session));
1853 g_return_if_fail (CAMEL_IS_SERVICE (service));
1855 class = CAMEL_SESSION_GET_CLASS (session);
1856 g_return_if_fail (class->authenticate != NULL);
1858 class->authenticate (
1859 session, service, mechanism, io_priority,
1860 cancellable, callback, user_data);
1864 * camel_session_authenticate_finish:
1865 * @session: a #CamelSession
1866 * @result: a #GAsyncResult
1867 * @error: return location for a #GError, or %NULL
1869 * Finishes the operation started with camel_session_authenticate().
1871 * If an error occurred, or if authentication was aborted, the function
1872 * sets @error and returns %FALSE.
1874 * Returns: %TRUE on success, %FALSE on failure
1879 camel_session_authenticate_finish (CamelSession *session,
1880 GAsyncResult *result,
1883 CamelSessionClass *class;
1885 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1886 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
1888 class = CAMEL_SESSION_GET_CLASS (session);
1889 g_return_val_if_fail (class->authenticate_finish != NULL, FALSE);
1891 return class->authenticate_finish (session, result, error);
1895 * camel_session_forward_to:
1896 * @session: a #CamelSession
1897 * @folder: the #CamelFolder where @message is located
1898 * @message: the #CamelMimeMessage to forward
1899 * @address: the recipient's email address
1900 * @io_priority: the I/O priority for the request
1901 * @cancellable: optional #GCancellable object, or %NULL
1902 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1903 * @user_data: data to pass to the callback function
1905 * Asynchronously forwards @message in @folder to the email address(s)
1906 * given by @address.
1908 * When the operation is finished, @callback will be called. You can
1909 * then call camel_session_forward_to_finish() to get the result of the
1915 camel_session_forward_to (CamelSession *session,
1916 CamelFolder *folder,
1917 CamelMimeMessage *message,
1918 const gchar *address,
1920 GCancellable *cancellable,
1921 GAsyncReadyCallback callback,
1924 CamelSessionClass *class;
1926 g_return_if_fail (CAMEL_IS_SESSION (session));
1927 g_return_if_fail (CAMEL_IS_FOLDER (folder));
1928 g_return_if_fail (CAMEL_IS_MIME_MESSAGE (message));
1929 g_return_if_fail (address != NULL);
1931 class = CAMEL_SESSION_GET_CLASS (session);
1932 g_return_if_fail (class->forward_to != NULL);
1935 session, folder, message, address,
1936 io_priority, cancellable, callback, user_data);
1940 * camel_session_forward_to_finish:
1941 * @session: a #CamelSession
1942 * @result: a #GAsyncResult
1943 * @error: return location for a #GError, or %NULL
1945 * Finishes the operation started with camel_session_forward_to().
1947 * If an error occurred, the function sets @error and returns %FALSE.
1949 * Returns: %TRUE on success, %FALSE on failure
1954 camel_session_forward_to_finish (CamelSession *session,
1955 GAsyncResult *result,
1958 CamelSessionClass *class;
1960 g_return_val_if_fail (CAMEL_IS_SESSION (session), FALSE);
1961 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
1963 class = CAMEL_SESSION_GET_CLASS (session);
1964 g_return_val_if_fail (class->forward_to_finish != NULL, FALSE);
1966 return class->forward_to_finish (session, result, error);