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