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