1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-service.c : Abstract class for an email service */
7 * Bertrand Guiheneuf <bertrand@helixcode.com>
9 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
11 * This library is free software you can redistribute it and/or modify it
12 * under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation.
15 * This library is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, see <http://www.gnu.org/licenses/>.
33 #include <glib/gstdio.h>
34 #include <glib/gi18n-lib.h>
36 #include "camel-async-closure.h"
37 #include "camel-debug.h"
38 #include "camel-enumtypes.h"
39 #include "camel-local-settings.h"
40 #include "camel-network-service.h"
41 #include "camel-network-settings.h"
42 #include "camel-operation.h"
43 #include "camel-service.h"
44 #include "camel-session.h"
49 #define CAMEL_SERVICE_GET_PRIVATE(obj) \
50 (G_TYPE_INSTANCE_GET_PRIVATE \
51 ((obj), CAMEL_TYPE_SERVICE, CamelServicePrivate))
53 #define DISPATCH_DATA_KEY "camel-service-dispatch-data"
55 typedef struct _AsyncContext AsyncContext;
56 typedef struct _ConnectionOp ConnectionOp;
57 typedef struct _DispatchData DispatchData;
59 struct _CamelServicePrivate {
63 CamelSettings *settings;
64 GProxyResolver *proxy_resolver;
66 CamelProvider *provider;
70 gchar *user_cache_dir;
74 GMutex connection_lock;
75 ConnectionOp *connection_op;
76 CamelServiceConnectionStatus status;
78 /* Queues of GTasks, by source object. */
79 GHashTable *task_table;
80 GMutex task_table_lock;
82 gboolean network_service_inited;
85 struct _AsyncContext {
86 gchar *auth_mechanism;
90 /* The GQueue is only modified while CamelService's
91 * connection_lock is held, so it does not need its
93 struct _ConnectionOp {
94 volatile gint ref_count;
98 GCancellable *cancellable;
102 struct _DispatchData {
104 gboolean return_on_cancel;
105 GTaskThreadFunc task_func;
110 PROP_CONNECTION_STATUS,
120 /* Forward Declarations */
121 void camel_network_service_init (CamelNetworkService *service);
122 static void camel_service_initable_init (GInitableIface *iface);
123 static void service_task_dispatch (CamelService *service,
126 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
127 CamelService, camel_service, CAMEL_TYPE_OBJECT,
128 G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_service_initable_init))
131 async_context_free (AsyncContext *async_context)
133 g_free (async_context->auth_mechanism);
135 g_slice_free (AsyncContext, async_context);
138 static ConnectionOp *
139 connection_op_new (GTask *task,
140 GCancellable *cancellable)
144 op = g_slice_new0 (ConnectionOp);
146 g_mutex_init (&op->task_lock);
147 op->task = g_object_ref (task);
149 if (G_IS_CANCELLABLE (cancellable))
150 op->cancellable = g_object_ref (cancellable);
155 static ConnectionOp *
156 connection_op_ref (ConnectionOp *op)
158 g_return_val_if_fail (op != NULL, NULL);
159 g_return_val_if_fail (op->ref_count > 0, NULL);
161 g_atomic_int_inc (&op->ref_count);
167 connection_op_unref (ConnectionOp *op)
169 g_return_if_fail (op != NULL);
170 g_return_if_fail (op->ref_count > 0);
172 if (g_atomic_int_dec_and_test (&op->ref_count)) {
174 /* The pending queue should be empty. */
175 g_warn_if_fail (g_queue_is_empty (&op->pending));
177 g_mutex_clear (&op->task_lock);
179 if (op->task != NULL)
180 g_object_unref (op->task);
182 if (op->cancel_id > 0)
183 g_cancellable_disconnect (
184 op->cancellable, op->cancel_id);
186 if (op->cancellable != NULL)
187 g_object_unref (op->cancellable);
189 g_slice_free (ConnectionOp, op);
194 connection_op_complete (ConnectionOp *op,
197 g_mutex_lock (&op->task_lock);
199 if (op->task != NULL) {
201 g_task_return_error (op->task, g_error_copy (error));
203 g_task_return_boolean (op->task, TRUE);
206 g_clear_object (&op->task);
209 g_mutex_unlock (&op->task_lock);
213 connection_op_cancelled (GCancellable *cancellable,
216 /* A cancelled GTask will always propagate an error, so we
217 * don't need to explicitly set a G_IO_ERROR_CANCELLED here. */
219 g_mutex_lock (&op->task_lock);
221 g_clear_object (&op->task);
223 g_mutex_unlock (&op->task_lock);
227 connection_op_add_pending (ConnectionOp *op,
229 GCancellable *cancellable)
231 ConnectionOp *pending_op;
233 g_return_if_fail (op != NULL);
235 pending_op = connection_op_new (task, cancellable);
237 if (pending_op->cancellable != NULL)
238 pending_op->cancel_id = g_cancellable_connect (
239 pending_op->cancellable,
240 G_CALLBACK (connection_op_cancelled),
241 pending_op, (GDestroyNotify) NULL);
243 g_queue_push_tail (&op->pending, pending_op);
247 connection_op_complete_pending (ConnectionOp *op,
250 ConnectionOp *pending_op;
252 g_return_if_fail (op != NULL);
254 while (!g_queue_is_empty (&op->pending)) {
255 pending_op = g_queue_pop_head (&op->pending);
256 connection_op_complete (pending_op, error);
257 connection_op_unref (pending_op);
262 dispatch_data_free (DispatchData *dispatch_data)
264 g_weak_ref_set (&dispatch_data->service, NULL);
266 g_slice_free (DispatchData, dispatch_data);
270 task_queue_free (GQueue *task_queue)
272 g_queue_free_full (task_queue, g_object_unref);
276 service_task_table_push (CamelService *service,
280 gpointer source_object;
281 gboolean queue_was_empty;
283 g_return_if_fail (CAMEL_IS_SERVICE (service));
284 g_return_if_fail (G_IS_TASK (task));
286 source_object = g_task_get_source_object (task);
287 if (source_object == NULL)
288 source_object = service;
290 g_mutex_lock (&service->priv->task_table_lock);
292 task_queue = g_hash_table_lookup (
293 service->priv->task_table, source_object);
295 /* Create on demand. */
296 if (task_queue == NULL) {
297 task_queue = g_queue_new ();
298 g_hash_table_insert (
299 service->priv->task_table,
300 source_object, task_queue);
303 queue_was_empty = g_queue_is_empty (task_queue);
304 g_queue_push_tail (task_queue, g_object_ref (task));
306 g_mutex_unlock (&service->priv->task_table_lock);
309 service_task_dispatch (service, task);
313 service_task_table_done (CamelService *service,
317 gpointer source_object;
320 g_return_if_fail (CAMEL_IS_SERVICE (service));
321 g_return_if_fail (G_IS_TASK (task));
323 source_object = g_task_get_source_object (task);
324 if (source_object == NULL)
325 source_object = service;
327 g_mutex_lock (&service->priv->task_table_lock);
329 task_queue = g_hash_table_lookup (
330 service->priv->task_table, source_object);
332 if (task_queue != NULL) {
333 if (g_queue_remove (task_queue, task))
334 g_object_unref (task);
336 next = g_queue_peek_head (task_queue);
341 g_mutex_unlock (&service->priv->task_table_lock);
344 service_task_dispatch (service, next);
345 g_object_unref (next);
350 service_task_thread (GTask *task,
351 gpointer source_object,
353 GCancellable *cancellable)
355 CamelService *service;
358 data = g_object_get_data (G_OBJECT (task), DISPATCH_DATA_KEY);
359 g_return_if_fail (data != NULL);
361 service = g_weak_ref_get (&data->service);
362 g_return_if_fail (service != NULL);
364 data->task_func (task, source_object, task_data, cancellable);
366 service_task_table_done (service, task);
368 g_object_unref (service);
372 service_task_dispatch (CamelService *service,
377 data = g_object_get_data (G_OBJECT (task), DISPATCH_DATA_KEY);
378 g_return_if_fail (data != NULL);
380 /* Restore the task's previous "return-on-cancel" flag.
381 * This returns FALSE if the task is already cancelled,
382 * in which case we skip calling g_task_run_in_thread()
383 * so the task doesn't complete twice. */
384 if (g_task_set_return_on_cancel (task, data->return_on_cancel))
385 g_task_run_in_thread (task, service_task_thread);
387 service_task_table_done (service, task);
391 service_find_old_data_dir (CamelService *service)
393 CamelProvider *provider;
394 CamelSession *session;
397 gboolean allows_host;
398 gboolean allows_user;
402 const gchar *base_dir;
405 provider = camel_service_get_provider (service);
406 url = camel_service_new_camel_url (service);
408 allows_host = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_HOST);
409 allows_user = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_USER);
411 needs_host = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST);
412 needs_path = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH);
413 needs_user = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER);
415 /* This function reproduces the way service data directories used
416 * to be determined before we moved to just using the UID. If the
417 * old data directory exists, try renaming it to the new form.
419 * A virtual class method was used to determine the directory path,
420 * but no known CamelProviders ever overrode the default algorithm
421 * below. So this should work for everyone. */
423 path = g_string_new (provider->protocol);
426 g_string_append_c (path, '/');
427 if (url->user != NULL)
428 g_string_append (path, url->user);
430 g_string_append_c (path, '@');
431 if (url->host != NULL)
432 g_string_append (path, url->host);
434 g_string_append_c (path, ':');
435 g_string_append_printf (path, "%d", url->port);
437 } else if (!needs_user) {
438 g_string_append_c (path, '@');
441 } else if (allows_host) {
442 g_string_append_c (path, '/');
444 g_string_append_c (path, '@');
445 if (url->host != NULL)
446 g_string_append (path, url->host);
448 g_string_append_c (path, ':');
449 g_string_append_printf (path, "%d", url->port);
453 if (needs_path && url->path) {
454 if (*url->path != '/')
455 g_string_append_c (path, '/');
456 g_string_append (path, url->path);
459 session = camel_service_ref_session (service);
461 base_dir = camel_session_get_user_data_dir (session);
462 old_data_dir = g_build_filename (base_dir, path->str, NULL);
464 g_object_unref (session);
466 g_string_free (path, TRUE);
468 if (!g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
469 g_free (old_data_dir);
473 camel_url_free (url);
479 service_notify_connection_status_cb (gpointer user_data)
481 CamelService *service = CAMEL_SERVICE (user_data);
483 g_object_notify (G_OBJECT (service), "connection-status");
489 service_queue_notify_connection_status (CamelService *service)
491 CamelSession *session;
493 session = camel_service_ref_session (service);
495 /* most-likely exitting the application */
499 /* Prioritize ahead of GTK+ redraws. */
500 camel_session_idle_add (
501 session, G_PRIORITY_HIGH_IDLE,
502 service_notify_connection_status_cb,
503 g_object_ref (service),
504 (GDestroyNotify) g_object_unref);
506 g_object_unref (session);
510 service_shared_connect_cb (GObject *source_object,
511 GAsyncResult *result,
514 CamelService *service;
515 ConnectionOp *op = user_data;
517 GError *local_error = NULL;
519 service = CAMEL_SERVICE (source_object);
520 success = g_task_propagate_boolean (G_TASK (result), &local_error);
522 g_mutex_lock (&service->priv->connection_lock);
524 if (service->priv->connection_op == op) {
525 connection_op_unref (service->priv->connection_op);
526 service->priv->connection_op = NULL;
528 service->priv->status = CAMEL_SERVICE_CONNECTED;
530 service->priv->status = CAMEL_SERVICE_DISCONNECTED;
531 service_queue_notify_connection_status (service);
534 connection_op_complete (op, local_error);
535 connection_op_complete_pending (op, local_error);
537 g_mutex_unlock (&service->priv->connection_lock);
539 connection_op_unref (op);
540 g_clear_error (&local_error);
544 service_shared_connect_thread (GTask *task,
545 gpointer source_object,
547 GCancellable *cancellable)
549 CamelServiceClass *class;
551 GError *local_error = NULL;
553 /* Note we call the class method directly here. */
555 class = CAMEL_SERVICE_GET_CLASS (source_object);
556 g_return_if_fail (class->connect_sync != NULL);
558 success = class->connect_sync (
559 CAMEL_SERVICE (source_object),
560 cancellable, &local_error);
562 if (local_error != NULL) {
563 g_task_return_error (task, local_error);
565 g_task_return_boolean (task, success);
570 service_shared_connect (CamelService *service,
577 service, op->cancellable,
578 service_shared_connect_cb,
579 connection_op_ref (op));
581 g_task_set_source_tag (task, service_shared_connect);
582 g_task_set_priority (task, io_priority);
584 g_task_run_in_thread (task, service_shared_connect_thread);
586 g_object_unref (task);
590 service_shared_disconnect_cb (GObject *source_object,
591 GAsyncResult *result,
594 CamelService *service;
595 ConnectionOp *op = user_data;
597 GError *local_error = NULL;
599 service = CAMEL_SERVICE (source_object);
600 success = g_task_propagate_boolean (G_TASK (result), &local_error);
602 g_mutex_lock (&service->priv->connection_lock);
604 if (service->priv->connection_op == op) {
605 connection_op_unref (service->priv->connection_op);
606 service->priv->connection_op = NULL;
607 if (success || service->priv->status == CAMEL_SERVICE_CONNECTING)
608 service->priv->status = CAMEL_SERVICE_DISCONNECTED;
610 service->priv->status = CAMEL_SERVICE_CONNECTED;
611 service_queue_notify_connection_status (service);
614 connection_op_complete (op, local_error);
615 connection_op_complete_pending (op, local_error);
617 g_mutex_unlock (&service->priv->connection_lock);
619 connection_op_unref (op);
620 g_clear_error (&local_error);
624 service_shared_disconnect_thread (GTask *task,
625 gpointer source_object,
627 GCancellable *cancellable)
629 CamelServiceClass *class;
630 AsyncContext *async_context;
632 GError *local_error = NULL;
634 /* Note we call the class method directly here. */
636 async_context = (AsyncContext *) task_data;
638 class = CAMEL_SERVICE_GET_CLASS (source_object);
639 g_return_if_fail (class->disconnect_sync != NULL);
641 success = class->disconnect_sync (
642 CAMEL_SERVICE (source_object),
643 async_context->clean,
644 cancellable, &local_error);
646 if (local_error != NULL) {
647 g_task_return_error (task, local_error);
649 g_task_return_boolean (task, success);
654 service_shared_disconnect (CamelService *service,
660 AsyncContext *async_context;
662 async_context = g_slice_new0 (AsyncContext);
663 async_context->clean = clean;
666 service, op->cancellable,
667 service_shared_disconnect_cb,
668 connection_op_ref (op));
670 g_task_set_source_tag (task, service_shared_disconnect);
671 g_task_set_priority (task, io_priority);
673 g_task_set_task_data (
675 (GDestroyNotify) async_context_free);
677 g_task_run_in_thread (task, service_shared_disconnect_thread);
679 g_object_unref (task);
683 service_set_provider (CamelService *service,
684 CamelProvider *provider)
686 g_return_if_fail (provider != NULL);
687 g_return_if_fail (service->priv->provider == NULL);
689 service->priv->provider = provider;
693 service_set_session (CamelService *service,
694 CamelSession *session)
696 g_return_if_fail (CAMEL_IS_SESSION (session));
698 g_weak_ref_set (&service->priv->session, session);
702 service_set_uid (CamelService *service,
705 g_return_if_fail (uid != NULL);
706 g_return_if_fail (service->priv->uid == NULL);
708 service->priv->uid = g_strdup (uid);
712 service_set_property (GObject *object,
717 switch (property_id) {
718 case PROP_DISPLAY_NAME:
719 camel_service_set_display_name (
720 CAMEL_SERVICE (object),
721 g_value_get_string (value));
725 camel_service_set_password (
726 CAMEL_SERVICE (object),
727 g_value_get_string (value));
731 service_set_provider (
732 CAMEL_SERVICE (object),
733 g_value_get_pointer (value));
736 case PROP_PROXY_RESOLVER:
737 camel_service_set_proxy_resolver (
738 CAMEL_SERVICE (object),
739 g_value_get_object (value));
743 service_set_session (
744 CAMEL_SERVICE (object),
745 g_value_get_object (value));
749 camel_service_set_settings (
750 CAMEL_SERVICE (object),
751 g_value_get_object (value));
756 CAMEL_SERVICE (object),
757 g_value_get_string (value));
761 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
765 service_get_property (GObject *object,
770 switch (property_id) {
771 case PROP_CONNECTION_STATUS:
774 camel_service_get_connection_status (
775 CAMEL_SERVICE (object)));
778 case PROP_DISPLAY_NAME:
779 g_value_take_string (
781 camel_service_dup_display_name (
782 CAMEL_SERVICE (object)));
786 g_value_take_string (
788 camel_service_dup_password (
789 CAMEL_SERVICE (object)));
793 g_value_set_pointer (
795 camel_service_get_provider (
796 CAMEL_SERVICE (object)));
799 case PROP_PROXY_RESOLVER:
800 g_value_take_object (
802 camel_service_ref_proxy_resolver (
803 CAMEL_SERVICE (object)));
807 g_value_take_object (
809 camel_service_ref_session (
810 CAMEL_SERVICE (object)));
814 g_value_take_object (
816 camel_service_ref_settings (
817 CAMEL_SERVICE (object)));
823 camel_service_get_uid (
824 CAMEL_SERVICE (object)));
828 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
832 service_dispose (GObject *object)
834 CamelServicePrivate *priv;
836 priv = CAMEL_SERVICE_GET_PRIVATE (object);
838 if (priv->status == CAMEL_SERVICE_CONNECTED)
839 CAMEL_SERVICE_GET_CLASS (object)->disconnect_sync (
840 CAMEL_SERVICE (object), TRUE, NULL, NULL);
842 g_weak_ref_set (&priv->session, NULL);
844 g_clear_object (&priv->settings);
845 g_clear_object (&priv->proxy_resolver);
847 g_hash_table_remove_all (priv->task_table);
849 /* Chain up to parent's dispose() method. */
850 G_OBJECT_CLASS (camel_service_parent_class)->dispose (object);
854 service_finalize (GObject *object)
856 CamelServicePrivate *priv;
858 priv = CAMEL_SERVICE_GET_PRIVATE (object);
860 g_mutex_clear (&priv->property_lock);
862 g_free (priv->display_name);
863 g_free (priv->user_data_dir);
864 g_free (priv->user_cache_dir);
866 g_free (priv->password);
868 /* There should be no outstanding connection operations. */
869 g_warn_if_fail (priv->connection_op == NULL);
870 g_mutex_clear (&priv->connection_lock);
872 g_hash_table_destroy (priv->task_table);
873 g_mutex_clear (&priv->task_table_lock);
875 /* Chain up to parent's finalize() method. */
876 G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
880 service_constructed (GObject *object)
882 CamelService *service;
883 CamelSession *session;
884 const gchar *base_dir;
887 /* Chain up to parent's constructed() method. */
888 G_OBJECT_CLASS (camel_service_parent_class)->constructed (object);
890 service = CAMEL_SERVICE (object);
891 session = camel_service_ref_session (service);
893 uid = camel_service_get_uid (service);
895 base_dir = camel_session_get_user_data_dir (session);
896 service->priv->user_data_dir = g_build_filename (base_dir, uid, NULL);
898 base_dir = camel_session_get_user_cache_dir (session);
899 service->priv->user_cache_dir = g_build_filename (base_dir, uid, NULL);
901 g_object_unref (session);
903 /* The CamelNetworkService interface needs initialization. */
904 if (CAMEL_IS_NETWORK_SERVICE (service)) {
905 camel_network_service_init (CAMEL_NETWORK_SERVICE (service));
906 service->priv->network_service_inited = TRUE;
911 service_get_name (CamelService *service,
915 "%s does not implement CamelServiceClass::get_name()",
916 G_OBJECT_TYPE_NAME (service));
918 return g_strdup (G_OBJECT_TYPE_NAME (service));
922 service_connect_sync (CamelService *service,
923 GCancellable *cancellable,
926 /* Default behavior for local storage providers. */
931 service_disconnect_sync (CamelService *service,
933 GCancellable *cancellable,
936 /* Default behavior for local storage providers. */
941 service_query_auth_types_sync (CamelService *service,
942 GCancellable *cancellable,
949 service_initable_init (GInitable *initable,
950 GCancellable *cancellable,
953 /* Nothing to do here, but we may need add something in the future.
954 * For now this is a placeholder so subclasses can safely chain up. */
960 camel_service_class_init (CamelServiceClass *class)
962 GObjectClass *object_class;
964 g_type_class_add_private (class, sizeof (CamelServicePrivate));
966 object_class = G_OBJECT_CLASS (class);
967 object_class->set_property = service_set_property;
968 object_class->get_property = service_get_property;
969 object_class->dispose = service_dispose;
970 object_class->finalize = service_finalize;
971 object_class->constructed = service_constructed;
973 class->settings_type = CAMEL_TYPE_SETTINGS;
974 class->get_name = service_get_name;
975 class->connect_sync = service_connect_sync;
976 class->disconnect_sync = service_disconnect_sync;
977 class->query_auth_types_sync = service_query_auth_types_sync;
979 g_object_class_install_property (
981 PROP_CONNECTION_STATUS,
985 "The connection status for the service",
986 CAMEL_TYPE_SERVICE_CONNECTION_STATUS,
987 CAMEL_SERVICE_DISCONNECTED,
989 G_PARAM_STATIC_STRINGS));
991 g_object_class_install_property (
994 g_param_spec_string (
997 "The display name for the service",
1001 G_PARAM_STATIC_STRINGS));
1003 g_object_class_install_property (
1006 g_param_spec_string (
1009 "The password for the service",
1013 G_PARAM_STATIC_STRINGS));
1015 g_object_class_install_property (
1018 g_param_spec_pointer (
1021 "The CamelProvider for the service",
1023 G_PARAM_CONSTRUCT_ONLY |
1024 G_PARAM_STATIC_STRINGS));
1026 g_object_class_install_property (
1028 PROP_PROXY_RESOLVER,
1029 g_param_spec_object (
1032 "The proxy resolver for the service",
1033 G_TYPE_PROXY_RESOLVER,
1035 G_PARAM_STATIC_STRINGS));
1037 g_object_class_install_property (
1040 g_param_spec_object (
1043 "A CamelSession instance",
1046 G_PARAM_CONSTRUCT_ONLY |
1047 G_PARAM_STATIC_STRINGS));
1049 g_object_class_install_property (
1052 g_param_spec_object (
1055 "A CamelSettings instance",
1056 CAMEL_TYPE_SETTINGS,
1059 G_PARAM_STATIC_STRINGS));
1061 g_object_class_install_property (
1064 g_param_spec_string (
1067 "The unique identity of the service",
1070 G_PARAM_CONSTRUCT_ONLY |
1071 G_PARAM_STATIC_STRINGS));
1075 camel_service_initable_init (GInitableIface *iface)
1077 iface->init = service_initable_init;
1081 camel_service_init (CamelService *service)
1083 GHashTable *task_table;
1085 task_table = g_hash_table_new_full (
1086 (GHashFunc) g_direct_hash,
1087 (GEqualFunc) g_direct_equal,
1088 (GDestroyNotify) NULL,
1089 (GDestroyNotify) task_queue_free);
1091 service->priv = CAMEL_SERVICE_GET_PRIVATE (service);
1093 g_mutex_init (&service->priv->property_lock);
1094 g_mutex_init (&service->priv->connection_lock);
1095 service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1097 service->priv->proxy_resolver = g_proxy_resolver_get_default ();
1098 if (service->priv->proxy_resolver != NULL)
1099 g_object_ref (service->priv->proxy_resolver);
1101 service->priv->task_table = task_table;
1102 g_mutex_init (&service->priv->task_table_lock);
1105 G_DEFINE_QUARK (camel-service-error-quark, camel_service_error)
1108 * camel_service_migrate_files:
1109 * @service: a #CamelService
1111 * Performs any necessary file migrations for @service. This should be
1112 * called after installing or configuring the @service's #CamelSettings,
1113 * since it requires building a URL string for @service.
1118 camel_service_migrate_files (CamelService *service)
1120 const gchar *new_data_dir;
1121 gchar *old_data_dir;
1123 g_return_if_fail (CAMEL_IS_SERVICE (service));
1125 new_data_dir = camel_service_get_user_data_dir (service);
1126 old_data_dir = service_find_old_data_dir (service);
1128 /* If the old data directory name exists, try renaming
1129 * it to the new data directory. Failure is non-fatal. */
1130 if (old_data_dir != NULL) {
1131 if (g_rename (old_data_dir, new_data_dir) == -1) {
1133 "%s: Failed to rename '%s' to '%s': %s",
1134 G_STRFUNC, old_data_dir, new_data_dir, g_strerror (errno));
1136 g_free (old_data_dir);
1141 * camel_service_new_camel_url:
1142 * @service: a #CamelService
1144 * Returns a new #CamelURL representing @service.
1145 * Free the returned #CamelURL with camel_url_free().
1147 * Returns: a new #CamelURL
1152 camel_service_new_camel_url (CamelService *service)
1155 CamelProvider *provider;
1156 CamelSettings *settings;
1162 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1164 provider = camel_service_get_provider (service);
1165 g_return_val_if_fail (provider != NULL, NULL);
1167 settings = camel_service_ref_settings (service);
1169 /* Allocate as camel_url_new_with_base() does. */
1170 url = g_new0 (CamelURL, 1);
1172 if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
1173 CamelNetworkSettings *network_settings;
1175 network_settings = CAMEL_NETWORK_SETTINGS (settings);
1176 host = camel_network_settings_dup_host (network_settings);
1177 port = camel_network_settings_get_port (network_settings);
1178 user = camel_network_settings_dup_user (network_settings);
1181 if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
1182 CamelLocalSettings *local_settings;
1184 local_settings = CAMEL_LOCAL_SETTINGS (settings);
1185 path = camel_local_settings_dup_path (local_settings);
1188 camel_url_set_protocol (url, provider->protocol);
1189 camel_url_set_host (url, host);
1190 camel_url_set_port (url, port);
1191 camel_url_set_user (url, user);
1192 camel_url_set_path (url, path);
1198 g_object_unref (settings);
1204 * camel_service_get_connection_status:
1205 * @service: a #CamelService
1207 * Returns the connection status for @service.
1209 * Returns: the connection status
1213 CamelServiceConnectionStatus
1214 camel_service_get_connection_status (CamelService *service)
1216 g_return_val_if_fail (
1217 CAMEL_IS_SERVICE (service),
1218 CAMEL_SERVICE_DISCONNECTED);
1220 return service->priv->status;
1224 * camel_service_get_display_name:
1225 * @service: a #CamelService
1227 * Returns the display name for @service, or %NULL if @service has not
1228 * been given a display name. The display name is intended for use in
1229 * a user interface and should generally be given a user-defined name.
1231 * Compare this with camel_service_get_name(), which returns a built-in
1232 * description of the type of service (IMAP, SMTP, etc.).
1234 * Returns: the display name for @service, or %NULL
1239 camel_service_get_display_name (CamelService *service)
1241 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1243 return service->priv->display_name;
1247 * camel_service_dup_display_name:
1248 * @service: a #CamelService
1250 * Thread-safe variation of camel_service_get_display_name().
1251 * Use this function when accessing @service from multiple threads.
1253 * The returned string should be freed with g_free() when no longer needed.
1255 * Returns: a newly-allocated copy of #CamelService:display-name
1260 camel_service_dup_display_name (CamelService *service)
1262 const gchar *protected;
1265 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1267 g_mutex_lock (&service->priv->property_lock);
1269 protected = camel_service_get_display_name (service);
1270 duplicate = g_strdup (protected);
1272 g_mutex_unlock (&service->priv->property_lock);
1278 * camel_service_set_display_name:
1279 * @service: a #CamelService
1280 * @display_name: a valid UTF-8 string, or %NULL
1282 * Assigns a UTF-8 display name to @service. The display name is intended
1283 * for use in a user interface and should generally be given a user-defined
1286 * Compare this with camel_service_get_name(), which returns a built-in
1287 * description of the type of service (IMAP, SMTP, etc.).
1292 camel_service_set_display_name (CamelService *service,
1293 const gchar *display_name)
1295 g_return_if_fail (CAMEL_IS_SERVICE (service));
1297 if (display_name != NULL)
1298 g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
1300 g_mutex_lock (&service->priv->property_lock);
1302 if (g_strcmp0 (service->priv->display_name, display_name) == 0) {
1303 g_mutex_unlock (&service->priv->property_lock);
1307 g_free (service->priv->display_name);
1308 service->priv->display_name = g_strdup (display_name);
1310 g_mutex_unlock (&service->priv->property_lock);
1312 g_object_notify (G_OBJECT (service), "display-name");
1316 * camel_service_get_password:
1317 * @service: a #CamelService
1319 * Returns the password for @service. Some SASL mechanisms use this
1320 * when attempting to authenticate.
1322 * Returns: the password for @service
1327 camel_service_get_password (CamelService *service)
1329 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1331 return service->priv->password;
1335 * camel_service_dup_password:
1336 * @service: a #CamelService
1338 * Thread-safe variation of camel_service_get_password().
1339 * Use this function when accessing @service from multiple threads.
1341 * The returned string should be freed with g_free() when no longer needed.
1343 * Returns: a newly-allocated copy of #CamelService:password
1348 camel_service_dup_password (CamelService *service)
1350 const gchar *protected;
1353 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1355 g_mutex_lock (&service->priv->property_lock);
1357 protected = camel_service_get_password (service);
1358 duplicate = g_strdup (protected);
1360 g_mutex_unlock (&service->priv->property_lock);
1366 * camel_service_set_password:
1367 * @service: a #CamelService
1368 * @password: the password for @service
1370 * Sets the password for @service. Use this function to cache the password
1371 * in memory after obtaining it through camel_session_get_password(). Some
1372 * SASL mechanisms use this when attempting to authenticate.
1377 camel_service_set_password (CamelService *service,
1378 const gchar *password)
1380 g_return_if_fail (CAMEL_IS_SERVICE (service));
1382 g_mutex_lock (&service->priv->property_lock);
1384 if (g_strcmp0 (service->priv->password, password) == 0) {
1385 g_mutex_unlock (&service->priv->property_lock);
1389 g_free (service->priv->password);
1390 service->priv->password = g_strdup (password);
1392 g_mutex_unlock (&service->priv->property_lock);
1394 g_object_notify (G_OBJECT (service), "password");
1398 * camel_service_get_user_data_dir:
1399 * @service: a #CamelService
1401 * Returns the base directory under which to store user-specific data
1402 * for @service. The directory is formed by appending the directory
1403 * returned by camel_session_get_user_data_dir() with the service's
1404 * #CamelService:uid value.
1406 * Returns: the base directory for @service
1411 camel_service_get_user_data_dir (CamelService *service)
1413 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1415 return service->priv->user_data_dir;
1419 * camel_service_get_user_cache_dir:
1420 * @service: a #CamelService
1422 * Returns the base directory under which to store cache data
1423 * for @service. The directory is formed by appending the directory
1424 * returned by camel_session_get_user_cache_dir() with the service's
1425 * #CamelService:uid value.
1427 * Returns: the base cache directory for @service
1432 camel_service_get_user_cache_dir (CamelService *service)
1434 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1436 return service->priv->user_cache_dir;
1440 * camel_service_get_name:
1441 * @service: a #CamelService
1442 * @brief: whether or not to use a briefer form
1444 * This gets the name of the service in a "friendly" (suitable for
1445 * humans) form. If @brief is %TRUE, this should be a brief description
1446 * such as for use in the folder tree. If @brief is %FALSE, it should
1447 * be a more complete and mostly unambiguous description.
1449 * Returns: a description of the service which the caller must free
1452 camel_service_get_name (CamelService *service,
1455 CamelServiceClass *class;
1457 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1459 class = CAMEL_SERVICE_GET_CLASS (service);
1460 g_return_val_if_fail (class->get_name != NULL, NULL);
1462 return class->get_name (service, brief);
1466 * camel_service_get_provider:
1467 * @service: a #CamelService
1469 * Gets the #CamelProvider associated with the service.
1471 * Returns: the #CamelProvider
1474 camel_service_get_provider (CamelService *service)
1476 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1478 return service->priv->provider;
1482 * camel_service_ref_proxy_resolver:
1483 * @service: a #CamelService
1485 * Returns the #GProxyResolver for @service. If an application needs to
1486 * override this, it should do so prior to calling functions on @service
1487 * that may require a network connection.
1489 * The returned #GProxyResolver is referenced for thread-safety and must
1490 * be unreferenced with g_object_unref() when finished with it.
1492 * Returns: a #GProxyResolver, or %NULL
1497 camel_service_ref_proxy_resolver (CamelService *service)
1499 GProxyResolver *proxy_resolver = NULL;
1501 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1503 g_mutex_lock (&service->priv->property_lock);
1505 if (service->priv->proxy_resolver != NULL)
1506 proxy_resolver = g_object_ref (service->priv->proxy_resolver);
1508 g_mutex_unlock (&service->priv->property_lock);
1510 return proxy_resolver;
1514 * camel_service_set_proxy_resolver:
1515 * @service: a #CamelService
1516 * @proxy_resolver: a #GProxyResolver, or %NULL for the default
1518 * Sets the #GProxyResolver for @service. If an application needs to
1519 * override this, it should do so prior to calling functions on @service
1520 * that may require a network connection.
1525 camel_service_set_proxy_resolver (CamelService *service,
1526 GProxyResolver *proxy_resolver)
1528 gboolean notify = FALSE;
1530 if (proxy_resolver == NULL)
1531 proxy_resolver = g_proxy_resolver_get_default ();
1533 g_return_if_fail (CAMEL_IS_SERVICE (service));
1534 g_return_if_fail (G_IS_PROXY_RESOLVER (proxy_resolver));
1536 g_mutex_lock (&service->priv->property_lock);
1538 /* Emitting a "notify" signal unnecessarily might have
1539 * unwanted side effects like cancelling a SoupMessage.
1540 * Only emit if we now have a different GProxyResolver. */
1542 if (proxy_resolver != service->priv->proxy_resolver) {
1543 g_clear_object (&service->priv->proxy_resolver);
1544 service->priv->proxy_resolver = proxy_resolver;
1546 if (proxy_resolver != NULL)
1547 g_object_ref (proxy_resolver);
1552 g_mutex_unlock (&service->priv->property_lock);
1555 g_object_notify (G_OBJECT (service), "proxy-resolver");
1559 * camel_service_ref_session:
1560 * @service: a #CamelService
1562 * Returns the #CamelSession associated with the service.
1564 * The returned #CamelSession is referenced for thread-safety. Unreference
1565 * the #CamelSession with g_object_unref() when finished with it.
1567 * Returns: the #CamelSession
1572 camel_service_ref_session (CamelService *service)
1574 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1576 return g_weak_ref_get (&service->priv->session);
1580 * camel_service_ref_settings:
1581 * @service: a #CamelService
1583 * Returns the #CamelSettings instance associated with the service.
1585 * The returned #CamelSettings is referenced for thread-safety and must
1586 * be unreferenced with g_object_unref() when finished with it.
1588 * Returns: the #CamelSettings
1593 camel_service_ref_settings (CamelService *service)
1595 CamelSettings *settings;
1597 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1599 /* Every service should have a settings object. */
1600 g_return_val_if_fail (service->priv->settings != NULL, NULL);
1602 g_mutex_lock (&service->priv->property_lock);
1604 settings = g_object_ref (service->priv->settings);
1606 g_mutex_unlock (&service->priv->property_lock);
1612 * camel_service_set_settings:
1613 * @service: a #CamelService
1614 * @settings: an instance derviced from #CamelSettings, or %NULL
1616 * Associates a new #CamelSettings instance with the service.
1617 * The @settings instance must match the settings type defined in
1618 * #CamelServiceClass. If @settings is %NULL, a new #CamelSettings
1619 * instance of the appropriate type is created with all properties
1625 camel_service_set_settings (CamelService *service,
1626 CamelSettings *settings)
1628 CamelServiceClass *class;
1630 g_return_if_fail (CAMEL_IS_SERVICE (service));
1632 class = CAMEL_SERVICE_GET_CLASS (service);
1634 if (settings != NULL) {
1637 G_OBJECT_TYPE (settings),
1638 class->settings_type));
1639 g_object_ref (settings);
1644 class->settings_type,
1645 CAMEL_TYPE_SETTINGS));
1646 settings = g_object_new (class->settings_type, NULL);
1649 g_mutex_lock (&service->priv->property_lock);
1651 if (service->priv->settings != NULL)
1652 g_object_unref (service->priv->settings);
1654 service->priv->settings = settings; /* takes ownership */
1656 g_mutex_unlock (&service->priv->property_lock);
1658 /* If the service is a CamelNetworkService, it needs to
1659 * replace its GSocketConnectable for the new settings. */
1660 if (service->priv->network_service_inited)
1661 camel_network_service_set_connectable (
1662 CAMEL_NETWORK_SERVICE (service), NULL);
1664 g_object_notify (G_OBJECT (service), "settings");
1668 * camel_service_get_uid:
1669 * @service: a #CamelService
1671 * Gets the unique identifier string associated with the service.
1673 * Returns: the UID string
1678 camel_service_get_uid (CamelService *service)
1680 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1682 return service->priv->uid;
1686 * camel_service_queue_task:
1687 * @service: a #CamelService
1689 * @task_func: function to call when @task is dispatched
1691 * Adds @task to a queue of waiting tasks with the same source object.
1692 * Queued tasks execute one at a time in the order they were added. When
1693 * @task reaches the front of the queue, it will be dispatched by invoking
1694 * @task_func in a separate thread. If @task is cancelled while queued,
1695 * it will complete immediately with an appropriate error.
1697 * This is primarily intended for use by #CamelStore, #CamelTransport and
1698 * #CamelFolder to achieve ordered invocation of synchronous class methods.
1703 camel_service_queue_task (CamelService *service,
1705 GTaskThreadFunc task_func)
1707 DispatchData *dispatch_data;
1708 gboolean return_on_cancel;
1710 g_return_if_fail (CAMEL_IS_SERVICE (service));
1711 g_return_if_fail (G_IS_TASK (task));
1712 g_return_if_fail (task_func != NULL);
1714 return_on_cancel = g_task_get_return_on_cancel (task);
1716 dispatch_data = g_slice_new0 (DispatchData);
1717 g_weak_ref_set (&dispatch_data->service, service);
1718 dispatch_data->return_on_cancel = return_on_cancel;
1719 dispatch_data->task_func = task_func;
1721 /* Complete immediately if cancelled while queued. */
1722 g_task_set_return_on_cancel (task, TRUE);
1724 /* Stash this until it's time to dispatch the GTask. */
1725 g_object_set_data_full (
1726 G_OBJECT (task), DISPATCH_DATA_KEY,
1727 dispatch_data, (GDestroyNotify) dispatch_data_free);
1729 service_task_table_push (service, task);
1733 * camel_service_connect_sync:
1734 * @service: a #CamelService
1735 * @cancellable: optional #GCancellable object, or %NULL
1736 * @error: return location for a #GError, or %NULL
1738 * Connects @service to a remote server using the information in its
1739 * #CamelService:settings instance.
1741 * If a connect operation is already in progress when this function is
1742 * called, its results will be reflected in this connect operation.
1744 * Returns: %TRUE if the connection is made or %FALSE otherwise
1749 camel_service_connect_sync (CamelService *service,
1750 GCancellable *cancellable,
1753 CamelAsyncClosure *closure;
1754 GAsyncResult *result;
1757 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1759 closure = camel_async_closure_new ();
1761 camel_service_connect (
1762 service, G_PRIORITY_DEFAULT, cancellable,
1763 camel_async_closure_callback, closure);
1765 result = camel_async_closure_wait (closure);
1767 success = camel_service_connect_finish (service, result, error);
1769 camel_async_closure_free (closure);
1775 * camel_service_connect:
1776 * @service: a #CamelService
1777 * @io_priority: the I/O priority of the request
1778 * @cancellable: optional #GCancellable object, or %NULL
1779 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1780 * @user_data: data to pass to the callback function
1782 * Asynchronously connects @service to a remote server using the information
1783 * in its #CamelService:settings instance.
1785 * If a connect operation is already in progress when this function is
1786 * called, its results will be reflected in this connect operation.
1788 * If any disconnect operations are in progress when this function is
1789 * called, they will be cancelled.
1791 * When the operation is finished, @callback will be called. You can
1792 * then call camel_service_connect_finish() to get the result of the
1798 camel_service_connect (CamelService *service,
1800 GCancellable *cancellable,
1801 GAsyncReadyCallback callback,
1807 g_return_if_fail (CAMEL_IS_SERVICE (service));
1809 task = g_task_new (service, cancellable, callback, user_data);
1810 g_task_set_source_tag (task, camel_service_connect);
1811 g_task_set_priority (task, io_priority);
1813 g_mutex_lock (&service->priv->connection_lock);
1815 switch (service->priv->status) {
1817 /* If a connect operation is already in progress,
1818 * queue this operation so it completes at the same
1819 * time the first connect operation completes. */
1820 case CAMEL_SERVICE_CONNECTING:
1821 connection_op_add_pending (
1822 service->priv->connection_op,
1826 /* If we're already connected, just report success. */
1827 case CAMEL_SERVICE_CONNECTED:
1828 g_task_return_boolean (task, TRUE);
1831 /* If a disconnect operation is currently in progress,
1832 * cancel it and make room for the connect operation. */
1833 case CAMEL_SERVICE_DISCONNECTING:
1835 service->priv->connection_op != NULL);
1836 g_cancellable_cancel (
1837 service->priv->connection_op->cancellable);
1838 connection_op_unref (service->priv->connection_op);
1839 service->priv->connection_op = NULL;
1842 /* Start a new connect operation. Subsequent connect
1843 * operations are queued until this operation completes
1844 * and will share this operation's result. */
1845 case CAMEL_SERVICE_DISCONNECTED:
1847 service->priv->connection_op == NULL);
1849 op = connection_op_new (task, cancellable);
1850 service->priv->connection_op = op;
1852 service->priv->status = CAMEL_SERVICE_CONNECTING;
1853 service_queue_notify_connection_status (service);
1855 service_shared_connect (service, io_priority, op);
1859 g_warn_if_reached ();
1862 g_mutex_unlock (&service->priv->connection_lock);
1864 g_object_unref (task);
1868 * camel_service_connect_finish:
1869 * @service: a #CamelService
1870 * @result: a #GAsyncResult
1871 * @error: return location for a #GError, or %NULL
1873 * Finishes the operation started with camel_service_connect().
1875 * Returns: %TRUE if the connection was made or %FALSE otherwise
1880 camel_service_connect_finish (CamelService *service,
1881 GAsyncResult *result,
1884 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1885 g_return_val_if_fail (g_task_is_valid (result, service), FALSE);
1887 g_return_val_if_fail (
1888 g_async_result_is_tagged (
1889 result, camel_service_connect), FALSE);
1891 return g_task_propagate_boolean (G_TASK (result), error);
1895 * camel_service_disconnect_sync:
1896 * @service: a #CamelService
1897 * @clean: whether or not to try to disconnect cleanly
1898 * @cancellable: optional #GCancellable object, or %NULL
1899 * @error: return location for a #GError, or %NULL
1901 * Disconnect from the service. If @clean is %FALSE, it should not
1902 * try to do any synchronizing or other cleanup of the connection.
1904 * If a disconnect operation is already in progress when this function is
1905 * called, its results will be reflected in this disconnect operation.
1907 * If any connect operations are in progress when this function is called,
1908 * they will be cancelled.
1910 * Returns: %TRUE if the connection was severed or %FALSE otherwise
1915 camel_service_disconnect_sync (CamelService *service,
1917 GCancellable *cancellable,
1920 CamelAsyncClosure *closure;
1921 GAsyncResult *result;
1924 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1926 closure = camel_async_closure_new ();
1928 camel_service_disconnect (
1929 service, clean, G_PRIORITY_DEFAULT, cancellable,
1930 camel_async_closure_callback, closure);
1932 result = camel_async_closure_wait (closure);
1934 success = camel_service_disconnect_finish (service, result, error);
1936 camel_async_closure_free (closure);
1942 * camel_service_disconnect:
1943 * @service: a #CamelService
1944 * @clean: whether or not to try to disconnect cleanly
1945 * @io_priority: the I/O priority of the request
1946 * @cancellable: optional #GCancellable object, or %NULL
1947 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1948 * @user_data: data to pass to the callback function
1950 * If a disconnect operation is already in progress when this function is
1951 * called, its results will be reflected in this disconnect operation.
1953 * If any connect operations are in progress when this function is called,
1954 * they will be cancelled.
1956 * When the operation is finished, @callback will be called. You can
1957 * then call camel_service_disconnect_finish() to get the result of the
1963 camel_service_disconnect (CamelService *service,
1966 GCancellable *cancellable,
1967 GAsyncReadyCallback callback,
1973 g_return_if_fail (CAMEL_IS_SERVICE (service));
1975 task = g_task_new (service, cancellable, callback, user_data);
1976 g_task_set_source_tag (task, camel_service_disconnect);
1977 g_task_set_priority (task, io_priority);
1979 g_mutex_lock (&service->priv->connection_lock);
1981 switch (service->priv->status) {
1983 /* If a connect operation is currently in progress,
1984 * cancel it and make room for the disconnect operation. */
1985 case CAMEL_SERVICE_CONNECTING:
1987 service->priv->connection_op != NULL);
1988 g_cancellable_cancel (
1989 service->priv->connection_op->cancellable);
1990 connection_op_unref (service->priv->connection_op);
1991 service->priv->connection_op = NULL;
1994 /* Start a new disconnect operation. Subsequent disconnect
1995 * operations are queued until this operation completes and
1996 * will share this operation's result. */
1997 case CAMEL_SERVICE_CONNECTED:
1999 service->priv->connection_op == NULL);
2001 op = connection_op_new (task, cancellable);
2002 service->priv->connection_op = op;
2004 /* Do not change the status if CONNECTING, in case a
2005 * provider calls disconnect() during the connection
2006 * phase, which confuses the other logic here and
2007 * effectively makes the service's connection state
2008 * CONNECTED instead of DISCONNECTED at the end. */
2009 if (service->priv->status != CAMEL_SERVICE_CONNECTING) {
2010 service->priv->status = CAMEL_SERVICE_DISCONNECTING;
2011 service_queue_notify_connection_status (service);
2014 service_shared_disconnect (
2015 service, clean, io_priority, op);
2018 /* If a disconnect operation is already in progress,
2019 * queue this operation so it completes at the same
2020 * time the first disconnect operation completes. */
2021 case CAMEL_SERVICE_DISCONNECTING:
2022 connection_op_add_pending (
2023 service->priv->connection_op,
2027 /* If we're already disconnected, just report success. */
2028 case CAMEL_SERVICE_DISCONNECTED:
2029 g_task_return_boolean (task, TRUE);
2033 g_warn_if_reached ();
2036 g_mutex_unlock (&service->priv->connection_lock);
2038 g_object_unref (task);
2042 * camel_service_disconnect_finish:
2043 * @service: a #CamelService
2044 * @result: a #GAsyncResult
2045 * @error: return location for a #GError, or %NULL
2047 * Finishes the operation started with camel_service_disconnect().
2049 * Returns: %TRUE if the connection was severed or %FALSE otherwise
2054 camel_service_disconnect_finish (CamelService *service,
2055 GAsyncResult *result,
2058 g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
2059 g_return_val_if_fail (g_task_is_valid (result, service), FALSE);
2061 g_return_val_if_fail (
2062 g_async_result_is_tagged (
2063 result, camel_service_disconnect), FALSE);
2065 return g_task_propagate_boolean (G_TASK (result), error);
2069 * camel_service_authenticate_sync:
2070 * @service: a #CamelService
2071 * @mechanism: a SASL mechanism name, or %NULL
2072 * @cancellable: optional #GCancellable object, or %NULL
2073 * @error: return location for a #GError, or %NULL
2075 * Attempts to authenticate @service using @mechanism and, if necessary,
2076 * @service's #CamelService:password property. The function makes only
2077 * ONE attempt at authentication and does not loop.
2079 * If the authentication attempt completed and the server accepted the
2080 * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2082 * If the authentication attempt completed but the server rejected the
2083 * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2085 * If the authentication attempt failed to complete due to a network
2086 * communication issue or some other mishap, the function sets @error
2087 * and returns #CAMEL_AUTHENTICATION_ERROR.
2089 * Generally this function should only be called from a #CamelSession
2090 * subclass in order to implement its own authentication loop.
2092 * Returns: the authentication result
2096 CamelAuthenticationResult
2097 camel_service_authenticate_sync (CamelService *service,
2098 const gchar *mechanism,
2099 GCancellable *cancellable,
2102 CamelServiceClass *class;
2103 CamelAuthenticationResult result;
2105 g_return_val_if_fail (
2106 CAMEL_IS_SERVICE (service),
2107 CAMEL_AUTHENTICATION_ERROR);
2109 class = CAMEL_SERVICE_GET_CLASS (service);
2110 g_return_val_if_fail (
2111 class->authenticate_sync != NULL,
2112 CAMEL_AUTHENTICATION_ERROR);
2114 result = class->authenticate_sync (
2115 service, mechanism, cancellable, error);
2116 CAMEL_CHECK_GERROR (
2117 service, authenticate_sync,
2118 result != CAMEL_AUTHENTICATION_ERROR, error);
2123 /* Helper for camel_service_authenticate() */
2125 service_authenticate_thread (GTask *task,
2126 gpointer source_object,
2128 GCancellable *cancellable)
2130 CamelAuthenticationResult auth_result;
2131 AsyncContext *async_context;
2132 GError *local_error = NULL;
2134 async_context = (AsyncContext *) task_data;
2136 auth_result = camel_service_authenticate_sync (
2137 CAMEL_SERVICE (source_object),
2138 async_context->auth_mechanism,
2139 cancellable, &local_error);
2141 if (local_error != NULL) {
2142 g_task_return_error (task, local_error);
2144 g_task_return_int (task, auth_result);
2149 * camel_service_authenticate:
2150 * @service: a #CamelService
2151 * @mechanism: a SASL mechanism name, or %NULL
2152 * @io_priority: the I/O priority of the request
2153 * @cancellable: optional #GCancellable object, or %NULL
2154 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2155 * @user_data: data to pass to the callback function
2157 * Asynchronously attempts to authenticate @service using @mechanism and,
2158 * if necessary, @service's #CamelService:password property. The function
2159 * makes only ONE attempt at authentication and does not loop.
2161 * Generally this function should only be called from a #CamelSession
2162 * subclass in order to implement its own authentication loop.
2164 * When the operation is finished, @callback will be called. You can
2165 * then call camel_service_authenticate_finish() to get the result of
2171 camel_service_authenticate (CamelService *service,
2172 const gchar *mechanism,
2174 GCancellable *cancellable,
2175 GAsyncReadyCallback callback,
2179 AsyncContext *async_context;
2181 g_return_if_fail (CAMEL_IS_SERVICE (service));
2183 async_context = g_slice_new0 (AsyncContext);
2184 async_context->auth_mechanism = g_strdup (mechanism);
2186 task = g_task_new (service, cancellable, callback, user_data);
2187 g_task_set_source_tag (task, camel_service_authenticate);
2188 g_task_set_priority (task, io_priority);
2190 g_task_set_task_data (
2191 task, async_context,
2192 (GDestroyNotify) async_context_free);
2194 g_task_run_in_thread (task, service_authenticate_thread);
2196 g_object_unref (task);
2200 * camel_service_authenticate_finish:
2201 * @service: a #CamelService
2202 * @result: a #GAsyncResult
2203 * @error: return location for a #GError, or %NULL
2205 * Finishes the operation started with camel_service_authenticate().
2207 * If the authentication attempt completed and the server accepted the
2208 * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2210 * If the authentication attempt completed but the server rejected the
2211 * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2213 * If the authentication attempt failed to complete due to a network
2214 * communication issue or some other mishap, the function sets @error
2215 * and returns #CAMEL_AUTHENTICATION_ERROR.
2217 * Returns: the authentication result
2221 CamelAuthenticationResult
2222 camel_service_authenticate_finish (CamelService *service,
2223 GAsyncResult *result,
2226 CamelAuthenticationResult auth_result;
2228 g_return_val_if_fail (
2229 CAMEL_IS_SERVICE (service),
2230 CAMEL_AUTHENTICATION_ERROR);
2231 g_return_val_if_fail (
2232 g_task_is_valid (result, service),
2233 CAMEL_AUTHENTICATION_ERROR);
2235 g_return_val_if_fail (
2236 g_async_result_is_tagged (
2237 result, camel_service_authenticate),
2238 CAMEL_AUTHENTICATION_ERROR);
2240 /* XXX A little hackish, but best way to return enum values
2241 * from GTask in GLib 2.36. Recommended by Dan Winship. */
2243 auth_result = g_task_propagate_int (G_TASK (result), error);
2245 if (auth_result == (CamelAuthenticationResult) -1)
2246 return CAMEL_AUTHENTICATION_ERROR;
2252 * camel_service_query_auth_types_sync:
2253 * @service: a #CamelService
2254 * @cancellable: optional #GCancellable object, or %NULL
2255 * @error: return location for a #GError, or %NULL
2257 * Obtains a list of authentication types supported by @service.
2258 * Free the returned list with g_list_free().
2260 * Returns: a list of #CamelServiceAuthType structs
2263 camel_service_query_auth_types_sync (CamelService *service,
2264 GCancellable *cancellable,
2267 CamelServiceClass *class;
2269 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2271 class = CAMEL_SERVICE_GET_CLASS (service);
2272 g_return_val_if_fail (class->query_auth_types_sync != NULL, NULL);
2274 return class->query_auth_types_sync (service, cancellable, error);
2277 /* Helper for camel_service_query_auth_types() */
2279 service_query_auth_types_thread (GTask *task,
2280 gpointer source_object,
2282 GCancellable *cancellable)
2285 GError *local_error = NULL;
2287 auth_types = camel_service_query_auth_types_sync (
2288 CAMEL_SERVICE (source_object),
2289 cancellable, &local_error);
2291 if (local_error != NULL) {
2292 g_warn_if_fail (auth_types == NULL);
2293 g_task_return_error (task, local_error);
2295 g_task_return_pointer (
2297 (GDestroyNotify) g_list_free);
2302 * camel_service_query_auth_types:
2303 * @service: a #CamelService
2304 * @io_priority: the I/O priority of the request
2305 * @cancellable: optional #GCancellable object, or %NULL
2306 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2307 * @user_data: data to pass to the callback function
2309 * Asynchronously obtains a list of authentication types supported by
2312 * When the operation is finished, @callback will be called. You can
2313 * then call camel_service_query_auth_types_finish() to get the result
2319 camel_service_query_auth_types (CamelService *service,
2321 GCancellable *cancellable,
2322 GAsyncReadyCallback callback,
2327 g_return_if_fail (CAMEL_IS_SERVICE (service));
2329 task = g_task_new (service, cancellable, callback, user_data);
2330 g_task_set_source_tag (task, camel_service_query_auth_types);
2331 g_task_set_priority (task, io_priority);
2333 g_task_run_in_thread (task, service_query_auth_types_thread);
2335 g_object_unref (task);
2339 * camel_service_query_auth_types_finish:
2340 * @service: a #CamelService
2341 * @result: a #GAsyncResult
2342 * @error: return location for a #GError, or %NULL
2344 * Finishes the operation started with camel_service_query_auth_types().
2345 * Free the returned list with g_list_free().
2347 * Returns: a list of #CamelServiceAuthType structs
2352 camel_service_query_auth_types_finish (CamelService *service,
2353 GAsyncResult *result,
2356 g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2357 g_return_val_if_fail (g_task_is_valid (result, service), NULL);
2359 g_return_val_if_fail (
2360 g_async_result_is_tagged (
2361 result, camel_service_query_auth_types), NULL);
2363 return g_task_propagate_pointer (G_TASK (result), error);