Make use of G_DEFINE_QUARK()
[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-enumtypes.h"
40 #include "camel-local-settings.h"
41 #include "camel-network-settings.h"
42 #include "camel-operation.h"
43 #include "camel-service.h"
44 #include "camel-session.h"
45
46 #define d(x)
47 #define w(x)
48
49 #define CAMEL_SERVICE_GET_PRIVATE(obj) \
50         (G_TYPE_INSTANCE_GET_PRIVATE \
51         ((obj), CAMEL_TYPE_SERVICE, CamelServicePrivate))
52
53 typedef struct _AsyncClosure AsyncClosure;
54 typedef struct _AsyncContext AsyncContext;
55 typedef struct _ConnectionOp ConnectionOp;
56
57 struct _CamelServicePrivate {
58         gpointer session;  /* weak pointer */
59
60         CamelSettings *settings;
61         GMutex settings_lock;
62
63         CamelProvider *provider;
64
65         gchar *display_name;
66         gchar *user_data_dir;
67         gchar *user_cache_dir;
68         gchar *uid;
69         gchar *password;
70
71         GMutex connection_lock;
72         ConnectionOp *connection_op;
73         CamelServiceConnectionStatus status;
74 };
75
76 /* This is copied from EAsyncClosure in libedataserver.
77  * If this proves useful elsewhere in Camel we may want
78  * to split this out and make it part of the public API. */
79 struct _AsyncClosure {
80         GMainLoop *loop;
81         GMainContext *context;
82         GAsyncResult *result;
83 };
84
85 struct _AsyncContext {
86         GList *auth_types;
87         gchar *auth_mechanism;
88         CamelAuthenticationResult auth_result;
89 };
90
91 /* The GQueue is only modified while CamelService's
92  * connection_lock is held, so it does not need its
93  * own mutex. */
94 struct _ConnectionOp {
95         volatile gint ref_count;
96         GQueue pending;
97         GMutex simple_lock;
98         GSimpleAsyncResult *simple;
99         GCancellable *cancellable;
100         gulong cancel_id;
101 };
102
103 enum {
104         PROP_0,
105         PROP_CONNECTION_STATUS,
106         PROP_DISPLAY_NAME,
107         PROP_PASSWORD,
108         PROP_PROVIDER,
109         PROP_SESSION,
110         PROP_SETTINGS,
111         PROP_UID
112 };
113
114 /* Forward Declarations */
115 static void camel_service_initable_init (GInitableIface *interface);
116
117 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (
118         CamelService, camel_service, CAMEL_TYPE_OBJECT,
119         G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, camel_service_initable_init))
120
121 static AsyncClosure *
122 async_closure_new (void)
123 {
124         AsyncClosure *closure;
125
126         closure = g_slice_new0 (AsyncClosure);
127         closure->context = g_main_context_new ();
128         closure->loop = g_main_loop_new (closure->context, FALSE);
129
130         g_main_context_push_thread_default (closure->context);
131
132         return closure;
133 }
134
135 static GAsyncResult *
136 async_closure_wait (AsyncClosure *closure)
137 {
138         g_return_val_if_fail (closure != NULL, NULL);
139
140         g_main_loop_run (closure->loop);
141
142         return closure->result;
143 }
144
145 static void
146 async_closure_free (AsyncClosure *closure)
147 {
148         g_return_if_fail (closure != NULL);
149
150         g_main_context_pop_thread_default (closure->context);
151
152         g_main_loop_unref (closure->loop);
153         g_main_context_unref (closure->context);
154
155         if (closure->result != NULL)
156                 g_object_unref (closure->result);
157
158         g_slice_free (AsyncClosure, closure);
159 }
160
161 static void
162 async_closure_callback (GObject *object,
163                         GAsyncResult *result,
164                         gpointer closure)
165 {
166         AsyncClosure *real_closure;
167
168         g_return_if_fail (G_IS_OBJECT (object));
169         g_return_if_fail (G_IS_ASYNC_RESULT (result));
170         g_return_if_fail (closure != NULL);
171
172         real_closure = closure;
173
174         /* Replace any previous result. */
175         if (real_closure->result != NULL)
176                 g_object_unref (real_closure->result);
177         real_closure->result = g_object_ref (result);
178
179         g_main_loop_quit (real_closure->loop);
180 }
181
182 static void
183 async_context_free (AsyncContext *async_context)
184 {
185         g_list_free (async_context->auth_types);
186
187         g_free (async_context->auth_mechanism);
188
189         g_slice_free (AsyncContext, async_context);
190 }
191
192 static ConnectionOp *
193 connection_op_new (GSimpleAsyncResult *simple,
194                    GCancellable *cancellable)
195 {
196         ConnectionOp *op;
197
198         op = g_slice_new0 (ConnectionOp);
199         op->ref_count = 1;
200         g_mutex_init (&op->simple_lock);
201         op->simple = g_object_ref (simple);
202
203         if (G_IS_CANCELLABLE (cancellable))
204                 op->cancellable = g_object_ref (cancellable);
205
206         return op;
207 }
208
209 static ConnectionOp *
210 connection_op_ref (ConnectionOp *op)
211 {
212         g_return_val_if_fail (op != NULL, NULL);
213         g_return_val_if_fail (op->ref_count > 0, NULL);
214
215         g_atomic_int_inc (&op->ref_count);
216
217         return op;
218 }
219
220 static void
221 connection_op_unref (ConnectionOp *op)
222 {
223         g_return_if_fail (op != NULL);
224         g_return_if_fail (op->ref_count > 0);
225
226         if (g_atomic_int_dec_and_test (&op->ref_count)) {
227
228                 /* The pending queue should be empty. */
229                 g_warn_if_fail (g_queue_is_empty (&op->pending));
230
231                 g_mutex_clear (&op->simple_lock);
232
233                 if (op->simple != NULL)
234                         g_object_unref (op->simple);
235
236                 if (op->cancel_id > 0)
237                         g_cancellable_disconnect (
238                                 op->cancellable, op->cancel_id);
239
240                 if (op->cancellable != NULL)
241                         g_object_unref (op->cancellable);
242
243                 g_slice_free (ConnectionOp, op);
244         }
245 }
246
247 static void
248 connection_op_complete (ConnectionOp *op,
249                         const GError *error)
250 {
251         g_mutex_lock (&op->simple_lock);
252
253         if (op->simple != NULL && error != NULL)
254                 g_simple_async_result_set_from_error (op->simple, error);
255
256         if (op->simple != NULL) {
257                 g_simple_async_result_complete_in_idle (op->simple);
258                 g_object_unref (op->simple);
259                 op->simple = NULL;
260         }
261
262         g_mutex_unlock (&op->simple_lock);
263 }
264
265 static void
266 connection_op_cancelled (GCancellable *cancellable,
267                          ConnectionOp *op)
268 {
269         /* Because we called g_simple_async_result_set_check_cancellable()
270          * we don't need to explicitly set a G_IO_ERROR_CANCELLED here. */
271         connection_op_complete (op, NULL);
272 }
273
274 static void
275 connection_op_add_pending (ConnectionOp *op,
276                            GSimpleAsyncResult *simple,
277                            GCancellable *cancellable)
278 {
279         ConnectionOp *pending_op;
280
281         g_return_if_fail (op != NULL);
282
283         pending_op = connection_op_new (simple, cancellable);
284
285         if (pending_op->cancellable != NULL)
286                 pending_op->cancel_id = g_cancellable_connect (
287                         pending_op->cancellable,
288                         G_CALLBACK (connection_op_cancelled),
289                         pending_op, (GDestroyNotify) NULL);
290
291         g_queue_push_tail (&op->pending, pending_op);
292 }
293
294 static void
295 connection_op_complete_pending (ConnectionOp *op,
296                                 const GError *error)
297 {
298         ConnectionOp *pending_op;
299
300         g_return_if_fail (op != NULL);
301
302         while (!g_queue_is_empty (&op->pending)) {
303                 pending_op = g_queue_pop_head (&op->pending);
304                 connection_op_complete (pending_op, error);
305                 connection_op_unref (pending_op);
306         }
307 }
308
309 static gchar *
310 service_find_old_data_dir (CamelService *service)
311 {
312         CamelProvider *provider;
313         CamelSession *session;
314         CamelURL *url;
315         GString *path;
316         gboolean allows_host;
317         gboolean allows_user;
318         gboolean needs_host;
319         gboolean needs_path;
320         gboolean needs_user;
321         const gchar *base_dir;
322         gchar *old_data_dir;
323
324         provider = camel_service_get_provider (service);
325         session = camel_service_get_session (service);
326         url = camel_service_new_camel_url (service);
327
328         allows_host = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_HOST);
329         allows_user = CAMEL_PROVIDER_ALLOWS (provider, CAMEL_URL_PART_USER);
330
331         needs_host = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_HOST);
332         needs_path = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_PATH);
333         needs_user = CAMEL_PROVIDER_NEEDS (provider, CAMEL_URL_PART_USER);
334
335         /* This function reproduces the way service data directories used
336          * to be determined before we moved to just using the UID.  If the
337          * old data directory exists, try renaming it to the new form.
338          *
339          * A virtual class method was used to determine the directory path,
340          * but no known CamelProviders ever overrode the default algorithm
341          * below.  So this should work for everyone. */
342
343         path = g_string_new (provider->protocol);
344
345         if (allows_user) {
346                 g_string_append_c (path, '/');
347                 if (url->user != NULL)
348                         g_string_append (path, url->user);
349                 if (allows_host) {
350                         g_string_append_c (path, '@');
351                         if (url->host != NULL)
352                                 g_string_append (path, url->host);
353                         if (url->port) {
354                                 g_string_append_c (path, ':');
355                                 g_string_append_printf (path, "%d", url->port);
356                         }
357                 } else if (!needs_user) {
358                         g_string_append_c (path, '@');
359                 }
360
361         } else if (allows_host) {
362                 g_string_append_c (path, '/');
363                 if (!needs_host)
364                         g_string_append_c (path, '@');
365                 if (url->host != NULL)
366                         g_string_append (path, url->host);
367                 if (url->port) {
368                         g_string_append_c (path, ':');
369                         g_string_append_printf (path, "%d", url->port);
370                 }
371         }
372
373         if (needs_path && url->path) {
374                 if (*url->path != '/')
375                         g_string_append_c (path, '/');
376                 g_string_append (path, url->path);
377         }
378
379         base_dir = camel_session_get_user_data_dir (session);
380         old_data_dir = g_build_filename (base_dir, path->str, NULL);
381
382         g_string_free (path, TRUE);
383
384         if (!g_file_test (old_data_dir, G_FILE_TEST_IS_DIR)) {
385                 g_free (old_data_dir);
386                 old_data_dir = NULL;
387         }
388
389         camel_url_free (url);
390
391         return old_data_dir;
392 }
393
394 static gboolean
395 service_notify_connection_status_cb (gpointer user_data)
396 {
397         CamelService *service = CAMEL_SERVICE (user_data);
398
399         g_object_notify (G_OBJECT (service), "connection-status");
400
401         return FALSE;
402 }
403
404 static void
405 service_queue_notify_connection_status (CamelService *service)
406 {
407         CamelSession *session;
408
409         session = camel_service_get_session (service);
410
411         camel_session_idle_add (
412                 session, G_PRIORITY_DEFAULT_IDLE,
413                 service_notify_connection_status_cb,
414                 g_object_ref (service),
415                 (GDestroyNotify) g_object_unref);
416 }
417
418 static void
419 service_shared_connect_cb (GObject *source_object,
420                            GAsyncResult *result,
421                            gpointer user_data)
422 {
423         CamelService *service;
424         CamelServiceClass *class;
425         ConnectionOp *op = user_data;
426         gboolean success;
427         GError *error = NULL;
428
429         /* This avoids a compiler warning
430          * in the CAMEL_CHECK_GERROR macro. */
431         GError **p_error = &error;
432
433         service = CAMEL_SERVICE (source_object);
434         class = CAMEL_SERVICE_GET_CLASS (service);
435         g_return_if_fail (class->connect_finish != NULL);
436
437         success = class->connect_finish (service, result, &error);
438         CAMEL_CHECK_GERROR (service, connect_sync, success, p_error);
439
440         g_mutex_lock (&service->priv->connection_lock);
441
442         if (service->priv->connection_op == op) {
443                 connection_op_unref (service->priv->connection_op);
444                 service->priv->connection_op = NULL;
445                 if (success)
446                         service->priv->status = CAMEL_SERVICE_CONNECTED;
447                 else
448                         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
449                 service_queue_notify_connection_status (service);
450         }
451
452         connection_op_complete (op, error);
453         connection_op_complete_pending (op, error);
454
455         g_mutex_unlock (&service->priv->connection_lock);
456
457         connection_op_unref (op);
458         g_clear_error (&error);
459 }
460
461 static void
462 service_shared_disconnect_cb (GObject *source_object,
463                               GAsyncResult *result,
464                               gpointer user_data)
465 {
466         CamelService *service;
467         CamelServiceClass *class;
468         ConnectionOp *op = user_data;
469         gboolean success;
470         GError *error = NULL;
471
472         /* This avoids a compiler warning
473          * in the CAMEL_CHECK_GERROR macro. */
474         GError **p_error = &error;
475
476         service = CAMEL_SERVICE (source_object);
477         class = CAMEL_SERVICE_GET_CLASS (service);
478         g_return_if_fail (class->disconnect_finish != NULL);
479
480         success = class->disconnect_finish (service, result, &error);
481         CAMEL_CHECK_GERROR (service, disconnect_sync, success, p_error);
482
483         g_mutex_lock (&service->priv->connection_lock);
484
485         if (service->priv->connection_op == op) {
486                 connection_op_unref (service->priv->connection_op);
487                 service->priv->connection_op = NULL;
488                 if (success)
489                         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
490                 else
491                         service->priv->status = CAMEL_SERVICE_CONNECTED;
492                 service_queue_notify_connection_status (service);
493         }
494
495         connection_op_complete (op, error);
496         connection_op_complete_pending (op, error);
497
498         g_mutex_unlock (&service->priv->connection_lock);
499
500         connection_op_unref (op);
501         g_clear_error (&error);
502 }
503
504 static void
505 service_set_provider (CamelService *service,
506                       CamelProvider *provider)
507 {
508         g_return_if_fail (provider != NULL);
509         g_return_if_fail (service->priv->provider == NULL);
510
511         service->priv->provider = provider;
512 }
513
514 static void
515 service_set_session (CamelService *service,
516                      CamelSession *session)
517 {
518         g_return_if_fail (CAMEL_IS_SESSION (session));
519         g_return_if_fail (service->priv->session == NULL);
520
521         service->priv->session = session;
522
523         g_object_add_weak_pointer (
524                 G_OBJECT (session), &service->priv->session);
525 }
526
527 static void
528 service_set_uid (CamelService *service,
529                  const gchar *uid)
530 {
531         g_return_if_fail (uid != NULL);
532         g_return_if_fail (service->priv->uid == NULL);
533
534         service->priv->uid = g_strdup (uid);
535 }
536
537 static void
538 service_set_property (GObject *object,
539                       guint property_id,
540                       const GValue *value,
541                       GParamSpec *pspec)
542 {
543         switch (property_id) {
544                 case PROP_DISPLAY_NAME:
545                         camel_service_set_display_name (
546                                 CAMEL_SERVICE (object),
547                                 g_value_get_string (value));
548                         return;
549
550                 case PROP_PASSWORD:
551                         camel_service_set_password (
552                                 CAMEL_SERVICE (object),
553                                 g_value_get_string (value));
554                         return;
555
556                 case PROP_PROVIDER:
557                         service_set_provider (
558                                 CAMEL_SERVICE (object),
559                                 g_value_get_pointer (value));
560                         return;
561
562                 case PROP_SESSION:
563                         service_set_session (
564                                 CAMEL_SERVICE (object),
565                                 g_value_get_object (value));
566                         return;
567
568                 case PROP_SETTINGS:
569                         camel_service_set_settings (
570                                 CAMEL_SERVICE (object),
571                                 g_value_get_object (value));
572                         return;
573
574                 case PROP_UID:
575                         service_set_uid (
576                                 CAMEL_SERVICE (object),
577                                 g_value_get_string (value));
578                         return;
579         }
580
581         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
582 }
583
584 static void
585 service_get_property (GObject *object,
586                       guint property_id,
587                       GValue *value,
588                       GParamSpec *pspec)
589 {
590         switch (property_id) {
591                 case PROP_CONNECTION_STATUS:
592                         g_value_set_enum (
593                                 value, camel_service_get_connection_status (
594                                 CAMEL_SERVICE (object)));
595                         return;
596
597                 case PROP_DISPLAY_NAME:
598                         g_value_set_string (
599                                 value, camel_service_get_display_name (
600                                 CAMEL_SERVICE (object)));
601                         return;
602
603                 case PROP_PASSWORD:
604                         g_value_set_string (
605                                 value, camel_service_get_password (
606                                 CAMEL_SERVICE (object)));
607                         return;
608
609                 case PROP_PROVIDER:
610                         g_value_set_pointer (
611                                 value, camel_service_get_provider (
612                                 CAMEL_SERVICE (object)));
613                         return;
614
615                 case PROP_SESSION:
616                         g_value_set_object (
617                                 value, camel_service_get_session (
618                                 CAMEL_SERVICE (object)));
619                         return;
620
621                 case PROP_SETTINGS:
622                         g_value_take_object (
623                                 value, camel_service_ref_settings (
624                                 CAMEL_SERVICE (object)));
625                         return;
626
627                 case PROP_UID:
628                         g_value_set_string (
629                                 value, camel_service_get_uid (
630                                 CAMEL_SERVICE (object)));
631                         return;
632         }
633
634         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
635 }
636
637 static void
638 service_dispose (GObject *object)
639 {
640         CamelServicePrivate *priv;
641
642         priv = CAMEL_SERVICE_GET_PRIVATE (object);
643
644         if (priv->session != NULL) {
645                 g_object_remove_weak_pointer (
646                         G_OBJECT (priv->session), &priv->session);
647                 priv->session = NULL;
648         }
649
650         if (priv->settings != NULL) {
651                 g_object_unref (priv->settings);
652                 priv->settings = NULL;
653         }
654
655         /* Chain up to parent's dispose() method. */
656         G_OBJECT_CLASS (camel_service_parent_class)->dispose (object);
657 }
658
659 static void
660 service_finalize (GObject *object)
661 {
662         CamelServicePrivate *priv;
663
664         priv = CAMEL_SERVICE_GET_PRIVATE (object);
665
666         if (priv->status == CAMEL_SERVICE_CONNECTED)
667                 CAMEL_SERVICE_GET_CLASS (object)->disconnect_sync (
668                         CAMEL_SERVICE (object), TRUE, NULL, NULL);
669
670         g_mutex_clear (&priv->settings_lock);
671
672         g_free (priv->display_name);
673         g_free (priv->user_data_dir);
674         g_free (priv->user_cache_dir);
675         g_free (priv->uid);
676         g_free (priv->password);
677
678         /* There should be no outstanding connection operations. */
679         g_warn_if_fail (priv->connection_op == NULL);
680         g_mutex_clear (&priv->connection_lock);
681
682         /* Chain up to parent's finalize() method. */
683         G_OBJECT_CLASS (camel_service_parent_class)->finalize (object);
684 }
685
686 static void
687 service_constructed (GObject *object)
688 {
689         CamelService *service;
690         CamelSession *session;
691         const gchar *base_dir;
692         const gchar *uid;
693
694         /* Chain up to parent's constructed() method. */
695         G_OBJECT_CLASS (camel_service_parent_class)->constructed (object);
696
697         service = CAMEL_SERVICE (object);
698         session = camel_service_get_session (service);
699
700         uid = camel_service_get_uid (service);
701
702         base_dir = camel_session_get_user_data_dir (session);
703         service->priv->user_data_dir = g_build_filename (base_dir, uid, NULL);
704
705         base_dir = camel_session_get_user_cache_dir (session);
706         service->priv->user_cache_dir = g_build_filename (base_dir, uid, NULL);
707 }
708
709 static gchar *
710 service_get_name (CamelService *service,
711                   gboolean brief)
712 {
713         g_warning (
714                 "%s does not implement CamelServiceClass::get_name()",
715                 G_OBJECT_TYPE_NAME (service));
716
717         return g_strdup (G_OBJECT_TYPE_NAME (service));
718 }
719
720 static gboolean
721 service_connect_sync (CamelService *service,
722                       GCancellable *cancellable,
723                       GError **error)
724 {
725         /* Default behavior for local storage providers. */
726         return TRUE;
727 }
728
729 static gboolean
730 service_disconnect_sync (CamelService *service,
731                          gboolean clean,
732                          GCancellable *cancellable,
733                          GError **error)
734 {
735         /* Default behavior for local storage providers. */
736         return TRUE;
737 }
738
739 static GList *
740 service_query_auth_types_sync (CamelService *service,
741                                GCancellable *cancellable,
742                                GError **error)
743 {
744         return NULL;
745 }
746
747 static void
748 service_connect_thread (GSimpleAsyncResult *simple,
749                         GObject *object,
750                         GCancellable *cancellable)
751 {
752         CamelService *service;
753         CamelServiceClass *class;
754         GError *error = NULL;
755
756         /* Note we call the class method directly here. */
757
758         service = CAMEL_SERVICE (object);
759
760         class = CAMEL_SERVICE_GET_CLASS (service);
761         g_return_if_fail (class->connect_sync != NULL);
762
763         class->connect_sync (service, cancellable, &error);
764
765         if (error != NULL)
766                 g_simple_async_result_take_error (simple, error);
767 }
768
769 static void
770 service_connect (CamelService *service,
771                  gint io_priority,
772                  GCancellable *cancellable,
773                  GAsyncReadyCallback callback,
774                  gpointer user_data)
775 {
776         GSimpleAsyncResult *simple;
777
778         simple = g_simple_async_result_new (
779                 G_OBJECT (service), callback, user_data, service_connect);
780
781         g_simple_async_result_set_check_cancellable (simple, cancellable);
782
783         g_simple_async_result_run_in_thread (
784                 simple, service_connect_thread, io_priority, cancellable);
785
786         g_object_unref (simple);
787 }
788
789 static gboolean
790 service_connect_finish (CamelService *service,
791                         GAsyncResult *result,
792                         GError **error)
793 {
794         GSimpleAsyncResult *simple;
795
796         g_return_val_if_fail (
797                 g_simple_async_result_is_valid (
798                 result, G_OBJECT (service), service_connect), FALSE);
799
800         simple = G_SIMPLE_ASYNC_RESULT (result);
801
802         /* Assume success unless a GError is set. */
803         return !g_simple_async_result_propagate_error (simple, error);
804 }
805
806 static void
807 service_disconnect_thread (GSimpleAsyncResult *simple,
808                            GObject *object,
809                            GCancellable *cancellable)
810 {
811         CamelService *service;
812         CamelServiceClass *class;
813         gboolean clean;
814         GError *error = NULL;
815
816         /* Note we call the class method directly here. */
817
818         service = CAMEL_SERVICE (object);
819         clean = g_simple_async_result_get_op_res_gboolean (simple);
820
821         class = CAMEL_SERVICE_GET_CLASS (service);
822         g_return_if_fail (class->disconnect_sync != NULL);
823
824         class->disconnect_sync (service, clean, cancellable, &error);
825
826         if (error != NULL)
827                 g_simple_async_result_take_error (simple, error);
828 }
829
830 static void
831 service_disconnect (CamelService *service,
832                     gboolean clean,
833                     gint io_priority,
834                     GCancellable *cancellable,
835                     GAsyncReadyCallback callback,
836                     gpointer user_data)
837 {
838         GSimpleAsyncResult *simple;
839
840         simple = g_simple_async_result_new (
841                 G_OBJECT (service), callback, user_data, service_disconnect);
842
843         g_simple_async_result_set_check_cancellable (simple, cancellable);
844
845         g_simple_async_result_set_op_res_gboolean (simple, clean);
846
847         g_simple_async_result_run_in_thread (
848                 simple, service_disconnect_thread, io_priority, cancellable);
849
850         g_object_unref (simple);
851 }
852
853 static gboolean
854 service_disconnect_finish (CamelService *service,
855                            GAsyncResult *result,
856                            GError **error)
857 {
858         GSimpleAsyncResult *simple;
859
860         g_return_val_if_fail (
861                 g_simple_async_result_is_valid (
862                 result, G_OBJECT (service), service_disconnect), FALSE);
863
864         simple = G_SIMPLE_ASYNC_RESULT (result);
865
866         /* Assume success unless a GError is set. */
867         return !g_simple_async_result_propagate_error (simple, error);
868 }
869
870 static void
871 service_authenticate_thread (GSimpleAsyncResult *simple,
872                              GObject *object,
873                              GCancellable *cancellable)
874 {
875         AsyncContext *async_context;
876         GError *error = NULL;
877
878         async_context = g_simple_async_result_get_op_res_gpointer (simple);
879
880         async_context->auth_result = camel_service_authenticate_sync (
881                 CAMEL_SERVICE (object), async_context->auth_mechanism,
882                 cancellable, &error);
883
884         if (error != NULL)
885                 g_simple_async_result_take_error (simple, error);
886 }
887
888 static void
889 service_authenticate (CamelService *service,
890                       const gchar *mechanism,
891                       gint io_priority,
892                       GCancellable *cancellable,
893                       GAsyncReadyCallback callback,
894                       gpointer user_data)
895 {
896         GSimpleAsyncResult *simple;
897         AsyncContext *async_context;
898
899         async_context = g_slice_new0 (AsyncContext);
900         async_context->auth_mechanism = g_strdup (mechanism);
901
902         simple = g_simple_async_result_new (
903                 G_OBJECT (service), callback, user_data, service_authenticate);
904
905         g_simple_async_result_set_check_cancellable (simple, cancellable);
906
907         g_simple_async_result_set_op_res_gpointer (
908                 simple, async_context, (GDestroyNotify) async_context_free);
909
910         g_simple_async_result_run_in_thread (
911                 simple, service_authenticate_thread, io_priority, cancellable);
912
913         g_object_unref (simple);
914 }
915
916 static CamelAuthenticationResult
917 service_authenticate_finish (CamelService *service,
918                              GAsyncResult *result,
919                              GError **error)
920 {
921         GSimpleAsyncResult *simple;
922         AsyncContext *async_context;
923
924         g_return_val_if_fail (
925                 g_simple_async_result_is_valid (
926                 result, G_OBJECT (service), service_authenticate),
927                 CAMEL_AUTHENTICATION_REJECTED);
928
929         simple = G_SIMPLE_ASYNC_RESULT (result);
930         async_context = g_simple_async_result_get_op_res_gpointer (simple);
931
932         if (g_simple_async_result_propagate_error (simple, error))
933                 return CAMEL_AUTHENTICATION_ERROR;
934
935         return async_context->auth_result;
936 }
937
938 static void
939 service_query_auth_types_thread (GSimpleAsyncResult *simple,
940                                  GObject *object,
941                                  GCancellable *cancellable)
942 {
943         AsyncContext *async_context;
944         GError *error = NULL;
945
946         async_context = g_simple_async_result_get_op_res_gpointer (simple);
947
948         async_context->auth_types = camel_service_query_auth_types_sync (
949                 CAMEL_SERVICE (object), cancellable, &error);
950
951         if (error != NULL)
952                 g_simple_async_result_take_error (simple, error);
953 }
954
955 static void
956 service_query_auth_types (CamelService *service,
957                           gint io_priority,
958                           GCancellable *cancellable,
959                           GAsyncReadyCallback callback,
960                           gpointer user_data)
961 {
962         GSimpleAsyncResult *simple;
963         AsyncContext *async_context;
964
965         async_context = g_slice_new0 (AsyncContext);
966
967         simple = g_simple_async_result_new (
968                 G_OBJECT (service), callback,
969                 user_data, service_query_auth_types);
970
971         g_simple_async_result_set_check_cancellable (simple, cancellable);
972
973         g_simple_async_result_set_op_res_gpointer (
974                 simple, async_context, (GDestroyNotify) async_context_free);
975
976         g_simple_async_result_run_in_thread (
977                 simple, service_query_auth_types_thread,
978                 io_priority, cancellable);
979
980         g_object_unref (simple);
981 }
982
983 static GList *
984 service_query_auth_types_finish (CamelService *service,
985                                  GAsyncResult *result,
986                                  GError **error)
987 {
988         GSimpleAsyncResult *simple;
989         AsyncContext *async_context;
990
991         g_return_val_if_fail (
992                 g_simple_async_result_is_valid (
993                 result, G_OBJECT (service),
994                 service_query_auth_types), NULL);
995
996         simple = G_SIMPLE_ASYNC_RESULT (result);
997         async_context = g_simple_async_result_get_op_res_gpointer (simple);
998
999         if (g_simple_async_result_propagate_error (simple, error))
1000                 return NULL;
1001
1002         return g_list_copy (async_context->auth_types);
1003 }
1004
1005 static gboolean
1006 service_initable_init (GInitable *initable,
1007                        GCancellable *cancellable,
1008                        GError **error)
1009 {
1010         /* Nothing to do here, but we may need add something in the future.
1011          * For now this is a placeholder so subclasses can safely chain up. */
1012
1013         return TRUE;
1014 }
1015
1016 static void
1017 camel_service_class_init (CamelServiceClass *class)
1018 {
1019         GObjectClass *object_class;
1020
1021         g_type_class_add_private (class, sizeof (CamelServicePrivate));
1022
1023         object_class = G_OBJECT_CLASS (class);
1024         object_class->set_property = service_set_property;
1025         object_class->get_property = service_get_property;
1026         object_class->dispose = service_dispose;
1027         object_class->finalize = service_finalize;
1028         object_class->constructed = service_constructed;
1029
1030         class->settings_type = CAMEL_TYPE_SETTINGS;
1031         class->get_name = service_get_name;
1032         class->connect_sync = service_connect_sync;
1033         class->disconnect_sync = service_disconnect_sync;
1034         class->query_auth_types_sync = service_query_auth_types_sync;
1035
1036         class->connect = service_connect;
1037         class->connect_finish = service_connect_finish;
1038         class->disconnect = service_disconnect;
1039         class->disconnect_finish = service_disconnect_finish;
1040         class->authenticate = service_authenticate;
1041         class->authenticate_finish = service_authenticate_finish;
1042         class->query_auth_types = service_query_auth_types;
1043         class->query_auth_types_finish = service_query_auth_types_finish;
1044
1045         g_object_class_install_property (
1046                 object_class,
1047                 PROP_CONNECTION_STATUS,
1048                 g_param_spec_enum (
1049                         "connection-status",
1050                         "Connection Status",
1051                         "The connection status for the service",
1052                         CAMEL_TYPE_SERVICE_CONNECTION_STATUS,
1053                         CAMEL_SERVICE_DISCONNECTED,
1054                         G_PARAM_READABLE |
1055                         G_PARAM_STATIC_STRINGS));
1056
1057         g_object_class_install_property (
1058                 object_class,
1059                 PROP_DISPLAY_NAME,
1060                 g_param_spec_string (
1061                         "display-name",
1062                         "Display Name",
1063                         "The display name for the service",
1064                         NULL,
1065                         G_PARAM_READWRITE |
1066                         G_PARAM_CONSTRUCT |
1067                         G_PARAM_STATIC_STRINGS));
1068
1069         g_object_class_install_property (
1070                 object_class,
1071                 PROP_PASSWORD,
1072                 g_param_spec_string (
1073                         "password",
1074                         "Password",
1075                         "The password for the service",
1076                         NULL,
1077                         G_PARAM_READWRITE |
1078                         G_PARAM_CONSTRUCT |
1079                         G_PARAM_STATIC_STRINGS));
1080
1081         g_object_class_install_property (
1082                 object_class,
1083                 PROP_PROVIDER,
1084                 g_param_spec_pointer (
1085                         "provider",
1086                         "Provider",
1087                         "The CamelProvider for the service",
1088                         G_PARAM_READWRITE |
1089                         G_PARAM_CONSTRUCT_ONLY |
1090                         G_PARAM_STATIC_STRINGS));
1091
1092         g_object_class_install_property (
1093                 object_class,
1094                 PROP_SESSION,
1095                 g_param_spec_object (
1096                         "session",
1097                         "Session",
1098                         "A CamelSession instance",
1099                         CAMEL_TYPE_SESSION,
1100                         G_PARAM_READWRITE |
1101                         G_PARAM_CONSTRUCT_ONLY |
1102                         G_PARAM_STATIC_STRINGS));
1103
1104         g_object_class_install_property (
1105                 object_class,
1106                 PROP_SETTINGS,
1107                 g_param_spec_object (
1108                         "settings",
1109                         "Settings",
1110                         "A CamelSettings instance",
1111                         CAMEL_TYPE_SETTINGS,
1112                         G_PARAM_READWRITE |
1113                         G_PARAM_CONSTRUCT |
1114                         G_PARAM_STATIC_STRINGS));
1115
1116         g_object_class_install_property (
1117                 object_class,
1118                 PROP_UID,
1119                 g_param_spec_string (
1120                         "uid",
1121                         "UID",
1122                         "The unique identity of the service",
1123                         NULL,
1124                         G_PARAM_READWRITE |
1125                         G_PARAM_CONSTRUCT_ONLY |
1126                         G_PARAM_STATIC_STRINGS));
1127 }
1128
1129 static void
1130 camel_service_initable_init (GInitableIface *interface)
1131 {
1132         interface->init = service_initable_init;
1133 }
1134
1135 static void
1136 camel_service_init (CamelService *service)
1137 {
1138         service->priv = CAMEL_SERVICE_GET_PRIVATE (service);
1139
1140         g_mutex_init (&service->priv->settings_lock);
1141         g_mutex_init (&service->priv->connection_lock);
1142         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1143 }
1144
1145 G_DEFINE_QUARK (camel-service-error-quark, camel_service_error)
1146
1147 /**
1148  * camel_service_migrate_files:
1149  * @service: a #CamelService
1150  *
1151  * Performs any necessary file migrations for @service.  This should be
1152  * called after installing or configuring the @service's #CamelSettings,
1153  * since it requires building a URL string for @service.
1154  *
1155  * Since: 3.4
1156  **/
1157 void
1158 camel_service_migrate_files (CamelService *service)
1159 {
1160         const gchar *new_data_dir;
1161         gchar *old_data_dir;
1162
1163         g_return_if_fail (CAMEL_IS_SERVICE (service));
1164
1165         new_data_dir = camel_service_get_user_data_dir (service);
1166         old_data_dir = service_find_old_data_dir (service);
1167
1168         /* If the old data directory name exists, try renaming
1169          * it to the new data directory.  Failure is non-fatal. */
1170         if (old_data_dir != NULL) {
1171                 g_rename (old_data_dir, new_data_dir);
1172                 g_free (old_data_dir);
1173         }
1174 }
1175
1176 /**
1177  * camel_service_new_camel_url:
1178  * @service: a #CamelService
1179  *
1180  * Returns a new #CamelURL representing @service.
1181  * Free the returned #CamelURL with camel_url_free().
1182  *
1183  * Returns: a new #CamelURL
1184  *
1185  * Since: 3.2
1186  **/
1187 CamelURL *
1188 camel_service_new_camel_url (CamelService *service)
1189 {
1190         CamelURL *url;
1191         CamelProvider *provider;
1192         CamelSettings *settings;
1193         gchar *host = NULL;
1194         gchar *user = NULL;
1195         gchar *path = NULL;
1196         guint16 port = 0;
1197
1198         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1199
1200         provider = camel_service_get_provider (service);
1201         g_return_val_if_fail (provider != NULL, NULL);
1202
1203         settings = camel_service_ref_settings (service);
1204
1205         /* Allocate as camel_url_new_with_base() does. */
1206         url = g_new0 (CamelURL, 1);
1207
1208         if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
1209                 CamelNetworkSettings *network_settings;
1210
1211                 network_settings = CAMEL_NETWORK_SETTINGS (settings);
1212                 host = camel_network_settings_dup_host (network_settings);
1213                 port = camel_network_settings_get_port (network_settings);
1214                 user = camel_network_settings_dup_user (network_settings);
1215         }
1216
1217         if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
1218                 CamelLocalSettings *local_settings;
1219
1220                 local_settings = CAMEL_LOCAL_SETTINGS (settings);
1221                 path = camel_local_settings_dup_path (local_settings);
1222         }
1223
1224         camel_url_set_protocol (url, provider->protocol);
1225         camel_url_set_host (url, host);
1226         camel_url_set_port (url, port);
1227         camel_url_set_user (url, user);
1228         camel_url_set_path (url, path);
1229
1230         g_free (host);
1231         g_free (user);
1232         g_free (path);
1233
1234         g_object_unref (settings);
1235
1236         return url;
1237 }
1238
1239 /**
1240  * camel_service_get_connection_status:
1241  * @service: a #CamelService
1242  *
1243  * Returns the connection status for @service.
1244  *
1245  * Returns: the connection status
1246  *
1247  * Since: 3.2
1248  **/
1249 CamelServiceConnectionStatus
1250 camel_service_get_connection_status (CamelService *service)
1251 {
1252         g_return_val_if_fail (
1253                 CAMEL_IS_SERVICE (service),
1254                 CAMEL_SERVICE_DISCONNECTED);
1255
1256         return service->priv->status;
1257 }
1258
1259 /**
1260  * camel_service_get_display_name:
1261  * @service: a #CamelService
1262  *
1263  * Returns the display name for @service, or %NULL if @service has not
1264  * been given a display name.  The display name is intended for use in
1265  * a user interface and should generally be given a user-defined name.
1266  *
1267  * Compare this with camel_service_get_name(), which returns a built-in
1268  * description of the type of service (IMAP, SMTP, etc.).
1269  *
1270  * Returns: the display name for @service, or %NULL
1271  *
1272  * Since: 3.2
1273  **/
1274 const gchar *
1275 camel_service_get_display_name (CamelService *service)
1276 {
1277         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1278
1279         return service->priv->display_name;
1280 }
1281
1282 /**
1283  * camel_service_set_display_name:
1284  * @service: a #CamelService
1285  * @display_name: a valid UTF-8 string, or %NULL
1286  *
1287  * Assigns a UTF-8 display name to @service.  The display name is intended
1288  * for use in a user interface and should generally be given a user-defined
1289  * name.
1290  *
1291  * Compare this with camel_service_get_name(), which returns a built-in
1292  * description of the type of service (IMAP, SMTP, etc.).
1293  *
1294  * Since: 3.2
1295  **/
1296 void
1297 camel_service_set_display_name (CamelService *service,
1298                                 const gchar *display_name)
1299 {
1300         g_return_if_fail (CAMEL_IS_SERVICE (service));
1301
1302         if (g_strcmp0 (service->priv->display_name, display_name) == 0)
1303                 return;
1304
1305         if (display_name != NULL)
1306                 g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
1307
1308         g_free (service->priv->display_name);
1309         service->priv->display_name = g_strdup (display_name);
1310
1311         g_object_notify (G_OBJECT (service), "display-name");
1312 }
1313
1314 /**
1315  * camel_service_get_password:
1316  * @service: a #CamelService
1317  *
1318  * Returns the password for @service.  Some SASL mechanisms use this
1319  * when attempting to authenticate.
1320  *
1321  * Returns: the password for @service
1322  *
1323  * Since: 3.4
1324  **/
1325 const gchar *
1326 camel_service_get_password (CamelService *service)
1327 {
1328         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1329
1330         return service->priv->password;
1331 }
1332
1333 /**
1334  * camel_service_set_password:
1335  * @service: a #CamelService
1336  * @password: the password for @service
1337  *
1338  * Sets the password for @service.  Use this function to cache the password
1339  * in memory after obtaining it through camel_session_get_password().  Some
1340  * SASL mechanisms use this when attempting to authenticate.
1341  *
1342  * Since: 3.4
1343  **/
1344 void
1345 camel_service_set_password (CamelService *service,
1346                             const gchar *password)
1347 {
1348         g_return_if_fail (CAMEL_IS_SERVICE (service));
1349
1350         if (g_strcmp0 (service->priv->password, password) == 0)
1351                 return;
1352
1353         g_free (service->priv->password);
1354         service->priv->password = g_strdup (password);
1355
1356         g_object_notify (G_OBJECT (service), "password");
1357 }
1358
1359 /**
1360  * camel_service_get_user_data_dir:
1361  * @service: a #CamelService
1362  *
1363  * Returns the base directory under which to store user-specific data
1364  * for @service.  The directory is formed by appending the directory
1365  * returned by camel_session_get_user_data_dir() with the service's
1366  * #CamelService:uid value.
1367  *
1368  * Returns: the base directory for @service
1369  *
1370  * Since: 3.2
1371  **/
1372 const gchar *
1373 camel_service_get_user_data_dir (CamelService *service)
1374 {
1375         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1376
1377         return service->priv->user_data_dir;
1378 }
1379
1380 /**
1381  * camel_service_get_user_cache_dir:
1382  * @service: a #CamelService
1383  *
1384  * Returns the base directory under which to store cache data
1385  * for @service.  The directory is formed by appending the directory
1386  * returned by camel_session_get_user_cache_dir() with the service's
1387  * #CamelService:uid value.
1388  *
1389  * Returns: the base cache directory for @service
1390  *
1391  * Since: 3.4
1392  **/
1393 const gchar *
1394 camel_service_get_user_cache_dir (CamelService *service)
1395 {
1396         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1397
1398         return service->priv->user_cache_dir;
1399 }
1400
1401 /**
1402  * camel_service_get_name:
1403  * @service: a #CamelService
1404  * @brief: whether or not to use a briefer form
1405  *
1406  * This gets the name of the service in a "friendly" (suitable for
1407  * humans) form. If @brief is %TRUE, this should be a brief description
1408  * such as for use in the folder tree. If @brief is %FALSE, it should
1409  * be a more complete and mostly unambiguous description.
1410  *
1411  * Returns: a description of the service which the caller must free
1412  **/
1413 gchar *
1414 camel_service_get_name (CamelService *service,
1415                         gboolean brief)
1416 {
1417         CamelServiceClass *class;
1418
1419         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1420
1421         class = CAMEL_SERVICE_GET_CLASS (service);
1422         g_return_val_if_fail (class->get_name != NULL, NULL);
1423
1424         return class->get_name (service, brief);
1425 }
1426
1427 /**
1428  * camel_service_get_provider:
1429  * @service: a #CamelService
1430  *
1431  * Gets the #CamelProvider associated with the service.
1432  *
1433  * Returns: the #CamelProvider
1434  **/
1435 CamelProvider *
1436 camel_service_get_provider (CamelService *service)
1437 {
1438         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1439
1440         return service->priv->provider;
1441 }
1442
1443 /**
1444  * camel_service_get_session:
1445  * @service: a #CamelService
1446  *
1447  * Gets the #CamelSession associated with the service.
1448  *
1449  * Returns: the #CamelSession
1450  **/
1451 CamelSession *
1452 camel_service_get_session (CamelService *service)
1453 {
1454         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1455
1456         return CAMEL_SESSION (service->priv->session);
1457 }
1458
1459 /**
1460  * camel_service_ref_settings:
1461  * @service: a #CamelService
1462  *
1463  * Returns the #CamelSettings instance associated with the service.
1464  *
1465  * The returned #CamelSettings is referenced for thread-safety and must
1466  * be unreferenced with g_object_unref() when finished with it.
1467  *
1468  * Returns: the #CamelSettings
1469  *
1470  * Since: 3.6
1471  **/
1472 CamelSettings *
1473 camel_service_ref_settings (CamelService *service)
1474 {
1475         CamelSettings *settings;
1476
1477         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1478
1479         /* Every service should have a settings object. */
1480         g_return_val_if_fail (service->priv->settings != NULL, NULL);
1481
1482         g_mutex_lock (&service->priv->settings_lock);
1483
1484         settings = g_object_ref (service->priv->settings);
1485
1486         g_mutex_unlock (&service->priv->settings_lock);
1487
1488         return settings;
1489 }
1490
1491 /**
1492  * camel_service_set_settings:
1493  * @service: a #CamelService
1494  * @settings: an instance derviced from #CamelSettings, or %NULL
1495  *
1496  * Associates a new #CamelSettings instance with the service.
1497  * The @settings instance must match the settings type defined in
1498  * #CamelServiceClass.  If @settings is %NULL, a new #CamelSettings
1499  * instance of the appropriate type is created with all properties
1500  * set to defaults.
1501  *
1502  * Since: 3.2
1503  **/
1504 void
1505 camel_service_set_settings (CamelService *service,
1506                             CamelSettings *settings)
1507 {
1508         CamelServiceClass *class;
1509
1510         g_return_if_fail (CAMEL_IS_SERVICE (service));
1511
1512         class = CAMEL_SERVICE_GET_CLASS (service);
1513
1514         if (settings != NULL) {
1515                 g_return_if_fail (
1516                         g_type_is_a (
1517                                 G_OBJECT_TYPE (settings),
1518                                 class->settings_type));
1519                 g_object_ref (settings);
1520
1521         } else {
1522                 g_return_if_fail (
1523                         g_type_is_a (
1524                                 class->settings_type,
1525                                 CAMEL_TYPE_SETTINGS));
1526                 settings = g_object_new (class->settings_type, NULL);
1527         }
1528
1529         g_mutex_lock (&service->priv->settings_lock);
1530
1531         if (service->priv->settings != NULL)
1532                 g_object_unref (service->priv->settings);
1533
1534         service->priv->settings = settings;  /* takes ownership */
1535
1536         g_mutex_unlock (&service->priv->settings_lock);
1537
1538         g_object_notify (G_OBJECT (service), "settings");
1539 }
1540
1541 /**
1542  * camel_service_get_uid:
1543  * @service: a #CamelService
1544  *
1545  * Gets the unique identifier string associated with the service.
1546  *
1547  * Returns: the UID string
1548  *
1549  * Since: 3.2
1550  **/
1551 const gchar *
1552 camel_service_get_uid (CamelService *service)
1553 {
1554         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1555
1556         return service->priv->uid;
1557 }
1558
1559 /**
1560  * camel_service_connect_sync:
1561  * @service: a #CamelService
1562  * @cancellable: optional #GCancellable object, or %NULL
1563  * @error: return location for a #GError, or %NULL
1564  *
1565  * Connects @service to a remote server using the information in its
1566  * #CamelService:settings instance.
1567  *
1568  * If a connect operation is already in progress when this function is
1569  * called, its results will be reflected in this connect operation.
1570  *
1571  * Returns: %TRUE if the connection is made or %FALSE otherwise
1572  *
1573  * Since: 3.6
1574  **/
1575 gboolean
1576 camel_service_connect_sync (CamelService *service,
1577                             GCancellable *cancellable,
1578                             GError **error)
1579 {
1580         AsyncClosure *closure;
1581         GAsyncResult *result;
1582         gboolean success;
1583
1584         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1585
1586         closure = async_closure_new ();
1587
1588         camel_service_connect (
1589                 service, G_PRIORITY_DEFAULT, cancellable,
1590                 async_closure_callback, closure);
1591
1592         result = async_closure_wait (closure);
1593
1594         success = camel_service_connect_finish (service, result, error);
1595
1596         async_closure_free (closure);
1597
1598         return success;
1599 }
1600
1601 /**
1602  * camel_service_connect:
1603  * @service: a #CamelService
1604  * @io_priority: the I/O priority of the request
1605  * @cancellable: optional #GCancellable object, or %NULL
1606  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1607  * @user_data: data to pass to the callback function
1608  *
1609  * Asynchronously connects @service to a remote server using the information
1610  * in its #CamelService:settings instance.
1611  *
1612  * If a connect operation is already in progress when this function is
1613  * called, its results will be reflected in this connect operation.
1614  *
1615  * If any disconnect operations are in progress when this function is
1616  * called, they will be cancelled.
1617  *
1618  * When the operation is finished, @callback will be called.  You can
1619  * then call camel_service_connect_finish() to get the result of the
1620  * operation.
1621  *
1622  * Since: 3.6
1623  **/
1624 void
1625 camel_service_connect (CamelService *service,
1626                        gint io_priority,
1627                        GCancellable *cancellable,
1628                        GAsyncReadyCallback callback,
1629                        gpointer user_data)
1630 {
1631         ConnectionOp *op;
1632         CamelServiceClass *class;
1633         GSimpleAsyncResult *simple;
1634
1635         g_return_if_fail (CAMEL_IS_SERVICE (service));
1636
1637         class = CAMEL_SERVICE_GET_CLASS (service);
1638         g_return_if_fail (class->connect != NULL);
1639
1640         simple = g_simple_async_result_new (
1641                 G_OBJECT (service), callback,
1642                 user_data, camel_service_connect);
1643
1644         g_simple_async_result_set_check_cancellable (simple, cancellable);
1645
1646         g_mutex_lock (&service->priv->connection_lock);
1647
1648         switch (service->priv->status) {
1649
1650                 /* If a connect operation is already in progress,
1651                  * queue this operation so it completes at the same
1652                  * time the first connect operation completes. */
1653                 case CAMEL_SERVICE_CONNECTING:
1654                         connection_op_add_pending (
1655                                 service->priv->connection_op,
1656                                 simple, cancellable);
1657                         break;
1658
1659                 /* If we're already connected, just report success. */
1660                 case CAMEL_SERVICE_CONNECTED:
1661                         g_simple_async_result_complete_in_idle (simple);
1662                         break;
1663
1664                 /* If a disconnect operation is currently in progress,
1665                  * cancel it and make room for the connect operation. */
1666                 case CAMEL_SERVICE_DISCONNECTING:
1667                         g_return_if_fail (
1668                                 service->priv->connection_op != NULL);
1669                         g_cancellable_cancel (
1670                                 service->priv->connection_op->cancellable);
1671                         connection_op_unref (service->priv->connection_op);
1672                         service->priv->connection_op = NULL;
1673                         /* fall through */
1674
1675                 /* Start a new connect operation.  Subsequent connect
1676                  * operations are queued until this operation completes
1677                  * and will share this operation's result. */
1678                 case CAMEL_SERVICE_DISCONNECTED:
1679                         g_return_if_fail (
1680                                 service->priv->connection_op == NULL);
1681
1682                         op = connection_op_new (simple, cancellable);
1683                         service->priv->connection_op = op;
1684
1685                         service->priv->status = CAMEL_SERVICE_CONNECTING;
1686                         service_queue_notify_connection_status (service);
1687
1688                         class->connect (
1689                                 service,
1690                                 io_priority,
1691                                 cancellable,
1692                                 service_shared_connect_cb,
1693                                 connection_op_ref (op));
1694                         break;
1695
1696                 default:
1697                         g_warn_if_reached ();
1698         }
1699
1700         g_mutex_unlock (&service->priv->connection_lock);
1701
1702         g_object_unref (simple);
1703 }
1704
1705 /**
1706  * camel_service_connect_finish:
1707  * @service: a #CamelService
1708  * @result: a #GAsyncResult
1709  * @error: return location for a #GError, or %NULL
1710  *
1711  * Finishes the operation started with camel_service_connect().
1712  *
1713  * Returns: %TRUE if the connection was made or %FALSE otherwise
1714  *
1715  * Since: 3.6
1716  **/
1717 gboolean
1718 camel_service_connect_finish (CamelService *service,
1719                               GAsyncResult *result,
1720                               GError **error)
1721 {
1722         GSimpleAsyncResult *simple;
1723
1724         g_return_val_if_fail (
1725                 g_simple_async_result_is_valid (
1726                 result, G_OBJECT (service), camel_service_connect), FALSE);
1727
1728         simple = G_SIMPLE_ASYNC_RESULT (result);
1729
1730         /* Assume success unless a GError is set. */
1731         return !g_simple_async_result_propagate_error (simple, error);
1732 }
1733
1734 /**
1735  * camel_service_disconnect_sync:
1736  * @service: a #CamelService
1737  * @clean: whether or not to try to disconnect cleanly
1738  * @cancellable: optional #GCancellable object, or %NULL
1739  * @error: return location for a #GError, or %NULL
1740  *
1741  * Disconnect from the service. If @clean is %FALSE, it should not
1742  * try to do any synchronizing or other cleanup of the connection.
1743  *
1744  * If a disconnect operation is already in progress when this function is
1745  * called, its results will be reflected in this disconnect operation.
1746  *
1747  * If any connect operations are in progress when this function is called,
1748  * they will be cancelled.
1749  *
1750  * Returns: %TRUE if the connection was severed or %FALSE otherwise
1751  *
1752  * Since: 3.6
1753  **/
1754 gboolean
1755 camel_service_disconnect_sync (CamelService *service,
1756                                gboolean clean,
1757                                GCancellable *cancellable,
1758                                GError **error)
1759 {
1760         AsyncClosure *closure;
1761         GAsyncResult *result;
1762         gboolean success;
1763
1764         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1765
1766         closure = async_closure_new ();
1767
1768         camel_service_disconnect (
1769                 service, clean, G_PRIORITY_DEFAULT,
1770                 cancellable, async_closure_callback, closure);
1771
1772         result = async_closure_wait (closure);
1773
1774         success = camel_service_disconnect_finish (service, result, error);
1775
1776         async_closure_free (closure);
1777
1778         return success;
1779 }
1780
1781 /**
1782  * camel_service_disconnect:
1783  * @service: a #CamelService
1784  * @clean: whether or not to try to disconnect cleanly
1785  * @io_priority: the I/O priority of the request
1786  * @cancellable: optional #GCancellable object, or %NULL
1787  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1788  * @user_data: data to pass to the callback function
1789  *
1790  * If a disconnect operation is already in progress when this function is
1791  * called, its results will be reflected in this disconnect operation.
1792  *
1793  * If any connect operations are in progress when this function is called,
1794  * they will be cancelled.
1795  *
1796  * When the operation is finished, @callback will be called.  You can
1797  * then call camel_service_disconnect_finish() to get the result of the
1798  * operation.
1799  *
1800  * Since: 3.6
1801  **/
1802 void
1803 camel_service_disconnect (CamelService *service,
1804                           gboolean clean,
1805                           gint io_priority,
1806                           GCancellable *cancellable,
1807                           GAsyncReadyCallback callback,
1808                           gpointer user_data)
1809 {
1810         ConnectionOp *op;
1811         CamelServiceClass *class;
1812         GSimpleAsyncResult *simple;
1813
1814         g_return_if_fail (CAMEL_IS_SERVICE (service));
1815
1816         class = CAMEL_SERVICE_GET_CLASS (service);
1817         g_return_if_fail (class->disconnect != NULL);
1818
1819         simple = g_simple_async_result_new (
1820                 G_OBJECT (service), callback,
1821                 user_data, camel_service_disconnect);
1822
1823         g_simple_async_result_set_check_cancellable (simple, cancellable);
1824
1825         g_mutex_lock (&service->priv->connection_lock);
1826
1827         switch (service->priv->status) {
1828
1829                 /* If a connect operation is currently in progress,
1830                  * cancel it and make room for the disconnect operation. */
1831                 case CAMEL_SERVICE_CONNECTING:
1832                         g_return_if_fail (
1833                                 service->priv->connection_op != NULL);
1834                         g_cancellable_cancel (
1835                                 service->priv->connection_op->cancellable);
1836                         connection_op_unref (service->priv->connection_op);
1837                         service->priv->connection_op = NULL;
1838                         /* fall through */
1839
1840                 /* Start a new disconnect operation.  Subsequent disconnect
1841                  * operations are queued until this operation completes and
1842                  * will share this operation's result. */
1843                 case CAMEL_SERVICE_CONNECTED:
1844                         g_return_if_fail (
1845                                 service->priv->connection_op == NULL);
1846
1847                         op = connection_op_new (simple, cancellable);
1848                         service->priv->connection_op = op;
1849
1850                         service->priv->status = CAMEL_SERVICE_DISCONNECTING;
1851                         service_queue_notify_connection_status (service);
1852
1853                         class->disconnect (
1854                                 service, clean,
1855                                 io_priority,
1856                                 cancellable,
1857                                 service_shared_disconnect_cb,
1858                                 connection_op_ref (op));
1859                         break;
1860
1861                 /* If a disconnect operation is already in progress,
1862                  * queue this operation so it completes at the same
1863                  * time the first disconnect operation completes. */
1864                 case CAMEL_SERVICE_DISCONNECTING:
1865                         connection_op_add_pending (
1866                                 service->priv->connection_op,
1867                                 simple, cancellable);
1868                         break;
1869
1870                 /* If we're already disconnected, just report success. */
1871                 case CAMEL_SERVICE_DISCONNECTED:
1872                         g_simple_async_result_complete_in_idle (simple);
1873                         break;
1874
1875                 default:
1876                         g_warn_if_reached ();
1877         }
1878
1879         g_mutex_unlock (&service->priv->connection_lock);
1880
1881         g_object_unref (simple);
1882 }
1883
1884 /**
1885  * camel_service_disconnect_finish:
1886  * @service: a #CamelService
1887  * @result: a #GAsyncResult
1888  * @error: return location for a #GError, or %NULL
1889  *
1890  * Finishes the operation started with camel_service_disconnect().
1891  *
1892  * Returns: %TRUE if the connection was severed or %FALSE otherwise
1893  *
1894  * Since: 3.6
1895  **/
1896 gboolean
1897 camel_service_disconnect_finish (CamelService *service,
1898                                  GAsyncResult *result,
1899                                  GError **error)
1900 {
1901         GSimpleAsyncResult *simple;
1902
1903         g_return_val_if_fail (
1904                 g_simple_async_result_is_valid (
1905                 result, G_OBJECT (service), camel_service_disconnect), FALSE);
1906
1907         simple = G_SIMPLE_ASYNC_RESULT (result);
1908
1909         /* Assume success unless a GError is set. */
1910         return !g_simple_async_result_propagate_error (simple, error);
1911 }
1912
1913 /**
1914  * camel_service_authenticate_sync:
1915  * @service: a #CamelService
1916  * @mechanism: a SASL mechanism name, or %NULL
1917  * @cancellable: optional #GCancellable object, or %NULL
1918  * @error: return location for a #GError, or %NULL
1919  *
1920  * Attempts to authenticate @service using @mechanism and, if necessary,
1921  * @service's #CamelService:password property.  The function makes only
1922  * ONE attempt at authentication and does not loop.
1923  *
1924  * If the authentication attempt completed and the server accepted the
1925  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
1926  *
1927  * If the authentication attempt completed but the server rejected the
1928  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
1929  *
1930  * If the authentication attempt failed to complete due to a network
1931  * communication issue or some other mishap, the function sets @error
1932  * and returns #CAMEL_AUTHENTICATION_ERROR.
1933  *
1934  * Generally this function should only be called from a #CamelSession
1935  * subclass in order to implement its own authentication loop.
1936  *
1937  * Returns: the authentication result
1938  *
1939  * Since: 3.4
1940  **/
1941 CamelAuthenticationResult
1942 camel_service_authenticate_sync (CamelService *service,
1943                                  const gchar *mechanism,
1944                                  GCancellable *cancellable,
1945                                  GError **error)
1946 {
1947         CamelServiceClass *class;
1948         CamelAuthenticationResult result;
1949
1950         g_return_val_if_fail (
1951                 CAMEL_IS_SERVICE (service),
1952                 CAMEL_AUTHENTICATION_REJECTED);
1953
1954         class = CAMEL_SERVICE_GET_CLASS (service);
1955         g_return_val_if_fail (
1956                 class->authenticate_sync != NULL,
1957                 CAMEL_AUTHENTICATION_REJECTED);
1958
1959         result = class->authenticate_sync (
1960                 service, mechanism, cancellable, error);
1961         CAMEL_CHECK_GERROR (
1962                 service, authenticate_sync,
1963                 result != CAMEL_AUTHENTICATION_ERROR, error);
1964
1965         return result;
1966 }
1967
1968 /**
1969  * camel_service_authenticate:
1970  * @service: a #CamelService
1971  * @mechanism: a SASL mechanism name, or %NULL
1972  * @io_priority: the I/O priority of the request
1973  * @cancellable: optional #GCancellable object, or %NULL
1974  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1975  * @user_data: data to pass to the callback function
1976  *
1977  * Asynchronously attempts to authenticate @service using @mechanism and,
1978  * if necessary, @service's #CamelService:password property.  The function
1979  * makes only ONE attempt at authentication and does not loop.
1980  *
1981  * Generally this function should only be called from a #CamelSession
1982  * subclass in order to implement its own authentication loop.
1983  *
1984  * When the operation is finished, @callback will be called.  You can
1985  * then call camel_service_authenticate_finish() to get the result of
1986  * the operation.
1987  *
1988  * Since: 3.4
1989  **/
1990 void
1991 camel_service_authenticate (CamelService *service,
1992                             const gchar *mechanism,
1993                             gint io_priority,
1994                             GCancellable *cancellable,
1995                             GAsyncReadyCallback callback,
1996                             gpointer user_data)
1997 {
1998         CamelServiceClass *class;
1999
2000         g_return_if_fail (CAMEL_IS_SERVICE (service));
2001
2002         class = CAMEL_SERVICE_GET_CLASS (service);
2003         g_return_if_fail (class->authenticate != NULL);
2004
2005         class->authenticate (
2006                 service, mechanism, io_priority,
2007                 cancellable, callback, user_data);
2008 }
2009
2010 /**
2011  * camel_service_authenticate_finish:
2012  * @service: a #CamelService
2013  * @result: a #GAsyncResult
2014  * @error: return location for a #GError, or %NULL
2015  *
2016  * Finishes the operation started with camel_service_authenticate().
2017  *
2018  * If the authentication attempt completed and the server accepted the
2019  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2020  *
2021  * If the authentication attempt completed but the server rejected the
2022  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2023  *
2024  * If the authentication attempt failed to complete due to a network
2025  * communication issue or some other mishap, the function sets @error
2026  * and returns #CAMEL_AUTHENTICATION_ERROR.
2027  *
2028  * Returns: the authentication result
2029  *
2030  * Since: 3.4
2031  **/
2032 CamelAuthenticationResult
2033 camel_service_authenticate_finish (CamelService *service,
2034                                    GAsyncResult *result,
2035                                    GError **error)
2036 {
2037         CamelServiceClass *class;
2038
2039         g_return_val_if_fail (
2040                 CAMEL_IS_SERVICE (service),
2041                 CAMEL_AUTHENTICATION_REJECTED);
2042         g_return_val_if_fail (
2043                 G_IS_ASYNC_RESULT (result),
2044                 CAMEL_AUTHENTICATION_REJECTED);
2045
2046         class = CAMEL_SERVICE_GET_CLASS (service);
2047         g_return_val_if_fail (
2048                 class->authenticate_finish,
2049                 CAMEL_AUTHENTICATION_REJECTED);
2050
2051         return class->authenticate_finish (service, result, error);
2052 }
2053
2054 /**
2055  * camel_service_query_auth_types_sync:
2056  * @service: a #CamelService
2057  * @cancellable: optional #GCancellable object, or %NULL
2058  * @error: return location for a #GError, or %NULL
2059  *
2060  * Obtains a list of authentication types supported by @service.
2061  * Free the returned list with g_list_free().
2062  *
2063  * Returns: a list of #CamelServiceAuthType structs
2064  **/
2065 GList *
2066 camel_service_query_auth_types_sync (CamelService *service,
2067                                      GCancellable *cancellable,
2068                                      GError **error)
2069 {
2070         CamelServiceClass *class;
2071
2072         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2073
2074         class = CAMEL_SERVICE_GET_CLASS (service);
2075         g_return_val_if_fail (class->query_auth_types_sync != NULL, NULL);
2076
2077         return class->query_auth_types_sync (service, cancellable, error);
2078 }
2079
2080 /**
2081  * camel_service_query_auth_types:
2082  * @service: a #CamelService
2083  * @io_priority: the I/O priority of the request
2084  * @cancellable: optional #GCancellable object, or %NULL
2085  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2086  * @user_data: data to pass to the callback function
2087  *
2088  * Asynchronously obtains a list of authentication types supported by
2089  * @service.
2090  *
2091  * When the operation is finished, @callback will be called.  You can
2092  * then call camel_service_query_auth_types_finish() to get the result
2093  * of the operation.
2094  *
2095  * Since: 3.2
2096  **/
2097 void
2098 camel_service_query_auth_types (CamelService *service,
2099                                 gint io_priority,
2100                                 GCancellable *cancellable,
2101                                 GAsyncReadyCallback callback,
2102                                 gpointer user_data)
2103 {
2104         CamelServiceClass *class;
2105
2106         g_return_if_fail (CAMEL_IS_SERVICE (service));
2107
2108         class = CAMEL_SERVICE_GET_CLASS (service);
2109         g_return_if_fail (class->query_auth_types != NULL);
2110
2111         class->query_auth_types (
2112                 service, io_priority,
2113                 cancellable, callback, user_data);
2114 }
2115
2116 /**
2117  * camel_service_query_auth_types_finish:
2118  * @service: a #CamelService
2119  * @result: a #GAsyncResult
2120  * @error: return location for a #GError, or %NULL
2121  *
2122  * Finishes the operation started with camel_service_query_auth_types().
2123  * Free the returned list with g_list_free().
2124  *
2125  * Returns: a list of #CamelServiceAuthType structs
2126  *
2127  * Since: 3.2
2128  **/
2129 GList *
2130 camel_service_query_auth_types_finish (CamelService *service,
2131                                        GAsyncResult *result,
2132                                        GError **error)
2133 {
2134         CamelServiceClass *class;
2135
2136         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2137         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2138
2139         class = CAMEL_SERVICE_GET_CLASS (service);
2140         g_return_val_if_fail (class->query_auth_types_finish != NULL, NULL);
2141
2142         return class->query_auth_types_finish (service, result, error);
2143 }
2144