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