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