goa: Add missing linker flag (for real).
[platform/upstream/evolution-data-server.git] / libedataserver / e-source.c
1 /*
2  * e-source.c
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) version 3.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with the program; if not, see <http://www.gnu.org/licenses/>
16  *
17  */
18
19 /**
20  * SECTION: e-source
21  * @include: libedataserver/libedataserver.h
22  * @short_description: Hierarchical data sources
23  *
24  * An #ESource (or "data source") is a description of a file or network
25  * location where data can be obtained (such as a mail account), or a
26  * description of a resource at that location (such as a mail folder).
27  *
28  * In more concrete terms, it's an interface for a key file.  All such
29  * key files have a main group named [Data Source].  The keys in a
30  * [Data Source] group map to #GObject properties in an #ESource.
31  *
32  * Additional groups in the key file are referred to as "extensions".
33  * #ESourceExtension serves as the base class for writing interfaces
34  * for these additional key file groups.  The keys in one of these
35  * key file groups map to #GObject properties in some custom subclass
36  * of #ESourceExtension which was written specifically for that key
37  * file group.  For example, a key file might include a group named
38  * [Calendar], whose keys map to #GObject properties in an extension
39  * class named #ESourceCalendar.
40  *
41  * Each #ESource contains an internal dictionary of extension objects,
42  * accessible by their key file group name.  e_source_get_extension()
43  * can look up extension objects by name.
44  *
45  * An #ESource is identified by a unique identifier string, or "UID",
46  * which is also the basename of the corresponding key file.  Additional
47  * files related to the #ESource, such as cache files, are usually kept
48  * in a directory named after the UID of the #ESource.  Similarly, the
49  * password for an account described by an #ESource is kept in GNOME
50  * Keyring under the UID of the #ESource.  This makes finding these
51  * additional resources simple.
52  *
53  * Several extensions for common information such as authentication
54  * details are built into libedataserver (#ESourceAuthentication, for
55  * example).  Backend modules may also define their own extensions for
56  * information and settings unique to the backend.  #ESourceExtension
57  * subclasses written for specific backends are generally not available
58  * to applications and shared libraries.  This is by design, to try and
59  * keep backend-specific knowledge from creeping into places it doesn't
60  * belong.
61  **/
62
63 #include "e-source.h"
64
65 #include <config.h>
66 #include <string.h>
67 #include <glib/gi18n-lib.h>
68
69 /* Private D-Bus classes. */
70 #include <e-dbus-source.h>
71
72 #include "e-data-server-util.h"
73 #include "e-source-extension.h"
74 #include "e-uid.h"
75
76 /* built-in extension types */
77 #include "e-source-address-book.h"
78 #include "e-source-alarms.h"
79 #include "e-source-authentication.h"
80 #include "e-source-autocomplete.h"
81 #include "e-source-calendar.h"
82 #include "e-source-camel.h"
83 #include "e-source-collection.h"
84 #include "e-source-goa.h"
85 #include "e-source-mail-account.h"
86 #include "e-source-mail-composition.h"
87 #include "e-source-mail-identity.h"
88 #include "e-source-mail-signature.h"
89 #include "e-source-mail-submission.h"
90 #include "e-source-mail-transport.h"
91 #include "e-source-mdn.h"
92 #include "e-source-offline.h"
93 #include "e-source-openpgp.h"
94 #include "e-source-refresh.h"
95 #include "e-source-resource.h"
96 #include "e-source-revision-guards.h"
97 #include "e-source-security.h"
98 #include "e-source-selectable.h"
99 #include "e-source-smime.h"
100 #include "e-source-uoa.h"
101 #include "e-source-webdav.h"
102
103 #define E_SOURCE_GET_PRIVATE(obj) \
104         (G_TYPE_INSTANCE_GET_PRIVATE \
105         ((obj), E_TYPE_SOURCE, ESourcePrivate))
106
107 #define PRIMARY_GROUP_NAME      "Data Source"
108
109 typedef struct _AsyncContext AsyncContext;
110
111 struct _ESourcePrivate {
112         GDBusObject *dbus_object;
113         GMainContext *main_context;
114
115         GSource *changed;
116         GMutex changed_lock;
117
118         GMutex property_lock;
119
120         gchar *display_name;
121         gchar *collate_key;
122         gchar *parent;
123         gchar *uid;
124
125         /* The lock guards the key file and hash table. */
126
127         GKeyFile *key_file;
128         GRecMutex lock;
129         GHashTable *extensions;
130
131         gboolean enabled;
132         gboolean initialized;
133 };
134
135 struct _AsyncContext {
136         ESource *scratch_source;
137         gchar *access_token;
138         gint expires_in;
139 };
140
141 enum {
142         PROP_0,
143         PROP_DBUS_OBJECT,
144         PROP_DISPLAY_NAME,
145         PROP_ENABLED,
146         PROP_MAIN_CONTEXT,
147         PROP_PARENT,
148         PROP_REMOTE_CREATABLE,
149         PROP_REMOTE_DELETABLE,
150         PROP_REMOVABLE,
151         PROP_UID,
152         PROP_WRITABLE
153 };
154
155 enum {
156         CHANGED,
157         LAST_SIGNAL
158 };
159
160 static guint signals[LAST_SIGNAL];
161
162 /* Forward Declarations */
163 static void     e_source_initable_init          (GInitableIface *interface);
164
165 G_DEFINE_TYPE_WITH_CODE (
166         ESource,
167         e_source,
168         G_TYPE_OBJECT,
169         G_IMPLEMENT_INTERFACE (
170                 G_TYPE_INITABLE,
171                 e_source_initable_init))
172
173 static void
174 async_context_free (AsyncContext *async_context)
175 {
176         if (async_context->scratch_source != NULL)
177                 g_object_unref (async_context->scratch_source);
178
179         g_free (async_context->access_token);
180
181         g_slice_free (AsyncContext, async_context);
182 }
183
184 static void
185 source_find_extension_classes_rec (GType parent_type,
186                                    GHashTable *hash_table)
187 {
188         GType *children;
189         guint n_children, ii;
190
191         children = g_type_children (parent_type, &n_children);
192
193         for (ii = 0; ii < n_children; ii++) {
194                 GType type = children[ii];
195                 ESourceExtensionClass *class;
196                 gpointer key;
197
198                 /* Recurse over the child's children. */
199                 source_find_extension_classes_rec (type, hash_table);
200
201                 /* Skip abstract types. */
202                 if (G_TYPE_IS_ABSTRACT (type))
203                         continue;
204
205                 class = g_type_class_ref (type);
206                 key = (gpointer) class->name;
207
208                 if (key != NULL)
209                         g_hash_table_insert (hash_table, key, class);
210                 else
211                         g_type_class_unref (class);
212         }
213
214         g_free (children);
215 }
216
217 static GHashTable *
218 source_find_extension_classes (void)
219 {
220         GHashTable *hash_table;
221
222         hash_table = g_hash_table_new_full (
223                 (GHashFunc) g_str_hash,
224                 (GEqualFunc) g_str_equal,
225                 (GDestroyNotify) NULL,
226                 (GDestroyNotify) g_type_class_unref);
227
228         source_find_extension_classes_rec (
229                 E_TYPE_SOURCE_EXTENSION, hash_table);
230
231         return hash_table;
232 }
233
234 static void
235 source_localized_hack (GKeyFile *key_file,
236                        const gchar *group_name,
237                        const gchar *key,
238                        const gchar *new_value)
239 {
240         const gchar * const *language_names;
241         gint ii;
242
243         /* XXX If we're changing a string key that has translations,
244          *     set "key[$CURRENT_LOCALE]" (if available) to the new
245          *     value so g_key_file_get_locale_string() will pick it
246          *     up.  This is not a perfect solution however.  When a
247          *     different locale is used the value may revert to its
248          *     original localized string.  Good enough for now. */
249
250         language_names = g_get_language_names ();
251
252         for (ii = 0; language_names[ii] != NULL; ii++) {
253                 gboolean has_localized_key;
254                 gchar *localized_key;
255
256                 localized_key = g_strdup_printf (
257                         "%s[%s]", key, language_names[ii]);
258                 has_localized_key = g_key_file_has_key (
259                         key_file, group_name, localized_key, NULL);
260
261                 if (has_localized_key)
262                         g_key_file_set_string (
263                                 key_file, group_name,
264                                 localized_key, new_value);
265
266                 g_free (localized_key);
267
268                 if (has_localized_key)
269                         return;
270         }
271
272         g_key_file_set_string (key_file, group_name, key, new_value);
273 }
274
275 static void
276 source_set_key_file_from_property (GObject *object,
277                                    GParamSpec *pspec,
278                                    GKeyFile *key_file,
279                                    const gchar *group_name)
280 {
281         GValue *pvalue;
282         GValue *svalue;
283         gchar *key;
284
285         pvalue = g_slice_new0 (GValue);
286         g_value_init (pvalue, pspec->value_type);
287         g_object_get_property (object, pspec->name, pvalue);
288
289         svalue = g_slice_new0 (GValue);
290         g_value_init (svalue, G_TYPE_STRING);
291
292         key = e_source_parameter_to_key (pspec->name);
293
294         /* For the most part we can just transform any supported
295          * property type to a string, with a couple exceptions. */
296
297         /* Transforming a boolean GValue to a string results in
298          * "TRUE" or "FALSE" (all uppercase), but GKeyFile only
299          * recognizes "true" or "false" (all lowercase).  So we
300          * have to use g_key_file_set_boolean(). */
301         if (G_VALUE_HOLDS_BOOLEAN (pvalue)) {
302                 gboolean v_boolean = g_value_get_boolean (pvalue);
303                 g_key_file_set_boolean (key_file, group_name, key, v_boolean);
304
305         /* Store UIN64 in hexa */
306         } else if (G_VALUE_HOLDS_UINT64 (pvalue)) {
307                 gchar *v_str;
308
309                 v_str = g_strdup_printf (
310                         "%016" G_GINT64_MODIFIER "X",
311                         g_value_get_uint64 (pvalue));
312                 g_key_file_set_string (key_file, group_name, key, v_str);
313                 g_free (v_str);
314
315         /* String GValues may contain characters that need escaping. */
316         } else if (G_VALUE_HOLDS_STRING (pvalue)) {
317                 const gchar *v_string = g_value_get_string (pvalue);
318
319                 if (v_string == NULL)
320                         v_string = "";
321
322                 /* Special case for localized "DisplayName" keys. */
323                 source_localized_hack (key_file, group_name, key, v_string);
324
325         /* Transforming an enum GValue to a string results in
326          * the GEnumValue name.  We want the shorter nickname. */
327         } else if (G_VALUE_HOLDS_ENUM (pvalue)) {
328                 GParamSpecEnum *enum_pspec;
329                 GEnumClass *enum_class;
330                 GEnumValue *enum_value;
331                 gint value;
332
333                 enum_pspec = G_PARAM_SPEC_ENUM (pspec);
334                 enum_class = enum_pspec->enum_class;
335
336                 value = g_value_get_enum (pvalue);
337                 enum_value = g_enum_get_value (enum_class, value);
338
339                 if (enum_value == NULL) {
340                         value = enum_pspec->default_value;
341                         enum_value = g_enum_get_value (enum_class, value);
342                 }
343
344                 if (enum_value != NULL)
345                         g_key_file_set_string (
346                                 key_file, group_name, key,
347                                 enum_value->value_nick);
348
349         } else if (G_VALUE_HOLDS (pvalue, G_TYPE_STRV)) {
350                 const gchar **strv = g_value_get_boxed (pvalue);
351                 guint length = 0;
352
353                 if (strv != NULL)
354                         length = g_strv_length ((gchar **) strv);
355                 g_key_file_set_string_list (
356                         key_file, group_name, key, strv, length);
357
358         /* For GValues holding a GFile object we save the URI. */
359         } else if (G_VALUE_HOLDS (pvalue, G_TYPE_FILE)) {
360                 GFile *file = g_value_get_object (pvalue);
361                 gchar *uri = NULL;
362
363                 if (file != NULL)
364                         uri = g_file_get_uri (file);
365                 g_key_file_set_string (
366                         key_file, group_name, key,
367                         (uri != NULL) ? uri : "");
368                 g_free (uri);
369
370         } else if (g_value_transform (pvalue, svalue)) {
371                 const gchar *value = g_value_get_string (svalue);
372                 g_key_file_set_value (key_file, group_name, key, value);
373         }
374
375         g_free (key);
376         g_value_unset (pvalue);
377         g_value_unset (svalue);
378         g_slice_free (GValue, pvalue);
379         g_slice_free (GValue, svalue);
380 }
381
382 static void
383 source_set_property_from_key_file (GObject *object,
384                                    GParamSpec *pspec,
385                                    GKeyFile *key_file,
386                                    const gchar *group_name)
387 {
388         gchar *key;
389         GValue *value;
390         GError *error = NULL;
391
392         value = g_slice_new0 (GValue);
393         key = e_source_parameter_to_key (pspec->name);
394
395         if (G_IS_PARAM_SPEC_CHAR (pspec) ||
396             G_IS_PARAM_SPEC_UCHAR (pspec) ||
397             G_IS_PARAM_SPEC_INT (pspec) ||
398             G_IS_PARAM_SPEC_UINT (pspec) ||
399             G_IS_PARAM_SPEC_LONG (pspec) ||
400             G_IS_PARAM_SPEC_ULONG (pspec)) {
401                 gint v_int;
402
403                 v_int = g_key_file_get_integer (
404                         key_file, group_name, key, &error);
405                 if (error == NULL) {
406                         g_value_init (value, G_TYPE_INT);
407                         g_value_set_int (value, v_int);
408                 }
409
410         } else if (G_IS_PARAM_SPEC_INT64 (pspec)) {
411                 gint64 v_int64;
412
413                 v_int64 = g_key_file_get_int64 (
414                         key_file, group_name, key, &error);
415                 if (error == NULL) {
416                         g_value_init (value, G_TYPE_INT64);
417                         g_value_set_int64 (value, v_int64);
418                 }
419
420         } else if (G_IS_PARAM_SPEC_UINT64 (pspec)) {
421                 guint64 v_uint64;
422                 gchar *v_str;
423
424                 v_str = g_key_file_get_string (
425                         key_file, group_name, key, &error);
426                 if (error == NULL) {
427                         v_uint64 = g_ascii_strtoull (v_str, NULL, 16);
428
429                         g_value_init (value, G_TYPE_UINT64);
430                         g_value_set_uint64 (value, v_uint64);
431                 }
432
433                 g_free (v_str);
434
435         } else if (G_IS_PARAM_SPEC_BOOLEAN (pspec)) {
436                 gboolean v_boolean;
437
438                 v_boolean = g_key_file_get_boolean (
439                         key_file, group_name, key, &error);
440                 if (error == NULL) {
441                         g_value_init (value, G_TYPE_BOOLEAN);
442                         g_value_set_boolean (value, v_boolean);
443                 }
444
445         } else if (G_IS_PARAM_SPEC_ENUM (pspec)) {
446                 gchar *nick;
447
448                 nick = g_key_file_get_string (
449                         key_file, group_name, key, &error);
450                 if (error == NULL) {
451                         GParamSpecEnum *enum_pspec;
452                         GEnumValue *enum_value;
453
454                         enum_pspec = G_PARAM_SPEC_ENUM (pspec);
455                         enum_value = g_enum_get_value_by_nick (
456                                 enum_pspec->enum_class, nick);
457                         if (enum_value != NULL) {
458                                 g_value_init (value, pspec->value_type);
459                                 g_value_set_enum (value, enum_value->value);
460                         }
461                         g_free (nick);
462                 }
463
464         } else if (G_IS_PARAM_SPEC_FLOAT (pspec) ||
465                    G_IS_PARAM_SPEC_DOUBLE (pspec)) {
466                 gdouble v_double;
467
468                 v_double = g_key_file_get_double (
469                         key_file, group_name, key, &error);
470                 if (error == NULL) {
471                         g_value_init (value, G_TYPE_DOUBLE);
472                         g_value_set_double (value, v_double);
473                 }
474
475         } else if (G_IS_PARAM_SPEC_STRING (pspec)) {
476                 gchar *v_string;
477
478                 /* Get the localized string if present. */
479                 v_string = g_key_file_get_locale_string (
480                         key_file, group_name, key, NULL, &error);
481                 if (error == NULL) {
482                         g_value_init (value, G_TYPE_STRING);
483                         g_value_take_string (value, v_string);
484                 }
485
486         } else if (g_type_is_a (pspec->value_type, G_TYPE_STRV)) {
487                 gchar **strv;
488
489                 strv = g_key_file_get_string_list (
490                         key_file, group_name, key, NULL, &error);
491                 if (error == NULL) {
492                         g_value_init (value, G_TYPE_STRV);
493                         g_value_take_boxed (value, strv);
494                 }
495
496         } else if (g_type_is_a (pspec->value_type, G_TYPE_FILE)) {
497                 gchar *uri;
498
499                 /* Create the GFile from the URI string. */
500                 uri = g_key_file_get_locale_string (
501                         key_file, group_name, key, NULL, &error);
502                 if (error == NULL) {
503                         GFile *file = NULL;
504                         if (uri != NULL && *uri != '\0')
505                                 file = g_file_new_for_uri (uri);
506                         g_value_init (value, pspec->value_type);
507                         g_value_take_object (value, file);
508                         g_free (uri);
509                 }
510
511         } else {
512                 g_warning (
513                         "No GKeyFile-to-GValue converter defined "
514                         "for type '%s'", G_PARAM_SPEC_TYPE_NAME (pspec));
515         }
516
517         /* If a value could not be retrieved from the key
518          * file, restore the property to its default value. */
519         if (error != NULL) {
520                 g_value_init (value, pspec->value_type);
521                 g_param_value_set_default (pspec, value);
522                 g_error_free (error);
523         }
524
525         if (G_IS_VALUE (value)) {
526                 g_object_set_property (object, pspec->name, value);
527                 g_value_unset (value);
528         }
529
530         g_slice_free (GValue, value);
531         g_free (key);
532 }
533
534 static void
535 source_load_from_key_file (GObject *object,
536                            GKeyFile *key_file,
537                            const gchar *group_name)
538 {
539         GObjectClass *class;
540         GParamSpec **properties;
541         guint n_properties, ii;
542
543         class = G_OBJECT_GET_CLASS (object);
544         properties = g_object_class_list_properties (class, &n_properties);
545
546         g_object_freeze_notify (object);
547
548         for (ii = 0; ii < n_properties; ii++) {
549                 if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) {
550                         source_set_property_from_key_file (
551                                 object, properties[ii], key_file, group_name);
552                 }
553         }
554
555         g_object_thaw_notify (object);
556
557         g_free (properties);
558 }
559
560 static void
561 source_save_to_key_file (GObject *object,
562                          GKeyFile *key_file,
563                          const gchar *group_name)
564 {
565         GObjectClass *class;
566         GParamSpec **properties;
567         guint n_properties, ii;
568
569         class = G_OBJECT_GET_CLASS (object);
570         properties = g_object_class_list_properties (class, &n_properties);
571
572         for (ii = 0; ii < n_properties; ii++) {
573                 if (properties[ii]->flags & E_SOURCE_PARAM_SETTING) {
574                         source_set_key_file_from_property (
575                                 object, properties[ii], key_file, group_name);
576                 }
577         }
578
579         g_free (properties);
580 }
581
582 static gboolean
583 source_parse_dbus_data (ESource *source,
584                         GError **error)
585 {
586         GHashTableIter iter;
587         EDBusObject *dbus_object;
588         EDBusSource *dbus_source;
589         GKeyFile *key_file;
590         gpointer group_name;
591         gpointer extension;
592         gchar *data;
593         gboolean success;
594
595         dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
596
597         dbus_source = e_dbus_object_get_source (dbus_object);
598         data = e_dbus_source_dup_data (dbus_source);
599         g_object_unref (dbus_source);
600
601         g_return_val_if_fail (data != NULL, FALSE);
602
603         key_file = source->priv->key_file;
604
605         success = g_key_file_load_from_data (
606                 key_file, data, strlen (data),
607                 G_KEY_FILE_KEEP_COMMENTS |
608                 G_KEY_FILE_KEEP_TRANSLATIONS,
609                 error);
610
611         g_free (data);
612         data = NULL;
613
614         if (!success)
615                 return FALSE;
616
617         /* Make sure the key file has a [Data Source] group. */
618         if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
619                 g_set_error (
620                         error, G_KEY_FILE_ERROR,
621                         G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
622                         _("Source file is missing a [%s] group"),
623                         PRIMARY_GROUP_NAME);
624                 return FALSE;
625         }
626
627         /* Load key file values from the [Data Source] group and from
628          * any other groups for which an extension object has already
629          * been created.  Note that not all the extension classes may
630          * be registered at this point, so avoid attempting to create
631          * new extension objects here.  Extension objects are created
632          * on-demand in e_source_get_extension(). */
633
634         source_load_from_key_file (
635                 G_OBJECT (source), key_file, PRIMARY_GROUP_NAME);
636
637         g_hash_table_iter_init (&iter, source->priv->extensions);
638         while (g_hash_table_iter_next (&iter, &group_name, &extension))
639                 source_load_from_key_file (extension, key_file, group_name);
640
641         return TRUE;
642 }
643
644 static void
645 source_notify_dbus_data_cb (EDBusSource *dbus_source,
646                             GParamSpec *pspec,
647                             ESource *source)
648 {
649         GError *error = NULL;
650
651         g_rec_mutex_lock (&source->priv->lock);
652
653         /* Since the source data came from a GKeyFile structure on the
654          * server-side, this should never fail.  But we'll print error
655          * messages to the terminal just in case. */
656         if (!source_parse_dbus_data (source, &error)) {
657                 g_return_if_fail (error != NULL);
658                 g_warning ("%s", error->message);
659                 g_error_free (error);
660         }
661
662         g_rec_mutex_unlock (&source->priv->lock);
663 }
664
665 static gboolean
666 source_idle_changed_cb (gpointer user_data)
667 {
668         ESource *source = E_SOURCE (user_data);
669
670         /* If the ESource is still initializing itself in a different
671          * thread, skip the signal emission and try again on the next
672          * main loop iteration.  This is a busy wait but it should be
673          * a very short wait. */
674         if (!source->priv->initialized)
675                 return TRUE;
676
677         g_mutex_lock (&source->priv->changed_lock);
678         if (source->priv->changed != NULL) {
679                 g_source_unref (source->priv->changed);
680                 source->priv->changed = NULL;
681         }
682         g_mutex_unlock (&source->priv->changed_lock);
683
684         g_signal_emit (source, signals[CHANGED], 0);
685
686         return FALSE;
687 }
688
689 static void
690 source_set_dbus_object (ESource *source,
691                         EDBusObject *dbus_object)
692 {
693         /* D-Bus object will be NULL when configuring a new source. */
694         if (dbus_object == NULL)
695                 return;
696
697         g_return_if_fail (E_DBUS_IS_OBJECT (dbus_object));
698         g_return_if_fail (source->priv->dbus_object == NULL);
699
700         source->priv->dbus_object = g_object_ref (dbus_object);
701 }
702
703 static void
704 source_set_main_context (ESource *source,
705                          GMainContext *main_context)
706 {
707         g_return_if_fail (source->priv->main_context == NULL);
708
709         source->priv->main_context =
710                 (main_context != NULL) ?
711                 g_main_context_ref (main_context) :
712                 g_main_context_ref_thread_default ();
713 }
714
715 static void
716 source_set_uid (ESource *source,
717                 const gchar *uid)
718 {
719         /* The "uid" argument will usually be NULL unless called
720          * from e_source_new_with_uid().  If NULL, we'll pick up
721          * a UID in source_initable_init(). */
722
723         g_return_if_fail (source->priv->uid == NULL);
724
725         source->priv->uid = g_strdup (uid);
726 }
727
728 static void
729 source_set_property (GObject *object,
730                      guint property_id,
731                      const GValue *value,
732                      GParamSpec *pspec)
733 {
734         switch (property_id) {
735                 case PROP_DBUS_OBJECT:
736                         source_set_dbus_object (
737                                 E_SOURCE (object),
738                                 g_value_get_object (value));
739                         return;
740
741                 case PROP_DISPLAY_NAME:
742                         e_source_set_display_name (
743                                 E_SOURCE (object),
744                                 g_value_get_string (value));
745                         return;
746
747                 case PROP_ENABLED:
748                         e_source_set_enabled (
749                                 E_SOURCE (object),
750                                 g_value_get_boolean (value));
751                         return;
752
753                 case PROP_MAIN_CONTEXT:
754                         source_set_main_context (
755                                 E_SOURCE (object),
756                                 g_value_get_boxed (value));
757                         return;
758
759                 case PROP_PARENT:
760                         e_source_set_parent (
761                                 E_SOURCE (object),
762                                 g_value_get_string (value));
763                         return;
764
765                 case PROP_UID:
766                         source_set_uid (
767                                 E_SOURCE (object),
768                                 g_value_get_string (value));
769                         return;
770         }
771
772         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
773 }
774
775 static void
776 source_get_property (GObject *object,
777                      guint property_id,
778                      GValue *value,
779                      GParamSpec *pspec)
780 {
781         switch (property_id) {
782                 case PROP_DBUS_OBJECT:
783                         g_value_take_object (
784                                 value, e_source_ref_dbus_object (
785                                 E_SOURCE (object)));
786                         return;
787
788                 case PROP_DISPLAY_NAME:
789                         g_value_take_string (
790                                 value, e_source_dup_display_name (
791                                 E_SOURCE (object)));
792                         return;
793
794                 case PROP_ENABLED:
795                         g_value_set_boolean (
796                                 value, e_source_get_enabled (
797                                 E_SOURCE (object)));
798                         return;
799
800                 case PROP_MAIN_CONTEXT:
801                         g_value_take_boxed (
802                                 value, e_source_ref_main_context (
803                                 E_SOURCE (object)));
804                         return;
805
806                 case PROP_PARENT:
807                         g_value_take_string (
808                                 value, e_source_dup_parent (
809                                 E_SOURCE (object)));
810                         return;
811
812                 case PROP_REMOTE_CREATABLE:
813                         g_value_set_boolean (
814                                 value, e_source_get_remote_creatable (
815                                 E_SOURCE (object)));
816                         return;
817
818                 case PROP_REMOTE_DELETABLE:
819                         g_value_set_boolean (
820                                 value, e_source_get_remote_deletable (
821                                 E_SOURCE (object)));
822                         return;
823
824                 case PROP_REMOVABLE:
825                         g_value_set_boolean (
826                                 value, e_source_get_removable (
827                                 E_SOURCE (object)));
828                         return;
829
830                 case PROP_UID:
831                         g_value_take_string (
832                                 value, e_source_dup_uid (
833                                 E_SOURCE (object)));
834                         return;
835
836                 case PROP_WRITABLE:
837                         g_value_set_boolean (
838                                 value, e_source_get_writable (
839                                 E_SOURCE (object)));
840                         return;
841         }
842
843         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
844 }
845
846 static void
847 source_dispose (GObject *object)
848 {
849         ESourcePrivate *priv;
850
851         priv = E_SOURCE_GET_PRIVATE (object);
852
853         if (priv->dbus_object != NULL) {
854                 EDBusObject *dbus_object;
855                 EDBusSource *dbus_source;
856
857                 dbus_object = E_DBUS_OBJECT (priv->dbus_object);
858
859                 dbus_source = e_dbus_object_get_source (dbus_object);
860                 if (dbus_source != NULL) {
861                         g_signal_handlers_disconnect_matched (
862                                 dbus_source, G_SIGNAL_MATCH_DATA,
863                                 0, 0, NULL, NULL, object);
864                         g_object_unref (dbus_source);
865                 }
866
867                 g_object_unref (priv->dbus_object);
868                 priv->dbus_object = NULL;
869         }
870
871         if (priv->main_context != NULL) {
872                 g_main_context_unref (priv->main_context);
873                 priv->main_context = NULL;
874         }
875
876         /* XXX Maybe not necessary to acquire the lock? */
877         g_mutex_lock (&priv->changed_lock);
878         if (priv->changed != NULL) {
879                 g_source_destroy (priv->changed);
880                 g_source_unref (priv->changed);
881                 priv->changed = NULL;
882         }
883         g_mutex_unlock (&priv->changed_lock);
884
885         g_hash_table_remove_all (priv->extensions);
886
887         /* Chain up to parent's dispose() method. */
888         G_OBJECT_CLASS (e_source_parent_class)->dispose (object);
889 }
890
891 static void
892 source_finalize (GObject *object)
893 {
894         ESourcePrivate *priv;
895
896         priv = E_SOURCE_GET_PRIVATE (object);
897
898         g_mutex_clear (&priv->changed_lock);
899         g_mutex_clear (&priv->property_lock);
900
901         g_free (priv->display_name);
902         g_free (priv->collate_key);
903         g_free (priv->parent);
904         g_free (priv->uid);
905
906         g_key_file_free (priv->key_file);
907         g_rec_mutex_clear (&priv->lock);
908         g_hash_table_destroy (priv->extensions);
909
910         /* Chain up to parent's finalize() method. */
911         G_OBJECT_CLASS (e_source_parent_class)->finalize (object);
912 }
913
914 static void
915 source_notify (GObject *object,
916                GParamSpec *pspec)
917 {
918         if ((pspec->flags & E_SOURCE_PARAM_SETTING) != 0)
919                 e_source_changed (E_SOURCE (object));
920 }
921
922 static gboolean
923 source_remove_sync (ESource *source,
924                     GCancellable *cancellable,
925                     GError **error)
926 {
927         EDBusSourceRemovable *dbus_interface = NULL;
928         GDBusObject *dbus_object;
929         gboolean success;
930
931         dbus_object = e_source_ref_dbus_object (source);
932         if (dbus_object != NULL) {
933                 dbus_interface =
934                         e_dbus_object_get_source_removable (
935                         E_DBUS_OBJECT (dbus_object));
936                 g_object_unref (dbus_object);
937         }
938
939         if (dbus_interface == NULL) {
940                 g_set_error (
941                         error, G_IO_ERROR,
942                         G_IO_ERROR_PERMISSION_DENIED,
943                         _("Data source '%s' is not removable"),
944                         e_source_get_display_name (source));
945                 return FALSE;
946         }
947
948         success = e_dbus_source_removable_call_remove_sync (
949                 dbus_interface, cancellable, error);
950
951         g_object_unref (dbus_interface);
952
953         return success;
954 }
955
956 /* Helper for source_remove() */
957 static void
958 source_remove_thread (GSimpleAsyncResult *simple,
959                       GObject *object,
960                       GCancellable *cancellable)
961 {
962         GError *error = NULL;
963
964         e_source_remove_sync (E_SOURCE (object), cancellable, &error);
965
966         if (error != NULL)
967                 g_simple_async_result_take_error (simple, error);
968 }
969
970 static void
971 source_remove (ESource *source,
972                GCancellable *cancellable,
973                GAsyncReadyCallback callback,
974                gpointer user_data)
975 {
976         GSimpleAsyncResult *simple;
977
978         simple = g_simple_async_result_new (
979                 G_OBJECT (source), callback, user_data, source_remove);
980
981         g_simple_async_result_set_check_cancellable (simple, cancellable);
982
983         g_simple_async_result_run_in_thread (
984                 simple, source_remove_thread,
985                 G_PRIORITY_DEFAULT, cancellable);
986
987         g_object_unref (simple);
988 }
989
990 static gboolean
991 source_remove_finish (ESource *source,
992                       GAsyncResult *result,
993                       GError **error)
994 {
995         GSimpleAsyncResult *simple;
996
997         g_return_val_if_fail (
998                 g_simple_async_result_is_valid (
999                 result, G_OBJECT (source), source_remove), FALSE);
1000
1001         simple = G_SIMPLE_ASYNC_RESULT (result);
1002
1003         /* Assume success unless a GError is set. */
1004         return !g_simple_async_result_propagate_error (simple, error);
1005 }
1006
1007 static gboolean
1008 source_write_sync (ESource *source,
1009                    GCancellable *cancellable,
1010                    GError **error)
1011 {
1012         EDBusSourceWritable *dbus_interface = NULL;
1013         GDBusObject *dbus_object;
1014         gboolean success;
1015         gchar *data;
1016
1017         dbus_object = e_source_ref_dbus_object (source);
1018         if (dbus_object != NULL) {
1019                 dbus_interface =
1020                         e_dbus_object_get_source_writable (
1021                         E_DBUS_OBJECT (dbus_object));
1022                 g_object_unref (dbus_object);
1023         }
1024
1025         if (dbus_interface == NULL) {
1026                 g_set_error (
1027                         error, G_IO_ERROR,
1028                         G_IO_ERROR_PERMISSION_DENIED,
1029                         _("Data source '%s' is not writable"),
1030                         e_source_get_display_name (source));
1031                 return FALSE;
1032         }
1033
1034         data = e_source_to_string (source, NULL);
1035
1036         success = e_dbus_source_writable_call_write_sync (
1037                 dbus_interface, data, cancellable, error);
1038
1039         g_free (data);
1040
1041         g_object_unref (dbus_interface);
1042
1043         return success;
1044 }
1045
1046 /* Helper for source_write() */
1047 static void
1048 source_write_thread (GSimpleAsyncResult *simple,
1049                      GObject *object,
1050                      GCancellable *cancellable)
1051 {
1052         GError *error = NULL;
1053
1054         e_source_write_sync (E_SOURCE (object), cancellable, &error);
1055
1056         if (error != NULL)
1057                 g_simple_async_result_take_error (simple, error);
1058 }
1059
1060 static void
1061 source_write (ESource *source,
1062               GCancellable *cancellable,
1063               GAsyncReadyCallback callback,
1064               gpointer user_data)
1065 {
1066         GSimpleAsyncResult *simple;
1067
1068         simple = g_simple_async_result_new (
1069                 G_OBJECT (source), callback, user_data, source_write);
1070
1071         g_simple_async_result_set_check_cancellable (simple, cancellable);
1072
1073         g_simple_async_result_run_in_thread (
1074                 simple, source_write_thread,
1075                 G_PRIORITY_DEFAULT, cancellable);
1076
1077         g_object_unref (simple);
1078 }
1079
1080 static gboolean
1081 source_write_finish (ESource *source,
1082                      GAsyncResult *result,
1083                      GError **error)
1084 {
1085         GSimpleAsyncResult *simple;
1086
1087         g_return_val_if_fail (
1088                 g_simple_async_result_is_valid (
1089                 result, G_OBJECT (source), source_write), FALSE);
1090
1091         simple = G_SIMPLE_ASYNC_RESULT (result);
1092
1093         /* Assume success unless a GError is set. */
1094         return !g_simple_async_result_propagate_error (simple, error);
1095 }
1096
1097 static gboolean
1098 source_remote_create_sync (ESource *source,
1099                            ESource *scratch_source,
1100                            GCancellable *cancellable,
1101                            GError **error)
1102 {
1103         EDBusSourceRemoteCreatable *dbus_interface = NULL;
1104         GDBusObject *dbus_object;
1105         gchar *uid, *data;
1106         gboolean success;
1107
1108         dbus_object = e_source_ref_dbus_object (source);
1109         if (dbus_object != NULL) {
1110                 dbus_interface =
1111                         e_dbus_object_get_source_remote_creatable (
1112                         E_DBUS_OBJECT (dbus_object));
1113                 g_object_unref (dbus_object);
1114         }
1115
1116         if (dbus_interface == NULL) {
1117                 g_set_error (
1118                         error, G_IO_ERROR,
1119                         G_IO_ERROR_NOT_SUPPORTED,
1120                         _("Data source '%s' does not "
1121                         "support creating remote resources"),
1122                         e_source_get_display_name (source));
1123                 return FALSE;
1124         }
1125
1126         uid = e_source_dup_uid (scratch_source);
1127         data = e_source_to_string (scratch_source, NULL);
1128
1129         success = e_dbus_source_remote_creatable_call_create_sync (
1130                 dbus_interface, uid, data, cancellable, error);
1131
1132         g_free (data);
1133         g_free (uid);
1134
1135         g_object_unref (dbus_interface);
1136
1137         return success;
1138 }
1139
1140 /* Helper for source_remote_create() */
1141 static void
1142 source_remote_create_thread (GSimpleAsyncResult *simple,
1143                              GObject *object,
1144                              GCancellable *cancellable)
1145 {
1146         AsyncContext *async_context;
1147         GError *error = NULL;
1148
1149         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1150
1151         e_source_remote_create_sync (
1152                 E_SOURCE (object),
1153                 async_context->scratch_source,
1154                 cancellable, &error);
1155
1156         if (error != NULL)
1157                 g_simple_async_result_take_error (simple, error);
1158 }
1159
1160 static void
1161 source_remote_create (ESource *source,
1162                       ESource *scratch_source,
1163                       GCancellable *cancellable,
1164                       GAsyncReadyCallback callback,
1165                       gpointer user_data)
1166 {
1167         GSimpleAsyncResult *simple;
1168         AsyncContext *async_context;
1169
1170         async_context = g_slice_new0 (AsyncContext);
1171         async_context->scratch_source = g_object_ref (scratch_source);
1172
1173         simple = g_simple_async_result_new (
1174                 G_OBJECT (source), callback,
1175                 user_data, source_remote_create);
1176
1177         g_simple_async_result_set_check_cancellable (simple, cancellable);
1178
1179         g_simple_async_result_set_op_res_gpointer (
1180                 simple, async_context, (GDestroyNotify) async_context_free);
1181
1182         g_simple_async_result_run_in_thread (
1183                 simple, source_remote_create_thread,
1184                 G_PRIORITY_DEFAULT, cancellable);
1185
1186         g_object_unref (simple);
1187 }
1188
1189 static gboolean
1190 source_remote_create_finish (ESource *source,
1191                              GAsyncResult *result,
1192                              GError **error)
1193 {
1194         GSimpleAsyncResult *simple;
1195
1196         g_return_val_if_fail (
1197                 g_simple_async_result_is_valid (
1198                 result, G_OBJECT (source), source_remote_create), FALSE);
1199
1200         simple = G_SIMPLE_ASYNC_RESULT (result);
1201
1202         /* Assume success unless a GError is set. */
1203         return !g_simple_async_result_propagate_error (simple, error);
1204 }
1205
1206 static gboolean
1207 source_remote_delete_sync (ESource *source,
1208                            GCancellable *cancellable,
1209                            GError **error)
1210 {
1211         EDBusSourceRemoteDeletable *dbus_interface = NULL;
1212         GDBusObject *dbus_object;
1213         gboolean success;
1214
1215         dbus_object = e_source_ref_dbus_object (source);
1216         if (dbus_object != NULL) {
1217                 dbus_interface =
1218                         e_dbus_object_get_source_remote_deletable (
1219                         E_DBUS_OBJECT (dbus_object));
1220                 g_object_unref (dbus_object);
1221         }
1222
1223         if (dbus_interface == NULL) {
1224                 g_set_error (
1225                         error, G_IO_ERROR,
1226                         G_IO_ERROR_NOT_SUPPORTED,
1227                         _("Data source '%s' does not "
1228                         "support deleting remote resources"),
1229                         e_source_get_display_name (source));
1230                 return FALSE;
1231         }
1232
1233         success = e_dbus_source_remote_deletable_call_delete_sync (
1234                 dbus_interface, cancellable, error);
1235
1236         g_object_unref (dbus_interface);
1237
1238         return success;
1239 }
1240
1241 /* Helper for source_remote_delete() */
1242 static void
1243 source_remote_delete_thread (GSimpleAsyncResult *simple,
1244                              GObject *object,
1245                              GCancellable *cancellable)
1246 {
1247         GError *error = NULL;
1248
1249         e_source_remote_delete_sync (
1250                 E_SOURCE (object), cancellable, &error);
1251
1252         if (error != NULL)
1253                 g_simple_async_result_take_error (simple, error);
1254 }
1255
1256 static void
1257 source_remote_delete (ESource *source,
1258                       GCancellable *cancellable,
1259                       GAsyncReadyCallback callback,
1260                       gpointer user_data)
1261 {
1262         GSimpleAsyncResult *simple;
1263
1264         simple = g_simple_async_result_new (
1265                 G_OBJECT (source), callback,
1266                 user_data, source_remote_delete);
1267
1268         g_simple_async_result_set_check_cancellable (simple, cancellable);
1269
1270         g_simple_async_result_run_in_thread (
1271                 simple, source_remote_delete_thread,
1272                 G_PRIORITY_DEFAULT, cancellable);
1273
1274         g_object_unref (simple);
1275 }
1276
1277 static gboolean
1278 source_remote_delete_finish (ESource *source,
1279                              GAsyncResult *result,
1280                              GError **error)
1281 {
1282         GSimpleAsyncResult *simple;
1283
1284         g_return_val_if_fail (
1285                 g_simple_async_result_is_valid (
1286                 result, G_OBJECT (source), source_remote_delete), FALSE);
1287
1288         simple = G_SIMPLE_ASYNC_RESULT (result);
1289
1290         /* Assume success unless a GError is set. */
1291         return !g_simple_async_result_propagate_error (simple, error);
1292 }
1293
1294 static gboolean
1295 source_get_oauth2_access_token_sync (ESource *source,
1296                                      GCancellable *cancellable,
1297                                      gchar **out_access_token,
1298                                      gint *out_expires_in,
1299                                      GError **error)
1300 {
1301         EDBusSourceOAuth2Support *dbus_interface = NULL;
1302         GDBusObject *dbus_object;
1303         gboolean success;
1304
1305         dbus_object = e_source_ref_dbus_object (source);
1306         if (dbus_object != NULL) {
1307                 dbus_interface =
1308                         e_dbus_object_get_source_oauth2_support (
1309                         E_DBUS_OBJECT (dbus_object));
1310                 g_object_unref (dbus_object);
1311         }
1312
1313         if (dbus_interface == NULL) {
1314                 g_set_error (
1315                         error, G_IO_ERROR,
1316                         G_IO_ERROR_NOT_SUPPORTED,
1317                         _("Data source '%s' does not "
1318                         "support OAuth 2.0 authentication"),
1319                         e_source_get_display_name (source));
1320                 return FALSE;
1321         }
1322
1323         success = e_dbus_source_oauth2_support_call_get_access_token_sync (
1324                 dbus_interface, out_access_token,
1325                 out_expires_in, cancellable, error);
1326
1327         g_object_unref (dbus_interface);
1328
1329         return success;
1330 }
1331
1332 /* Helper for source_get_oauth2_access_token() */
1333 static void
1334 source_get_oauth2_access_token_thread (GSimpleAsyncResult *simple,
1335                                        GObject *object,
1336                                        GCancellable *cancellable)
1337 {
1338         AsyncContext *async_context;
1339         GError *error = NULL;
1340
1341         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1342
1343         e_source_get_oauth2_access_token_sync (
1344                 E_SOURCE (object), cancellable,
1345                 &async_context->access_token,
1346                 &async_context->expires_in,
1347                 &error);
1348
1349         if (error != NULL)
1350                 g_simple_async_result_take_error (simple, error);
1351 }
1352
1353 static void
1354 source_get_oauth2_access_token (ESource *source,
1355                                 GCancellable *cancellable,
1356                                 GAsyncReadyCallback callback,
1357                                 gpointer user_data)
1358 {
1359         GSimpleAsyncResult *simple;
1360         AsyncContext *async_context;
1361
1362         async_context = g_slice_new0 (AsyncContext);
1363
1364         simple = g_simple_async_result_new (
1365                 G_OBJECT (source), callback, user_data,
1366                 source_get_oauth2_access_token);
1367
1368         g_simple_async_result_set_check_cancellable (simple, cancellable);
1369
1370         g_simple_async_result_set_op_res_gpointer (
1371                 simple, async_context, (GDestroyNotify) async_context_free);
1372
1373         g_simple_async_result_run_in_thread (
1374                 simple, source_get_oauth2_access_token_thread,
1375                 G_PRIORITY_DEFAULT, cancellable);
1376
1377         g_object_unref (simple);
1378 }
1379
1380 static gboolean
1381 source_get_oauth2_access_token_finish (ESource *source,
1382                                        GAsyncResult *result,
1383                                        gchar **out_access_token,
1384                                        gint *out_expires_in,
1385                                        GError **error)
1386 {
1387         GSimpleAsyncResult *simple;
1388         AsyncContext *async_context;
1389
1390         g_return_val_if_fail (
1391                 g_simple_async_result_is_valid (
1392                 result, G_OBJECT (source),
1393                 source_get_oauth2_access_token), FALSE);
1394
1395         simple = G_SIMPLE_ASYNC_RESULT (result);
1396         async_context = g_simple_async_result_get_op_res_gpointer (simple);
1397
1398         if (g_simple_async_result_propagate_error (simple, error))
1399                 return FALSE;
1400
1401         g_return_val_if_fail (async_context->access_token != NULL, FALSE);
1402
1403         if (out_access_token != NULL) {
1404                 *out_access_token = async_context->access_token;
1405                 async_context->access_token = NULL;
1406         }
1407
1408         if (out_expires_in != NULL)
1409                 *out_expires_in = async_context->expires_in;
1410
1411         return TRUE;
1412 }
1413
1414 static gboolean
1415 source_initable_init (GInitable *initable,
1416                       GCancellable *cancellable,
1417                       GError **error)
1418 {
1419         ESource *source;
1420         gboolean success = TRUE;
1421
1422         source = E_SOURCE (initable);
1423
1424         /* The D-Bus object has the unique identifier (UID). */
1425         if (source->priv->dbus_object != NULL) {
1426                 EDBusObject *dbus_object;
1427                 EDBusSource *dbus_source;
1428
1429                 dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
1430
1431                 /* An EDBusObject lacking an EDBusSource
1432                  * interface indicates a programmer error. */
1433                 dbus_source = e_dbus_object_get_source (dbus_object);
1434                 g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
1435
1436                 /* The UID never changes, so we can cache a copy.
1437                  *
1438                  * XXX Note, EServerSideSource may have already set this
1439                  *     by way of the "uid" construct-only property, hence
1440                  *     the g_free() call.  Not a problem, we'll just free
1441                  *     our UID string and set it to the same value again. */
1442                 g_free (source->priv->uid);
1443                 source->priv->uid = e_dbus_source_dup_uid (dbus_source);
1444
1445                 g_signal_connect (
1446                         dbus_source, "notify::data",
1447                         G_CALLBACK (source_notify_dbus_data_cb), source);
1448
1449                 success = source_parse_dbus_data (source, error);
1450
1451                 g_object_unref (dbus_source);
1452
1453         /* No D-Bus object implies we're configuring a new source,
1454          * so generate a new unique identifier (UID) unless one was
1455          * explicitly provided through e_source_new_with_uid(). */
1456         } else if (source->priv->uid == NULL) {
1457                 source->priv->uid = e_uid_new ();
1458         }
1459
1460         /* Try to avoid a spurious "changed" emission. */
1461         g_mutex_lock (&source->priv->changed_lock);
1462         if (source->priv->changed != NULL) {
1463                 g_source_destroy (source->priv->changed);
1464                 g_source_unref (source->priv->changed);
1465                 source->priv->changed = NULL;
1466         }
1467         g_mutex_unlock (&source->priv->changed_lock);
1468
1469         source->priv->initialized = TRUE;
1470
1471         return success;
1472 }
1473
1474 static void
1475 e_source_class_init (ESourceClass *class)
1476 {
1477         GObjectClass *object_class;
1478
1479         g_type_class_add_private (class, sizeof (ESourcePrivate));
1480
1481         object_class = G_OBJECT_CLASS (class);
1482         object_class->set_property = source_set_property;
1483         object_class->get_property = source_get_property;
1484         object_class->dispose = source_dispose;
1485         object_class->finalize = source_finalize;
1486         object_class->notify = source_notify;
1487
1488         class->remove_sync = source_remove_sync;
1489         class->remove = source_remove;
1490         class->remove_finish = source_remove_finish;
1491         class->write_sync = source_write_sync;
1492         class->write = source_write;
1493         class->write_finish = source_write_finish;
1494         class->remote_create_sync = source_remote_create_sync;
1495         class->remote_create = source_remote_create;
1496         class->remote_create_finish = source_remote_create_finish;
1497         class->remote_delete_sync = source_remote_delete_sync;
1498         class->remote_delete = source_remote_delete;
1499         class->remote_delete_finish = source_remote_delete_finish;
1500         class->get_oauth2_access_token_sync = source_get_oauth2_access_token_sync;
1501         class->get_oauth2_access_token = source_get_oauth2_access_token;
1502         class->get_oauth2_access_token_finish = source_get_oauth2_access_token_finish;
1503
1504         g_object_class_install_property (
1505                 object_class,
1506                 PROP_DBUS_OBJECT,
1507                 g_param_spec_object (
1508                         "dbus-object",
1509                         "D-Bus Object",
1510                         "The D-Bus object for the data source",
1511                         E_DBUS_TYPE_OBJECT,
1512                         G_PARAM_READWRITE |
1513                         G_PARAM_CONSTRUCT_ONLY |
1514                         G_PARAM_STATIC_STRINGS));
1515
1516         g_object_class_install_property (
1517                 object_class,
1518                 PROP_DISPLAY_NAME,
1519                 g_param_spec_string (
1520                         "display-name",
1521                         "Display Name",
1522                         "The human-readable name of the data source",
1523                         _("Unnamed"),
1524                         G_PARAM_READWRITE |
1525                         G_PARAM_CONSTRUCT |
1526                         G_PARAM_STATIC_STRINGS |
1527                         E_SOURCE_PARAM_SETTING));
1528
1529         g_object_class_install_property (
1530                 object_class,
1531                 PROP_ENABLED,
1532                 g_param_spec_boolean (
1533                         "enabled",
1534                         "Enabled",
1535                         "Whether the data source is enabled",
1536                         TRUE,
1537                         G_PARAM_READWRITE |
1538                         G_PARAM_CONSTRUCT |
1539                         G_PARAM_STATIC_STRINGS |
1540                         E_SOURCE_PARAM_SETTING));
1541
1542         g_object_class_install_property (
1543                 object_class,
1544                 PROP_MAIN_CONTEXT,
1545                 g_param_spec_boxed (
1546                         "main-context",
1547                         "Main Context",
1548                         "The main loop context on "
1549                         "which to attach event sources",
1550                         G_TYPE_MAIN_CONTEXT,
1551                         G_PARAM_READWRITE |
1552                         G_PARAM_CONSTRUCT_ONLY |
1553                         G_PARAM_STATIC_STRINGS));
1554
1555         g_object_class_install_property (
1556                 object_class,
1557                 PROP_PARENT,
1558                 g_param_spec_string (
1559                         "parent",
1560                         "Parent",
1561                         "The unique identity of the parent data source",
1562                         NULL,
1563                         G_PARAM_READWRITE |
1564                         G_PARAM_STATIC_STRINGS |
1565                         E_SOURCE_PARAM_SETTING));
1566
1567         g_object_class_install_property (
1568                 object_class,
1569                 PROP_REMOTE_CREATABLE,
1570                 g_param_spec_boolean (
1571                         "remote-creatable",
1572                         "Remote Creatable",
1573                         "Whether the data source "
1574                         "can create remote resources",
1575                         FALSE,
1576                         G_PARAM_READABLE |
1577                         G_PARAM_STATIC_STRINGS));
1578
1579         g_object_class_install_property (
1580                 object_class,
1581                 PROP_REMOTE_DELETABLE,
1582                 g_param_spec_boolean (
1583                         "remote-deletable",
1584                         "Remote Deletable",
1585                         "Whether the data source "
1586                         "can delete remote resources",
1587                         FALSE,
1588                         G_PARAM_READABLE |
1589                         G_PARAM_STATIC_STRINGS));
1590
1591         g_object_class_install_property (
1592                 object_class,
1593                 PROP_REMOVABLE,
1594                 g_param_spec_boolean (
1595                         "removable",
1596                         "Removable",
1597                         "Whether the data source is removable",
1598                         FALSE,
1599                         G_PARAM_READABLE |
1600                         G_PARAM_STATIC_STRINGS));
1601
1602         g_object_class_install_property (
1603                 object_class,
1604                 PROP_UID,
1605                 g_param_spec_string (
1606                         "uid",
1607                         "UID",
1608                         "The unique identity of the data source",
1609                         NULL,
1610                         G_PARAM_READWRITE |
1611                         G_PARAM_CONSTRUCT_ONLY |
1612                         G_PARAM_STATIC_STRINGS));
1613
1614         g_object_class_install_property (
1615                 object_class,
1616                 PROP_WRITABLE,
1617                 g_param_spec_boolean (
1618                         "writable",
1619                         "Writable",
1620                         "Whether the data source is writable",
1621                         FALSE,
1622                         G_PARAM_READABLE |
1623                         G_PARAM_STATIC_STRINGS));
1624
1625         /**
1626          * ESource::changed:
1627          * @source: the #ESource that received the signal
1628          *
1629          * The ::changed signal is emitted when a property in @source or
1630          * one of its extension objects changes.  A common use for this
1631          * signal is to notify a #GtkTreeModel containing data collected
1632          * from #ESource<!-- -->s that it needs to update a row.
1633          **/
1634         signals[CHANGED] = g_signal_new (
1635                 "changed",
1636                 G_TYPE_FROM_CLASS (class),
1637                 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
1638                 G_STRUCT_OFFSET (ESourceClass, changed),
1639                 NULL, NULL,
1640                 g_cclosure_marshal_VOID__VOID,
1641                 G_TYPE_NONE, 0);
1642
1643         /* Register built-in ESourceExtension types. */
1644         g_type_ensure (E_TYPE_SOURCE_ADDRESS_BOOK);
1645         g_type_ensure (E_TYPE_SOURCE_ALARMS);
1646         g_type_ensure (E_TYPE_SOURCE_AUTHENTICATION);
1647         g_type_ensure (E_TYPE_SOURCE_AUTOCOMPLETE);
1648         g_type_ensure (E_TYPE_SOURCE_CALENDAR);
1649         g_type_ensure (E_TYPE_SOURCE_COLLECTION);
1650         g_type_ensure (E_TYPE_SOURCE_GOA);
1651         g_type_ensure (E_TYPE_SOURCE_MAIL_ACCOUNT);
1652         g_type_ensure (E_TYPE_SOURCE_MAIL_COMPOSITION);
1653         g_type_ensure (E_TYPE_SOURCE_MAIL_IDENTITY);
1654         g_type_ensure (E_TYPE_SOURCE_MAIL_SIGNATURE);
1655         g_type_ensure (E_TYPE_SOURCE_MAIL_SUBMISSION);
1656         g_type_ensure (E_TYPE_SOURCE_MAIL_TRANSPORT);
1657         g_type_ensure (E_TYPE_SOURCE_MDN);
1658         g_type_ensure (E_TYPE_SOURCE_MEMO_LIST);
1659         g_type_ensure (E_TYPE_SOURCE_OFFLINE);
1660         g_type_ensure (E_TYPE_SOURCE_OPENPGP);
1661         g_type_ensure (E_TYPE_SOURCE_REFRESH);
1662         g_type_ensure (E_TYPE_SOURCE_RESOURCE);
1663         g_type_ensure (E_TYPE_SOURCE_REVISION_GUARDS);
1664         g_type_ensure (E_TYPE_SOURCE_SECURITY);
1665         g_type_ensure (E_TYPE_SOURCE_SELECTABLE);
1666         g_type_ensure (E_TYPE_SOURCE_SMIME);
1667         g_type_ensure (E_TYPE_SOURCE_TASK_LIST);
1668         g_type_ensure (E_TYPE_SOURCE_UOA);
1669         g_type_ensure (E_TYPE_SOURCE_WEBDAV);
1670 }
1671
1672 static void
1673 e_source_initable_init (GInitableIface *interface)
1674 {
1675         interface->init = source_initable_init;
1676 }
1677
1678 static void
1679 e_source_init (ESource *source)
1680 {
1681         GHashTable *extensions;
1682
1683         /* Don't do this as part of class initialization because it
1684          * loads Camel modules and can screw up introspection, which
1685          * occurs at compile-time before Camel modules are installed. */
1686         e_source_camel_register_types ();
1687
1688         extensions = g_hash_table_new_full (
1689                 (GHashFunc) g_str_hash,
1690                 (GEqualFunc) g_str_equal,
1691                 (GDestroyNotify) g_free,
1692                 (GDestroyNotify) g_object_unref);
1693
1694         source->priv = E_SOURCE_GET_PRIVATE (source);
1695         g_mutex_init (&source->priv->changed_lock);
1696         g_mutex_init (&source->priv->property_lock);
1697         source->priv->key_file = g_key_file_new ();
1698         source->priv->extensions = extensions;
1699
1700         g_rec_mutex_init (&source->priv->lock);
1701 }
1702
1703 /**
1704  * e_source_new:
1705  * @dbus_object: (allow-none): a #GDBusObject or %NULL
1706  * @main_context: (allow-none): a #GMainContext or %NULL
1707  * @error: return location for a #GError, or %NULL
1708  *
1709  * Creates a new #ESource instance.
1710  *
1711  * The #ESource::changed signal will be emitted from @main_context if given,
1712  * or else from the thread-default #GMainContext at the time this function is
1713  * called.
1714  *
1715  * The only time the function should be called outside of #ESourceRegistry
1716  * is to create a so-called "scratch" #ESource for editing in a Properties
1717  * window or an account setup assistant.
1718  *
1719  * FIXME: Elaborate on scratch sources.
1720  *
1721  * Returns: a new #ESource, or %NULL on error
1722  *
1723  * Since: 3.6
1724  **/
1725 ESource *
1726 e_source_new (GDBusObject *dbus_object,
1727               GMainContext *main_context,
1728               GError **error)
1729 {
1730         if (dbus_object != NULL)
1731                 g_return_val_if_fail (E_DBUS_IS_OBJECT (dbus_object), NULL);
1732
1733         return g_initable_new (
1734                 E_TYPE_SOURCE, NULL, error,
1735                 "dbus-object", dbus_object,
1736                 "main-context", main_context,
1737                 NULL);
1738 }
1739
1740 /**
1741  * e_source_new_with_uid:
1742  * @uid: a new unique identifier string
1743  * @main_context: (allow-none): a #GMainContext or %NULL
1744  * @error: return location for a #GError, or %NULL
1745  *
1746  * Creates a new "scratch" #ESource with a predetermined unique identifier.
1747  *
1748  * The #ESource::changed signal will be emitted from @main_context if given,
1749  * or else from the thread-default #GMainContext at the time this function is
1750  * called.
1751  *
1752  * Returns: a new scratch #ESource, or %NULL on error
1753  *
1754  * Since: 3.6
1755  **/
1756 ESource *
1757 e_source_new_with_uid (const gchar *uid,
1758                        GMainContext *main_context,
1759                        GError **error)
1760 {
1761         g_return_val_if_fail (uid != NULL, NULL);
1762
1763         return g_initable_new (
1764                 E_TYPE_SOURCE, NULL, error,
1765                 "main-context", main_context,
1766                 "uid", uid, NULL);
1767 }
1768
1769 /**
1770  * e_source_hash:
1771  * @source: an #ESource
1772  *
1773  * Generates a hash value for @source.  This function is intended for
1774  * easily hashing an #ESource to add to a #GHashTable or similar data
1775  * structure.
1776  *
1777  * Returns: a hash value for @source.
1778  *
1779  * Since: 3.6
1780  **/
1781 guint
1782 e_source_hash (ESource *source)
1783 {
1784         const gchar *uid;
1785
1786         g_return_val_if_fail (E_IS_SOURCE (source), 0);
1787
1788         uid = e_source_get_uid (source);
1789
1790         return g_str_hash (uid);
1791 }
1792
1793 /**
1794  * e_source_equal:
1795  * @source1: the first #ESource
1796  * @source2: the second #ESource
1797  *
1798  * Checks two #ESource instances for equality.  #ESource instances are
1799  * equal if their unique identifier strings are equal.
1800  *
1801  * Returns: %TRUE if @source1 and @source2 are equal
1802  *
1803  * Since: 3.6
1804  **/
1805 gboolean
1806 e_source_equal (ESource *source1,
1807                 ESource *source2)
1808 {
1809         const gchar *uid1, *uid2;
1810
1811         g_return_val_if_fail (E_IS_SOURCE (source1), FALSE);
1812         g_return_val_if_fail (E_IS_SOURCE (source2), FALSE);
1813
1814         if (source1 == source2)
1815                 return TRUE;
1816
1817         uid1 = e_source_get_uid (source1);
1818         uid2 = e_source_get_uid (source2);
1819
1820         return g_str_equal (uid1, uid2);
1821 }
1822
1823 /**
1824  * e_source_changed:
1825  * @source: an #ESource
1826  *
1827  * Emits the #ESource::changed signal from an idle callback in
1828  * @source's #ESource:main-context.
1829  *
1830  * This function is primarily intended for use by #ESourceExtension
1831  * when emitting a #GObject::notify signal on one of its properties.
1832  *
1833  * Since: 3.6
1834  **/
1835 void
1836 e_source_changed (ESource *source)
1837 {
1838         g_return_if_fail (E_IS_SOURCE (source));
1839
1840         g_mutex_lock (&source->priv->changed_lock);
1841         if (source->priv->changed == NULL) {
1842                 source->priv->changed = g_idle_source_new ();
1843                 g_source_set_callback (
1844                         source->priv->changed,
1845                         source_idle_changed_cb,
1846                         source, (GDestroyNotify) NULL);
1847                 g_source_attach (
1848                         source->priv->changed,
1849                         source->priv->main_context);
1850         }
1851         g_mutex_unlock (&source->priv->changed_lock);
1852 }
1853
1854 /**
1855  * e_source_get_uid:
1856  * @source: an #ESource
1857  *
1858  * Returns the unique identifier string for @source.
1859  *
1860  * Returns: the UID for @source
1861  *
1862  * Since: 3.6
1863  **/
1864 const gchar *
1865 e_source_get_uid (ESource *source)
1866 {
1867         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1868
1869         return source->priv->uid;
1870 }
1871
1872 /**
1873  * e_source_dup_uid:
1874  * @source: an #ESource
1875  *
1876  * Thread-safe variation of e_source_get_uid().
1877  * Use this function when accessing @source from multiple threads.
1878  *
1879  * The returned string should be freed with g_free() when no longer needed.
1880  *
1881  * Returns: a newly-allocated copy of #ESource:uid
1882  *
1883  * Since: 3.6
1884  **/
1885 gchar *
1886 e_source_dup_uid (ESource *source)
1887 {
1888         const gchar *protected;
1889         gchar *duplicate;
1890
1891         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1892
1893         /* Perhaps we don't need to lock the mutex since
1894          * this is a read-only property but it can't hurt. */
1895
1896         g_mutex_lock (&source->priv->property_lock);
1897
1898         protected = e_source_get_uid (source);
1899         duplicate = g_strdup (protected);
1900
1901         g_mutex_unlock (&source->priv->property_lock);
1902
1903         return duplicate;
1904 }
1905
1906 /**
1907  * e_source_get_parent:
1908  * @source: an #ESource
1909  *
1910  * Returns the unique identifier string of the parent #ESource.
1911  *
1912  * Returns: the UID of the parent #ESource
1913  *
1914  * Since: 3.6
1915  **/
1916 const gchar *
1917 e_source_get_parent (ESource *source)
1918 {
1919         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1920
1921         return source->priv->parent;
1922 }
1923
1924 /**
1925  * e_source_dup_parent:
1926  * @source: an #ESource
1927  *
1928  * Thread-safe variation of e_source_get_parent().
1929  * Use this function when accessing @source from multiple threads.
1930  *
1931  * The returned string should be freed with g_free() when no longer needed.
1932  *
1933  * Returns: a newly-allocated copy of #ESource:parent
1934  *
1935  * Since: 3.6
1936  **/
1937 gchar *
1938 e_source_dup_parent (ESource *source)
1939 {
1940         const gchar *protected;
1941         gchar *duplicate;
1942
1943         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
1944
1945         g_mutex_lock (&source->priv->property_lock);
1946
1947         protected = e_source_get_parent (source);
1948         duplicate = g_strdup (protected);
1949
1950         g_mutex_unlock (&source->priv->property_lock);
1951
1952         return duplicate;
1953 }
1954
1955 /**
1956  * e_source_set_parent:
1957  * @source: an #ESource
1958  * @parent: (allow-none): the UID of the parent #ESource, or %NULL
1959  *
1960  * Identifies the parent of @source by its unique identifier string.
1961  * This can only be set prior to adding @source to an #ESourceRegistry.
1962  *
1963  * The internal copy of #ESource:parent is automatically stripped of leading
1964  * and trailing whitespace.  If the resulting string is empty, %NULL is set
1965  * instead.
1966  *
1967  * Since: 3.6
1968  **/
1969 void
1970 e_source_set_parent (ESource *source,
1971                      const gchar *parent)
1972 {
1973         g_return_if_fail (E_IS_SOURCE (source));
1974
1975         g_mutex_lock (&source->priv->property_lock);
1976
1977         if (g_strcmp0 (source->priv->parent, parent) == 0) {
1978                 g_mutex_unlock (&source->priv->property_lock);
1979                 return;
1980         }
1981
1982         g_free (source->priv->parent);
1983         source->priv->parent = e_util_strdup_strip (parent);
1984
1985         g_mutex_unlock (&source->priv->property_lock);
1986
1987         g_object_notify (G_OBJECT (source), "parent");
1988 }
1989
1990 /**
1991  * e_source_get_enabled:
1992  * @source: an #ESource
1993  *
1994  * Returns %TRUE if @source is enabled.
1995  *
1996  * An application should try to honor this setting if at all possible,
1997  * even if it does not provide a way to change the setting through its
1998  * user interface.  Disabled data sources should generally be hidden.
1999  *
2000  * <note><para>
2001  *   This function does not take into account @source's ancestors in the
2002  *   #ESource hierarchy, each of which have their own enabled state.  If
2003  *   any of @source's ancestors are disabled, then @source itself should
2004  *   be treated as disabled.  Use e_source_registry_check_enabled() to
2005  *   easily check for this.
2006  * </para></note>
2007  *
2008  * Returns: whether @source is enabled
2009  *
2010  * Since: 3.6
2011  **/
2012 gboolean
2013 e_source_get_enabled (ESource *source)
2014 {
2015         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2016
2017         return source->priv->enabled;
2018 }
2019
2020 /**
2021  * e_source_set_enabled:
2022  * @source: an #ESource
2023  * @enabled: whether to enable @source
2024  *
2025  * Enables or disables @source.
2026  *
2027  * An application should try to honor this setting if at all possible,
2028  * even if it does not provide a way to change the setting through its
2029  * user interface.  Disabled data sources should generally be hidden.
2030  *
2031  * Since: 3.6
2032  **/
2033 void
2034 e_source_set_enabled (ESource *source,
2035                       gboolean enabled)
2036 {
2037         g_return_if_fail (E_IS_SOURCE (source));
2038
2039         if (source->priv->enabled == enabled)
2040                 return;
2041
2042         source->priv->enabled = enabled;
2043
2044         g_object_notify (G_OBJECT (source), "enabled");
2045 }
2046
2047 /**
2048  * e_source_get_writable:
2049  * @source: an #ESource
2050  *
2051  * Returns whether the D-Bus service will accept changes to @source.
2052  * If @source is not writable, calls to e_source_write() will fail.
2053  *
2054  * Returns: whether @source is writable
2055  *
2056  * Since: 3.6
2057  **/
2058 gboolean
2059 e_source_get_writable (ESource *source)
2060 {
2061         EDBusObject *dbus_object;
2062         EDBusSourceWritable *dbus_source;
2063
2064         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2065
2066         dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
2067         dbus_source = e_dbus_object_peek_source_writable (dbus_object);
2068
2069         return (dbus_source != NULL);
2070 }
2071
2072 /**
2073  * e_source_get_removable:
2074  * @source: an #ESource
2075  *
2076  * Returns whether the D-Bus service will allow @source to be removed.
2077  * If @source is not writable, calls to e_source_remove() will fail.
2078  *
2079  * Returns: whether @source is removable
2080  *
2081  * Since: 3.6
2082  **/
2083 gboolean
2084 e_source_get_removable (ESource *source)
2085 {
2086         EDBusObject *dbus_object;
2087         EDBusSourceRemovable *dbus_source;
2088
2089         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2090
2091         dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
2092         dbus_source = e_dbus_object_peek_source_removable (dbus_object);
2093
2094         return (dbus_source != NULL);
2095 }
2096
2097 /**
2098  * e_source_get_remote_creatable:
2099  * @source: an #ESource
2100  *
2101  * Returns whether new resources can be created on a remote server by
2102  * calling e_source_remote_create() on @source.
2103  *
2104  * Generally this is only %TRUE if @source has an #ESourceCollection
2105  * extension, which means there is an #ECollectionBackend in the D-Bus
2106  * service that can handle create requests.  If @source does not have
2107  * this capability, calls to e_source_remote_create() will fail.
2108  *
2109  * Returns: whether @source can create remote resources
2110  *
2111  * Since: 3.6
2112  **/
2113 gboolean
2114 e_source_get_remote_creatable (ESource *source)
2115 {
2116         EDBusObject *dbus_object;
2117         EDBusSourceRemoteCreatable *dbus_source;
2118
2119         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2120
2121         dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
2122         dbus_source = e_dbus_object_peek_source_remote_creatable (dbus_object);
2123
2124         return (dbus_source != NULL);
2125 }
2126
2127 /**
2128  * e_source_get_remote_deletable:
2129  * @source: an #ESource
2130  *
2131  * Returns whether the resource represented by @source can be deleted
2132  * from a remote server by calling e_source_remote_delete().
2133  *
2134  * Generally this is only %TRUE if @source is a child of an #ESource
2135  * which has an #ESourceCollection extension, which means there is an
2136  * #ECollectionBackend in the D-Bus service that can handle delete
2137  * requests.  If @source does not have this capability, calls to
2138  * e_source_remote_delete() will fail.
2139  *
2140  * Returns: whether @source can delete remote resources
2141  *
2142  * Since: 3.6
2143  **/
2144 gboolean
2145 e_source_get_remote_deletable (ESource *source)
2146 {
2147         EDBusObject *dbus_object;
2148         EDBusSourceRemoteDeletable *dbus_source;
2149
2150         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2151
2152         dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
2153         dbus_source = e_dbus_object_peek_source_remote_deletable (dbus_object);
2154
2155         return (dbus_source != NULL);
2156 }
2157
2158 /**
2159  * e_source_get_extension:
2160  * @source: an #ESource
2161  * @extension_name: an extension name
2162  *
2163  * Returns an instance of some #ESourceExtension subclass which registered
2164  * itself under @extension_name.  If no such instance exists within @source,
2165  * one will be created.  It is the caller's responsibility to know which
2166  * subclass is being returned.
2167  *
2168  * If you just want to test for the existence of an extension within @source
2169  * without creating it, use e_source_has_extension().
2170  *
2171  * Extension instances are owned by their #ESource and should not be
2172  * referenced directly.  Instead, reference the #ESource instance and
2173  * use this function to fetch the extension instance as needed.
2174  *
2175  * Returns: (type ESourceExtension) (transfer none): an instance of some
2176  * #ESourceExtension subclass
2177  *
2178  * Since: 3.6
2179  **/
2180 gpointer
2181 e_source_get_extension (ESource *source,
2182                         const gchar *extension_name)
2183 {
2184         ESourceExtension *extension;
2185         GHashTable *hash_table;
2186         GTypeClass *class;
2187
2188         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2189         g_return_val_if_fail (extension_name != NULL, NULL);
2190
2191         g_rec_mutex_lock (&source->priv->lock);
2192
2193         /* Check if we already have the extension. */
2194         extension = g_hash_table_lookup (
2195                 source->priv->extensions, extension_name);
2196         if (extension != NULL)
2197                 goto exit;
2198
2199         /* Find all subclasses of ESourceExtensionClass. */
2200         hash_table = source_find_extension_classes ();
2201         class = g_hash_table_lookup (hash_table, extension_name);
2202
2203         /* Create a new instance of the appropriate GType. */
2204         if (class != NULL) {
2205                 extension = g_object_new (
2206                         G_TYPE_FROM_CLASS (class),
2207                         "source", source, NULL);
2208                 source_load_from_key_file (
2209                         G_OBJECT (extension),
2210                         source->priv->key_file,
2211                         extension_name);
2212                 g_hash_table_insert (
2213                         source->priv->extensions,
2214                         g_strdup (extension_name), extension);
2215         } else {
2216                 /* XXX Tie this into a debug setting for ESources. */
2217 #ifdef DEBUG
2218                 g_critical (
2219                         "No registered GType for ESource "
2220                         "extension '%s'", extension_name);
2221 #endif
2222         }
2223
2224         g_hash_table_destroy (hash_table);
2225
2226 exit:
2227         g_rec_mutex_unlock (&source->priv->lock);
2228
2229         return extension;
2230 }
2231
2232 /**
2233  * e_source_has_extension:
2234  * @source: an #ESource
2235  * @extension_name: an extension name
2236  *
2237  * Checks whether @source has an #ESourceExtension with the given name.
2238  *
2239  * Returns: %TRUE if @source has such an extension, %FALSE if not
2240  *
2241  * Since: 3.6
2242  **/
2243 gboolean
2244 e_source_has_extension (ESource *source,
2245                         const gchar *extension_name)
2246 {
2247         ESourceExtension *extension;
2248
2249         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2250         g_return_val_if_fail (extension_name != NULL, FALSE);
2251
2252         g_rec_mutex_lock (&source->priv->lock);
2253
2254         /* Two cases to check for, either one is good enough:
2255          * 1) Our internal GKeyFile has a group named 'extension_name'.
2256          * 2) Our 'extensions' table has an entry for 'extension_name'.
2257          *
2258          * We have to check both data structures in case a new extension
2259          * not present in the GKeyFile was instantiated, but we have not
2260          * yet updated our internal GKeyFile.  A common occurrence when
2261          * editing a brand new data source.
2262          *
2263          * When checking the GKeyFile we want to actually fetch the
2264          * extension with e_source_get_extension() to make sure it's
2265          * a registered extension name and not just an arbitrary key
2266          * file group name. */
2267
2268         if (g_key_file_has_group (source->priv->key_file, extension_name)) {
2269                 extension = e_source_get_extension (source, extension_name);
2270         } else {
2271                 GHashTable *hash_table = source->priv->extensions;
2272                 extension = g_hash_table_lookup (hash_table, extension_name);
2273         }
2274
2275         g_rec_mutex_unlock (&source->priv->lock);
2276
2277         return (extension != NULL);
2278 }
2279
2280 /**
2281  * e_source_ref_dbus_object:
2282  * @source: an #ESource
2283  *
2284  * Returns the #GDBusObject that was passed to e_source_new().
2285  *
2286  * The returned #GDBusObject is referenced for thread-safety and must be
2287  * unreferenced with g_object_unref() when finished with it.
2288  *
2289  * Returns: (transfer full): the #GDBusObject for @source, or %NULL
2290  *
2291  * Since: 3.6
2292  **/
2293 GDBusObject *
2294 e_source_ref_dbus_object (ESource *source)
2295 {
2296         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2297
2298         if (source->priv->dbus_object == NULL)
2299                 return NULL;
2300
2301         return g_object_ref (source->priv->dbus_object);
2302 }
2303
2304 /**
2305  * e_source_ref_main_context:
2306  * @source: an #ESource
2307  *
2308  * Returns the #GMainContext on which event sources for @source are to
2309  * be attached.
2310  *
2311  * The returned #GMainContext is referenced for thread-safety and must be
2312  * unreferenced with g_main_context_unref() when finished with it.
2313  *
2314  * Returns: (transfer full): a #GMainContext
2315  *
2316  * Since: 3.6
2317  **/
2318 GMainContext *
2319 e_source_ref_main_context (ESource *source)
2320 {
2321         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2322
2323         return g_main_context_ref (source->priv->main_context);
2324 }
2325
2326 /**
2327  * e_source_get_display_name:
2328  * @source: an #ESource
2329  *
2330  * Returns the display name for @source.  Use the display name to
2331  * represent the #ESource in a user interface.
2332  *
2333  * Returns: the display name for @source
2334  *
2335  * Since: 3.6
2336  **/
2337 const gchar *
2338 e_source_get_display_name (ESource *source)
2339 {
2340         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2341
2342         return source->priv->display_name;
2343 }
2344
2345 /**
2346  * e_source_dup_display_name:
2347  * @source: an #ESource
2348  *
2349  * Thread-safe variation of e_source_get_display_name().
2350  * Use this function when accessing @source from multiple threads.
2351  *
2352  * The returned string should be freed with g_free() when no longer needed.
2353  *
2354  * Returns: a newly-allocated copy of #ESource:display-name
2355  *
2356  * Since: 3.6
2357  **/
2358 gchar *
2359 e_source_dup_display_name (ESource *source)
2360 {
2361         const gchar *protected;
2362         gchar *duplicate;
2363
2364         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2365
2366         g_mutex_lock (&source->priv->property_lock);
2367
2368         protected = e_source_get_display_name (source);
2369         duplicate = g_strdup (protected);
2370
2371         g_mutex_unlock (&source->priv->property_lock);
2372
2373         return duplicate;
2374 }
2375
2376 /**
2377  * e_source_set_display_name:
2378  * @source: an #ESource
2379  * @display_name: a display name
2380  *
2381  * Sets the display name for @source.  The @display_name argument must be a
2382  * valid UTF-8 string.  Use the display name to represent the #ESource in a
2383  * user interface.
2384  *
2385  * The internal copy of @display_name is automatically stripped of leading
2386  * and trailing whitespace.
2387  *
2388  * Since: 3.6
2389  **/
2390 void
2391 e_source_set_display_name (ESource *source,
2392                            const gchar *display_name)
2393 {
2394         g_return_if_fail (E_IS_SOURCE (source));
2395         g_return_if_fail (display_name != NULL);
2396         g_return_if_fail (g_utf8_validate (display_name, -1, NULL));
2397
2398         g_mutex_lock (&source->priv->property_lock);
2399
2400         if (g_strcmp0 (source->priv->display_name, display_name) == 0) {
2401                 g_mutex_unlock (&source->priv->property_lock);
2402                 return;
2403         }
2404
2405         g_free (source->priv->display_name);
2406         source->priv->display_name = g_strdup (display_name);
2407
2408         /* Strip leading and trailing whitespace. */
2409         g_strstrip (source->priv->display_name);
2410
2411         /* This is used in e_source_compare_by_display_name(). */
2412         g_free (source->priv->collate_key);
2413         source->priv->collate_key = g_utf8_collate_key (display_name, -1);
2414
2415         g_mutex_unlock (&source->priv->property_lock);
2416
2417         g_object_notify (G_OBJECT (source), "display-name");
2418 }
2419
2420 /**
2421  * e_source_compare_by_display_name:
2422  * @source1: the first #ESource
2423  * @source2: the second #ESource
2424  *
2425  * Compares two #ESource instances by their display names.  Useful for
2426  * ordering sources in a user interface.
2427  *
2428  * Returns: a negative value if @source1 compares before @source2, zero if
2429  *          they compare equal, or a positive value if @source1 compares
2430  *          after @source2
2431  *
2432  * Since: 3.6
2433  **/
2434 gint
2435 e_source_compare_by_display_name (ESource *source1,
2436                                   ESource *source2)
2437 {
2438         return g_strcmp0 (
2439                 source1->priv->collate_key,
2440                 source2->priv->collate_key);
2441 }
2442
2443 /**
2444  * e_source_to_string:
2445  * @source: an #ESource
2446  * @length: (allow-none): return location for the length of the returned
2447  *          string, or %NULL
2448  *
2449  * Outputs the current contents of @source as a key file string.
2450  * Free the returned string with g_free().
2451  *
2452  * Returns: a newly-allocated string
2453  *
2454  * Since: 3.6
2455  **/
2456 gchar *
2457 e_source_to_string (ESource *source,
2458                     gsize *length)
2459 {
2460         GHashTableIter iter;
2461         GKeyFile *key_file;
2462         gpointer group_name;
2463         gpointer extension;
2464         gchar *data;
2465
2466         g_return_val_if_fail (E_IS_SOURCE (source), NULL);
2467
2468         g_rec_mutex_lock (&source->priv->lock);
2469
2470         key_file = source->priv->key_file;
2471
2472         source_save_to_key_file (
2473                 G_OBJECT (source), key_file, PRIMARY_GROUP_NAME);
2474
2475         g_hash_table_iter_init (&iter, source->priv->extensions);
2476         while (g_hash_table_iter_next (&iter, &group_name, &extension))
2477                 source_save_to_key_file (extension, key_file, group_name);
2478
2479         data = g_key_file_to_data (key_file, length, NULL);
2480
2481         g_rec_mutex_unlock (&source->priv->lock);
2482
2483         return data;
2484 }
2485
2486 /**
2487  * e_source_parameter_to_key:
2488  * @param_name: a #GParamSpec name
2489  *
2490  * Converts a #GParamSpec name (e.g. "foo-bar" or "foo_bar")
2491  * to "CamelCase" for use as a #GKeyFile key (e.g. "FooBar").
2492  *
2493  * This function is made public only to aid in account migration.
2494  * Applications should not need to use this.
2495  *
2496  * Since: 3.6
2497  **/
2498 gchar *
2499 e_source_parameter_to_key (const gchar *param_name)
2500 {
2501         gboolean uppercase = TRUE;
2502         gchar *key, *cp;
2503         gint ii;
2504
2505         g_return_val_if_fail (param_name != NULL, NULL);
2506
2507         key = cp = g_malloc0 (strlen (param_name) + 1);
2508
2509         for (ii = 0; param_name[ii] != '\0'; ii++) {
2510                 if (g_ascii_isalnum (param_name[ii]) && uppercase) {
2511                         *cp++ = g_ascii_toupper (param_name[ii]);
2512                         uppercase = FALSE;
2513                 } else if (param_name[ii] == '-' || param_name[ii] == '_')
2514                         uppercase = TRUE;
2515                 else
2516                         *cp++ = param_name[ii];
2517         }
2518
2519         return key;
2520 }
2521
2522 /**
2523  * e_source_remove_sync:
2524  * @source: the #ESource to be removed
2525  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2526  * @error: return location for a #GError, or %NULL
2527  *
2528  * Requests the D-Bus service to delete the key files for @source and all of
2529  * its descendants and broadcast their removal to all clients.  The @source
2530  * must be #ESource:removable.
2531  *
2532  * If an error occurs, the functon will set @error and return %FALSE.
2533  *
2534  * Returns: %TRUE on success, %FALSE on failure
2535  *
2536  * Since: 3.6
2537  **/
2538 gboolean
2539 e_source_remove_sync (ESource *source,
2540                       GCancellable *cancellable,
2541                       GError **error)
2542 {
2543         ESourceClass *class;
2544
2545         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2546
2547         class = E_SOURCE_GET_CLASS (source);
2548         g_return_val_if_fail (class->remove_sync != NULL, FALSE);
2549
2550         return class->remove_sync (source, cancellable, error);
2551 }
2552
2553 /**
2554  * e_source_remove:
2555  * @source: the #ESource to be removed
2556  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2557  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
2558  *            is satisfied
2559  * @user_data: (closure): data to pass to the callback function
2560  *
2561  * Asynchronously requests the D-Bus service to delete the key files for
2562  * @source and all of its descendants and broadcast their removal to all
2563  * clients.  The @source must be #ESource:removable.
2564  *
2565  * When the operation is finished, @callback will be called.  You can then
2566  * call e_source_remove_finish() to get the result of the operation.
2567  *
2568  * Since: 3.6
2569  **/
2570 void
2571 e_source_remove (ESource *source,
2572                  GCancellable *cancellable,
2573                  GAsyncReadyCallback callback,
2574                  gpointer user_data)
2575 {
2576         ESourceClass *class;
2577
2578         g_return_if_fail (E_IS_SOURCE (source));
2579
2580         class = E_SOURCE_GET_CLASS (source);
2581         g_return_if_fail (class->remove != NULL);
2582
2583         class->remove (source, cancellable, callback, user_data);
2584 }
2585
2586 /**
2587  * e_source_remove_finish:
2588  * @source: the #ESource to be removed
2589  * @result: a #GAsyncResult
2590  * @error: return location for a #GError, or %NULL
2591  *
2592  * Finishes the operation started with e_source_remove().  If an
2593  * error occurred, the function will set @error and return %FALSE.
2594  *
2595  * Returns: %TRUE on success, %FALSE of failure
2596  *
2597  * Since: 3.6
2598  **/
2599 gboolean
2600 e_source_remove_finish (ESource *source,
2601                         GAsyncResult *result,
2602                         GError **error)
2603 {
2604         ESourceClass *class;
2605
2606         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2607         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2608
2609         class = E_SOURCE_GET_CLASS (source);
2610         g_return_val_if_fail (class->remove_finish != NULL, FALSE);
2611
2612         return class->remove_finish (source, result, error);
2613 }
2614
2615 /**
2616  * e_source_write_sync:
2617  * @source: a writable #ESource
2618  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2619  * @error: return location for a #GError, or %NULL
2620  *
2621  * Submits the current contents of @source to the D-Bus service to be
2622  * written to disk and broadcast to other clients.  The @source must
2623  * be #ESource:writable.
2624  *
2625  * If an error occurs, the functon will set @error and return %FALSE.
2626  *
2627  * Returns: %TRUE on success, %FALSE on failure
2628  *
2629  * Since: 3.6
2630  **/
2631 gboolean
2632 e_source_write_sync (ESource *source,
2633                      GCancellable *cancellable,
2634                      GError **error)
2635 {
2636         ESourceClass *class;
2637
2638         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2639
2640         class = E_SOURCE_GET_CLASS (source);
2641         g_return_val_if_fail (class->write_sync != NULL, FALSE);
2642
2643         return class->write_sync (source, cancellable, error);
2644 }
2645
2646 /**
2647  * e_source_write:
2648  * @source: a writable #ESource
2649  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2650  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
2651  *            is satisfied
2652  * @user_data: (closure): data to pass to the callback function
2653  *
2654  * Asynchronously submits the current contents of @source to the D-Bus
2655  * service to be written to disk and broadcast to other clients.  The
2656  * @source must be #ESource:writable.
2657  *
2658  * When the operation is finished, @callback will be called.  You can then
2659  * call e_source_write_finish() to get the result of the operation.
2660  *
2661  * Since: 3.6
2662  **/
2663 void
2664 e_source_write (ESource *source,
2665                 GCancellable *cancellable,
2666                 GAsyncReadyCallback callback,
2667                 gpointer user_data)
2668 {
2669         ESourceClass *class;
2670
2671         g_return_if_fail (E_IS_SOURCE (source));
2672
2673         class = E_SOURCE_GET_CLASS (source);
2674         g_return_if_fail (class->write != NULL);
2675
2676         class->write (source, cancellable, callback, user_data);
2677 }
2678
2679 /**
2680  * e_source_write_finish:
2681  * @source: a writable #ESource
2682  * @result: a #GAsyncResult
2683  * @error: return location for a #GError, or %NULL
2684  *
2685  * Finishes the operation started with e_source_write().  If an
2686  * error occurred, the function will set @error and return %FALSE.
2687  *
2688  * Returns: %TRUE on success, %FALSE on failure
2689  *
2690  * Since: 3.6
2691  **/
2692 gboolean
2693 e_source_write_finish (ESource *source,
2694                        GAsyncResult *result,
2695                        GError **error)
2696 {
2697         ESourceClass *class;
2698
2699         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2700         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
2701
2702         class = E_SOURCE_GET_CLASS (source);
2703         g_return_val_if_fail (class->write_finish != NULL, FALSE);
2704
2705         return class->write_finish (source, result, error);
2706 }
2707
2708 /**
2709  * e_source_remote_create_sync:
2710  * @source: an #ESource
2711  * @scratch_source: an #ESource describing the resource to create
2712  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2713  * @error: return location for a #GError, or %NULL
2714  *
2715  * Creates a new remote resource by picking out relevant details from
2716  * @scratch_source.  The @scratch_source must be an #ESource with no
2717  * #GDBusObject.  The @source must be #ESource:remote-creatable.
2718  *
2719  * The details required to create the resource vary by #ECollectionBackend,
2720  * but in most cases the @scratch_source need only define the resource type
2721  * (address book, calendar, etc.), a display name for the resource, and
2722  * possibly a server-side path or ID for the resource.
2723  *
2724  * If an error occurs, the function will set @error and return %FALSE.
2725  *
2726  * Returns: %TRUE on success, %FALSE on failure
2727  *
2728  * Since: 3.6
2729  **/
2730 gboolean
2731 e_source_remote_create_sync (ESource *source,
2732                              ESource *scratch_source,
2733                              GCancellable *cancellable,
2734                              GError **error)
2735 {
2736         ESourceClass *class;
2737
2738         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2739         g_return_val_if_fail (E_IS_SOURCE (scratch_source), FALSE);
2740
2741         class = E_SOURCE_GET_CLASS (source);
2742         g_return_val_if_fail (class->remote_create_sync != NULL, FALSE);
2743
2744         return class->remote_create_sync (
2745                 source, scratch_source, cancellable, error);
2746 }
2747
2748 /**
2749  * e_source_remote_create:
2750  * @source: an #ESource
2751  * @scratch_source: an #ESource describing the resource to create
2752  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2753  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
2754  *            is satisfied
2755  * @user_data: (closure): data to pass to the callback function
2756  *
2757  * Asynchronously creates a new remote resource by picking out relevant
2758  * details from @scratch_source.  The @scratch_source must be an #ESource
2759  * with no #GDBusObject.  The @source must be #ESource:remote-creatable.
2760  *
2761  * The details required to create the resource vary by #ECollectionBackend,
2762  * but in most cases the @scratch_source need only define the resource type
2763  * (address book, calendar, etc.), a display name for the resource, and
2764  * possibly a server-side path or ID for the resource.
2765  *
2766  * When the operation is finished, @callback will be called.  You can then
2767  * call 3_source_remote_create_finish() to get the result of the operation.
2768  *
2769  * Since: 3.6
2770  **/
2771 void
2772 e_source_remote_create (ESource *source,
2773                         ESource *scratch_source,
2774                         GCancellable *cancellable,
2775                         GAsyncReadyCallback callback,
2776                         gpointer user_data)
2777 {
2778         ESourceClass *class;
2779
2780         g_return_if_fail (E_IS_SOURCE (source));
2781         g_return_if_fail (E_IS_SOURCE (scratch_source));
2782
2783         class = E_SOURCE_GET_CLASS (source);
2784         g_return_if_fail (class->remote_create != NULL);
2785
2786         class->remote_create (
2787                 source, scratch_source,
2788                 cancellable, callback, user_data);
2789 }
2790
2791 /**
2792  * e_source_remote_create_finish:
2793  * @source: an #ESource
2794  * @result: a #GAsyncResult
2795  * @error: return location for a #GError, or %NULL
2796  *
2797  * Finishes the operation started with e_source_remote_create().  If
2798  * an error occurred, the function will set @error and return %FALSE.
2799  *
2800  * Returns: %TRUE on success, %FALSE on failure
2801  *
2802  * Since: 3.6
2803  **/
2804 gboolean
2805 e_source_remote_create_finish (ESource *source,
2806                                GAsyncResult *result,
2807                                GError **error)
2808 {
2809         ESourceClass *class;
2810
2811         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2812
2813         class = E_SOURCE_GET_CLASS (source);
2814         g_return_val_if_fail (class->remote_create_finish != NULL, FALSE);
2815
2816         return class->remote_create_finish (source, result, error);
2817 }
2818
2819 /**
2820  * e_source_remote_delete_sync:
2821  * @source: an #ESource
2822  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2823  * @error: return location for a #GError, or %NULL
2824  *
2825  * Deletes the resource represented by @source from a remote server.
2826  * The @source must be #ESource:remote-deletable.  This will also delete
2827  * the key file for @source and broadcast its removal to all clients,
2828  * similar to e_source_remove_sync().
2829  *
2830  * If an error occurs, the function will set @error and return %FALSE.
2831  *
2832  * Returns: %TRUE on success, %FALSE on failure
2833  *
2834  * Since: 3.6
2835  **/
2836 gboolean
2837 e_source_remote_delete_sync (ESource *source,
2838                              GCancellable *cancellable,
2839                              GError **error)
2840 {
2841         ESourceClass *class;
2842
2843         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2844
2845         class = E_SOURCE_GET_CLASS (source);
2846         g_return_val_if_fail (class->remote_delete_sync != NULL, FALSE);
2847
2848         return class->remote_delete_sync (source, cancellable, error);
2849 }
2850
2851 /**
2852  * e_source_remote_delete:
2853  * @source: an #ESource
2854  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2855  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
2856  *            is satisfied
2857  * @user_data: (closure): data to pass to the callback function
2858  *
2859  * Asynchronously deletes the resource represented by @source from a remote
2860  * server.  The @source must be #ESource:remote-deletable.  This will also
2861  * delete the key file for @source and broadcast its removal to all clients,
2862  * similar to e_source_remove().
2863  *
2864  * When the operation is finished, @callback will be called.  You can then
2865  * call e_source_remote_delete_finish() to get the result of the operation.
2866  *
2867  * Since: 3.6
2868  **/
2869 void
2870 e_source_remote_delete (ESource *source,
2871                         GCancellable *cancellable,
2872                         GAsyncReadyCallback callback,
2873                         gpointer user_data)
2874 {
2875         ESourceClass *class;
2876
2877         g_return_if_fail (E_IS_SOURCE (source));
2878
2879         class = E_SOURCE_GET_CLASS (source);
2880         g_return_if_fail (class->remote_delete != NULL);
2881
2882         class->remote_delete (source, cancellable, callback, user_data);
2883 }
2884
2885 /**
2886  * e_source_remote_delete_finish:
2887  * @source: an #ESource
2888  * @result: a #GAsyncResult
2889  * @error: return location for a #GError, or %NULL
2890  *
2891  * Finishes the operation started with e_source_remote_delete().  If
2892  * an error occurred, the function will set @error and return %FALSE.
2893  *
2894  * Returns: %TRUE on success, %FALSE on failure
2895  *
2896  * Since: 3.6
2897  **/
2898 gboolean
2899 e_source_remote_delete_finish (ESource *source,
2900                                GAsyncResult *result,
2901                                GError **error)
2902 {
2903         ESourceClass *class;
2904
2905         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2906
2907         class = E_SOURCE_GET_CLASS (source);
2908         g_return_val_if_fail (class->remote_delete_finish != NULL, FALSE);
2909
2910         return class->remote_delete_finish (source, result, error);
2911 }
2912
2913 /**
2914  * e_source_get_oauth2_access_token_sync:
2915  * @source: an #ESource
2916  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2917  * @out_access_token: (allow-none): return location for the access token,
2918  *                    or %NULL
2919  * @out_expires_in: (allow-none): return location for the token expiry,
2920  *                  or %NULL
2921  * @error: return location for a #GError, or %NULL
2922  *
2923  * Obtains the OAuth 2.0 access token for @source along with its expiry
2924  * in seconds from the current time (or 0 if unknown).  The @source must
2925  * have #ESource:supports-oauth2 set for this to work.
2926  *
2927  * Free the returned access token with g_free() when finished with it.
2928  * If an error occurs, the function will set @error and return %FALSE.
2929  *
2930  * Returns: %TRUE on success, %FALSE on failure
2931  *
2932  * Since: 3.8
2933  **/
2934 gboolean
2935 e_source_get_oauth2_access_token_sync (ESource *source,
2936                                        GCancellable *cancellable,
2937                                        gchar **out_access_token,
2938                                        gint *out_expires_in,
2939                                        GError **error)
2940 {
2941         ESourceClass *class;
2942
2943         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
2944
2945         class = E_SOURCE_GET_CLASS (source);
2946         g_return_val_if_fail (
2947                 class->get_oauth2_access_token_sync != NULL, FALSE);
2948
2949         return class->get_oauth2_access_token_sync (
2950                 source, cancellable, out_access_token, out_expires_in, error);
2951 }
2952
2953 /**
2954  * e_source_get_oauth2_access_token:
2955  * @source: an #ESource
2956  * @cancellable: (allow-none): optional #GCancellable object, or %NULL
2957  * @callback: (scope async): a #GAsyncReadyCallback to call when the request
2958  *            is satisfied
2959  * @user_data: (closure): data to pass to the callback function
2960  *
2961  * Asynchronously obtains the OAuth 2.0 access token for @source along
2962  * with its expiry in seconds from the current time (or 0 if unknown).
2963  * The @source must have #ESource:supports-oauth2 set for this to work.
2964  *
2965  * When the operation is finished, @callback will be called.  You can then
2966  * call e_source_get_oauth2_access_token_finish() to get the result of the
2967  * operation.
2968  *
2969  * Since: 3.8
2970  **/
2971 void
2972 e_source_get_oauth2_access_token (ESource *source,
2973                                   GCancellable *cancellable,
2974                                   GAsyncReadyCallback callback,
2975                                   gpointer user_data)
2976 {
2977         ESourceClass *class;
2978
2979         g_return_if_fail (E_IS_SOURCE (source));
2980
2981         class = E_SOURCE_GET_CLASS (source);
2982         g_return_if_fail (class->get_oauth2_access_token != NULL);
2983
2984         return class->get_oauth2_access_token (
2985                 source, cancellable, callback, user_data);
2986 }
2987
2988 /**
2989  * e_source_get_oauth2_access_token_finish:
2990  * @source: an #ESource
2991  * @result: a #GAsyncResult
2992  * @out_access_token: (allow-none): return location for the access token,
2993  *                    or %NULL
2994  * @out_expires_in: (allow-none): return location for the token expiry,
2995  *                  or %NULL
2996  * @error: return location for a #GError, or %NULL
2997  *
2998  * Finishes the operation started with e_source_get_oauth2_access_token().
2999  *
3000  * Free the returned access token with g_free() when finished with it.
3001  * If an error occurred, the function will set @error and return %FALSE.
3002  *
3003  * Returns: %TRUE on success, %FALSE on failure
3004  *
3005  * Since: 3.8
3006  **/
3007 gboolean
3008 e_source_get_oauth2_access_token_finish (ESource *source,
3009                                          GAsyncResult *result,
3010                                          gchar **out_access_token,
3011                                          gint *out_expires_in,
3012                                          GError **error)
3013 {
3014         ESourceClass *class;
3015
3016         g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
3017         g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
3018
3019         class = E_SOURCE_GET_CLASS (source);
3020         g_return_val_if_fail (
3021                 class->get_oauth2_access_token_finish != NULL, FALSE);
3022
3023         return class->get_oauth2_access_token_finish (
3024                 source, result, out_access_token, out_expires_in, error);
3025 }
3026