updated changelog
[platform/upstream/evolution-data-server.git] / libebackend / e-server-side-source.c
1 /*
2  * e-server-side-source.c
3  *
4  * This library is free software you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License as published by
6  * the Free Software Foundation.
7  *
8  * This library is distributed in the hope that it will be useful, but
9  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this library; if not, see <http://www.gnu.org/licenses/>.
15  *
16  */
17
18 /**
19  * SECTION: e-server-side-source
20  * @include: libebackend/libebackend.h
21  * @short_description: A server-side data source
22  *
23  * An #EServerSideSource is an #ESource with some additional capabilities
24  * exclusive to the registry D-Bus service.
25  **/
26
27 #include "e-server-side-source.h"
28
29 #include <config.h>
30 #include <glib/gi18n-lib.h>
31
32 /* Private D-Bus classes. */
33 #include <e-dbus-source.h>
34
35 #define E_SERVER_SIDE_SOURCE_GET_PRIVATE(obj) \
36         (G_TYPE_INSTANCE_GET_PRIVATE \
37         ((obj), E_TYPE_SERVER_SIDE_SOURCE, EServerSideSourcePrivate))
38
39 #define DBUS_OBJECT_PATH        E_SOURCE_REGISTRY_SERVER_OBJECT_PATH "/Source"
40
41 #define PRIMARY_GROUP_NAME      "Data Source"
42
43 typedef struct _AsyncContext AsyncContext;
44
45 struct _EServerSideSourcePrivate {
46         gpointer server;  /* weak pointer */
47         GWeakRef oauth2_support;
48
49         GNode node;
50         GFile *file;
51
52         /* For comparison. */
53         gchar *file_contents;
54
55         gboolean allow_auth_prompt;
56         GType auth_session_type;
57         gchar *write_directory;
58 };
59
60 struct _AsyncContext {
61         EDBusSourceRemoteCreatable *remote_creatable;
62         EDBusSourceRemoteDeletable *remote_deletable;
63         EDBusSourceOAuth2Support *oauth2_support;
64         GDBusMethodInvocation *invocation;
65 };
66
67 enum {
68         PROP_0,
69         PROP_ALLOW_AUTH_PROMPT,
70         PROP_AUTH_SESSION_TYPE,
71         PROP_EXPORTED,
72         PROP_FILE,
73         PROP_OAUTH2_SUPPORT,
74         PROP_REMOTE_CREATABLE,
75         PROP_REMOTE_DELETABLE,
76         PROP_REMOVABLE,
77         PROP_SERVER,
78         PROP_WRITABLE,
79         PROP_WRITE_DIRECTORY
80 };
81
82 static GInitableIface *initable_parent_interface;
83
84 /* Forward Declarations */
85 static void     e_server_side_source_initable_init
86                                                 (GInitableIface *iface);
87
88 G_DEFINE_TYPE_WITH_CODE (
89         EServerSideSource,
90         e_server_side_source,
91         E_TYPE_SOURCE,
92         G_IMPLEMENT_INTERFACE (
93                 G_TYPE_INITABLE,
94                 e_server_side_source_initable_init))
95
96 static void
97 async_context_free (AsyncContext *async_context)
98 {
99         if (async_context->remote_creatable != NULL)
100                 g_object_unref (async_context->remote_creatable);
101
102         if (async_context->remote_deletable != NULL)
103                 g_object_unref (async_context->remote_deletable);
104
105         if (async_context->oauth2_support != NULL)
106                 g_object_unref (async_context->oauth2_support);
107
108         if (async_context->invocation != NULL)
109                 g_object_unref (async_context->invocation);
110
111         g_slice_free (AsyncContext, async_context);
112 }
113
114 static gboolean
115 server_side_source_parse_data (GKeyFile *key_file,
116                                const gchar *data,
117                                gsize length,
118                                GError **error)
119 {
120         gboolean success;
121
122         success = g_key_file_load_from_data (
123                 key_file, data, length, G_KEY_FILE_NONE, error);
124
125         if (!success)
126                 return FALSE;
127
128         /* Make sure the key file has a [Data Source] group. */
129         if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
130                 g_set_error (
131                         error, G_KEY_FILE_ERROR,
132                         G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
133                         _("Data source is missing a [%s] group"),
134                         PRIMARY_GROUP_NAME);
135                 return FALSE;
136         }
137
138         return TRUE;
139 }
140
141 static void
142 server_side_source_print_diff (ESource *source,
143                                const gchar *old_data,
144                                const gchar *new_data)
145 {
146         gchar **old_strv = NULL;
147         gchar **new_strv = NULL;
148         guint old_length = 0;
149         guint new_length = 0;
150         guint ii;
151
152         g_print ("Saving %s\n", e_source_get_uid (source));
153
154         if (old_data != NULL) {
155                 old_strv = g_strsplit (old_data, "\n", 0);
156                 old_length = g_strv_length (old_strv);
157         }
158
159         if (new_data != NULL) {
160                 new_strv = g_strsplit (new_data, "\n", 0);
161                 new_length = g_strv_length (new_strv);
162         }
163
164         for (ii = 0; ii < MIN (old_length, new_length); ii++) {
165                 if (g_strcmp0 (old_strv[ii], new_strv[ii]) != 0) {
166                         g_print (" - : %s\n", old_strv[ii]);
167                         g_print (" + : %s\n", new_strv[ii]);
168                 } else {
169                         g_print ("   : %s\n", old_strv[ii]);
170                 }
171         }
172
173         for (; ii < old_length; ii++)
174                 g_print (" - : %s\n", old_strv[ii]);
175
176         for (; ii < new_length; ii++)
177                 g_print (" + : %s\n", new_strv[ii]);
178
179         g_strfreev (old_strv);
180         g_strfreev (new_strv);
181 }
182
183 static gboolean
184 server_side_source_traverse_cb (GNode *node,
185                                 GQueue *queue)
186 {
187         g_queue_push_tail (queue, g_object_ref (node->data));
188
189         return FALSE;
190 }
191
192 static gboolean
193 server_side_source_allow_auth_prompt_cb (EDBusSource *dbus_interface,
194                                          GDBusMethodInvocation *invocation,
195                                          EServerSideSource *source)
196 {
197         e_server_side_source_set_allow_auth_prompt (source, TRUE);
198
199         e_dbus_source_complete_allow_auth_prompt (dbus_interface, invocation);
200
201         return TRUE;
202 }
203
204 static gboolean
205 server_side_source_remove_cb (EDBusSourceRemovable *dbus_interface,
206                               GDBusMethodInvocation *invocation,
207                               EServerSideSource *source)
208 {
209         GError *error = NULL;
210
211         /* Note we don't need to verify the source is removable here
212          * since if it isn't, the remove() method won't be available.
213          * Descendants of the source are removed whether they export
214          * a remove() method or not. */
215
216         e_source_remove_sync (E_SOURCE (source), NULL, &error);
217
218         if (error != NULL)
219                 g_dbus_method_invocation_take_error (invocation, error);
220         else
221                 e_dbus_source_removable_complete_remove (
222                         dbus_interface, invocation);
223
224         return TRUE;
225 }
226
227 static gboolean
228 server_side_source_write_cb (EDBusSourceWritable *dbus_interface,
229                              GDBusMethodInvocation *invocation,
230                              const gchar *data,
231                              ESource *source)
232 {
233         GKeyFile *key_file;
234         GDBusObject *dbus_object;
235         EDBusSource *dbus_source;
236         GError *error = NULL;
237
238         /* Note we don't need to verify the source is writable here
239          * since if it isn't, the write() method won't be available. */
240
241         dbus_object = e_source_ref_dbus_object (source);
242         dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
243
244         /* Validate the raw data before making the changes live. */
245         key_file = g_key_file_new ();
246         server_side_source_parse_data (key_file, data, strlen (data), &error);
247         g_key_file_free (key_file);
248
249         /* Q: How does this trigger data being written to disk?
250          *
251          * A: Here's the sequence of events:
252          *
253          *    1) We set the EDBusSource:data property.
254          *    2) ESource picks up the "notify::data" signal and parses
255          *       the raw data, which triggers an ESource:changed signal.
256          *    3) Our changed() method schedules an idle callback.
257          *    4) The idle callback calls e_source_write_sync().
258          *    5) e_source_write_sync() calls e_dbus_source_dup_data()
259          *       and synchronously writes the resulting string to disk.
260          *
261          * XXX: This should be done more straigtforward, because rely
262          *      on two different signals and having actual data file
263          *      save in 5 steps is ridiculous, not talking that
264          *      the returned GError from this D-Bus call doesn't handle
265          *      errors from actual file save, which can also break, thus
266          *      the caller doesn't know about any real problem during saving
267          *      and thinks that everything went fine.
268          */
269
270         if (error == NULL) {
271                 e_dbus_source_set_data (dbus_source, data);
272
273                 /* Make sure the ESource::changed signal is called, otherwise
274                  * the above Q&A doesn't work and changed data are not saved. */
275                 e_source_changed (source);
276         }
277
278         if (error != NULL)
279                 g_dbus_method_invocation_take_error (invocation, error);
280         else
281                 e_dbus_source_writable_complete_write (
282                         dbus_interface, invocation);
283
284         g_object_unref (dbus_source);
285         g_object_unref (dbus_object);
286
287         return TRUE;
288 }
289
290 /* Helper for server_side_source_remote_create_cb() */
291 static void
292 server_side_source_remote_create_done_cb (GObject *source_object,
293                                           GAsyncResult *result,
294                                           gpointer user_data)
295 {
296         ESource *source;
297         AsyncContext *async_context;
298         GError *error = NULL;
299
300         source = E_SOURCE (source_object);
301         async_context = (AsyncContext *) user_data;
302
303         e_source_remote_create_finish (source, result, &error);
304
305         if (error != NULL)
306                 g_dbus_method_invocation_take_error (
307                         async_context->invocation, error);
308         else
309                 e_dbus_source_remote_creatable_complete_create (
310                         async_context->remote_creatable,
311                         async_context->invocation);
312
313         async_context_free (async_context);
314 }
315
316 static gboolean
317 server_side_source_remote_create_cb (EDBusSourceRemoteCreatable *dbus_interface,
318                                      GDBusMethodInvocation *invocation,
319                                      const gchar *uid,
320                                      const gchar *data,
321                                      ESource *source)
322 {
323         EServerSideSource *server_side_source;
324         ESourceRegistryServer *server;
325         AsyncContext *async_context;
326         ESource *scratch_source;
327         GDBusObject *dbus_object;
328         EDBusSource *dbus_source;
329         GKeyFile *key_file;
330         GFile *file;
331         GError *error = NULL;
332
333         /* Create a new EServerSideSource from 'uid' and 'data' but
334          * DO NOT add it to the ESourceRegistryServer yet.  It's up
335          * to the ECollectionBackend whether to use source as given
336          * or create its own equivalent EServerSideSource, possibly
337          * in response to a notification from a remote server. */
338
339         /* Validate the raw data. */
340         key_file = g_key_file_new ();
341         server_side_source_parse_data (key_file, data, strlen (data), &error);
342         g_key_file_free (key_file);
343
344         if (error != NULL) {
345                 g_dbus_method_invocation_take_error (invocation, error);
346                 return TRUE;
347         }
348
349         server_side_source = E_SERVER_SIDE_SOURCE (source);
350         server = e_server_side_source_get_server (server_side_source);
351
352         file = e_server_side_source_new_user_file (uid);
353         scratch_source = e_server_side_source_new (server, file, &error);
354         g_object_unref (file);
355
356         /* Sanity check. */
357         g_warn_if_fail (
358                 ((scratch_source != NULL) && (error == NULL)) ||
359                 ((scratch_source == NULL) && (error != NULL)));
360
361         if (error != NULL) {
362                 g_dbus_method_invocation_take_error (invocation, error);
363                 return TRUE;
364         }
365
366         dbus_object = e_source_ref_dbus_object (scratch_source);
367         dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
368
369         e_dbus_source_set_data (dbus_source, data);
370
371         g_object_unref (dbus_object);
372         g_object_unref (dbus_source);
373
374         async_context = g_slice_new0 (AsyncContext);
375         async_context->remote_creatable = g_object_ref (dbus_interface);
376         async_context->invocation = g_object_ref (invocation);
377
378         e_source_remote_create (
379                 source, scratch_source, NULL,
380                 server_side_source_remote_create_done_cb,
381                 async_context);
382
383         g_object_unref (scratch_source);
384
385         return TRUE;
386 }
387
388 /* Helper for server_side_source_remote_delete_cb() */
389 static void
390 server_side_source_remote_delete_done_cb (GObject *source_object,
391                                           GAsyncResult *result,
392                                           gpointer user_data)
393 {
394         ESource *source;
395         AsyncContext *async_context;
396         GError *error = NULL;
397
398         source = E_SOURCE (source_object);
399         async_context = (AsyncContext *) user_data;
400
401         e_source_remote_delete_finish (source, result, &error);
402
403         if (error != NULL)
404                 g_dbus_method_invocation_take_error (
405                         async_context->invocation, error);
406         else
407                 e_dbus_source_remote_deletable_complete_delete (
408                         async_context->remote_deletable,
409                         async_context->invocation);
410
411         async_context_free (async_context);
412 }
413
414 static gboolean
415 server_side_source_remote_delete_cb (EDBusSourceRemoteDeletable *dbus_interface,
416                                      GDBusMethodInvocation *invocation,
417                                      ESource *source)
418 {
419         AsyncContext *async_context;
420
421         async_context = g_slice_new0 (AsyncContext);
422         async_context->remote_deletable = g_object_ref (dbus_interface);
423         async_context->invocation = g_object_ref (invocation);
424
425         e_source_remote_delete (
426                 source, NULL,
427                 server_side_source_remote_delete_done_cb,
428                 async_context);
429
430         return TRUE;
431 }
432
433 /* Helper for server_side_source_get_access_token_cb() */
434 static void
435 server_side_source_get_access_token_done_cb (GObject *source_object,
436                                              GAsyncResult *result,
437                                              gpointer user_data)
438 {
439         ESource *source;
440         AsyncContext *async_context;
441         gchar *access_token = NULL;
442         gint expires_in = 0;
443         GError *error = NULL;
444
445         source = E_SOURCE (source_object);
446         async_context = (AsyncContext *) user_data;
447
448         e_source_get_oauth2_access_token_finish (
449                 source, result, &access_token, &expires_in, &error);
450
451         /* Sanity check. */
452         g_return_if_fail (
453                 ((access_token != NULL) && (error == NULL)) ||
454                 ((access_token == NULL) && (error != NULL)));
455
456         if (error != NULL)
457                 g_dbus_method_invocation_take_error (
458                         async_context->invocation, error);
459         else
460                 e_dbus_source_oauth2_support_complete_get_access_token (
461                         async_context->oauth2_support,
462                         async_context->invocation,
463                         access_token, expires_in);
464
465         g_free (access_token);
466
467         async_context_free (async_context);
468 }
469
470 static gboolean
471 server_side_source_get_access_token_cb (EDBusSourceOAuth2Support *dbus_interface,
472                                         GDBusMethodInvocation *invocation,
473                                         ESource *source)
474 {
475         AsyncContext *async_context;
476
477         async_context = g_slice_new0 (AsyncContext);
478         async_context->oauth2_support = g_object_ref (dbus_interface);
479         async_context->invocation = g_object_ref (invocation);
480
481         e_source_get_oauth2_access_token (
482                 source, NULL,
483                 server_side_source_get_access_token_done_cb,
484                 async_context);
485
486         return TRUE;
487 }
488
489 static void
490 server_side_source_set_file (EServerSideSource *source,
491                              GFile *file)
492 {
493         g_return_if_fail (file == NULL || G_IS_FILE (file));
494         g_return_if_fail (source->priv->file == NULL);
495
496         if (file != NULL)
497                 source->priv->file = g_object_ref (file);
498 }
499
500 static void
501 server_side_source_set_server (EServerSideSource *source,
502                                ESourceRegistryServer *server)
503 {
504         g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
505         g_return_if_fail (source->priv->server == NULL);
506
507         source->priv->server = server;
508
509         g_object_add_weak_pointer (
510                 G_OBJECT (server), &source->priv->server);
511 }
512
513 static void
514 server_side_source_set_property (GObject *object,
515                                  guint property_id,
516                                  const GValue *value,
517                                  GParamSpec *pspec)
518 {
519         switch (property_id) {
520                 case PROP_ALLOW_AUTH_PROMPT:
521                         e_server_side_source_set_allow_auth_prompt (
522                                 E_SERVER_SIDE_SOURCE (object),
523                                 g_value_get_boolean (value));
524                         return;
525
526                 case PROP_AUTH_SESSION_TYPE:
527                         e_server_side_source_set_auth_session_type (
528                                 E_SERVER_SIDE_SOURCE (object),
529                                 g_value_get_gtype (value));
530                         return;
531
532                 case PROP_FILE:
533                         server_side_source_set_file (
534                                 E_SERVER_SIDE_SOURCE (object),
535                                 g_value_get_object (value));
536                         return;
537
538                 case PROP_OAUTH2_SUPPORT:
539                         e_server_side_source_set_oauth2_support (
540                                 E_SERVER_SIDE_SOURCE (object),
541                                 g_value_get_object (value));
542                         return;
543
544                 case PROP_REMOTE_CREATABLE:
545                         e_server_side_source_set_remote_creatable (
546                                 E_SERVER_SIDE_SOURCE (object),
547                                 g_value_get_boolean (value));
548                         return;
549
550                 case PROP_REMOTE_DELETABLE:
551                         e_server_side_source_set_remote_deletable (
552                                 E_SERVER_SIDE_SOURCE (object),
553                                 g_value_get_boolean (value));
554                         return;
555
556                 case PROP_REMOVABLE:
557                         e_server_side_source_set_removable (
558                                 E_SERVER_SIDE_SOURCE (object),
559                                 g_value_get_boolean (value));
560                         return;
561
562                 case PROP_SERVER:
563                         server_side_source_set_server (
564                                 E_SERVER_SIDE_SOURCE (object),
565                                 g_value_get_object (value));
566                         return;
567
568                 case PROP_WRITABLE:
569                         e_server_side_source_set_writable (
570                                 E_SERVER_SIDE_SOURCE (object),
571                                 g_value_get_boolean (value));
572                         return;
573
574                 case PROP_WRITE_DIRECTORY:
575                         e_server_side_source_set_write_directory (
576                                 E_SERVER_SIDE_SOURCE (object),
577                                 g_value_get_string (value));
578                         return;
579         }
580
581         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
582 }
583
584 static void
585 server_side_source_get_property (GObject *object,
586                                  guint property_id,
587                                  GValue *value,
588                                  GParamSpec *pspec)
589 {
590         switch (property_id) {
591                 case PROP_ALLOW_AUTH_PROMPT:
592                         g_value_set_boolean (
593                                 value,
594                                 e_server_side_source_get_allow_auth_prompt (
595                                 E_SERVER_SIDE_SOURCE (object)));
596                         return;
597
598                 case PROP_AUTH_SESSION_TYPE:
599                         g_value_set_gtype (
600                                 value,
601                                 e_server_side_source_get_auth_session_type (
602                                 E_SERVER_SIDE_SOURCE (object)));
603                         return;
604
605                 case PROP_EXPORTED:
606                         g_value_set_boolean (
607                                 value,
608                                 e_server_side_source_get_exported (
609                                 E_SERVER_SIDE_SOURCE (object)));
610                         return;
611
612                 case PROP_FILE:
613                         g_value_set_object (
614                                 value,
615                                 e_server_side_source_get_file (
616                                 E_SERVER_SIDE_SOURCE (object)));
617                         return;
618
619                 case PROP_OAUTH2_SUPPORT:
620                         g_value_take_object (
621                                 value,
622                                 e_server_side_source_ref_oauth2_support (
623                                 E_SERVER_SIDE_SOURCE (object)));
624                         return;
625
626                 case PROP_REMOTE_CREATABLE:
627                         g_value_set_boolean (
628                                 value,
629                                 e_source_get_remote_creatable (
630                                 E_SOURCE (object)));
631                         return;
632
633                 case PROP_REMOTE_DELETABLE:
634                         g_value_set_boolean (
635                                 value,
636                                 e_source_get_remote_deletable (
637                                 E_SOURCE (object)));
638                         return;
639
640                 case PROP_REMOVABLE:
641                         g_value_set_boolean (
642                                 value,
643                                 e_source_get_removable (
644                                 E_SOURCE (object)));
645                         return;
646
647                 case PROP_SERVER:
648                         g_value_set_object (
649                                 value,
650                                 e_server_side_source_get_server (
651                                 E_SERVER_SIDE_SOURCE (object)));
652                         return;
653
654                 case PROP_WRITABLE:
655                         g_value_set_boolean (
656                                 value,
657                                 e_source_get_writable (
658                                 E_SOURCE (object)));
659                         return;
660
661                 case PROP_WRITE_DIRECTORY:
662                         g_value_set_string (
663                                 value,
664                                 e_server_side_source_get_write_directory (
665                                 E_SERVER_SIDE_SOURCE (object)));
666                         return;
667         }
668
669         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
670 }
671
672 static void
673 server_side_source_dispose (GObject *object)
674 {
675         EServerSideSourcePrivate *priv;
676
677         priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
678
679         if (priv->server != NULL) {
680                 g_object_remove_weak_pointer (
681                         G_OBJECT (priv->server), &priv->server);
682                 priv->server = NULL;
683         }
684
685         g_weak_ref_set (&priv->oauth2_support, NULL);
686
687         if (priv->file != NULL) {
688                 g_object_unref (priv->file);
689                 priv->file = NULL;
690         }
691
692         /* Chain up to parent's dispose() method. */
693         G_OBJECT_CLASS (e_server_side_source_parent_class)->dispose (object);
694 }
695
696 static void
697 server_side_source_finalize (GObject *object)
698 {
699         EServerSideSourcePrivate *priv;
700
701         priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
702
703         g_node_unlink (&priv->node);
704
705         g_free (priv->file_contents);
706         g_free (priv->write_directory);
707
708         /* Chain up to parent's finalize() method. */
709         G_OBJECT_CLASS (e_server_side_source_parent_class)->finalize (object);
710 }
711
712 static void
713 server_side_source_changed (ESource *source)
714 {
715         GDBusObject *dbus_object;
716         EDBusSource *dbus_source;
717         gchar *old_data;
718         gchar *new_data;
719         GError *error = NULL;
720
721         /* Do not write changes to disk until the source has been exported. */
722         if (!e_server_side_source_get_exported (E_SERVER_SIDE_SOURCE (source)))
723                 return;
724
725         dbus_object = e_source_ref_dbus_object (source);
726         dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
727
728         old_data = e_dbus_source_dup_data (dbus_source);
729         new_data = e_source_to_string (source, NULL);
730
731         /* Setting the "data" property triggers the ESource::changed,
732          * signal, which invokes this callback, which sets the "data"
733          * property, etc.  This breaks an otherwise infinite loop. */
734         if (g_strcmp0 (old_data, new_data) != 0)
735                 e_dbus_source_set_data (dbus_source, new_data);
736
737         g_free (old_data);
738         g_free (new_data);
739
740         g_object_unref (dbus_source);
741         g_object_unref (dbus_object);
742
743         /* This writes the "data" property to disk. */
744         e_source_write_sync (source, NULL, &error);
745
746         if (error != NULL) {
747                 g_warning ("%s: %s", G_STRFUNC, error->message);
748                 g_error_free (error);
749         }
750 }
751
752 static gboolean
753 server_side_source_remove_sync (ESource *source,
754                                 GCancellable *cancellable,
755                                 GError **error)
756 {
757         EAsyncClosure *closure;
758         GAsyncResult *result;
759         gboolean success;
760
761         closure = e_async_closure_new ();
762
763         e_source_remove (
764                 source, cancellable, e_async_closure_callback, closure);
765
766         result = e_async_closure_wait (closure);
767
768         success = e_source_remove_finish (source, result, error);
769
770         e_async_closure_free (closure);
771
772         return success;
773 }
774
775 static void
776 server_side_source_remove (ESource *source,
777                            GCancellable *cancellable,
778                            GAsyncReadyCallback callback,
779                            gpointer user_data)
780 {
781         EServerSideSourcePrivate *priv;
782         GSimpleAsyncResult *simple;
783         ESourceRegistryServer *server;
784         GQueue queue = G_QUEUE_INIT;
785         GList *list, *link;
786         GError *error = NULL;
787
788         /* XXX Yes we block here.  We do this operation
789          *     synchronously to keep the server code simple. */
790
791         priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
792
793         simple = g_simple_async_result_new (
794                 G_OBJECT (source), callback, user_data,
795                 server_side_source_remove);
796
797         g_simple_async_result_set_check_cancellable (simple, cancellable);
798
799         /* Collect the source and its descendants into a queue.
800          * Do this before unexporting so we hold references to
801          * all the removed sources. */
802         g_node_traverse (
803                 &priv->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
804                 (GNodeTraverseFunc) server_side_source_traverse_cb, &queue);
805
806         /* Unexport the object and its descendants. */
807         server = E_SOURCE_REGISTRY_SERVER (priv->server);
808         e_source_registry_server_remove_source (server, source);
809
810         list = g_queue_peek_head_link (&queue);
811
812         /* Delete the key file for each source in the queue. */
813         for (link = list; link != NULL; link = g_list_next (link)) {
814                 EServerSideSource *child;
815                 GFile *file;
816
817                 child = E_SERVER_SIDE_SOURCE (link->data);
818                 file = e_server_side_source_get_file (child);
819
820                 if (file != NULL)
821                         g_file_delete (file, cancellable, &error);
822
823                 /* XXX Even though e_source_registry_server_remove_source()
824                  *     is called first, the object path is unexported from
825                  *     an idle callback some time after we have deleted the
826                  *     key file.  That creates a small window of time where
827                  *     the file is deleted but the object is still exported.
828                  *
829                  *     If a client calls e_source_remove() during that small
830                  *     window of time, we still want to report a successful
831                  *     removal, so disregard G_IO_ERROR_NOT_FOUND. */
832                 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND))
833                         g_clear_error (&error);
834
835                 if (error != NULL)
836                         goto exit;
837         }
838
839 exit:
840         while (!g_queue_is_empty (&queue))
841                 g_object_unref (g_queue_pop_head (&queue));
842
843         if (error != NULL)
844                 g_simple_async_result_take_error (simple, error);
845
846         g_simple_async_result_complete_in_idle (simple);
847         g_object_unref (simple);
848 }
849
850 static gboolean
851 server_side_source_remove_finish (ESource *source,
852                                   GAsyncResult *result,
853                                   GError **error)
854 {
855         GSimpleAsyncResult *simple;
856
857         g_return_val_if_fail (
858                 g_simple_async_result_is_valid (
859                 result, G_OBJECT (source),
860                 server_side_source_remove), FALSE);
861
862         simple = G_SIMPLE_ASYNC_RESULT (result);
863
864         /* Assume success unless a GError is set. */
865         return !g_simple_async_result_propagate_error (simple, error);
866 }
867
868 static gboolean
869 server_side_source_write_sync (ESource *source,
870                                GCancellable *cancellable,
871                                GError **error)
872 {
873         EAsyncClosure *closure;
874         GAsyncResult *result;
875         gboolean success;
876
877         closure = e_async_closure_new ();
878
879         e_source_write (
880                 source, cancellable, e_async_closure_callback, closure);
881
882         result = e_async_closure_wait (closure);
883
884         success = e_source_write_finish (source, result, error);
885
886         e_async_closure_free (closure);
887
888         return success;
889 }
890
891 static void
892 server_side_source_write (ESource *source,
893                           GCancellable *cancellable,
894                           GAsyncReadyCallback callback,
895                           gpointer user_data)
896 {
897         EServerSideSourcePrivate *priv;
898         GSimpleAsyncResult *simple;
899         GDBusObject *dbus_object;
900         EDBusSource *dbus_source;
901         gboolean replace_file;
902         const gchar *old_data;
903         gchar *new_data;
904         GError *error = NULL;
905
906         /* XXX Yes we block here.  We do this operation
907          *     synchronously to keep the server code simple. */
908
909         priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
910
911         simple = g_simple_async_result_new (
912                 G_OBJECT (source), callback, user_data,
913                 server_side_source_write);
914
915         g_simple_async_result_set_check_cancellable (simple, cancellable);
916
917         dbus_object = e_source_ref_dbus_object (source);
918         dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
919
920         old_data = priv->file_contents;
921         new_data = e_source_to_string (source, NULL);
922
923         /* When writing source data to disk, we always write to the
924          * source's specified "write-directory" even if the key file
925          * was originally read from a different directory.  To avoid
926          * polluting the write directory with key files identical to
927          * the original, we check that the data has actually changed
928          * before writing a copy to disk. */
929
930         replace_file =
931                 G_IS_FILE (priv->file) &&
932                 (g_strcmp0 (old_data, new_data) != 0);
933
934         if (replace_file) {
935                 GFile *file;
936                 GFile *write_directory;
937                 gchar *basename;
938
939                 g_warn_if_fail (priv->write_directory != NULL);
940
941                 basename = g_file_get_basename (priv->file);
942                 write_directory = g_file_new_for_path (priv->write_directory);
943                 file = g_file_get_child (write_directory, basename);
944                 g_free (basename);
945
946                 if (!g_file_equal (file, priv->file)) {
947                         g_object_unref (priv->file);
948                         priv->file = g_object_ref (file);
949                 }
950
951                 server_side_source_print_diff (source, old_data, new_data);
952
953                 g_file_make_directory_with_parents (
954                         write_directory, cancellable, &error);
955
956                 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
957                         g_clear_error (&error);
958
959                 if (error == NULL)
960                         g_file_replace_contents (
961                                 file, new_data, strlen (new_data),
962                                 NULL, FALSE, G_FILE_CREATE_NONE,
963                                 NULL, cancellable, &error);
964
965                 if (error == NULL) {
966                         g_free (priv->file_contents);
967                         priv->file_contents = new_data;
968                         new_data = NULL;
969                 }
970
971                 g_object_unref (write_directory);
972                 g_object_unref (file);
973         }
974
975         g_free (new_data);
976
977         g_object_unref (dbus_source);
978         g_object_unref (dbus_object);
979
980         if (error != NULL)
981                 g_simple_async_result_take_error (simple, error);
982
983         g_simple_async_result_complete_in_idle (simple);
984         g_object_unref (simple);
985 }
986
987 static gboolean
988 server_side_source_write_finish (ESource *source,
989                                  GAsyncResult *result,
990                                  GError **error)
991 {
992         GSimpleAsyncResult *simple;
993
994         g_return_val_if_fail (
995                 g_simple_async_result_is_valid (
996                 result, G_OBJECT (source),
997                 server_side_source_write), FALSE);
998
999         simple = G_SIMPLE_ASYNC_RESULT (result);
1000
1001         /* Assume success unless a GError is set. */
1002         return !g_simple_async_result_propagate_error (simple, error);
1003 }
1004
1005 static gboolean
1006 server_side_source_remote_create_sync (ESource *source,
1007                                        ESource *scratch_source,
1008                                        GCancellable *cancellable,
1009                                        GError **error)
1010 {
1011         ECollectionBackend *backend;
1012         ESourceRegistryServer *server;
1013         EServerSideSource *server_side_source;
1014         gboolean success;
1015
1016         if (!e_source_get_remote_creatable (source)) {
1017                 g_set_error (
1018                         error, G_IO_ERROR,
1019                         G_IO_ERROR_NOT_SUPPORTED,
1020                         _("Data source '%s' does not "
1021                         "support creating remote resources"),
1022                         e_source_get_display_name (source));
1023                 return FALSE;
1024         }
1025
1026         server_side_source = E_SERVER_SIDE_SOURCE (source);
1027         server = e_server_side_source_get_server (server_side_source);
1028         backend = e_source_registry_server_ref_backend (server, source);
1029
1030         if (backend == NULL) {
1031                 g_set_error (
1032                         error, G_IO_ERROR,
1033                         G_IO_ERROR_NOT_SUPPORTED,
1034                         _("Data source '%s' has no collection "
1035                         "backend to create the remote resource"),
1036                         e_source_get_display_name (source));
1037                 return FALSE;
1038         }
1039
1040         success = e_collection_backend_create_resource_sync (
1041                 backend, scratch_source, cancellable, error);
1042
1043         g_object_unref (backend);
1044
1045         return success;
1046 }
1047
1048 static gboolean
1049 server_side_source_remote_delete_sync (ESource *source,
1050                                        GCancellable *cancellable,
1051                                        GError **error)
1052 {
1053         ECollectionBackend *backend;
1054         ESourceRegistryServer *server;
1055         EServerSideSource *server_side_source;
1056         gboolean success;
1057
1058         if (!e_source_get_remote_deletable (source)) {
1059                 g_set_error (
1060                         error, G_IO_ERROR,
1061                         G_IO_ERROR_NOT_SUPPORTED,
1062                         _("Data source '%s' does not "
1063                         "support deleting remote resources"),
1064                         e_source_get_display_name (source));
1065                 return FALSE;
1066         }
1067
1068         server_side_source = E_SERVER_SIDE_SOURCE (source);
1069         server = e_server_side_source_get_server (server_side_source);
1070         backend = e_source_registry_server_ref_backend (server, source);
1071
1072         if (backend == NULL) {
1073                 g_set_error (
1074                         error, G_IO_ERROR,
1075                         G_IO_ERROR_NOT_SUPPORTED,
1076                         _("Data source '%s' has no collection "
1077                         "backend to delete the remote resource"),
1078                         e_source_get_display_name (source));
1079                 return FALSE;
1080         }
1081
1082         success = e_collection_backend_delete_resource_sync (
1083                 backend, source, cancellable, error);
1084
1085         g_object_unref (backend);
1086
1087         return success;
1088 }
1089
1090 static gboolean
1091 server_side_source_get_oauth2_access_token_sync (ESource *source,
1092                                                  GCancellable *cancellable,
1093                                                  gchar **out_access_token,
1094                                                  gint *out_expires_in,
1095                                                  GError **error)
1096 {
1097         EOAuth2Support *oauth2_support;
1098         gboolean success;
1099
1100         oauth2_support = e_server_side_source_ref_oauth2_support (
1101                 E_SERVER_SIDE_SOURCE (source));
1102
1103         if (oauth2_support == NULL) {
1104                 g_set_error (
1105                         error, G_IO_ERROR,
1106                         G_IO_ERROR_NOT_SUPPORTED,
1107                         _("Data source '%s' does not "
1108                         "support OAuth 2.0 authentication"),
1109                         e_source_get_display_name (source));
1110                 return FALSE;
1111         }
1112
1113         success = e_oauth2_support_get_access_token_sync (
1114                 oauth2_support, source, cancellable,
1115                 out_access_token, out_expires_in, error);
1116
1117         g_object_unref (oauth2_support);
1118
1119         return success;
1120 }
1121
1122 static gboolean
1123 server_side_source_initable_init (GInitable *initable,
1124                                   GCancellable *cancellable,
1125                                   GError **error)
1126 {
1127         EServerSideSource *source;
1128         GDBusObject *dbus_object;
1129         EDBusSource *dbus_source;
1130         gchar *uid;
1131
1132         source = E_SERVER_SIDE_SOURCE (initable);
1133
1134         dbus_source = e_dbus_source_skeleton_new ();
1135
1136         uid = e_source_dup_uid (E_SOURCE (source));
1137         if (uid == NULL)
1138                 uid = e_uid_new ();
1139         e_dbus_source_set_uid (dbus_source, uid);
1140         g_free (uid);
1141
1142         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1143         e_dbus_object_skeleton_set_source (
1144                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_source);
1145         g_object_unref (dbus_object);
1146
1147         g_signal_connect (
1148                 dbus_source, "handle-allow-auth-prompt",
1149                 G_CALLBACK (server_side_source_allow_auth_prompt_cb), source);
1150
1151         g_object_unref (dbus_source);
1152
1153         if (!e_server_side_source_load (source, cancellable, error))
1154                 return FALSE;
1155
1156         /* Chain up to parent interface's init() method. */
1157         return initable_parent_interface->init (initable, cancellable, error);
1158 }
1159
1160 static void
1161 e_server_side_source_class_init (EServerSideSourceClass *class)
1162 {
1163         GObjectClass *object_class;
1164         ESourceClass *source_class;
1165
1166         g_type_class_add_private (class, sizeof (EServerSideSourcePrivate));
1167
1168         object_class = G_OBJECT_CLASS (class);
1169         object_class->set_property = server_side_source_set_property;
1170         object_class->get_property = server_side_source_get_property;
1171         object_class->dispose = server_side_source_dispose;
1172         object_class->finalize = server_side_source_finalize;
1173
1174         source_class = E_SOURCE_CLASS (class);
1175         source_class->changed = server_side_source_changed;
1176         source_class->remove_sync = server_side_source_remove_sync;
1177         source_class->remove = server_side_source_remove;
1178         source_class->remove_finish = server_side_source_remove_finish;
1179         source_class->write_sync = server_side_source_write_sync;
1180         source_class->write = server_side_source_write;
1181         source_class->write_finish = server_side_source_write_finish;
1182         source_class->remote_create_sync =
1183                 server_side_source_remote_create_sync;
1184         source_class->remote_delete_sync =
1185                 server_side_source_remote_delete_sync;
1186         source_class->get_oauth2_access_token_sync =
1187                 server_side_source_get_oauth2_access_token_sync;
1188
1189         g_object_class_install_property (
1190                 object_class,
1191                 PROP_ALLOW_AUTH_PROMPT,
1192                 g_param_spec_boolean (
1193                         "allow-auth-prompt",
1194                         "Allow Auth Prompt",
1195                         "Whether authentication sessions may "
1196                         "interrupt the user for a password",
1197                         TRUE,
1198                         G_PARAM_READWRITE |
1199                         G_PARAM_CONSTRUCT |
1200                         G_PARAM_STATIC_STRINGS));
1201
1202         g_object_class_install_property (
1203                 object_class,
1204                 PROP_AUTH_SESSION_TYPE,
1205                 g_param_spec_gtype (
1206                         "auth-session-type",
1207                         "Auth Session Type",
1208                         "The type of authentication session "
1209                         "to instantiate for this source",
1210                         E_TYPE_AUTHENTICATION_SESSION,
1211                         G_PARAM_READWRITE |
1212                         G_PARAM_STATIC_STRINGS));
1213
1214         g_object_class_install_property (
1215                 object_class,
1216                 PROP_EXPORTED,
1217                 g_param_spec_boolean (
1218                         "exported",
1219                         "Exported",
1220                         "Whether the source has been exported over D-Bus",
1221                         FALSE,
1222                         G_PARAM_READABLE |
1223                         G_PARAM_STATIC_STRINGS));
1224
1225         g_object_class_install_property (
1226                 object_class,
1227                 PROP_FILE,
1228                 g_param_spec_object (
1229                         "file",
1230                         "File",
1231                         "The key file for the data source",
1232                         G_TYPE_FILE,
1233                         G_PARAM_READWRITE |
1234                         G_PARAM_CONSTRUCT_ONLY |
1235                         G_PARAM_STATIC_STRINGS));
1236
1237         g_object_class_install_property (
1238                 object_class,
1239                 PROP_OAUTH2_SUPPORT,
1240                 g_param_spec_object (
1241                         "oauth2-support",
1242                         "OAuth2 Support",
1243                         "The object providing OAuth 2.0 support",
1244                         E_TYPE_OAUTH2_SUPPORT,
1245                         G_PARAM_READWRITE |
1246                         G_PARAM_STATIC_STRINGS));
1247
1248         /* This overrides the "remote-creatable" property
1249          * in ESourceClass with a writable version. */
1250         g_object_class_install_property (
1251                 object_class,
1252                 PROP_REMOTE_CREATABLE,
1253                 g_param_spec_boolean (
1254                         "remote-creatable",
1255                         "Remote Creatable",
1256                         "Whether the data source "
1257                         "can create remote resources",
1258                         FALSE,
1259                         G_PARAM_READWRITE |
1260                         G_PARAM_STATIC_STRINGS));
1261
1262         /* This overrides the "remote-deletable" property
1263          * in ESourceClass with a writable version. */
1264         g_object_class_install_property (
1265                 object_class,
1266                 PROP_REMOTE_DELETABLE,
1267                 g_param_spec_boolean (
1268                         "remote-deletable",
1269                         "Remote Deletable",
1270                         "Whether the data source "
1271                         "can delete remote resources",
1272                         FALSE,
1273                         G_PARAM_READWRITE |
1274                         G_PARAM_STATIC_STRINGS));
1275
1276         /* This overrides the "removable" property
1277          * in ESourceClass with a writable version. */
1278         g_object_class_install_property (
1279                 object_class,
1280                 PROP_REMOVABLE,
1281                 g_param_spec_boolean (
1282                         "removable",
1283                         "Removable",
1284                         "Whether the data source is removable",
1285                         FALSE,
1286                         G_PARAM_READWRITE |
1287                         G_PARAM_STATIC_STRINGS));
1288
1289         g_object_class_install_property (
1290                 object_class,
1291                 PROP_SERVER,
1292                 g_param_spec_object (
1293                         "server",
1294                         "Server",
1295                         "The server to which the data source belongs",
1296                         E_TYPE_SOURCE_REGISTRY_SERVER,
1297                         G_PARAM_READWRITE |
1298                         G_PARAM_CONSTRUCT_ONLY |
1299                         G_PARAM_STATIC_STRINGS));
1300
1301         /* This overrides the "writable" property
1302          * in ESourceClass with a writable version. */
1303         g_object_class_install_property (
1304                 object_class,
1305                 PROP_WRITABLE,
1306                 g_param_spec_boolean (
1307                         "writable",
1308                         "Writable",
1309                         "Whether the data source is writable",
1310                         FALSE,
1311                         G_PARAM_READWRITE |
1312                         G_PARAM_STATIC_STRINGS));
1313
1314         /* Do not use G_PARAM_CONSTRUCT.  We initialize the
1315          * property ourselves in e_server_side_source_init(). */
1316         g_object_class_install_property (
1317                 object_class,
1318                 PROP_WRITE_DIRECTORY,
1319                 g_param_spec_string (
1320                         "write-directory",
1321                         "Write Directory",
1322                         "Directory in which to write changes to disk",
1323                         NULL,
1324                         G_PARAM_READWRITE |
1325                         G_PARAM_STATIC_STRINGS));
1326 }
1327
1328 static void
1329 e_server_side_source_initable_init (GInitableIface *iface)
1330 {
1331         initable_parent_interface = g_type_interface_peek_parent (iface);
1332
1333         iface->init = server_side_source_initable_init;
1334 }
1335
1336 static void
1337 e_server_side_source_init (EServerSideSource *source)
1338 {
1339         const gchar *user_dir;
1340
1341         source->priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
1342
1343         source->priv->node.data = source;
1344
1345         user_dir = e_server_side_source_get_user_dir ();
1346         source->priv->write_directory = g_strdup (user_dir);
1347
1348         source->priv->auth_session_type = E_TYPE_AUTHENTICATION_SESSION;
1349 }
1350
1351 /**
1352  * e_server_side_source_get_user_dir:
1353  *
1354  * Returns the directory where user-specific data source files are stored.
1355  *
1356  * Returns: the user-specific data source directory
1357  *
1358  * Since: 3.6
1359  **/
1360 const gchar *
1361 e_server_side_source_get_user_dir (void)
1362 {
1363         static gchar *dirname = NULL;
1364
1365         if (G_UNLIKELY (dirname == NULL)) {
1366                 const gchar *config_dir = e_get_user_config_dir ();
1367                 dirname = g_build_filename (config_dir, "sources", NULL);
1368                 g_mkdir_with_parents (dirname, 0700);
1369         }
1370
1371         return dirname;
1372 }
1373
1374 /**
1375  * e_server_side_source_new_user_file:
1376  * @uid: unique identifier for a data source, or %NULL
1377  *
1378  * Generates a unique file name for a new user-specific data source.
1379  * If @uid is non-%NULL it will be used in the basename of the file,
1380  * otherwise a unique basename will be generated using e_uid_new().
1381  *
1382  * The returned #GFile can then be passed to e_server_side_source_new().
1383  * Unreference the #GFile with g_object_unref() when finished with it.
1384  *
1385  * Note the data source file itself is not created here, only its name.
1386  *
1387  * Returns: the #GFile for a new data source
1388  *
1389  * Since: 3.6
1390  **/
1391 GFile *
1392 e_server_side_source_new_user_file (const gchar *uid)
1393 {
1394         GFile *file;
1395         gchar *safe_uid;
1396         gchar *basename;
1397         gchar *filename;
1398         const gchar *user_dir;
1399
1400         if (uid == NULL)
1401                 safe_uid = e_uid_new ();
1402         else
1403                 safe_uid = g_strdup (uid);
1404         e_filename_make_safe (safe_uid);
1405
1406         user_dir = e_server_side_source_get_user_dir ();
1407         basename = g_strconcat (safe_uid, ".source", NULL);
1408         filename = g_build_filename (user_dir, basename, NULL);
1409
1410         file = g_file_new_for_path (filename);
1411
1412         g_free (basename);
1413         g_free (filename);
1414         g_free (safe_uid);
1415
1416         return file;
1417 }
1418
1419 /**
1420  * e_server_side_source_uid_from_file:
1421  * @file: a #GFile for a data source
1422  * @error: return location for a #GError, or %NULL
1423  *
1424  * Extracts a unique identity string from the base name of @file.
1425  * If the base name of @file is missing a '.source' extension, the
1426  * function sets @error and returns %NULL.
1427  *
1428  * Returns: the unique identity string for @file, or %NULL
1429  *
1430  * Since: 3.6
1431  **/
1432 gchar *
1433 e_server_side_source_uid_from_file (GFile *file,
1434                                     GError **error)
1435 {
1436         gchar *basename;
1437         gchar *uid = NULL;
1438
1439         g_return_val_if_fail (G_IS_FILE (file), FALSE);
1440
1441         basename = g_file_get_basename (file);
1442
1443         if (*basename == '.') {
1444                 /* ignore hidden files */
1445         } else if (g_str_has_suffix (basename, ".source")) {
1446                 /* strlen(".source") --> 7 */
1447                 uid = g_strndup (basename, strlen (basename) - 7);
1448         } else {
1449                 g_set_error (
1450                         error, G_IO_ERROR,
1451                         G_IO_ERROR_INVALID_FILENAME,
1452                         _("File must have a '.source' extension"));
1453         }
1454
1455         g_free (basename);
1456
1457         return uid;
1458 }
1459
1460 /**
1461  * e_server_side_source_new:
1462  * @server: an #ESourceRegistryServer
1463  * @file: a #GFile, or %NULL
1464  * @error: return location for a #GError, or %NULL
1465  *
1466  * Creates a new #EServerSideSource which belongs to @server.  If @file
1467  * is non-%NULL and points to an existing file, the #EServerSideSource is
1468  * initialized from the file content.  If a read error occurs or the file
1469  * contains syntax errors, the function sets @error and returns %NULL.
1470  *
1471  * Returns: a new #EServerSideSource, or %NULL
1472  *
1473  * Since: 3.6
1474  **/
1475 ESource *
1476 e_server_side_source_new (ESourceRegistryServer *server,
1477                           GFile *file,
1478                           GError **error)
1479 {
1480         EDBusObjectSkeleton *dbus_object;
1481         ESource *source;
1482         gchar *uid = NULL;
1483
1484         g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
1485         g_return_val_if_fail (file == NULL || G_IS_FILE (file), NULL);
1486
1487         /* Extract a UID from the GFile, if we were given one. */
1488         if (file != NULL) {
1489                 uid = e_server_side_source_uid_from_file (file, error);
1490                 if (uid == NULL)
1491                         return NULL;
1492         }
1493
1494         /* XXX This is an awkward way of initializing the "dbus-object"
1495          *     property, but e_source_ref_dbus_object() needs to work. */
1496         dbus_object = e_dbus_object_skeleton_new (DBUS_OBJECT_PATH);
1497
1498         source = g_initable_new (
1499                 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1500                 "dbus-object", dbus_object,
1501                 "file", file, "server", server,
1502                 "uid", uid, NULL);
1503
1504         g_object_unref (dbus_object);
1505         g_free (uid);
1506
1507         return source;
1508 }
1509
1510 /**
1511  * e_server_side_source_new_memory_only:
1512  * @server: an #ESourceRegistryServer
1513  * @uid: a unique identifier, or %NULL
1514  * @error: return location for a #GError, or %NULL
1515  *
1516  * Creates a memory-only #EServerSideSource which belongs to @server.
1517  * No on-disk key file is created for this data source, so it will not
1518  * be remembered across sessions.
1519  *
1520  * Data source collections are often populated with memory-only data
1521  * sources to serve as proxies for resources discovered on a remote server.
1522  * These data sources are usually neither #EServerSideSource:writable nor
1523  * #EServerSideSource:removable by clients, at least not directly.
1524  *
1525  * If an error occurs while instantiating the #EServerSideSource, the
1526  * function sets @error and returns %NULL.  Although at this time there
1527  * are no known error conditions for memory-only data sources.
1528  *
1529  * Returns: a new memory-only #EServerSideSource, or %NULL
1530  *
1531  * Since: 3.6
1532  **/
1533 ESource *
1534 e_server_side_source_new_memory_only (ESourceRegistryServer *server,
1535                                       const gchar *uid,
1536                                       GError **error)
1537 {
1538         EDBusObjectSkeleton *dbus_object;
1539         ESource *source;
1540
1541         g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
1542
1543         /* XXX This is an awkward way of initializing the "dbus-object"
1544          *     property, but e_source_ref_dbus_object() needs to work. */
1545         dbus_object = e_dbus_object_skeleton_new (DBUS_OBJECT_PATH);
1546
1547         source = g_initable_new (
1548                 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1549                 "dbus-object", dbus_object,
1550                 "server", server, "uid", uid, NULL);
1551
1552         g_object_unref (dbus_object);
1553
1554         return source;
1555 }
1556
1557 /**
1558  * e_server_side_source_load:
1559  * @source: an #EServerSideSource
1560  * @cancellable: optional #GCancellable object, or %NULL
1561  * @error: return location for a #GError, or %NULL
1562  *
1563  * Reloads data source content from the file pointed to by the
1564  * #EServerSideSource:file property.
1565  *
1566  * If the #EServerSideSource:file property is %NULL or the file it points
1567  * to does not exist, the function does nothing and returns %TRUE.
1568  *
1569  * If a read error occurs or the file contains syntax errors, the function
1570  * sets @error and returns %FALSE.
1571  *
1572  * Returns: %TRUE on success, %FALSE on failure
1573  *
1574  * Since: 3.6
1575  **/
1576 gboolean
1577 e_server_side_source_load (EServerSideSource *source,
1578                            GCancellable *cancellable,
1579                            GError **error)
1580 {
1581         GDBusObject *dbus_object;
1582         EDBusSource *dbus_source;
1583         GKeyFile *key_file;
1584         GFile *file;
1585         gboolean success;
1586         gchar *data = NULL;
1587         gsize length;
1588         GError *local_error = NULL;
1589
1590         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1591
1592         file = e_server_side_source_get_file (source);
1593
1594         if (file != NULL)
1595                 g_file_load_contents (
1596                         file, cancellable, &data,
1597                         &length, NULL, &local_error);
1598
1599         /* Disregard G_IO_ERROR_NOT_FOUND and treat it as a successful load. */
1600         if (g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) {
1601                 g_error_free (local_error);
1602
1603         } else if (local_error != NULL) {
1604                 g_propagate_error (error, local_error);
1605                 return FALSE;
1606
1607         } else {
1608                 source->priv->file_contents = g_strdup (data);
1609         }
1610
1611         if (data == NULL) {
1612                 /* Create the bare minimum to pass parse_data(). */
1613                 data = g_strdup_printf ("[%s]", PRIMARY_GROUP_NAME);
1614                 length = strlen (data);
1615         }
1616
1617         key_file = g_key_file_new ();
1618
1619         success = server_side_source_parse_data (
1620                 key_file, data, length, error);
1621
1622         g_key_file_free (key_file);
1623
1624         if (!success) {
1625                 g_free (data);
1626                 return FALSE;
1627         }
1628
1629         /* Update the D-Bus interface properties. */
1630
1631         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1632         dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
1633
1634         e_dbus_source_set_data (dbus_source, data);
1635
1636         g_object_unref (dbus_source);
1637         g_object_unref (dbus_object);
1638
1639         g_free (data);
1640
1641         return TRUE;
1642 }
1643
1644 /**
1645  * e_server_side_source_get_file:
1646  * @source: an #EServerSideSource
1647  *
1648  * Returns the #GFile from which data source content is loaded and to
1649  * which changes are saved.  Note the @source may not have a #GFile.
1650  *
1651  * Returns: the #GFile for @source, or %NULL
1652  *
1653  * Since: 3.6
1654  **/
1655 GFile *
1656 e_server_side_source_get_file (EServerSideSource *source)
1657 {
1658         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1659
1660         return source->priv->file;
1661 }
1662
1663 /**
1664  * e_server_side_source_get_node:
1665  * @source: an #EServerSideSource
1666  *
1667  * Returns the #GNode representing the @source's hierarchical placement,
1668  * or %NULL if @source has not been placed in the data source hierarchy.
1669  * The data member of the #GNode points back to @source.  This is an easy
1670  * way to traverse ancestor and descendant data sources.
1671  *
1672  * Note that accessing other data sources this way is not thread-safe,
1673  * and this therefore function may be replaced at some later date.
1674  *
1675  * Returns: a #GNode, or %NULL
1676  *
1677  * Since: 3.6
1678  **/
1679 GNode *
1680 e_server_side_source_get_node (EServerSideSource *source)
1681 {
1682         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1683
1684         return &source->priv->node;
1685 }
1686
1687 /**
1688  * e_server_side_source_get_server:
1689  * @source: an #EServerSideSource
1690  *
1691  * Returns the #ESourceRegistryServer to which @source belongs.
1692  *
1693  * Returns: the #ESourceRegistryServer for @source
1694  *
1695  * Since: 3.6
1696  **/
1697 ESourceRegistryServer *
1698 e_server_side_source_get_server (EServerSideSource *source)
1699 {
1700         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1701
1702         return source->priv->server;
1703 }
1704
1705 /**
1706  * e_server_side_source_get_allow_auth_prompt:
1707  * @source: an #EServerSideSource
1708  *
1709  * Returns whether an authentication prompt is allowed to be shown
1710  * for @source.  #EAuthenticationSession will honor this setting by
1711  * dismissing the session if it can't find a valid stored password.
1712  *
1713  * See e_server_side_source_set_allow_auth_prompt() for further
1714  * discussion.
1715  *
1716  * Returns: whether auth prompts are allowed for @source
1717  *
1718  * Since: 3.6
1719  **/
1720 gboolean
1721 e_server_side_source_get_allow_auth_prompt (EServerSideSource *source)
1722 {
1723         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1724
1725         return source->priv->allow_auth_prompt;
1726 }
1727
1728 /**
1729  * e_server_side_source_set_allow_auth_prompt:
1730  * @source: an #EServerSideSource
1731  * @allow_auth_prompt: whether auth prompts are allowed for @source
1732  *
1733  * Sets whether an authentication prompt is allowed to be shown for @source.
1734  * #EAuthenticationSession will honor this setting by dismissing the session
1735  * if it can't find a valid stored password.
1736  *
1737  * If the user declines to provide a password for @source when prompted
1738  * by an #EAuthenticationSession, the #ESourceRegistryServer will set this
1739  * property to %FALSE to suppress any further prompting, which would likely
1740  * annoy the user.  However when an #ESourceRegistry instance is created by
1741  * a client application, the first thing it does is reset this property to
1742  * %TRUE for all registered data sources.  So suppressing authentication
1743  * prompts is only ever temporary.
1744  *
1745  * Since: 3.6
1746  **/
1747 void
1748 e_server_side_source_set_allow_auth_prompt (EServerSideSource *source,
1749                                             gboolean allow_auth_prompt)
1750 {
1751         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1752
1753         if (source->priv->allow_auth_prompt == allow_auth_prompt)
1754                 return;
1755
1756         source->priv->allow_auth_prompt = allow_auth_prompt;
1757
1758         g_object_notify (G_OBJECT (source), "allow-auth-prompt");
1759 }
1760
1761 /**
1762  * e_server_side_source_get_auth_session_type:
1763  * @source: an #EServerSideSource
1764  *
1765  * Returns the type of authentication session to use for @source,
1766  * which will get passed to e_source_registry_server_authenticate().
1767  *
1768  * This defaults to the #GType for #EAuthenticationSession.
1769  *
1770  * Returns: the type of authentication session to use for @source
1771  *
1772  * Since: 3.8
1773  **/
1774 GType
1775 e_server_side_source_get_auth_session_type (EServerSideSource *source)
1776 {
1777         g_return_val_if_fail (
1778                 E_IS_SERVER_SIDE_SOURCE (source),
1779                 E_TYPE_AUTHENTICATION_SESSION);
1780
1781         return source->priv->auth_session_type;
1782 }
1783
1784 /**
1785  * e_server_side_source_set_auth_session_type:
1786  * @source: an #EServerSideSource
1787  * @auth_session_type: the type of authentication session to use for @source
1788  *
1789  * Sets the type of authentication session to use for @source, which will
1790  * get passed to e_source_registry_server_authenticate().
1791  *
1792  * @auth_session_type must be derived from #EAuthenticationSession, or else
1793  * the function will reject the #GType with a runtime warning.
1794  *
1795  * This defaults to the #GType for #EAuthenticationSession.
1796  *
1797  * Since: 3.8
1798  **/
1799 void
1800 e_server_side_source_set_auth_session_type (EServerSideSource *source,
1801                                             GType auth_session_type)
1802 {
1803         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1804
1805         if (!g_type_is_a (auth_session_type, E_TYPE_AUTHENTICATION_SESSION)) {
1806                 g_warning (
1807                         "Invalid auth session type '%s' for source '%s'",
1808                         g_type_name (auth_session_type),
1809                         e_source_get_display_name (E_SOURCE (source)));
1810                 return;
1811         }
1812
1813         if (auth_session_type == source->priv->auth_session_type)
1814                 return;
1815
1816         source->priv->auth_session_type = auth_session_type;
1817
1818         g_object_notify (G_OBJECT (source), "auth-session-type");
1819 }
1820
1821 /**
1822  * e_server_side_source_get_exported:
1823  * @source: an #EServerSideSource
1824  *
1825  * Returns whether @source has been exported over D-Bus.
1826  *
1827  * The function returns %FALSE after @source is initially created, %TRUE
1828  * after passing @source to e_source_registry_add_source() (provided that
1829  * @source's #ESource:parent is also exported), and %FALSE after passing
1830  * @source to e_source_registry_remove_source().
1831  *
1832  * Returns: whether @source has been exported
1833  *
1834  * Since: 3.6
1835  **/
1836 gboolean
1837 e_server_side_source_get_exported (EServerSideSource *source)
1838 {
1839         ESourceRegistryServer *server;
1840         ESource *exported_source;
1841         gboolean exported = FALSE;
1842         const gchar *uid;
1843
1844         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1845
1846         uid = e_source_get_uid (E_SOURCE (source));
1847         server = e_server_side_source_get_server (source);
1848
1849         /* We're exported if we can look ourselves up in the registry. */
1850
1851         exported_source = e_source_registry_server_ref_source (server, uid);
1852         if (exported_source != NULL) {
1853                 exported = TRUE;
1854                 g_object_unref (exported_source);
1855         }
1856
1857         return exported;
1858 }
1859
1860 /**
1861  * e_server_side_source_get_write_directory:
1862  * @source: an #EServerSideSource
1863  *
1864  * Returns the local directory path where changes to @source are written.
1865  *
1866  * By default, changes are written to the local directory path returned by
1867  * e_server_side_source_get_user_dir(), but an #ECollectionBackend may wish
1868  * to override this to use its own private cache directory for data sources
1869  * it creates automatically.
1870  *
1871  * Returns: the directory where changes are written
1872  *
1873  * Since: 3.6
1874  **/
1875 const gchar *
1876 e_server_side_source_get_write_directory (EServerSideSource *source)
1877 {
1878         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1879
1880         return source->priv->write_directory;
1881 }
1882
1883 /**
1884  * e_server_side_source_set_write_directory:
1885  * @source: an #EServerSideSource
1886  * @write_directory: the directory where changes are to be written
1887  *
1888  * Sets the local directory path where changes to @source are to be written.
1889  *
1890  * By default, changes are written to the local directory path returned by
1891  * e_server_side_source_get_user_dir(), but an #ECollectionBackend may wish
1892  * to override this to use its own private cache directory for data sources
1893  * it creates automatically.
1894  *
1895  * Since: 3.6
1896  **/
1897 void
1898 e_server_side_source_set_write_directory (EServerSideSource *source,
1899                                           const gchar *write_directory)
1900 {
1901         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1902         g_return_if_fail (write_directory != NULL);
1903
1904         if (g_strcmp0 (source->priv->write_directory, write_directory) == 0)
1905                 return;
1906
1907         g_free (source->priv->write_directory);
1908         source->priv->write_directory = g_strdup (write_directory);
1909
1910         g_object_notify (G_OBJECT (source), "write-directory");
1911 }
1912
1913 /**
1914  * e_server_side_source_set_removable:
1915  * @source: an #EServerSideSource
1916  * @removable: whether to export the Removable interface
1917  *
1918  * Sets whether to allow registry clients to remove @source and its
1919  * descendants.  If %TRUE, the Removable D-Bus interface is exported at
1920  * the object path for @source.  If %FALSE, the Removable D-Bus interface
1921  * is unexported at the object path for @source, and any attempt by clients
1922  * to call e_source_remove() will fail.
1923  *
1924  * Note this is only enforced for clients of the registry D-Bus service.
1925  * The service itself may remove any data source at any time.
1926  *
1927  * Since: 3.6
1928  **/
1929 void
1930 e_server_side_source_set_removable (EServerSideSource *source,
1931                                     gboolean removable)
1932 {
1933         EDBusSourceRemovable *dbus_interface = NULL;
1934         GDBusObject *dbus_object;
1935         gboolean currently_removable;
1936
1937         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1938
1939         currently_removable = e_source_get_removable (E_SOURCE (source));
1940
1941         if (removable == currently_removable)
1942                 return;
1943
1944         if (removable) {
1945                 dbus_interface =
1946                         e_dbus_source_removable_skeleton_new ();
1947
1948                 g_signal_connect (
1949                         dbus_interface, "handle-remove",
1950                         G_CALLBACK (server_side_source_remove_cb), source);
1951         }
1952
1953         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1954         e_dbus_object_skeleton_set_source_removable (
1955                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
1956         g_object_unref (dbus_object);
1957
1958         if (dbus_interface != NULL)
1959                 g_object_unref (dbus_interface);
1960
1961         g_object_notify (G_OBJECT (source), "removable");
1962 }
1963
1964 /**
1965  * e_server_side_source_set_writable:
1966  * @source: an #EServerSideSource
1967  * @writable: whether to export the Writable interface
1968  *
1969  * Sets whether to allow registry clients to alter the content of @source.
1970  * If %TRUE, the Writable D-Bus interface is exported at the object path
1971  * for @source.  If %FALSE, the Writable D-Bus interface is unexported at
1972  * the object path for @source, and any attempt by clients to call
1973  * e_source_write() will fail.
1974  *
1975  * Note this is only enforced for clients of the registry D-Bus service.
1976  * The service itself can write to any data source at any time.
1977  *
1978  * Since: 3.6
1979  **/
1980 void
1981 e_server_side_source_set_writable (EServerSideSource *source,
1982                                    gboolean writable)
1983 {
1984         EDBusSourceWritable *dbus_interface = NULL;
1985         GDBusObject *dbus_object;
1986         gboolean currently_writable;
1987
1988         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1989
1990         currently_writable = e_source_get_writable (E_SOURCE (source));
1991
1992         if (writable == currently_writable)
1993                 return;
1994
1995         if (writable) {
1996                 dbus_interface =
1997                         e_dbus_source_writable_skeleton_new ();
1998
1999                 g_signal_connect (
2000                         dbus_interface, "handle-write",
2001                         G_CALLBACK (server_side_source_write_cb), source);
2002         }
2003
2004         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2005         e_dbus_object_skeleton_set_source_writable (
2006                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2007         g_object_unref (dbus_object);
2008
2009         if (dbus_interface != NULL)
2010                 g_object_unref (dbus_interface);
2011
2012         g_object_notify (G_OBJECT (source), "writable");
2013 }
2014
2015 /**
2016  * e_server_side_source_set_remote_creatable:
2017  * @source: an #EServerSideSource
2018  * @remote_creatable: whether to export the RemoteCreatable interface
2019  *
2020  * Indicates whether @source can be used to create resources on a remote
2021  * server.  Typically this is only set to %TRUE for collection sources.
2022  *
2023  * If %TRUE, the RemoteCreatable D-Bus interface is exported at the object
2024  * path for @source.  If %FALSE, the RemoteCreatable D-Bus interface is
2025  * unexported at the object path for @source, and any attempt by clients
2026  * to call e_source_remote_create() will fail.
2027  *
2028  * Unlike the #ESource:removable and #ESource:writable properties, this
2029  * is enforced for both clients of the registry D-Bus service and within
2030  * the registry D-Bus service itself.
2031  *
2032  * Since: 3.6
2033  **/
2034 void
2035 e_server_side_source_set_remote_creatable (EServerSideSource *source,
2036                                            gboolean remote_creatable)
2037 {
2038         EDBusSourceRemoteCreatable *dbus_interface = NULL;
2039         GDBusObject *dbus_object;
2040         gboolean currently_remote_creatable;
2041
2042         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2043
2044         currently_remote_creatable =
2045                 e_source_get_remote_creatable (E_SOURCE (source));
2046
2047         if (remote_creatable == currently_remote_creatable)
2048                 return;
2049
2050         if (remote_creatable) {
2051                 dbus_interface =
2052                         e_dbus_source_remote_creatable_skeleton_new ();
2053
2054                 g_signal_connect (
2055                         dbus_interface, "handle-create",
2056                         G_CALLBACK (server_side_source_remote_create_cb),
2057                         source);
2058         }
2059
2060         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2061         e_dbus_object_skeleton_set_source_remote_creatable (
2062                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2063         g_object_unref (dbus_object);
2064
2065         if (dbus_interface != NULL)
2066                 g_object_unref (dbus_interface);
2067
2068         g_object_notify (G_OBJECT (source), "remote-creatable");
2069 }
2070
2071 /**
2072  * e_server_side_source_set_remote_deletable:
2073  * @source: an #EServerSideSource
2074  * @remote_deletable: whether to export the RemoteDeletable interface
2075  *
2076  * Indicates whether @source can be used to delete resources on a remote
2077  * server.  Typically this is only set to %TRUE for sources created by an
2078  * #ECollectionBackend to represent a remote resource.
2079  *
2080  * If %TRUE, the RemoteDeletable D-Bus interface is exported at the object
2081  * path for @source.  If %FALSE, the RemoteDeletable D-Bus interface is
2082  * unexported at the object path for @source, and any attempt by clients
2083  * to call e_source_remote_delete() will fail.
2084  *
2085  * Unlike the #ESource:removable and #ESource:writable properties, this
2086  * is enforced for both clients of the registry D-Bus server and within
2087  * the registry D-Bus service itself.
2088  *
2089  * Since: 3.6
2090  **/
2091 void
2092 e_server_side_source_set_remote_deletable (EServerSideSource *source,
2093                                            gboolean remote_deletable)
2094 {
2095         EDBusSourceRemoteDeletable *dbus_interface = NULL;
2096         GDBusObject *dbus_object;
2097         gboolean currently_remote_deletable;
2098
2099         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2100
2101         currently_remote_deletable =
2102                 e_source_get_remote_deletable (E_SOURCE (source));
2103
2104         if (remote_deletable == currently_remote_deletable)
2105                 return;
2106
2107         if (remote_deletable) {
2108                 dbus_interface =
2109                         e_dbus_source_remote_deletable_skeleton_new ();
2110
2111                 g_signal_connect (
2112                         dbus_interface, "handle-delete",
2113                         G_CALLBACK (server_side_source_remote_delete_cb),
2114                         source);
2115         }
2116
2117         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2118         e_dbus_object_skeleton_set_source_remote_deletable (
2119                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2120         g_object_unref (dbus_object);
2121
2122         if (dbus_interface != NULL)
2123                 g_object_unref (dbus_interface);
2124
2125         g_object_notify (G_OBJECT (source), "remote-deletable");
2126 }
2127
2128 /**
2129  * e_server_side_source_ref_oauth2_support:
2130  * @source: an #EServerSideSource
2131  *
2132  * Returns the object implementing the #EOAuth2SupportInterface,
2133  * or %NULL if @source does not support OAuth 2.0 authentication.
2134  *
2135  * The returned #EOAuth2Support object is referenced for thread-safety.
2136  * Unreference the object with g_object_unref() when finished with it.
2137  *
2138  * Returns: an #EOAuth2Support object, or %NULL
2139  *
2140  * Since: 3.8
2141  **/
2142 EOAuth2Support *
2143 e_server_side_source_ref_oauth2_support (EServerSideSource *source)
2144 {
2145         g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
2146
2147         return g_weak_ref_get (&source->priv->oauth2_support);
2148 }
2149
2150 /**
2151  * e_server_side_source_set_oauth2_support:
2152  * @source: an #EServerSideSource
2153  * @oauth2_support: an #EOAuth2Support object, or %NULL
2154  *
2155  * Indicates whether @source supports OAuth 2.0 authentication.
2156  *
2157  * If @oauth2_support is non-%NULL, the OAuth2Support D-Bus interface is
2158  * exported at the object path for @source.  If @oauth2_support is %NULL,
2159  * the OAuth2Support D-Bus interface is unexported at the object path for
2160  * @source, and any attempt by clients to call
2161  * e_source_get_oauth2_access_token() will fail.
2162  *
2163  * Requests for OAuth 2.0 access tokens are forwarded to @oauth2_support,
2164  * which implements the #EOAuth2SupportInterface.
2165  *
2166  * Since: 3.8
2167  **/
2168 void
2169 e_server_side_source_set_oauth2_support (EServerSideSource *source,
2170                                          EOAuth2Support *oauth2_support)
2171 {
2172         EDBusSourceOAuth2Support *dbus_interface = NULL;
2173         GDBusObject *dbus_object;
2174
2175         g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
2176
2177         if (oauth2_support != NULL) {
2178                 g_return_if_fail (E_IS_OAUTH2_SUPPORT (oauth2_support));
2179
2180                 dbus_interface =
2181                         e_dbus_source_oauth2_support_skeleton_new ();
2182
2183                 g_signal_connect (
2184                         dbus_interface, "handle-get-access-token",
2185                         G_CALLBACK (server_side_source_get_access_token_cb),
2186                         source);
2187         }
2188
2189         g_weak_ref_set (&source->priv->oauth2_support, oauth2_support);
2190
2191         dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
2192         e_dbus_object_skeleton_set_source_oauth2_support (
2193                 E_DBUS_OBJECT_SKELETON (dbus_object), dbus_interface);
2194         g_object_unref (dbus_object);
2195
2196         if (dbus_interface != NULL)
2197                 g_object_unref (dbus_interface);
2198
2199         g_object_notify (G_OBJECT (source), "oauth2-support");
2200 }
2201