Camel: New authentication API.
[platform/upstream/evolution-data-server.git] / camel / camel-service.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* camel-service.c : Abstract class for an email service */
3
4 /*
5  *
6  * Author :
7  *  Bertrand Guiheneuf <bertrand@helixcode.com>
8  *
9  * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of version 2 of the GNU Lesser General Public
13  * License as published by the Free Software Foundation.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
23  * USA
24  */
25
26 #ifdef HAVE_CONFIG_H
27 #include <config.h>
28 #endif
29
30 #include <ctype.h>
31 #include <errno.h>
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include <glib/gstdio.h>
36 #include <glib/gi18n-lib.h>
37
38 #include "camel-debug.h"
39 #include "camel-operation.h"
40 #include "camel-service.h"
41 #include "camel-session.h"
42
43 #define d(x)
44 #define w(x)
45
46 #define CAMEL_SERVICE_GET_PRIVATE(obj) \
47         (G_TYPE_INSTANCE_GET_PRIVATE \
48         ((obj), CAMEL_TYPE_SERVICE, CamelServicePrivate))
49
50 typedef struct _AsyncContext AsyncContext;
51
52 struct _CamelServicePrivate {
53         gpointer session;  /* weak pointer */
54
55         CamelSettings *settings;
56         CamelProvider *provider;
57         CamelURL *url;
58
59         gchar *display_name;
60         gchar *user_data_dir;
61         gchar *user_cache_dir;
62         gchar *uid;
63         gchar *password;
64
65         GCancellable *connect_op;
66         CamelServiceConnectionStatus status;
67
68         GStaticRecMutex connect_lock;   /* for locking connection operations */
69         GStaticMutex connect_op_lock;   /* for locking the connection_op */
70 };
71
72 struct _AsyncContext {
73         GList *auth_types;
74         gchar *auth_mechanism;
75         CamelAuthenticationResult auth_result;
76 };
77
78 enum {
79         PROP_0,
80         PROP_DISPLAY_NAME,
81         PROP_PASSWORD,
82         PROP_PROVIDER,
83         PROP_SESSION,
84         PROP_SETTINGS,
85         PROP_UID,
86         PROP_URL
87 };
88
89 /* Forward Declarations */
90 static void camel_service_initable_init (GInitableIface *interface);
91
92 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
93         CamelService, camel_service, CAMEL_TYPE_OBJECT,
94         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_service_initable_init))
95
96 static void
97 async_context_free (AsyncContext *async_context)
98 {
99         g_list_free (async_context->auth_types);
100
101         g_free (async_context->auth_mechanism);
102
103         g_slice_free (AsyncContext, async_context);
104 }
105
106 static gchar *
107 service_find_old_data_dir (CamelService *service)
108 {
109         CamelProvider *provider;
110         CamelSession *session;
111         CamelURL *url;
112         GString *path;
113         gboolean allows_host;
114         gboolean allows_user;
115         gboolean needs_host;
116         gboolean needs_path;
117         gboolean needs_user;
118         const gchar *base_dir;
119         gchar *old_data_dir;
120
121         provider = camel_service_get_provider (service);
122         session = camel_service_get_session (service);
123         url = camel_service_get_camel_url (service);
124
125         allows_host = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_HOST);
126         allows_user = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_USER);
127
128         needs_host = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST);
129         needs_path = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH);
130         needs_user = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER);
131
132         /* This function reproduces the way service data directories used
133          * to be determined before we moved to just using the UID.  If the
134          * old data directory exists, try renaming it to the new form.
135          *
136          * A virtual class method was used to determine the directory path,
137          * but no known CamelProviders ever overrode the default algorithm
138          * below.  So this should work for everyone. */
139
140         path = g_string_new (provider->protocol);
141
142         if (allows_user) {
143                 g_string_append_c (path, '/');
144                 if (url->user != NULL)
145                         g_string_append (path, url->user);
146                 if (allows_host) {
147                         g_string_append_c (path, '@');
148                         if (url->host != NULL)
149                                 g_string_append (path, url->host);
150                         if (url->port) {
151                                 g_string_append_c (path, ':');
152                                 g_string_append_printf (path, "%d", url->port);
153                         }
154                 } else if (!needs_user) {
155                         g_string_append_c (path, '@');
156                 }
157
158         } else if (allows_host) {
159                 g_string_append_c (path, '/');
160                 if (!needs_host)
161                         g_string_append_c (path, '@');
162                 if (url->host != NULL)
163                         g_string_append (path, url->host);
164                 if (url->port) {
165                         g_string_append_c (path, ':');
166                         g_string_append_printf (path, "%d", url->port);
167                 }
168         }
169
170         if (needs_path) {
171                 if (*url->path != '/')
172                         g_string_append_c (path, '/');
173                 g_string_append (path, url->path);
174         }
175
176         base_dir = camel_session_get_user_data_dir (session);
177         old_data_dir = g_build_filename (base_dir, path->str, NULL);
178
179         g_string_free (path, TRUE);
180
181         if (!g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
182                 g_free (old_data_dir);
183                 old_data_dir = NULL;
184         }
185
186         return old_data_dir;
187 }
188
189 static void
190 service_set_provider (CamelService *service,
191                       CamelProvider *provider)
192 {
193         g_return_if_fail (provider != NULL);
194         g_return_if_fail (service->priv->provider == NULL);
195
196         service->priv->provider = provider;
197 }
198
199 static void
200 service_set_session (CamelService *service,
201                      CamelSession *session)
202 {
203         g_return_if_fail (CAMEL_IS_SESSION (session));
204         g_return_if_fail (service->priv->session == NULL);
205
206         service->priv->session = session;
207
208         g_object_add_weak_pointer (
209                 G_OBJECT (session), &service->priv->session);
210 }
211
212 static void
213 service_set_uid (CamelService *service,
214                  const gchar *uid)
215 {
216         g_return_if_fail (uid != NULL);
217         g_return_if_fail (service->priv->uid == NULL);
218
219         service->priv->uid = g_strdup (uid);
220 }
221
222 static void
223 service_set_url (CamelService *service,
224                  CamelURL *url)
225 {
226         g_return_if_fail (url != NULL);
227         g_return_if_fail (service->priv->url == NULL);
228
229         service->priv->url = camel_url_copy (url);
230 }
231
232 static void
233 service_set_property (GObject *object,
234                       guint property_id,
235                       const GValue *value,
236                       GParamSpec *pspec)
237 {
238         switch (property_id) {
239                 case PROP_DISPLAY_NAME:
240                         camel_service_set_display_name (
241                                 CAMEL_SERVICE (object),
242                                 g_value_get_string (value));
243                         return;
244
245                 case PROP_PASSWORD:
246                         camel_service_set_password (
247                                 CAMEL_SERVICE (object),
248                                 g_value_get_string (value));
249                         return;
250
251                 case PROP_PROVIDER:
252                         service_set_provider (
253                                 CAMEL_SERVICE (object),
254                                 g_value_get_pointer (value));
255                         return;
256
257                 case PROP_SESSION:
258                         service_set_session (
259                                 CAMEL_SERVICE (object),
260                                 g_value_get_object (value));
261                         return;
262
263                 case PROP_SETTINGS:
264                         camel_service_set_settings (
265                                 CAMEL_SERVICE (object),
266                                 g_value_get_object (value));
267                         return;
268
269                 case PROP_UID:
270                         service_set_uid (
271                                 CAMEL_SERVICE (object),
272                                 g_value_get_string (value));
273                         return;
274
275                 case PROP_URL:
276                         service_set_url (
277                                 CAMEL_SERVICE (object),
278                                 g_value_get_boxed (value));
279                         return;
280         }
281
282         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
283 }
284
285 static void
286 service_get_property (GObject *object,
287                       guint property_id,
288                       GValue *value,
289                       GParamSpec *pspec)
290 {
291         switch (property_id) {
292                 case PROP_DISPLAY_NAME:
293                         g_value_set_string (
294                                 value, camel_service_get_display_name (
295                                 CAMEL_SERVICE (object)));
296                         return;
297
298                 case PROP_PASSWORD:
299                         g_value_set_string (
300                                 value, camel_service_get_password (
301                                 CAMEL_SERVICE (object)));
302                         return;
303
304                 case PROP_PROVIDER:
305                         g_value_set_pointer (
306                                 value, camel_service_get_provider (
307                                 CAMEL_SERVICE (object)));
308                         return;
309
310                 case PROP_SESSION:
311                         g_value_set_object (
312                                 value, camel_service_get_session (
313                                 CAMEL_SERVICE (object)));
314                         return;
315
316                 case PROP_SETTINGS:
317                         g_value_set_object (
318                                 value, camel_service_get_settings (
319                                 CAMEL_SERVICE (object)));
320                         return;
321
322                 case PROP_UID:
323                         g_value_set_string (
324                                 value, camel_service_get_uid (
325                                 CAMEL_SERVICE (object)));
326                         return;
327
328                 case PROP_URL:
329                         g_value_set_boxed (
330                                 value, camel_service_get_url (
331                                 CAMEL_SERVICE (object)));
332                         return;
333         }
334
335         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
336 }
337
338 static void
339 service_dispose (GObject *object)
340 {
341         CamelServicePrivate *priv;
342
343         priv = CAMEL_SERVICE_GET_PRIVATE (object);
344
345         if (priv->session != NULL) {
346                 g_object_remove_weak_pointer (
347                         G_OBJECT (priv->session), &priv->session);
348                 priv->session = NULL;
349         }
350
351         if (priv->settings != NULL) {
352                 g_object_unref (priv->settings);
353                 priv->settings = NULL;
354         }
355
356         /* Chain up to parent's dispose() method. */
357         G_OBJECT_CLASS (camel_service_parent_class)->dispose (object);
358 }
359
360 static void
361 service_finalize (GObject *object)
362 {
363         CamelServicePrivate *priv;
364
365         priv = CAMEL_SERVICE_GET_PRIVATE (object);
366
367         if (priv->status == CAMEL_SERVICE_CONNECTED)
368                 CAMEL_SERVICE_GET_CLASS (object)->disconnect_sync (
369                         CAMEL_SERVICE (object), TRUE, NULL, NULL);
370
371         if (priv->url != NULL)
372                 camel_url_free (priv->url);
373
374         g_free (priv->display_name);
375         g_free (priv->user_data_dir);
376         g_free (priv->user_cache_dir);
377         g_free (priv->uid);
378         g_free (priv->password);
379
380         g_static_rec_mutex_free (&priv->connect_lock);
381         g_static_mutex_free (&priv->connect_op_lock);
382
383         /* Chain up to parent's finalize() method. */
384         G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
385 }
386
387 static void
388 service_constructed (GObject *object)
389 {
390         CamelService *service;
391         CamelSession *session;
392         const gchar *base_dir;
393         const gchar *uid;
394
395         /* Chain up to parent's constructed() method. */
396         G_OBJECT_CLASS (camel_service_parent_class)->constructed (object);
397
398         service = CAMEL_SERVICE (object);
399         session = camel_service_get_session (service);
400
401         uid = camel_service_get_uid (service);
402
403         base_dir = camel_session_get_user_data_dir (session);
404         service->priv->user_data_dir = g_build_filename (base_dir, uid, NULL);
405
406         base_dir = camel_session_get_user_cache_dir (session);
407         service->priv->user_cache_dir = g_build_filename (base_dir, uid, NULL);
408 }
409
410 static gchar *
411 service_get_name (CamelService *service,
412                   gboolean brief)
413 {
414         g_warning (
415                 "%s does not implement CamelServiceClass::get_name()",
416                 G_OBJECT_TYPE_NAME (service));
417
418         return g_strdup (G_OBJECT_TYPE_NAME (service));
419 }
420
421 static void
422 service_cancel_connect (CamelService *service)
423 {
424         g_cancellable_cancel (service->priv->connect_op);
425 }
426
427 static gboolean
428 service_connect_sync (CamelService *service,
429                       GCancellable *cancellable,
430                       GError **error)
431 {
432         /* Things like the CamelMboxStore can validly
433          * not define a connect function. */
434          return TRUE;
435 }
436
437 static gboolean
438 service_disconnect_sync (CamelService *service,
439                          gboolean clean,
440                          GCancellable *cancellable,
441                          GError **error)
442 {
443         /* We let people get away with not having a disconnect
444          * function -- CamelMboxStore, for example. */
445         return TRUE;
446 }
447
448 static GList *
449 service_query_auth_types_sync (CamelService *service,
450                                GCancellable *cancellable,
451                                GError **error)
452 {
453         return NULL;
454 }
455
456 static void
457 service_authenticate_thread (GSimpleAsyncResult *simple,
458                              GObject *object,
459                              GCancellable *cancellable)
460 {
461         AsyncContext *async_context;
462         GError *error = NULL;
463
464         async_context = g_simple_async_result_get_op_res_gpointer (simple);
465
466         async_context->auth_result = camel_service_authenticate_sync (
467                 CAMEL_SERVICE (object), async_context->auth_mechanism,
468                 cancellable, &error);
469
470         if (error != NULL)
471                 g_simple_async_result_take_error (simple, error);
472 }
473
474 static void
475 service_authenticate (CamelService *service,
476                       const gchar *mechanism,
477                       gint io_priority,
478                       GCancellable *cancellable,
479                       GAsyncReadyCallback callback,
480                       gpointer user_data)
481 {
482         GSimpleAsyncResult *simple;
483         AsyncContext *async_context;
484
485         async_context = g_slice_new0 (AsyncContext);
486         async_context->auth_mechanism = g_strdup (mechanism);
487
488         simple = g_simple_async_result_new (
489                 G_OBJECT (service), callback, user_data, service_authenticate);
490
491         g_simple_async_result_set_op_res_gpointer (
492                 simple, async_context, (GDestroyNotify) async_context_free);
493
494         g_simple_async_result_run_in_thread (
495                 simple, service_authenticate_thread, io_priority, cancellable);
496
497         g_object_unref (simple);
498 }
499
500 static CamelAuthenticationResult
501 service_authenticate_finish (CamelService *service,
502                              GAsyncResult *result,
503                              GError **error)
504 {
505         GSimpleAsyncResult *simple;
506         AsyncContext *async_context;
507
508         g_return_val_if_fail (
509                 g_simple_async_result_is_valid (
510                 result, G_OBJECT (service), service_authenticate),
511                 CAMEL_AUTHENTICATION_REJECTED);
512
513         simple = G_SIMPLE_ASYNC_RESULT (result);
514         async_context = g_simple_async_result_get_op_res_gpointer (simple);
515
516         if (g_simple_async_result_propagate_error (simple, error))
517                 return CAMEL_AUTHENTICATION_ERROR;
518
519         return async_context->auth_result;
520 }
521
522 static void
523 service_query_auth_types_thread (GSimpleAsyncResult *simple,
524                                  GObject *object,
525                                  GCancellable *cancellable)
526 {
527         AsyncContext *async_context;
528         GError *error = NULL;
529
530         async_context = g_simple_async_result_get_op_res_gpointer (simple);
531
532         async_context->auth_types = camel_service_query_auth_types_sync (
533                 CAMEL_SERVICE (object), cancellable, &error);
534
535         if (error != NULL)
536                 g_simple_async_result_take_error (simple, error);
537 }
538
539 static void
540 service_query_auth_types (CamelService *service,
541                           gint io_priority,
542                           GCancellable *cancellable,
543                           GAsyncReadyCallback callback,
544                           gpointer user_data)
545 {
546         GSimpleAsyncResult *simple;
547         AsyncContext *async_context;
548
549         async_context = g_slice_new0 (AsyncContext);
550
551         simple = g_simple_async_result_new (
552                 G_OBJECT (service), callback,
553                 user_data, service_query_auth_types);
554
555         g_simple_async_result_set_op_res_gpointer (
556                 simple, async_context, (GDestroyNotify) async_context_free);
557
558         g_simple_async_result_run_in_thread (
559                 simple, service_query_auth_types_thread,
560                 io_priority, cancellable);
561
562         g_object_unref (simple);
563 }
564
565 static GList *
566 service_query_auth_types_finish (CamelService *service,
567                                  GAsyncResult *result,
568                                  GError **error)
569 {
570         GSimpleAsyncResult *simple;
571         AsyncContext *async_context;
572
573         g_return_val_if_fail (
574                 g_simple_async_result_is_valid (
575                 result, G_OBJECT (service),
576                 service_query_auth_types), NULL);
577
578         simple = G_SIMPLE_ASYNC_RESULT (result);
579         async_context = g_simple_async_result_get_op_res_gpointer (simple);
580
581         if (g_simple_async_result_propagate_error (simple, error))
582                 return NULL;
583
584         return g_list_copy (async_context->auth_types);
585 }
586
587 static gboolean
588 service_initable_init (GInitable *initable,
589                        GCancellable *cancellable,
590                        GError **error)
591 {
592         CamelProvider *provider;
593         CamelService *service;
594         CamelURL *url;
595         gboolean success = FALSE;
596         const gchar *new_data_dir;
597         gchar *old_data_dir;
598         gchar *url_string;
599
600         service = CAMEL_SERVICE (initable);
601         url = camel_service_get_camel_url (service);
602         provider = camel_service_get_provider (service);
603
604         url_string = camel_url_to_string (url, 0);
605
606         if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER)) {
607                 if (url->user == NULL || *url->user == '\0') {
608                         g_set_error (
609                                 error, CAMEL_SERVICE_ERROR,
610                                 CAMEL_SERVICE_ERROR_URL_INVALID,
611                                 _("URL '%s' needs a user component"),
612                                 url_string);
613                         goto exit;
614                 }
615         }
616
617         if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST)) {
618                 if (url->host == NULL || *url->host == '\0') {
619                         g_set_error (
620                                 error, CAMEL_SERVICE_ERROR,
621                                 CAMEL_SERVICE_ERROR_URL_INVALID,
622                                 _("URL '%s' needs a host component"),
623                                 url_string);
624                         goto exit;
625                 }
626         }
627
628         if (CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH)) {
629                 if (url->path == NULL || *url->path == '\0') {
630                         g_set_error (
631                                 error, CAMEL_SERVICE_ERROR,
632                                 CAMEL_SERVICE_ERROR_URL_INVALID,
633                                 _("URL '%s' needs a path component"),
634                                 url_string);
635                         goto exit;
636                 }
637         }
638
639         new_data_dir = camel_service_get_user_data_dir (service);
640         old_data_dir = service_find_old_data_dir (service);
641
642         /* If the old data directory name exists, try renaming
643          * it to the new data directory.  Failure is non-fatal. */
644         if (old_data_dir != NULL) {
645                 g_rename (old_data_dir, new_data_dir);
646                 g_free (old_data_dir);
647         }
648
649         success = TRUE;
650
651 exit:
652         g_free (url_string);
653
654         return success;
655 }
656
657 static void
658 camel_service_class_init (CamelServiceClass *class)
659 {
660         GObjectClass *object_class;
661
662         g_type_class_add_private (class, sizeof (CamelServicePrivate));
663
664         object_class = G_OBJECT_CLASS (class);
665         object_class->set_property = service_set_property;
666         object_class->get_property = service_get_property;
667         object_class->dispose = service_dispose;
668         object_class->finalize = service_finalize;
669         object_class->constructed = service_constructed;
670
671         class->settings_type = CAMEL_TYPE_SETTINGS;
672         class->get_name = service_get_name;
673         class->cancel_connect = service_cancel_connect;
674         class->connect_sync = service_connect_sync;
675         class->disconnect_sync = service_disconnect_sync;
676         class->query_auth_types_sync = service_query_auth_types_sync;
677
678         class->authenticate = service_authenticate;
679         class->authenticate_finish = service_authenticate_finish;
680         class->query_auth_types = service_query_auth_types;
681         class->query_auth_types_finish = service_query_auth_types_finish;
682
683         g_object_class_install_property (
684                 object_class,
685                 PROP_DISPLAY_NAME,
686                 g_param_spec_string (
687                         "display-name",
688                         "Display Name",
689                         "The display name for the service",
690                         NULL,
691                         G_PARAM_READWRITE |
692                         G_PARAM_CONSTRUCT |
693                         G_PARAM_STATIC_STRINGS));
694
695         g_object_class_install_property (
696                 object_class,
697                 PROP_PASSWORD,
698                 g_param_spec_string (
699                         "password",
700                         "Password",
701                         "The password for the service",
702                         NULL,
703                         G_PARAM_READWRITE |
704                         G_PARAM_CONSTRUCT |
705                         G_PARAM_STATIC_STRINGS));
706
707         g_object_class_install_property (
708                 object_class,
709                 PROP_PROVIDER,
710                 g_param_spec_pointer (
711                         "provider",
712                         "Provider",
713                         "The CamelProvider for the service",
714                         G_PARAM_READWRITE |
715                         G_PARAM_CONSTRUCT_ONLY |
716                         G_PARAM_STATIC_STRINGS));
717
718         g_object_class_install_property (
719                 object_class,
720                 PROP_SESSION,
721                 g_param_spec_object (
722                         "session",
723                         "Session",
724                         "A CamelSession instance",
725                         CAMEL_TYPE_SESSION,
726                         G_PARAM_READWRITE |
727                         G_PARAM_CONSTRUCT_ONLY |
728                         G_PARAM_STATIC_STRINGS));
729
730         g_object_class_install_property (
731                 object_class,
732                 PROP_SETTINGS,
733                 g_param_spec_object (
734                         "settings",
735                         "Settings",
736                         "A CamelSettings instance",
737                         CAMEL_TYPE_SETTINGS,
738                         G_PARAM_READWRITE |
739                         G_PARAM_CONSTRUCT |
740                         G_PARAM_STATIC_STRINGS));
741
742         g_object_class_install_property (
743                 object_class,
744                 PROP_UID,
745                 g_param_spec_string (
746                         "uid",
747                         "UID",
748                         "The unique identity of the service",
749                         NULL,
750                         G_PARAM_READWRITE |
751                         G_PARAM_CONSTRUCT_ONLY |
752                         G_PARAM_STATIC_STRINGS));
753
754         g_object_class_install_property (
755                 object_class,
756                 PROP_URL,
757                 g_param_spec_boxed (
758                         "url",
759                         "URL",
760                         "The CamelURL for the service",
761                         CAMEL_TYPE_URL,
762                         G_PARAM_READWRITE |
763                         G_PARAM_CONSTRUCT_ONLY |
764                         G_PARAM_STATIC_STRINGS));
765 }
766
767 static void
768 camel_service_initable_init (GInitableIface *interface)
769 {
770         interface->init = service_initable_init;
771 }
772
773 static void
774 camel_service_init (CamelService *service)
775 {
776         service->priv = CAMEL_SERVICE_GET_PRIVATE (service);
777
778         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
779
780         g_static_rec_mutex_init (&service->priv->connect_lock);
781         g_static_mutex_init (&service->priv->connect_op_lock);
782 }
783
784 GQuark
785 camel_service_error_quark (void)
786 {
787         static GQuark quark = 0;
788
789         if (G_UNLIKELY (quark == 0)) {
790                 const gchar *string = "camel-service-error-quark";
791                 quark = g_quark_from_static_string (string);
792         }
793
794         return quark;
795 }
796
797 /**
798  * camel_service_cancel_connect:
799  * @service: a #CamelService
800  *
801  * If @service is currently attempting to connect to or disconnect
802  * from a server, this causes it to stop and fail. Otherwise it is a
803  * no-op.
804  **/
805 void
806 camel_service_cancel_connect (CamelService *service)
807 {
808         CamelServiceClass *class;
809
810         g_return_if_fail (CAMEL_IS_SERVICE (service));
811
812         class = CAMEL_SERVICE_GET_CLASS (service);
813         g_return_if_fail (class->cancel_connect != NULL);
814
815         camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
816         if (service->priv->connect_op)
817                 class->cancel_connect (service);
818         camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
819 }
820
821 /**
822  * camel_service_get_display_name:
823  * @service: a #CamelService
824  *
825  * Returns the display name for @service, or %NULL if @service has not
826  * been given a display name.  The display name is intended for use in
827  * a user interface and should generally be given a user-defined name.
828  *
829  * Compare this with camel_service_get_name(), which returns a built-in
830  * description of the type of service (IMAP, SMTP, etc.).
831  *
832  * Returns: the display name for @service, or %NULL
833  *
834  * Since: 3.2
835  **/
836 const gchar *
837 camel_service_get_display_name (CamelService *service)
838 {
839         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
840
841         return service->priv->display_name;
842 }
843
844 /**
845  * camel_service_set_display_name:
846  * @service: a #CamelService
847  * @display_name: a valid UTF-8 string, or %NULL
848  *
849  * Assigns a UTF-8 display name to @service.  The display name is intended
850  * for use in a user interface and should generally be given a user-defined
851  * name.
852  *
853  * Compare this with camel_service_get_name(), which returns a built-in
854  * description of the type of service (IMAP, SMTP, etc.).
855  *
856  * Since: 3.2
857  **/
858 void
859 camel_service_set_display_name (CamelService *service,
860                                 const gchar *display_name)
861 {
862         g_return_if_fail (CAMEL_IS_SERVICE (service));
863
864         if (display_name != NULL)
865                 g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
866
867         g_free (service->priv->display_name);
868         service->priv->display_name = g_strdup (display_name);
869
870         g_object_notify (G_OBJECT (service), "display-name");
871 }
872
873 /**
874  * camel_service_get_password:
875  * @service: a #CamelService
876  *
877  * Returns the password for @service.  Some SASL mechanisms use this
878  * when attempting to authenticate.
879  *
880  * Returns: the password for @service
881  *
882  * Since: 3.4
883  **/
884 const gchar *
885 camel_service_get_password (CamelService *service)
886 {
887         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
888
889         return service->priv->password;
890 }
891
892 /**
893  * camel_service_set_password:
894  * @service: a #CamelService
895  * @password: the password for @service
896  *
897  * Sets the password for @service.  Use this function to cache the password
898  * in memory after obtaining it through camel_session_get_password().  Some
899  * SASL mechanisms use this when attempting to authenticate.
900  *
901  * Since: 3.4
902  **/
903 void
904 camel_service_set_password (CamelService *service,
905                             const gchar *password)
906 {
907         g_return_if_fail (CAMEL_IS_SERVICE (service));
908
909         g_free (service->priv->password);
910         service->priv->password = g_strdup (password);
911
912         g_object_notify (G_OBJECT (service), "password");
913 }
914
915 /**
916  * camel_service_get_user_data_dir:
917  * @service: a #CamelService
918  *
919  * Returns the base directory under which to store user-specific data
920  * for @service.  The directory is formed by appending the directory
921  * returned by camel_session_get_user_data_dir() with the service's
922  * #CamelService:uid value.
923  *
924  * Returns: the base directory for @service
925  *
926  * Since: 3.2
927  **/
928 const gchar *
929 camel_service_get_user_data_dir (CamelService *service)
930 {
931         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
932
933         return service->priv->user_data_dir;
934 }
935
936 /**
937  * camel_service_get_user_cache_dir:
938  * @service: a #CamelService
939  *
940  * Returns the base directory under which to store cache data
941  * for @service.  The directory is formed by appending the directory
942  * returned by camel_session_get_user_cache_dir() with the service's
943  * #CamelService:uid value.
944  *
945  * Returns: the base cache directory for @service
946  *
947  * Since: 3.4
948  **/
949 const gchar *
950 camel_service_get_user_cache_dir (CamelService *service)
951 {
952         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
953
954         return service->priv->user_cache_dir;
955 }
956
957 /**
958  * camel_service_get_name:
959  * @service: a #CamelService
960  * @brief: whether or not to use a briefer form
961  *
962  * This gets the name of the service in a "friendly" (suitable for
963  * humans) form. If @brief is %TRUE, this should be a brief description
964  * such as for use in the folder tree. If @brief is %FALSE, it should
965  * be a more complete and mostly unambiguous description.
966  *
967  * Returns: a description of the service which the caller must free
968  **/
969 gchar *
970 camel_service_get_name (CamelService *service,
971                         gboolean brief)
972 {
973         CamelServiceClass *class;
974
975         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
976         g_return_val_if_fail (service->priv->url, NULL);
977
978         class = CAMEL_SERVICE_GET_CLASS (service);
979         g_return_val_if_fail (class->get_name != NULL, NULL);
980
981         return class->get_name (service, brief);
982 }
983
984 /**
985  * camel_service_get_provider:
986  * @service: a #CamelService
987  *
988  * Gets the #CamelProvider associated with the service.
989  *
990  * Returns: the #CamelProvider
991  **/
992 CamelProvider *
993 camel_service_get_provider (CamelService *service)
994 {
995         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
996
997         return service->priv->provider;
998 }
999
1000 /**
1001  * camel_service_get_session:
1002  * @service: a #CamelService
1003  *
1004  * Gets the #CamelSession associated with the service.
1005  *
1006  * Returns: the #CamelSession
1007  **/
1008 CamelSession *
1009 camel_service_get_session (CamelService *service)
1010 {
1011         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1012
1013         return CAMEL_SESSION (service->priv->session);
1014 }
1015
1016 /**
1017  * camel_service_get_settings:
1018  * @service: a #CamelService
1019  *
1020  * Returns the #CamelSettings instance associated with the service.
1021  *
1022  * Returns: the #CamelSettings
1023  *
1024  * Since: 3.2
1025  **/
1026 CamelSettings *
1027 camel_service_get_settings (CamelService *service)
1028 {
1029         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1030
1031         /* Every service should have a settings object. */
1032         g_warn_if_fail (service->priv->settings != NULL);
1033
1034         return service->priv->settings;
1035 }
1036
1037 /**
1038  * camel_service_set_settings:
1039  * @service: a #CamelService
1040  * @settings: an instance derviced from #CamelSettings, or %NULL
1041  *
1042  * Associates a new #CamelSettings instance with the service.
1043  * The @settings instance must match the settings type defined in
1044  * #CamelServiceClass.  If @settings is %NULL, a new #CamelSettings
1045  * instance of the appropriate type is created with all properties
1046  * set to defaults.
1047  *
1048  * Since: 3.2
1049  **/
1050 void
1051 camel_service_set_settings (CamelService *service,
1052                             CamelSettings *settings)
1053 {
1054         CamelServiceClass *class;
1055
1056         g_return_if_fail (CAMEL_IS_SERVICE (service));
1057
1058         class = CAMEL_SERVICE_GET_CLASS (service);
1059
1060         if (settings != NULL) {
1061                 g_return_if_fail (
1062                         g_type_is_a (
1063                                 G_OBJECT_TYPE (settings),
1064                                 class->settings_type));
1065                 g_object_ref (settings);
1066
1067         } else {
1068                 g_return_if_fail (
1069                         g_type_is_a (
1070                                 class->settings_type,
1071                                 CAMEL_TYPE_SETTINGS));
1072                 settings = g_object_new (class->settings_type, NULL);
1073                 camel_settings_load_from_url (settings, camel_service_get_camel_url (service));
1074         }
1075
1076         if (service->priv->settings != NULL)
1077                 g_object_unref (service->priv->settings);
1078
1079         service->priv->settings = settings;
1080
1081         g_object_notify (G_OBJECT (service), "settings");
1082 }
1083
1084 /**
1085  * camel_service_get_uid:
1086  * @service: a #CamelService
1087  *
1088  * Gets the unique identifier string associated with the service.
1089  *
1090  * Returns: the UID string
1091  *
1092  * Since: 3.2
1093  **/
1094 const gchar *
1095 camel_service_get_uid (CamelService *service)
1096 {
1097         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1098
1099         return service->priv->uid;
1100 }
1101
1102 /**
1103  * camel_service_get_camel_url:
1104  * @service: a #CamelService
1105  *
1106  * Returns the #CamelURL representing @service.
1107  *
1108  * Returns: the #CamelURL representing @service
1109  *
1110  * Since: 3.2
1111  **/
1112 CamelURL *
1113 camel_service_get_camel_url (CamelService *service)
1114 {
1115         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1116
1117         return service->priv->url;
1118 }
1119
1120 /**
1121  * camel_service_get_url:
1122  * @service: a #CamelService
1123  *
1124  * Gets the URL representing @service. The returned URL must be
1125  * freed when it is no longer needed.
1126  *
1127  * Returns: the URL representing @service
1128  **/
1129 gchar *
1130 camel_service_get_url (CamelService *service)
1131 {
1132         CamelURL *url;
1133
1134         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1135
1136         url = camel_service_get_camel_url (service);
1137
1138         return camel_url_to_string (url, 0);
1139 }
1140
1141 /**
1142  * camel_service_connect_sync:
1143  * @service: a #CamelService
1144  * @error: return location for a #GError, or %NULL
1145  *
1146  * Connect to the service using the parameters it was initialized
1147  * with.
1148  *
1149  * Returns: %TRUE if the connection is made or %FALSE otherwise
1150  **/
1151 gboolean
1152 camel_service_connect_sync (CamelService *service,
1153                             GError **error)
1154 {
1155         CamelServiceClass *class;
1156         GCancellable *op;
1157         gboolean ret = FALSE;
1158
1159         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1160         g_return_val_if_fail (service->priv->session != NULL, FALSE);
1161         g_return_val_if_fail (service->priv->url != NULL, FALSE);
1162
1163         class = CAMEL_SERVICE_GET_CLASS (service);
1164         g_return_val_if_fail (class->connect_sync != NULL, FALSE);
1165
1166         camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1167
1168         if (service->priv->status == CAMEL_SERVICE_CONNECTED) {
1169                 camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1170                 return TRUE;
1171         }
1172
1173         /* Register a separate operation for connecting, so that
1174          * the offline code can cancel it. */
1175         camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1176         service->priv->connect_op = camel_operation_new ();
1177         op = service->priv->connect_op;
1178         camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1179
1180         service->priv->status = CAMEL_SERVICE_CONNECTING;
1181         ret = class->connect_sync (service, service->priv->connect_op, error);
1182         CAMEL_CHECK_GERROR (service, connect_sync, ret, error);
1183         service->priv->status =
1184                 ret ? CAMEL_SERVICE_CONNECTED : CAMEL_SERVICE_DISCONNECTED;
1185
1186         camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1187         g_object_unref (op);
1188         if (op == service->priv->connect_op)
1189                 service->priv->connect_op = NULL;
1190         camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1191
1192         camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1193
1194         return ret;
1195 }
1196
1197 /**
1198  * camel_service_disconnect_sync:
1199  * @service: a #CamelService
1200  * @clean: whether or not to try to disconnect cleanly
1201  * @error: return location for a #GError, or %NULL
1202  *
1203  * Disconnect from the service. If @clean is %FALSE, it should not
1204  * try to do any synchronizing or other cleanup of the connection.
1205  *
1206  * Returns: %TRUE if the disconnect was successful or %FALSE otherwise
1207  **/
1208 gboolean
1209 camel_service_disconnect_sync (CamelService *service,
1210                                gboolean clean,
1211                                GError **error)
1212 {
1213         CamelServiceClass *class;
1214         gboolean res = TRUE;
1215
1216         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1217
1218         class = CAMEL_SERVICE_GET_CLASS (service);
1219         g_return_val_if_fail (class->disconnect_sync != NULL, FALSE);
1220
1221         camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1222
1223         if (service->priv->status != CAMEL_SERVICE_DISCONNECTED
1224             && service->priv->status != CAMEL_SERVICE_DISCONNECTING) {
1225                 GCancellable *op;
1226                 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1227                 service->priv->connect_op = camel_operation_new ();
1228                 op = service->priv->connect_op;
1229                 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1230
1231                 service->priv->status = CAMEL_SERVICE_DISCONNECTING;
1232                 res = class->disconnect_sync (
1233                         service, clean, service->priv->connect_op, error);
1234                 CAMEL_CHECK_GERROR (service, disconnect_sync, res, error);
1235                 service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1236
1237                 camel_service_lock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1238                 g_object_unref (op);
1239                 if (op == service->priv->connect_op)
1240                         service->priv->connect_op = NULL;
1241                 camel_service_unlock (service, CAMEL_SERVICE_CONNECT_OP_LOCK);
1242         }
1243
1244         camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1245
1246         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1247
1248         return res;
1249 }
1250
1251 /**
1252  * camel_service_get_connection_status:
1253  * @service: a #CamelService
1254  *
1255  * Returns the connection status for @service.
1256  *
1257  * Returns: the connection status
1258  *
1259  * Since: 3.2
1260  **/
1261 CamelServiceConnectionStatus
1262 camel_service_get_connection_status (CamelService *service)
1263 {
1264         g_return_val_if_fail (
1265                 CAMEL_IS_SERVICE (service), CAMEL_SERVICE_DISCONNECTED);
1266
1267         return service->priv->status;
1268 }
1269
1270 /**
1271  * camel_service_lock:
1272  * @service: a #CamelService
1273  * @lock: lock type to lock
1274  *
1275  * Locks @service's @lock. Unlock it with camel_service_unlock().
1276  *
1277  * Since: 2.32
1278  **/
1279 void
1280 camel_service_lock (CamelService *service,
1281                     CamelServiceLock lock)
1282 {
1283         g_return_if_fail (CAMEL_IS_SERVICE (service));
1284
1285         switch (lock) {
1286                 case CAMEL_SERVICE_REC_CONNECT_LOCK:
1287                         g_static_rec_mutex_lock (&service->priv->connect_lock);
1288                         break;
1289                 case CAMEL_SERVICE_CONNECT_OP_LOCK:
1290                         g_static_mutex_lock (&service->priv->connect_op_lock);
1291                         break;
1292                 default:
1293                         g_return_if_reached ();
1294         }
1295 }
1296
1297 /**
1298  * camel_service_unlock:
1299  * @service: a #CamelService
1300  * @lock: lock type to unlock
1301  *
1302  * Unlocks @service's @lock, previously locked with camel_service_lock().
1303  *
1304  * Since: 2.32
1305  **/
1306 void
1307 camel_service_unlock (CamelService *service,
1308                       CamelServiceLock lock)
1309 {
1310         g_return_if_fail (CAMEL_IS_SERVICE (service));
1311
1312         switch (lock) {
1313                 case CAMEL_SERVICE_REC_CONNECT_LOCK:
1314                         g_static_rec_mutex_unlock (&service->priv->connect_lock);
1315                         break;
1316                 case CAMEL_SERVICE_CONNECT_OP_LOCK:
1317                         g_static_mutex_unlock (&service->priv->connect_op_lock);
1318                         break;
1319                 default:
1320                         g_return_if_reached ();
1321         }
1322 }
1323
1324 /**
1325  * camel_service_authenticate_sync:
1326  * @service: a #CamelService
1327  * @mechanism: a SASL mechanism name, or %NULL
1328  * @cancellable: optional #GCancellable object, or %NULL
1329  * @error: return location for a #GError, or %NULL
1330  *
1331  * Attempts to authenticate @service using @mechanism and, if necessary,
1332  * @service's #CamelService:password property.  The function makes only
1333  * ONE attempt at authentication and does not loop.
1334  *
1335  * If the authentication attempt completed and the server accepted the
1336  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
1337  *
1338  * If the authentication attempt completed but the server rejected the
1339  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
1340  *
1341  * If the authentication attempt failed to complete due to a network
1342  * communication issue or some other mishap, the function sets @error
1343  * and returns #CAMEL_AUTHENTICATION_ERROR.
1344  *
1345  * Generally this function should only be called from a #CamelSession
1346  * subclass in order to implement its own authentication loop.
1347  *
1348  * Returns: the authentication result
1349  *
1350  * Since: 3.4
1351  **/
1352 CamelAuthenticationResult
1353 camel_service_authenticate_sync (CamelService *service,
1354                                  const gchar *mechanism,
1355                                  GCancellable *cancellable,
1356                                  GError **error)
1357 {
1358         CamelServiceClass *class;
1359         CamelAuthenticationResult result;
1360
1361         g_return_val_if_fail (
1362                 CAMEL_IS_SERVICE (service),
1363                 CAMEL_AUTHENTICATION_REJECTED);
1364
1365         class = CAMEL_SERVICE_GET_CLASS (service);
1366         g_return_val_if_fail (
1367                 class->authenticate_sync != NULL,
1368                 CAMEL_AUTHENTICATION_REJECTED);
1369
1370         result = class->authenticate_sync (
1371                 service, mechanism, cancellable, error);
1372         CAMEL_CHECK_GERROR (
1373                 service, authenticate_sync,
1374                 result != CAMEL_AUTHENTICATION_ERROR, error);
1375
1376         return result;
1377 }
1378
1379 /**
1380  * camel_service_authenticate:
1381  * @service: a #CamelService
1382  * @mechanism: a SASL mechanism name, or %NULL
1383  * @io_priority: the I/O priority of the request
1384  * @cancellable: optional #GCancellable object, or %NULL
1385  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1386  * @user_data: data to pass to the callback function
1387  *
1388  * Asynchronously attempts to authenticate @service using @mechanism and,
1389  * if necessary, @service's #CamelService:password property.  The function
1390  * makes only ONE attempt at authentication and does not loop.
1391  *
1392  * Generally this function should only be called from a #CamelSession
1393  * subclass in order to implement its own authentication loop.
1394  *
1395  * When the operation is finished, @callback will be called.  You can
1396  * then call camel_service_authenticate_finish() to get the result of
1397  * the operation.
1398  *
1399  * Since: 3.4
1400  **/
1401 void
1402 camel_service_authenticate (CamelService *service,
1403                             const gchar *mechanism,
1404                             gint io_priority,
1405                             GCancellable *cancellable,
1406                             GAsyncReadyCallback callback,
1407                             gpointer user_data)
1408 {
1409         CamelServiceClass *class;
1410
1411         g_return_if_fail (CAMEL_IS_SERVICE (service));
1412
1413         class = CAMEL_SERVICE_GET_CLASS (service);
1414         g_return_if_fail (class->authenticate != NULL);
1415
1416         class->authenticate (
1417                 service, mechanism, io_priority,
1418                 cancellable, callback, user_data);
1419 }
1420
1421 /**
1422  * camel_service_authenticate_finish:
1423  * @service: a #CamelService
1424  * @result: a #GAsyncResult
1425  * @error: return location for a #GError, or %NULL
1426  *
1427  * Finishes the operation started with camel_service_authenticate().
1428  *
1429  * If the authentication attempt completed and the server accepted the
1430  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
1431  *
1432  * If the authentication attempt completed but the server rejected the
1433  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
1434  *
1435  * If the authentication attempt failed to complete due to a network
1436  * communication issue or some other mishap, the function sets @error
1437  * and returns #CAMEL_AUTHENTICATION_ERROR.
1438  *
1439  * Returns: the authentication result
1440  *
1441  * Since: 3.4
1442  **/
1443 CamelAuthenticationResult
1444 camel_service_authenticate_finish (CamelService *service,
1445                                    GAsyncResult *result,
1446                                    GError **error)
1447 {
1448         CamelServiceClass *class;
1449
1450         g_return_val_if_fail (
1451                 CAMEL_IS_SERVICE (service),
1452                 CAMEL_AUTHENTICATION_REJECTED);
1453         g_return_val_if_fail (
1454                 G_IS_ASYNC_RESULT (result),
1455                 CAMEL_AUTHENTICATION_REJECTED);
1456
1457         class = CAMEL_SERVICE_GET_CLASS (service);
1458         g_return_val_if_fail (
1459                 class->authenticate_finish,
1460                 CAMEL_AUTHENTICATION_REJECTED);
1461
1462         return class->authenticate_finish (service, result, error);
1463 }
1464
1465 /**
1466  * camel_service_query_auth_types_sync:
1467  * @service: a #CamelService
1468  * @cancellable: optional #GCancellable object, or %NULL
1469  * @error: return location for a #GError, or %NULL
1470  *
1471  * Obtains a list of authentication types supported by @service.
1472  * Free the returned list with g_list_free().
1473  *
1474  * Returns: a list of #CamelServiceAuthType structs
1475  **/
1476 GList *
1477 camel_service_query_auth_types_sync (CamelService *service,
1478                                      GCancellable *cancellable,
1479                                      GError **error)
1480 {
1481         CamelServiceClass *class;
1482         GList *list;
1483
1484         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1485
1486         class = CAMEL_SERVICE_GET_CLASS (service);
1487         g_return_val_if_fail (class->query_auth_types_sync != NULL, NULL);
1488
1489         /* Note that we get the connect lock here, which means the
1490          * callee must not call the connect functions itself. */
1491         camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1492         list = class->query_auth_types_sync (service, cancellable, error);
1493         camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
1494
1495         return list;
1496 }
1497
1498 /**
1499  * camel_service_query_auth_types:
1500  * @service: a #CamelService
1501  * @io_priority: the I/O priority of the request
1502  * @cancellable: optional #GCancellable object, or %NULL
1503  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1504  * @user_data: data to pass to the callback function
1505  *
1506  * Asynchronously obtains a list of authentication types supported by
1507  * @service.
1508  *
1509  * When the operation is finished, @callback will be called.  You can
1510  * then call camel_service_query_auth_types_finish() to get the result
1511  * of the operation.
1512  *
1513  * Since: 3.2
1514  **/
1515 void
1516 camel_service_query_auth_types (CamelService *service,
1517                                 gint io_priority,
1518                                 GCancellable *cancellable,
1519                                 GAsyncReadyCallback callback,
1520                                 gpointer user_data)
1521 {
1522         CamelServiceClass *class;
1523
1524         g_return_if_fail (CAMEL_IS_SERVICE (service));
1525
1526         class = CAMEL_SERVICE_GET_CLASS (service);
1527         g_return_if_fail (class->query_auth_types != NULL);
1528
1529         class->query_auth_types (
1530                 service, io_priority,
1531                 cancellable, callback, user_data);
1532 }
1533
1534 /**
1535  * camel_service_query_auth_types_finish:
1536  * @service: a #CamelService
1537  * @result: a #GAsyncResult
1538  * @error: return location for a #GError, or %NULL
1539  *
1540  * Finishes the operation started with camel_service_query_auth_types().
1541  * Free the returned list with g_list_free().
1542  *
1543  * Returns: a list of #CamelServiceAuthType structs
1544  *
1545  * Since: 3.2
1546  **/
1547 GList *
1548 camel_service_query_auth_types_finish (CamelService *service,
1549                                        GAsyncResult *result,
1550                                        GError **error)
1551 {
1552         CamelServiceClass *class;
1553
1554         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1555         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
1556
1557         class = CAMEL_SERVICE_GET_CLASS (service);
1558         g_return_val_if_fail (class->query_auth_types_finish != NULL, NULL);
1559
1560         return class->query_auth_types_finish (service, result, error);
1561 }
1562