Rename camel_service_get_settings().
[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_free (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_free (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         service->priv->settings_lock = g_mutex_new ();
1141         service->priv->connection_lock = g_mutex_new ();
1142         service->priv->status = CAMEL_SERVICE_DISCONNECTED;
1143 }
1144
1145 GQuark
1146 camel_service_error_quark (void)
1147 {
1148         static GQuark quark = 0;
1149
1150         if (G_UNLIKELY (quark == 0)) {
1151                 const gchar *string = "camel-service-error-quark";
1152                 quark = g_quark_from_static_string (string);
1153         }
1154
1155         return quark;
1156 }
1157
1158 /**
1159  * camel_service_migrate_files:
1160  * @service: a #CamelService
1161  *
1162  * Performs any necessary file migrations for @service.  This should be
1163  * called after installing or configuring the @service's #CamelSettings,
1164  * since it requires building a URL string for @service.
1165  *
1166  * Since: 3.4
1167  **/
1168 void
1169 camel_service_migrate_files (CamelService *service)
1170 {
1171         const gchar *new_data_dir;
1172         gchar *old_data_dir;
1173
1174         g_return_if_fail (CAMEL_IS_SERVICE (service));
1175
1176         new_data_dir = camel_service_get_user_data_dir (service);
1177         old_data_dir = service_find_old_data_dir (service);
1178
1179         /* If the old data directory name exists, try renaming
1180          * it to the new data directory.  Failure is non-fatal. */
1181         if (old_data_dir != NULL) {
1182                 g_rename (old_data_dir, new_data_dir);
1183                 g_free (old_data_dir);
1184         }
1185 }
1186
1187 /**
1188  * camel_service_new_camel_url:
1189  * @service: a #CamelService
1190  *
1191  * Returns a new #CamelURL representing @service.
1192  * Free the returned #CamelURL with camel_url_free().
1193  *
1194  * Returns: a new #CamelURL
1195  *
1196  * Since: 3.2
1197  **/
1198 CamelURL *
1199 camel_service_new_camel_url (CamelService *service)
1200 {
1201         CamelURL *url;
1202         CamelProvider *provider;
1203         CamelSettings *settings;
1204         gchar *host = NULL;
1205         gchar *user = NULL;
1206         gchar *path = NULL;
1207         guint16 port = 0;
1208
1209         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1210
1211         provider = camel_service_get_provider (service);
1212         g_return_val_if_fail (provider != NULL, NULL);
1213
1214         settings = camel_service_ref_settings (service);
1215
1216         /* Allocate as camel_url_new_with_base() does. */
1217         url = g_new0 (CamelURL, 1);
1218
1219         if (CAMEL_IS_NETWORK_SETTINGS (settings)) {
1220                 CamelNetworkSettings *network_settings;
1221
1222                 network_settings = CAMEL_NETWORK_SETTINGS (settings);
1223                 host = camel_network_settings_dup_host (network_settings);
1224                 port = camel_network_settings_get_port (network_settings);
1225                 user = camel_network_settings_dup_user (network_settings);
1226         }
1227
1228         if (CAMEL_IS_LOCAL_SETTINGS (settings)) {
1229                 CamelLocalSettings *local_settings;
1230
1231                 local_settings = CAMEL_LOCAL_SETTINGS (settings);
1232                 path = camel_local_settings_dup_path (local_settings);
1233         }
1234
1235         camel_url_set_protocol (url, provider->protocol);
1236         camel_url_set_host (url, host);
1237         camel_url_set_port (url, port);
1238         camel_url_set_user (url, user);
1239         camel_url_set_path (url, path);
1240
1241         g_free (host);
1242         g_free (user);
1243         g_free (path);
1244
1245         g_object_unref (settings);
1246
1247         return url;
1248 }
1249
1250 /**
1251  * camel_service_get_connection_status:
1252  * @service: a #CamelService
1253  *
1254  * Returns the connection status for @service.
1255  *
1256  * Returns: the connection status
1257  *
1258  * Since: 3.2
1259  **/
1260 CamelServiceConnectionStatus
1261 camel_service_get_connection_status (CamelService *service)
1262 {
1263         g_return_val_if_fail (
1264                 CAMEL_IS_SERVICE (service),
1265                 CAMEL_SERVICE_DISCONNECTED);
1266
1267         return service->priv->status;
1268 }
1269
1270 /**
1271  * camel_service_get_display_name:
1272  * @service: a #CamelService
1273  *
1274  * Returns the display name for @service, or %NULL if @service has not
1275  * been given a display name.  The display name is intended for use in
1276  * a user interface and should generally be given a user-defined name.
1277  *
1278  * Compare this with camel_service_get_name(), which returns a built-in
1279  * description of the type of service (IMAP, SMTP, etc.).
1280  *
1281  * Returns: the display name for @service, or %NULL
1282  *
1283  * Since: 3.2
1284  **/
1285 const gchar *
1286 camel_service_get_display_name (CamelService *service)
1287 {
1288         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1289
1290         return service->priv->display_name;
1291 }
1292
1293 /**
1294  * camel_service_set_display_name:
1295  * @service: a #CamelService
1296  * @display_name: a valid UTF-8 string, or %NULL
1297  *
1298  * Assigns a UTF-8 display name to @service.  The display name is intended
1299  * for use in a user interface and should generally be given a user-defined
1300  * name.
1301  *
1302  * Compare this with camel_service_get_name(), which returns a built-in
1303  * description of the type of service (IMAP, SMTP, etc.).
1304  *
1305  * Since: 3.2
1306  **/
1307 void
1308 camel_service_set_display_name (CamelService *service,
1309                                 const gchar *display_name)
1310 {
1311         g_return_if_fail (CAMEL_IS_SERVICE (service));
1312
1313         if (g_strcmp0 (service->priv->display_name, display_name) == 0)
1314                 return;
1315
1316         if (display_name != NULL)
1317                 g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
1318
1319         g_free (service->priv->display_name);
1320         service->priv->display_name = g_strdup (display_name);
1321
1322         g_object_notify (G_OBJECT (service), "display-name");
1323 }
1324
1325 /**
1326  * camel_service_get_password:
1327  * @service: a #CamelService
1328  *
1329  * Returns the password for @service.  Some SASL mechanisms use this
1330  * when attempting to authenticate.
1331  *
1332  * Returns: the password for @service
1333  *
1334  * Since: 3.4
1335  **/
1336 const gchar *
1337 camel_service_get_password (CamelService *service)
1338 {
1339         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1340
1341         return service->priv->password;
1342 }
1343
1344 /**
1345  * camel_service_set_password:
1346  * @service: a #CamelService
1347  * @password: the password for @service
1348  *
1349  * Sets the password for @service.  Use this function to cache the password
1350  * in memory after obtaining it through camel_session_get_password().  Some
1351  * SASL mechanisms use this when attempting to authenticate.
1352  *
1353  * Since: 3.4
1354  **/
1355 void
1356 camel_service_set_password (CamelService *service,
1357                             const gchar *password)
1358 {
1359         g_return_if_fail (CAMEL_IS_SERVICE (service));
1360
1361         if (g_strcmp0 (service->priv->password, password) == 0)
1362                 return;
1363
1364         g_free (service->priv->password);
1365         service->priv->password = g_strdup (password);
1366
1367         g_object_notify (G_OBJECT (service), "password");
1368 }
1369
1370 /**
1371  * camel_service_get_user_data_dir:
1372  * @service: a #CamelService
1373  *
1374  * Returns the base directory under which to store user-specific data
1375  * for @service.  The directory is formed by appending the directory
1376  * returned by camel_session_get_user_data_dir() with the service's
1377  * #CamelService:uid value.
1378  *
1379  * Returns: the base directory for @service
1380  *
1381  * Since: 3.2
1382  **/
1383 const gchar *
1384 camel_service_get_user_data_dir (CamelService *service)
1385 {
1386         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1387
1388         return service->priv->user_data_dir;
1389 }
1390
1391 /**
1392  * camel_service_get_user_cache_dir:
1393  * @service: a #CamelService
1394  *
1395  * Returns the base directory under which to store cache data
1396  * for @service.  The directory is formed by appending the directory
1397  * returned by camel_session_get_user_cache_dir() with the service's
1398  * #CamelService:uid value.
1399  *
1400  * Returns: the base cache directory for @service
1401  *
1402  * Since: 3.4
1403  **/
1404 const gchar *
1405 camel_service_get_user_cache_dir (CamelService *service)
1406 {
1407         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1408
1409         return service->priv->user_cache_dir;
1410 }
1411
1412 /**
1413  * camel_service_get_name:
1414  * @service: a #CamelService
1415  * @brief: whether or not to use a briefer form
1416  *
1417  * This gets the name of the service in a "friendly" (suitable for
1418  * humans) form. If @brief is %TRUE, this should be a brief description
1419  * such as for use in the folder tree. If @brief is %FALSE, it should
1420  * be a more complete and mostly unambiguous description.
1421  *
1422  * Returns: a description of the service which the caller must free
1423  **/
1424 gchar *
1425 camel_service_get_name (CamelService *service,
1426                         gboolean brief)
1427 {
1428         CamelServiceClass *class;
1429
1430         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1431
1432         class = CAMEL_SERVICE_GET_CLASS (service);
1433         g_return_val_if_fail (class->get_name != NULL, NULL);
1434
1435         return class->get_name (service, brief);
1436 }
1437
1438 /**
1439  * camel_service_get_provider:
1440  * @service: a #CamelService
1441  *
1442  * Gets the #CamelProvider associated with the service.
1443  *
1444  * Returns: the #CamelProvider
1445  **/
1446 CamelProvider *
1447 camel_service_get_provider (CamelService *service)
1448 {
1449         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1450
1451         return service->priv->provider;
1452 }
1453
1454 /**
1455  * camel_service_get_session:
1456  * @service: a #CamelService
1457  *
1458  * Gets the #CamelSession associated with the service.
1459  *
1460  * Returns: the #CamelSession
1461  **/
1462 CamelSession *
1463 camel_service_get_session (CamelService *service)
1464 {
1465         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1466
1467         return CAMEL_SESSION (service->priv->session);
1468 }
1469
1470 /**
1471  * camel_service_ref_settings:
1472  * @service: a #CamelService
1473  *
1474  * Returns the #CamelSettings instance associated with the service.
1475  *
1476  * The returned #CamelSettings is referenced for thread-safety and must
1477  * be unreferenced with g_object_unref() when finished with it.
1478  *
1479  * Returns: the #CamelSettings
1480  *
1481  * Since: 3.6
1482  **/
1483 CamelSettings *
1484 camel_service_ref_settings (CamelService *service)
1485 {
1486         CamelSettings *settings;
1487
1488         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1489
1490         /* Every service should have a settings object. */
1491         g_return_val_if_fail (service->priv->settings != NULL, NULL);
1492
1493         g_mutex_lock (service->priv->settings_lock);
1494
1495         settings = g_object_ref (service->priv->settings);
1496
1497         g_mutex_unlock (service->priv->settings_lock);
1498
1499         return settings;
1500 }
1501
1502 /**
1503  * camel_service_set_settings:
1504  * @service: a #CamelService
1505  * @settings: an instance derviced from #CamelSettings, or %NULL
1506  *
1507  * Associates a new #CamelSettings instance with the service.
1508  * The @settings instance must match the settings type defined in
1509  * #CamelServiceClass.  If @settings is %NULL, a new #CamelSettings
1510  * instance of the appropriate type is created with all properties
1511  * set to defaults.
1512  *
1513  * Since: 3.2
1514  **/
1515 void
1516 camel_service_set_settings (CamelService *service,
1517                             CamelSettings *settings)
1518 {
1519         CamelServiceClass *class;
1520
1521         g_return_if_fail (CAMEL_IS_SERVICE (service));
1522
1523         class = CAMEL_SERVICE_GET_CLASS (service);
1524
1525         if (settings != NULL) {
1526                 g_return_if_fail (
1527                         g_type_is_a (
1528                                 G_OBJECT_TYPE (settings),
1529                                 class->settings_type));
1530                 g_object_ref (settings);
1531
1532         } else {
1533                 g_return_if_fail (
1534                         g_type_is_a (
1535                                 class->settings_type,
1536                                 CAMEL_TYPE_SETTINGS));
1537                 settings = g_object_new (class->settings_type, NULL);
1538         }
1539
1540         g_mutex_lock (service->priv->settings_lock);
1541
1542         if (service->priv->settings != NULL)
1543                 g_object_unref (service->priv->settings);
1544
1545         service->priv->settings = settings;  /* takes ownership */
1546
1547         g_mutex_unlock (service->priv->settings_lock);
1548
1549         g_object_notify (G_OBJECT (service), "settings");
1550 }
1551
1552 /**
1553  * camel_service_get_uid:
1554  * @service: a #CamelService
1555  *
1556  * Gets the unique identifier string associated with the service.
1557  *
1558  * Returns: the UID string
1559  *
1560  * Since: 3.2
1561  **/
1562 const gchar *
1563 camel_service_get_uid (CamelService *service)
1564 {
1565         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
1566
1567         return service->priv->uid;
1568 }
1569
1570 /**
1571  * camel_service_connect_sync:
1572  * @service: a #CamelService
1573  * @cancellable: optional #GCancellable object, or %NULL
1574  * @error: return location for a #GError, or %NULL
1575  *
1576  * Connects @service to a remote server using the information in its
1577  * #CamelService:settings instance.
1578  *
1579  * If a connect operation is already in progress when this function is
1580  * called, its results will be reflected in this connect operation.
1581  *
1582  * Returns: %TRUE if the connection is made or %FALSE otherwise
1583  *
1584  * Since: 3.6
1585  **/
1586 gboolean
1587 camel_service_connect_sync (CamelService *service,
1588                             GCancellable *cancellable,
1589                             GError **error)
1590 {
1591         AsyncClosure *closure;
1592         GAsyncResult *result;
1593         gboolean success;
1594
1595         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1596
1597         closure = async_closure_new ();
1598
1599         camel_service_connect (
1600                 service, G_PRIORITY_DEFAULT, cancellable,
1601                 async_closure_callback, closure);
1602
1603         result = async_closure_wait (closure);
1604
1605         success = camel_service_connect_finish (service, result, error);
1606
1607         async_closure_free (closure);
1608
1609         return success;
1610 }
1611
1612 /**
1613  * camel_service_connect:
1614  * @service: a #CamelService
1615  * @io_priority: the I/O priority of the request
1616  * @cancellable: optional #GCancellable object, or %NULL
1617  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1618  * @user_data: data to pass to the callback function
1619  *
1620  * Asynchronously connects @service to a remote server using the information
1621  * in its #CamelService:settings instance.
1622  *
1623  * If a connect operation is already in progress when this function is
1624  * called, its results will be reflected in this connect operation.
1625  *
1626  * If any disconnect operations are in progress when this function is
1627  * called, they will be cancelled.
1628  *
1629  * When the operation is finished, @callback will be called.  You can
1630  * then call camel_service_connect_finish() to get the result of the
1631  * operation.
1632  *
1633  * Since: 3.6
1634  **/
1635 void
1636 camel_service_connect (CamelService *service,
1637                        gint io_priority,
1638                        GCancellable *cancellable,
1639                        GAsyncReadyCallback callback,
1640                        gpointer user_data)
1641 {
1642         ConnectionOp *op;
1643         CamelServiceClass *class;
1644         GSimpleAsyncResult *simple;
1645
1646         g_return_if_fail (CAMEL_IS_SERVICE (service));
1647
1648         class = CAMEL_SERVICE_GET_CLASS (service);
1649         g_return_if_fail (class->connect != NULL);
1650
1651         simple = g_simple_async_result_new (
1652                 G_OBJECT (service), callback,
1653                 user_data, camel_service_connect);
1654
1655         g_simple_async_result_set_check_cancellable (simple, cancellable);
1656
1657         g_mutex_lock (service->priv->connection_lock);
1658
1659         switch (service->priv->status) {
1660
1661                 /* If a connect operation is already in progress,
1662                  * queue this operation so it completes at the same
1663                  * time the first connect operation completes. */
1664                 case CAMEL_SERVICE_CONNECTING:
1665                         connection_op_add_pending (
1666                                 service->priv->connection_op,
1667                                 simple, cancellable);
1668                         break;
1669
1670                 /* If we're already connected, just report success. */
1671                 case CAMEL_SERVICE_CONNECTED:
1672                         g_simple_async_result_complete_in_idle (simple);
1673                         break;
1674
1675                 /* If a disconnect operation is currently in progress,
1676                  * cancel it and make room for the connect operation. */
1677                 case CAMEL_SERVICE_DISCONNECTING:
1678                         g_return_if_fail (
1679                                 service->priv->connection_op != NULL);
1680                         g_cancellable_cancel (
1681                                 service->priv->connection_op->cancellable);
1682                         connection_op_unref (service->priv->connection_op);
1683                         service->priv->connection_op = NULL;
1684                         /* fall through */
1685
1686                 /* Start a new connect operation.  Subsequent connect
1687                  * operations are queued until this operation completes
1688                  * and will share this operation's result. */
1689                 case CAMEL_SERVICE_DISCONNECTED:
1690                         g_return_if_fail (
1691                                 service->priv->connection_op == NULL);
1692
1693                         op = connection_op_new (simple, cancellable);
1694                         service->priv->connection_op = op;
1695
1696                         service->priv->status = CAMEL_SERVICE_CONNECTING;
1697                         service_queue_notify_connection_status (service);
1698
1699                         class->connect (
1700                                 service,
1701                                 io_priority,
1702                                 cancellable,
1703                                 service_shared_connect_cb,
1704                                 connection_op_ref (op));
1705                         break;
1706
1707                 default:
1708                         g_warn_if_reached ();
1709         }
1710
1711         g_mutex_unlock (service->priv->connection_lock);
1712
1713         g_object_unref (simple);
1714 }
1715
1716 /**
1717  * camel_service_connect_finish:
1718  * @service: a #CamelService
1719  * @result: a #GAsyncResult
1720  * @error: return location for a #GError, or %NULL
1721  *
1722  * Finishes the operation started with camel_service_connect().
1723  *
1724  * Returns: %TRUE if the connection was made or %FALSE otherwise
1725  *
1726  * Since: 3.6
1727  **/
1728 gboolean
1729 camel_service_connect_finish (CamelService *service,
1730                               GAsyncResult *result,
1731                               GError **error)
1732 {
1733         GSimpleAsyncResult *simple;
1734
1735         g_return_val_if_fail (
1736                 g_simple_async_result_is_valid (
1737                 result, G_OBJECT (service), camel_service_connect), FALSE);
1738
1739         simple = G_SIMPLE_ASYNC_RESULT (result);
1740
1741         /* Assume success unless a GError is set. */
1742         return !g_simple_async_result_propagate_error (simple, error);
1743 }
1744
1745 /**
1746  * camel_service_disconnect_sync:
1747  * @service: a #CamelService
1748  * @clean: whether or not to try to disconnect cleanly
1749  * @cancellable: optional #GCancellable object, or %NULL
1750  * @error: return location for a #GError, or %NULL
1751  *
1752  * Disconnect from the service. If @clean is %FALSE, it should not
1753  * try to do any synchronizing or other cleanup of the connection.
1754  *
1755  * If a disconnect operation is already in progress when this function is
1756  * called, its results will be reflected in this disconnect operation.
1757  *
1758  * If any connect operations are in progress when this function is called,
1759  * they will be cancelled.
1760  *
1761  * Returns: %TRUE if the connection was severed or %FALSE otherwise
1762  **/
1763 gboolean
1764 camel_service_disconnect_sync (CamelService *service,
1765                                gboolean clean,
1766                                GCancellable *cancellable,
1767                                GError **error)
1768 {
1769         AsyncClosure *closure;
1770         GAsyncResult *result;
1771         gboolean success;
1772
1773         g_return_val_if_fail (CAMEL_IS_SERVICE (service), FALSE);
1774
1775         closure = async_closure_new ();
1776
1777         camel_service_disconnect (
1778                 service, clean, G_PRIORITY_DEFAULT,
1779                 cancellable, async_closure_callback, closure);
1780
1781         result = async_closure_wait (closure);
1782
1783         success = camel_service_disconnect_finish (service, result, error);
1784
1785         async_closure_free (closure);
1786
1787         return success;
1788 }
1789
1790 /**
1791  * camel_service_disconnect:
1792  * @service: a #CamelService
1793  * @clean: whether or not to try to disconnect cleanly
1794  * @io_priority: the I/O priority of the request
1795  * @cancellable: optional #GCancellable object, or %NULL
1796  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1797  * @user_data: data to pass to the callback function
1798  *
1799  * If a disconnect operation is already in progress when this function is
1800  * called, its results will be reflected in this disconnect operation.
1801  *
1802  * If any connect operations are in progress when this function is called,
1803  * they will be cancelled.
1804  *
1805  * When the operation is finished, @callback will be called.  You can
1806  * then call camel_service_disconnect_finish() to get the result of the
1807  * operation.
1808  *
1809  * Since: 3.6
1810  **/
1811 void
1812 camel_service_disconnect (CamelService *service,
1813                           gboolean clean,
1814                           gint io_priority,
1815                           GCancellable *cancellable,
1816                           GAsyncReadyCallback callback,
1817                           gpointer user_data)
1818 {
1819         ConnectionOp *op;
1820         CamelServiceClass *class;
1821         GSimpleAsyncResult *simple;
1822
1823         g_return_if_fail (CAMEL_IS_SERVICE (service));
1824
1825         class = CAMEL_SERVICE_GET_CLASS (service);
1826         g_return_if_fail (class->disconnect != NULL);
1827
1828         simple = g_simple_async_result_new (
1829                 G_OBJECT (service), callback,
1830                 user_data, camel_service_disconnect);
1831
1832         g_simple_async_result_set_check_cancellable (simple, cancellable);
1833
1834         g_mutex_lock (service->priv->connection_lock);
1835
1836         switch (service->priv->status) {
1837
1838                 /* If a connect operation is currently in progress,
1839                  * cancel it and make room for the disconnect operation. */
1840                 case CAMEL_SERVICE_CONNECTING:
1841                         g_return_if_fail (
1842                                 service->priv->connection_op != NULL);
1843                         g_cancellable_cancel (
1844                                 service->priv->connection_op->cancellable);
1845                         connection_op_unref (service->priv->connection_op);
1846                         service->priv->connection_op = NULL;
1847                         /* fall through */
1848
1849                 /* Start a new disconnect operation.  Subsequent disconnect
1850                  * operations are queued until this operation completes and
1851                  * will share this operation's result. */
1852                 case CAMEL_SERVICE_CONNECTED:
1853                         g_return_if_fail (
1854                                 service->priv->connection_op == NULL);
1855
1856                         op = connection_op_new (simple, cancellable);
1857                         service->priv->connection_op = op;
1858
1859                         service->priv->status = CAMEL_SERVICE_DISCONNECTING;
1860                         service_queue_notify_connection_status (service);
1861
1862                         class->disconnect (
1863                                 service, clean,
1864                                 io_priority,
1865                                 cancellable,
1866                                 service_shared_disconnect_cb,
1867                                 connection_op_ref (op));
1868                         break;
1869
1870                 /* If a disconnect operation is already in progress,
1871                  * queue this operation so it completes at the same
1872                  * time the first disconnect operation completes. */
1873                 case CAMEL_SERVICE_DISCONNECTING:
1874                         connection_op_add_pending (
1875                                 service->priv->connection_op,
1876                                 simple, cancellable);
1877                         break;
1878
1879                 /* If we're already disconnected, just report success. */
1880                 case CAMEL_SERVICE_DISCONNECTED:
1881                         g_simple_async_result_complete_in_idle (simple);
1882                         break;
1883
1884                 default:
1885                         g_warn_if_reached ();
1886         }
1887
1888         g_mutex_unlock (service->priv->connection_lock);
1889
1890         g_object_unref (simple);
1891 }
1892
1893 /**
1894  * camel_service_disconnect_finish:
1895  * @service: a #CamelService
1896  * @result: a #GAsyncResult
1897  * @error: return location for a #GError, or %NULL
1898  *
1899  * Finishes the operation started with camel_service_disconnect().
1900  *
1901  * Returns: %TRUE if the connection was severed or %FALSE otherwise
1902  *
1903  * Since: 3.6
1904  **/
1905 gboolean
1906 camel_service_disconnect_finish (CamelService *service,
1907                                  GAsyncResult *result,
1908                                  GError **error)
1909 {
1910         GSimpleAsyncResult *simple;
1911
1912         g_return_val_if_fail (
1913                 g_simple_async_result_is_valid (
1914                 result, G_OBJECT (service), camel_service_disconnect), FALSE);
1915
1916         simple = G_SIMPLE_ASYNC_RESULT (result);
1917
1918         /* Assume success unless a GError is set. */
1919         return !g_simple_async_result_propagate_error (simple, error);
1920 }
1921
1922 /**
1923  * camel_service_authenticate_sync:
1924  * @service: a #CamelService
1925  * @mechanism: a SASL mechanism name, or %NULL
1926  * @cancellable: optional #GCancellable object, or %NULL
1927  * @error: return location for a #GError, or %NULL
1928  *
1929  * Attempts to authenticate @service using @mechanism and, if necessary,
1930  * @service's #CamelService:password property.  The function makes only
1931  * ONE attempt at authentication and does not loop.
1932  *
1933  * If the authentication attempt completed and the server accepted the
1934  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
1935  *
1936  * If the authentication attempt completed but the server rejected the
1937  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
1938  *
1939  * If the authentication attempt failed to complete due to a network
1940  * communication issue or some other mishap, the function sets @error
1941  * and returns #CAMEL_AUTHENTICATION_ERROR.
1942  *
1943  * Generally this function should only be called from a #CamelSession
1944  * subclass in order to implement its own authentication loop.
1945  *
1946  * Returns: the authentication result
1947  *
1948  * Since: 3.4
1949  **/
1950 CamelAuthenticationResult
1951 camel_service_authenticate_sync (CamelService *service,
1952                                  const gchar *mechanism,
1953                                  GCancellable *cancellable,
1954                                  GError **error)
1955 {
1956         CamelServiceClass *class;
1957         CamelAuthenticationResult result;
1958
1959         g_return_val_if_fail (
1960                 CAMEL_IS_SERVICE (service),
1961                 CAMEL_AUTHENTICATION_REJECTED);
1962
1963         class = CAMEL_SERVICE_GET_CLASS (service);
1964         g_return_val_if_fail (
1965                 class->authenticate_sync != NULL,
1966                 CAMEL_AUTHENTICATION_REJECTED);
1967
1968         result = class->authenticate_sync (
1969                 service, mechanism, cancellable, error);
1970         CAMEL_CHECK_GERROR (
1971                 service, authenticate_sync,
1972                 result != CAMEL_AUTHENTICATION_ERROR, error);
1973
1974         return result;
1975 }
1976
1977 /**
1978  * camel_service_authenticate:
1979  * @service: a #CamelService
1980  * @mechanism: a SASL mechanism name, or %NULL
1981  * @io_priority: the I/O priority of the request
1982  * @cancellable: optional #GCancellable object, or %NULL
1983  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1984  * @user_data: data to pass to the callback function
1985  *
1986  * Asynchronously attempts to authenticate @service using @mechanism and,
1987  * if necessary, @service's #CamelService:password property.  The function
1988  * makes only ONE attempt at authentication and does not loop.
1989  *
1990  * Generally this function should only be called from a #CamelSession
1991  * subclass in order to implement its own authentication loop.
1992  *
1993  * When the operation is finished, @callback will be called.  You can
1994  * then call camel_service_authenticate_finish() to get the result of
1995  * the operation.
1996  *
1997  * Since: 3.4
1998  **/
1999 void
2000 camel_service_authenticate (CamelService *service,
2001                             const gchar *mechanism,
2002                             gint io_priority,
2003                             GCancellable *cancellable,
2004                             GAsyncReadyCallback callback,
2005                             gpointer user_data)
2006 {
2007         CamelServiceClass *class;
2008
2009         g_return_if_fail (CAMEL_IS_SERVICE (service));
2010
2011         class = CAMEL_SERVICE_GET_CLASS (service);
2012         g_return_if_fail (class->authenticate != NULL);
2013
2014         class->authenticate (
2015                 service, mechanism, io_priority,
2016                 cancellable, callback, user_data);
2017 }
2018
2019 /**
2020  * camel_service_authenticate_finish:
2021  * @service: a #CamelService
2022  * @result: a #GAsyncResult
2023  * @error: return location for a #GError, or %NULL
2024  *
2025  * Finishes the operation started with camel_service_authenticate().
2026  *
2027  * If the authentication attempt completed and the server accepted the
2028  * credentials, the function returns #CAMEL_AUTHENTICATION_ACCEPTED.
2029  *
2030  * If the authentication attempt completed but the server rejected the
2031  * credentials, the function returns #CAMEL_AUTHENTICATION_REJECTED.
2032  *
2033  * If the authentication attempt failed to complete due to a network
2034  * communication issue or some other mishap, the function sets @error
2035  * and returns #CAMEL_AUTHENTICATION_ERROR.
2036  *
2037  * Returns: the authentication result
2038  *
2039  * Since: 3.4
2040  **/
2041 CamelAuthenticationResult
2042 camel_service_authenticate_finish (CamelService *service,
2043                                    GAsyncResult *result,
2044                                    GError **error)
2045 {
2046         CamelServiceClass *class;
2047
2048         g_return_val_if_fail (
2049                 CAMEL_IS_SERVICE (service),
2050                 CAMEL_AUTHENTICATION_REJECTED);
2051         g_return_val_if_fail (
2052                 G_IS_ASYNC_RESULT (result),
2053                 CAMEL_AUTHENTICATION_REJECTED);
2054
2055         class = CAMEL_SERVICE_GET_CLASS (service);
2056         g_return_val_if_fail (
2057                 class->authenticate_finish,
2058                 CAMEL_AUTHENTICATION_REJECTED);
2059
2060         return class->authenticate_finish (service, result, error);
2061 }
2062
2063 /**
2064  * camel_service_query_auth_types_sync:
2065  * @service: a #CamelService
2066  * @cancellable: optional #GCancellable object, or %NULL
2067  * @error: return location for a #GError, or %NULL
2068  *
2069  * Obtains a list of authentication types supported by @service.
2070  * Free the returned list with g_list_free().
2071  *
2072  * Returns: a list of #CamelServiceAuthType structs
2073  **/
2074 GList *
2075 camel_service_query_auth_types_sync (CamelService *service,
2076                                      GCancellable *cancellable,
2077                                      GError **error)
2078 {
2079         CamelServiceClass *class;
2080
2081         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2082
2083         class = CAMEL_SERVICE_GET_CLASS (service);
2084         g_return_val_if_fail (class->query_auth_types_sync != NULL, NULL);
2085
2086         return class->query_auth_types_sync (service, cancellable, error);
2087 }
2088
2089 /**
2090  * camel_service_query_auth_types:
2091  * @service: a #CamelService
2092  * @io_priority: the I/O priority of the request
2093  * @cancellable: optional #GCancellable object, or %NULL
2094  * @callback: a #GAsyncReadyCallback to call when the request is satisfied
2095  * @user_data: data to pass to the callback function
2096  *
2097  * Asynchronously obtains a list of authentication types supported by
2098  * @service.
2099  *
2100  * When the operation is finished, @callback will be called.  You can
2101  * then call camel_service_query_auth_types_finish() to get the result
2102  * of the operation.
2103  *
2104  * Since: 3.2
2105  **/
2106 void
2107 camel_service_query_auth_types (CamelService *service,
2108                                 gint io_priority,
2109                                 GCancellable *cancellable,
2110                                 GAsyncReadyCallback callback,
2111                                 gpointer user_data)
2112 {
2113         CamelServiceClass *class;
2114
2115         g_return_if_fail (CAMEL_IS_SERVICE (service));
2116
2117         class = CAMEL_SERVICE_GET_CLASS (service);
2118         g_return_if_fail (class->query_auth_types != NULL);
2119
2120         class->query_auth_types (
2121                 service, io_priority,
2122                 cancellable, callback, user_data);
2123 }
2124
2125 /**
2126  * camel_service_query_auth_types_finish:
2127  * @service: a #CamelService
2128  * @result: a #GAsyncResult
2129  * @error: return location for a #GError, or %NULL
2130  *
2131  * Finishes the operation started with camel_service_query_auth_types().
2132  * Free the returned list with g_list_free().
2133  *
2134  * Returns: a list of #CamelServiceAuthType structs
2135  *
2136  * Since: 3.2
2137  **/
2138 GList *
2139 camel_service_query_auth_types_finish (CamelService *service,
2140                                        GAsyncResult *result,
2141                                        GError **error)
2142 {
2143         CamelServiceClass *class;
2144
2145         g_return_val_if_fail (CAMEL_IS_SERVICE (service), NULL);
2146         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), NULL);
2147
2148         class = CAMEL_SERVICE_GET_CLASS (service);
2149         g_return_val_if_fail (class->query_auth_types_finish != NULL, NULL);
2150
2151         return class->query_auth_types_finish (service, result, error);
2152 }
2153