2 * e-server-side-source.c
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.
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.
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/>
20 * SECTION: e-server-side-source
21 * @include: libebackend/libebackend.h
22 * @short_description: A server-side data source
24 * An #EServerSideSource is an #ESource with some additional capabilities
25 * exclusive to the registry D-Bus service.
28 #include "e-server-side-source.h"
31 #include <glib/gi18n-lib.h>
33 /* Private D-Bus classes. */
34 #include <e-dbus-source.h>
36 #define E_SERVER_SIDE_SOURCE_GET_PRIVATE(obj) \
37 (G_TYPE_INSTANCE_GET_PRIVATE \
38 ((obj), E_TYPE_SERVER_SIDE_SOURCE, EServerSideSourcePrivate))
40 #define DBUS_OBJECT_PATH E_SOURCE_REGISTRY_SERVER_OBJECT_PATH "/Source"
42 #define PRIMARY_GROUP_NAME "Data Source"
44 typedef struct _AsyncContext AsyncContext;
46 struct _EServerSideSourcePrivate {
47 gpointer server; /* weak pointer */
55 gboolean allow_auth_prompt;
56 gchar *write_directory;
59 struct _AsyncContext {
60 EDBusSourceRemoteCreatable *remote_creatable;
61 EDBusSourceRemoteDeletable *remote_deletable;
62 GDBusMethodInvocation *invocation;
67 PROP_ALLOW_AUTH_PROMPT,
70 PROP_REMOTE_CREATABLE,
71 PROP_REMOTE_DELETABLE,
78 static GInitableIface *initable_parent_interface;
80 /* Forward Declarations */
81 static void e_server_side_source_initable_init
82 (GInitableIface *interface);
84 G_DEFINE_TYPE_WITH_CODE (
88 G_IMPLEMENT_INTERFACE (
90 e_server_side_source_initable_init))
93 async_context_free (AsyncContext *async_context)
95 if (async_context->remote_creatable != NULL)
96 g_object_unref (async_context->remote_creatable);
98 if (async_context->remote_deletable != NULL)
99 g_object_unref (async_context->remote_deletable);
101 if (async_context->invocation != NULL)
102 g_object_unref (async_context->invocation);
104 g_slice_free (AsyncContext, async_context);
108 server_side_source_parse_data (GKeyFile *key_file,
115 success = g_key_file_load_from_data (
116 key_file, data, length, G_KEY_FILE_NONE, error);
121 /* Make sure the key file has a [Data Source] group. */
122 if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
124 error, G_KEY_FILE_ERROR,
125 G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
126 _("Data source is missing a [%s] group"),
135 server_side_source_print_diff (ESource *source,
136 const gchar *old_data,
137 const gchar *new_data)
139 gchar **old_strv = NULL;
140 gchar **new_strv = NULL;
141 guint old_length = 0;
142 guint new_length = 0;
145 g_print ("Saving %s\n", e_source_get_uid (source));
147 if (old_data != NULL) {
148 old_strv = g_strsplit (old_data, "\n", 0);
149 old_length = g_strv_length (old_strv);
152 if (new_data != NULL) {
153 new_strv = g_strsplit (new_data, "\n", 0);
154 new_length = g_strv_length (new_strv);
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]);
162 g_print (" : %s\n", old_strv[ii]);
166 for (; ii < old_length; ii++)
167 g_print (" - : %s\n", old_strv[ii]);
169 for (; ii < new_length; ii++)
170 g_print (" + : %s\n", new_strv[ii]);
172 g_strfreev (old_strv);
173 g_strfreev (new_strv);
177 server_side_source_traverse_cb (GNode *node,
180 g_queue_push_tail (queue, g_object_ref (node->data));
186 server_side_source_allow_auth_prompt_cb (EDBusSource *interface,
187 GDBusMethodInvocation *invocation,
188 EServerSideSource *source)
190 e_server_side_source_set_allow_auth_prompt (source, TRUE);
192 e_dbus_source_complete_allow_auth_prompt (interface, invocation);
198 server_side_source_remove_cb (EDBusSourceRemovable *interface,
199 GDBusMethodInvocation *invocation,
200 EServerSideSource *source)
202 GError *error = NULL;
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. */
209 e_source_remove_sync (E_SOURCE (source), NULL, &error);
212 g_dbus_method_invocation_take_error (invocation, error);
214 e_dbus_source_removable_complete_remove (
215 interface, invocation);
221 server_side_source_write_cb (EDBusSourceWritable *interface,
222 GDBusMethodInvocation *invocation,
227 GDBusObject *dbus_object;
228 EDBusSource *dbus_source;
229 GError *error = NULL;
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. */
234 dbus_object = e_source_ref_dbus_object (source);
235 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
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);
242 /* Q: How does this trigger data being written to disk?
244 * A: Here's the sequence of events:
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.
256 e_dbus_source_set_data (dbus_source, data);
259 g_dbus_method_invocation_take_error (invocation, error);
261 e_dbus_source_writable_complete_write (
262 interface, invocation);
264 g_object_unref (dbus_source);
265 g_object_unref (dbus_object);
270 /* Helper for server_side_source_remote_create_cb() */
272 server_side_source_remote_create_done_cb (GObject *source_object,
273 GAsyncResult *result,
277 AsyncContext *async_context;
278 GError *error = NULL;
280 source = E_SOURCE (source_object);
281 async_context = (AsyncContext *) user_data;
283 e_source_remote_create_finish (source, result, &error);
286 g_dbus_method_invocation_take_error (
287 async_context->invocation, error);
289 e_dbus_source_remote_creatable_complete_create (
290 async_context->remote_creatable,
291 async_context->invocation);
293 async_context_free (async_context);
297 server_side_source_remote_create_cb (EDBusSourceRemoteCreatable *interface,
298 GDBusMethodInvocation *invocation,
303 EServerSideSource *server_side_source;
304 ESourceRegistryServer *server;
305 AsyncContext *async_context;
306 ESource *scratch_source;
307 GDBusObject *dbus_object;
308 EDBusSource *dbus_source;
311 GError *error = NULL;
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. */
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);
325 g_dbus_method_invocation_take_error (invocation, error);
329 server_side_source = E_SERVER_SIDE_SOURCE (source);
330 server = e_server_side_source_get_server (server_side_source);
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);
338 ((scratch_source != NULL) && (error == NULL)) ||
339 ((scratch_source == NULL) && (error != NULL)));
342 g_dbus_method_invocation_take_error (invocation, error);
346 dbus_object = e_source_ref_dbus_object (scratch_source);
347 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
349 e_dbus_source_set_data (dbus_source, data);
351 g_object_unref (dbus_object);
352 g_object_unref (dbus_source);
354 async_context = g_slice_new0 (AsyncContext);
355 async_context->remote_creatable = g_object_ref (interface);
356 async_context->invocation = g_object_ref (invocation);
358 e_source_remote_create (
359 source, scratch_source, NULL,
360 server_side_source_remote_create_done_cb,
363 g_object_unref (scratch_source);
368 /* Helper for server_side_source_remote_delete_cb() */
370 server_side_source_remote_delete_done_cb (GObject *source_object,
371 GAsyncResult *result,
375 AsyncContext *async_context;
376 GError *error = NULL;
378 source = E_SOURCE (source_object);
379 async_context = (AsyncContext *) user_data;
381 e_source_remote_delete_finish (source, result, &error);
384 g_dbus_method_invocation_take_error (
385 async_context->invocation, error);
387 e_dbus_source_remote_deletable_complete_delete (
388 async_context->remote_deletable,
389 async_context->invocation);
391 async_context_free (async_context);
395 server_side_source_remote_delete_cb (EDBusSourceRemoteDeletable *interface,
396 GDBusMethodInvocation *invocation,
399 AsyncContext *async_context;
401 async_context = g_slice_new0 (AsyncContext);
402 async_context->remote_deletable = g_object_ref (interface);
403 async_context->invocation = g_object_ref (invocation);
405 e_source_remote_delete (
407 server_side_source_remote_delete_done_cb,
414 server_side_source_set_file (EServerSideSource *source,
417 g_return_if_fail (file == NULL || G_IS_FILE (file));
418 g_return_if_fail (source->priv->file == NULL);
421 source->priv->file = g_object_ref (file);
425 server_side_source_set_server (EServerSideSource *source,
426 ESourceRegistryServer *server)
428 g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
429 g_return_if_fail (source->priv->server == NULL);
431 source->priv->server = server;
433 g_object_add_weak_pointer (
434 G_OBJECT (server), &source->priv->server);
438 server_side_source_set_property (GObject *object,
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));
451 server_side_source_set_file (
452 E_SERVER_SIDE_SOURCE (object),
453 g_value_get_object (value));
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));
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));
469 e_server_side_source_set_removable (
470 E_SERVER_SIDE_SOURCE (object),
471 g_value_get_boolean (value));
475 server_side_source_set_server (
476 E_SERVER_SIDE_SOURCE (object),
477 g_value_get_object (value));
481 e_server_side_source_set_writable (
482 E_SERVER_SIDE_SOURCE (object),
483 g_value_get_boolean (value));
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));
493 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
497 server_side_source_get_property (GObject *object,
502 switch (property_id) {
503 case PROP_ALLOW_AUTH_PROMPT:
504 g_value_set_boolean (
506 e_server_side_source_get_allow_auth_prompt (
507 E_SERVER_SIDE_SOURCE (object)));
511 g_value_set_boolean (
513 e_server_side_source_get_exported (
514 E_SERVER_SIDE_SOURCE (object)));
520 e_server_side_source_get_file (
521 E_SERVER_SIDE_SOURCE (object)));
524 case PROP_REMOTE_CREATABLE:
525 g_value_set_boolean (
527 e_source_get_remote_creatable (
531 case PROP_REMOTE_DELETABLE:
532 g_value_set_boolean (
534 e_source_get_remote_deletable (
539 g_value_set_boolean (
541 e_source_get_removable (
548 e_server_side_source_get_server (
549 E_SERVER_SIDE_SOURCE (object)));
553 g_value_set_boolean (
555 e_source_get_writable (
559 case PROP_WRITE_DIRECTORY:
562 e_server_side_source_get_write_directory (
563 E_SERVER_SIDE_SOURCE (object)));
567 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
571 server_side_source_dispose (GObject *object)
573 EServerSideSourcePrivate *priv;
575 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
577 if (priv->server != NULL) {
578 g_object_remove_weak_pointer (
579 G_OBJECT (priv->server), &priv->server);
583 if (priv->file != NULL) {
584 g_object_unref (priv->file);
588 /* Chain up to parent's dispose() method. */
589 G_OBJECT_CLASS (e_server_side_source_parent_class)->dispose (object);
593 server_side_source_finalize (GObject *object)
595 EServerSideSourcePrivate *priv;
597 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
599 g_node_unlink (&priv->node);
601 g_free (priv->file_contents);
602 g_free (priv->write_directory);
604 /* Chain up to parent's finalize() method. */
605 G_OBJECT_CLASS (e_server_side_source_parent_class)->finalize (object);
609 server_side_source_changed (ESource *source)
611 GDBusObject *dbus_object;
612 EDBusSource *dbus_source;
615 GError *error = NULL;
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)))
621 dbus_object = e_source_ref_dbus_object (source);
622 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
624 old_data = e_dbus_source_dup_data (dbus_source);
625 new_data = e_source_to_string (source, NULL);
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);
636 g_object_unref (dbus_source);
637 g_object_unref (dbus_object);
639 /* This writes the "data" property to disk. */
640 e_source_write_sync (source, NULL, &error);
643 g_warning ("%s: %s", G_STRFUNC, error->message);
644 g_error_free (error);
649 server_side_source_remove_sync (ESource *source,
650 GCancellable *cancellable,
653 EAsyncClosure *closure;
654 GAsyncResult *result;
657 closure = e_async_closure_new ();
660 source, cancellable, e_async_closure_callback, closure);
662 result = e_async_closure_wait (closure);
664 success = e_source_remove_finish (source, result, error);
666 e_async_closure_free (closure);
672 server_side_source_remove (ESource *source,
673 GCancellable *cancellable,
674 GAsyncReadyCallback callback,
677 EServerSideSourcePrivate *priv;
678 GSimpleAsyncResult *simple;
679 ESourceRegistryServer *server;
680 GQueue queue = G_QUEUE_INIT;
682 GError *error = NULL;
684 /* XXX Yes we block here. We do this operation
685 * synchronously to keep the server code simple. */
687 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
689 simple = g_simple_async_result_new (
690 G_OBJECT (source), callback, user_data,
691 server_side_source_remove);
693 g_simple_async_result_set_check_cancellable (simple, cancellable);
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. */
699 &priv->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
700 (GNodeTraverseFunc) server_side_source_traverse_cb, &queue);
702 /* Unexport the object and its descendants. */
703 server = E_SOURCE_REGISTRY_SERVER (priv->server);
704 e_source_registry_server_remove_source (server, source);
706 list = g_queue_peek_head_link (&queue);
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;
713 child = E_SERVER_SIDE_SOURCE (link->data);
714 file = e_server_side_source_get_file (child);
717 g_file_delete (file, cancellable, &error);
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.
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);
736 while (!g_queue_is_empty (&queue))
737 g_object_unref (g_queue_pop_head (&queue));
740 g_simple_async_result_take_error (simple, error);
742 g_simple_async_result_complete_in_idle (simple);
743 g_object_unref (simple);
747 server_side_source_remove_finish (ESource *source,
748 GAsyncResult *result,
751 GSimpleAsyncResult *simple;
753 g_return_val_if_fail (
754 g_simple_async_result_is_valid (
755 result, G_OBJECT (source),
756 server_side_source_remove), FALSE);
758 simple = G_SIMPLE_ASYNC_RESULT (result);
760 /* Assume success unless a GError is set. */
761 return !g_simple_async_result_propagate_error (simple, error);
765 server_side_source_write_sync (ESource *source,
766 GCancellable *cancellable,
769 EAsyncClosure *closure;
770 GAsyncResult *result;
773 closure = e_async_closure_new ();
776 source, cancellable, e_async_closure_callback, closure);
778 result = e_async_closure_wait (closure);
780 success = e_source_write_finish (source, result, error);
782 e_async_closure_free (closure);
788 server_side_source_write (ESource *source,
789 GCancellable *cancellable,
790 GAsyncReadyCallback callback,
793 EServerSideSourcePrivate *priv;
794 GSimpleAsyncResult *simple;
795 GDBusObject *dbus_object;
796 EDBusSource *dbus_source;
797 gboolean replace_file;
798 const gchar *old_data;
800 GError *error = NULL;
802 /* XXX Yes we block here. We do this operation
803 * synchronously to keep the server code simple. */
805 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
807 simple = g_simple_async_result_new (
808 G_OBJECT (source), callback, user_data,
809 server_side_source_write);
811 g_simple_async_result_set_check_cancellable (simple, cancellable);
813 dbus_object = e_source_ref_dbus_object (source);
814 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
816 old_data = priv->file_contents;
817 new_data = e_source_to_string (source, NULL);
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. */
827 G_IS_FILE (priv->file) &&
828 (g_strcmp0 (old_data, new_data) != 0);
832 GFile *write_directory;
835 g_warn_if_fail (priv->write_directory != NULL);
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);
842 if (!g_file_equal (file, priv->file)) {
843 g_object_unref (priv->file);
844 priv->file = g_object_ref (file);
847 server_side_source_print_diff (source, old_data, new_data);
849 g_file_make_directory_with_parents (
850 write_directory, cancellable, &error);
852 if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_EXISTS))
853 g_clear_error (&error);
856 g_file_replace_contents (
857 file, new_data, strlen (new_data),
858 NULL, FALSE, G_FILE_CREATE_NONE,
859 NULL, cancellable, &error);
862 g_free (priv->file_contents);
863 priv->file_contents = new_data;
867 g_object_unref (write_directory);
868 g_object_unref (file);
873 g_object_unref (dbus_source);
874 g_object_unref (dbus_object);
877 g_simple_async_result_take_error (simple, error);
879 g_simple_async_result_complete_in_idle (simple);
880 g_object_unref (simple);
884 server_side_source_write_finish (ESource *source,
885 GAsyncResult *result,
888 GSimpleAsyncResult *simple;
890 g_return_val_if_fail (
891 g_simple_async_result_is_valid (
892 result, G_OBJECT (source),
893 server_side_source_write), FALSE);
895 simple = G_SIMPLE_ASYNC_RESULT (result);
897 /* Assume success unless a GError is set. */
898 return !g_simple_async_result_propagate_error (simple, error);
902 server_side_source_remote_create_sync (ESource *source,
903 ESource *scratch_source,
904 GCancellable *cancellable,
907 ECollectionBackend *backend;
908 ESourceRegistryServer *server;
909 EServerSideSource *server_side_source;
912 if (!e_source_get_remote_creatable (source)) {
915 G_IO_ERROR_NOT_SUPPORTED,
916 _("Data source '%s' does not "
917 "support creating remote resources"),
918 e_source_get_display_name (source));
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);
926 if (backend == NULL) {
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));
936 success = e_collection_backend_create_resource_sync (
937 backend, scratch_source, cancellable, error);
939 g_object_unref (backend);
945 server_side_source_remote_delete_sync (ESource *source,
946 GCancellable *cancellable,
949 ECollectionBackend *backend;
950 ESourceRegistryServer *server;
951 EServerSideSource *server_side_source;
954 if (!e_source_get_remote_deletable (source)) {
957 G_IO_ERROR_NOT_SUPPORTED,
958 _("Data source '%s' does not "
959 "support deleting remote resources"),
960 e_source_get_display_name (source));
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);
968 if (backend == NULL) {
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));
978 success = e_collection_backend_delete_resource_sync (
979 backend, source, cancellable, error);
981 g_object_unref (backend);
987 server_side_source_initable_init (GInitable *initable,
988 GCancellable *cancellable,
991 EServerSideSource *source;
992 GDBusObject *dbus_object;
993 EDBusSource *dbus_source;
996 source = E_SERVER_SIDE_SOURCE (initable);
998 dbus_source = e_dbus_source_skeleton_new ();
1000 uid = e_source_dup_uid (E_SOURCE (source));
1003 e_dbus_source_set_uid (dbus_source, uid);
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);
1012 dbus_source, "handle-allow-auth-prompt",
1013 G_CALLBACK (server_side_source_allow_auth_prompt_cb), source);
1015 g_object_unref (dbus_source);
1017 if (!e_server_side_source_load (source, cancellable, error))
1020 /* Chain up to parent interface's init() method. */
1021 return initable_parent_interface->init (initable, cancellable, error);
1025 e_server_side_source_class_init (EServerSideSourceClass *class)
1027 GObjectClass *object_class;
1028 ESourceClass *source_class;
1030 g_type_class_add_private (class, sizeof (EServerSideSourcePrivate));
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;
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;
1049 g_object_class_install_property (
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",
1060 G_PARAM_STATIC_STRINGS));
1062 g_object_class_install_property (
1065 g_param_spec_boolean (
1068 "Whether the source has been exported over D-Bus",
1071 G_PARAM_STATIC_STRINGS));
1073 g_object_class_install_property (
1076 g_param_spec_object (
1079 "The key file for the data source",
1082 G_PARAM_CONSTRUCT_ONLY |
1083 G_PARAM_STATIC_STRINGS));
1085 /* This overrides the "remote-creatable" property
1086 * in ESourceClass with a writable version. */
1087 g_object_class_install_property (
1089 PROP_REMOTE_CREATABLE,
1090 g_param_spec_boolean (
1093 "Whether the data source "
1094 "can create remote resources",
1097 G_PARAM_STATIC_STRINGS));
1099 /* This overrides the "remote-deletable" property
1100 * in ESourceClass with a writable version. */
1101 g_object_class_install_property (
1103 PROP_REMOTE_DELETABLE,
1104 g_param_spec_boolean (
1107 "Whether the data source "
1108 "can delete remote resources",
1111 G_PARAM_STATIC_STRINGS));
1113 /* This overrides the "removable" property
1114 * in ESourceClass with a writable version. */
1115 g_object_class_install_property (
1118 g_param_spec_boolean (
1121 "Whether the data source is removable",
1124 G_PARAM_STATIC_STRINGS));
1126 g_object_class_install_property (
1129 g_param_spec_object (
1132 "The server to which the data source belongs",
1133 E_TYPE_SOURCE_REGISTRY_SERVER,
1135 G_PARAM_CONSTRUCT_ONLY |
1136 G_PARAM_STATIC_STRINGS));
1138 /* This overrides the "writable" property
1139 * in ESourceClass with a writable version. */
1140 g_object_class_install_property (
1143 g_param_spec_boolean (
1146 "Whether the data source is writable",
1149 G_PARAM_STATIC_STRINGS));
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 (
1155 PROP_WRITE_DIRECTORY,
1156 g_param_spec_string (
1159 "Directory in which to write changes to disk",
1162 G_PARAM_STATIC_STRINGS));
1166 e_server_side_source_initable_init (GInitableIface *interface)
1168 initable_parent_interface = g_type_interface_peek_parent (interface);
1170 interface->init = server_side_source_initable_init;
1174 e_server_side_source_init (EServerSideSource *source)
1176 const gchar *user_dir;
1178 source->priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
1180 source->priv->node.data = source;
1182 user_dir = e_server_side_source_get_user_dir ();
1183 source->priv->write_directory = g_strdup (user_dir);
1187 * e_server_side_source_get_user_dir:
1189 * Returns the directory where user-specific data source files are stored.
1191 * Returns: the user-specific data source directory
1196 e_server_side_source_get_user_dir (void)
1198 static gchar *dirname = NULL;
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);
1210 * e_server_side_source_new_user_file:
1211 * @uid: unique identifier for a data source, or %NULL
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().
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.
1220 * Note the data source file itself is not created here, only its name.
1222 * Returns: the #GFile for a new data source
1227 e_server_side_source_new_user_file (const gchar *uid)
1233 const gchar *user_dir;
1236 safe_uid = e_uid_new ();
1238 safe_uid = g_strdup (uid);
1239 e_filename_make_safe (safe_uid);
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);
1245 file = g_file_new_for_path (filename);
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
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.
1263 * Returns: the unique identity string for @file, or %NULL
1268 e_server_side_source_uid_from_file (GFile *file,
1274 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1276 basename = g_file_get_basename (file);
1278 if (g_str_has_suffix (basename, ".source")) {
1279 /* strlen(".source") --> 7 */
1280 uid = g_strndup (basename, strlen (basename) - 7);
1284 G_IO_ERROR_INVALID_FILENAME,
1285 _("File must have a '.source' extension"));
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
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.
1304 * Returns: a new #EServerSideSource, or %NULL
1309 e_server_side_source_new (ESourceRegistryServer *server,
1313 EDBusObjectSkeleton *dbus_object;
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);
1320 /* Extract a UID from the GFile, if we were given one. */
1322 uid = e_server_side_source_uid_from_file (file, error);
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);
1331 source = g_initable_new (
1332 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1333 "dbus-object", dbus_object,
1334 "file", file, "server", server,
1337 g_object_unref (dbus_object);
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
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.
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.
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.
1361 * Returns: a new memory-only #EServerSideSource, or %NULL
1366 e_server_side_source_new_memory_only (ESourceRegistryServer *server,
1370 EDBusObjectSkeleton *dbus_object;
1373 g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
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);
1379 source = g_initable_new (
1380 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1381 "dbus-object", dbus_object,
1382 "server", server, "uid", uid, NULL);
1384 g_object_unref (dbus_object);
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
1395 * Reloads data source content from the file pointed to by the
1396 * #EServerSideSource:file property.
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.
1401 * If a read error occurs or the file contains syntax errors, the function
1402 * sets @error and returns %FALSE.
1404 * Returns: %TRUE on success, %FALSE on failure
1409 e_server_side_source_load (EServerSideSource *source,
1410 GCancellable *cancellable,
1413 GDBusObject *dbus_object;
1414 EDBusSource *dbus_source;
1420 GError *local_error = NULL;
1422 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1424 file = e_server_side_source_get_file (source);
1427 g_file_load_contents (
1428 file, cancellable, &data,
1429 &length, NULL, &local_error);
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);
1435 } else if (local_error != NULL) {
1436 g_propagate_error (error, local_error);
1440 source->priv->file_contents = g_strdup (data);
1444 /* Create the bare minimum to pass parse_data(). */
1445 data = g_strdup_printf ("[%s]", PRIMARY_GROUP_NAME);
1446 length = strlen (data);
1449 key_file = g_key_file_new ();
1451 success = server_side_source_parse_data (
1452 key_file, data, length, error);
1454 g_key_file_free (key_file);
1461 /* Update the D-Bus interface properties. */
1463 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1464 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
1466 e_dbus_source_set_data (dbus_source, data);
1468 g_object_unref (dbus_source);
1469 g_object_unref (dbus_object);
1477 * e_server_side_source_get_file:
1478 * @source: an #EServerSideSource
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.
1483 * Returns: the #GFile for @source, or %NULL
1488 e_server_side_source_get_file (EServerSideSource *source)
1490 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1492 return source->priv->file;
1496 * e_server_side_source_get_node:
1497 * @source: an #EServerSideSource
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.
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.
1507 * Returns: a #GNode, or %NULL
1512 e_server_side_source_get_node (EServerSideSource *source)
1514 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1516 return &source->priv->node;
1520 * e_server_side_source_get_server:
1521 * @source: an #EServerSideSource
1523 * Returns the #ESourceRegistryServer to which @source belongs.
1525 * Returns: the #ESourceRegistryServer for @source
1529 ESourceRegistryServer *
1530 e_server_side_source_get_server (EServerSideSource *source)
1532 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1534 return source->priv->server;
1538 * e_server_side_source_get_allow_auth_prompt:
1539 * @source: an #EServerSideSource
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.
1545 * See e_server_side_source_set_allow_auth_prompt() for further
1548 * Returns: whether auth prompts are allowed for @source
1553 e_server_side_source_get_allow_auth_prompt (EServerSideSource *source)
1555 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1557 return source->priv->allow_auth_prompt;
1561 * e_server_side_source_set_allow_auth_prompt:
1562 * @source: an #EServerSideSource
1563 * @allow_auth_prompt: whether auth prompts are allowed for @source
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.
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.
1580 e_server_side_source_set_allow_auth_prompt (EServerSideSource *source,
1581 gboolean allow_auth_prompt)
1583 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1585 if ((source->priv->allow_auth_prompt ? 1 : 0) == (allow_auth_prompt ? 1 : 0))
1588 source->priv->allow_auth_prompt = allow_auth_prompt;
1590 g_object_notify (G_OBJECT (source), "allow-auth-prompt");
1594 * e_server_side_source_get_exported:
1595 * @source: an #EServerSideSource
1597 * Returns whether @source has been exported over D-Bus.
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().
1604 * Returns: whether @source has been exported
1609 e_server_side_source_get_exported (EServerSideSource *source)
1611 ESourceRegistryServer *server;
1612 ESource *exported_source;
1613 gboolean exported = FALSE;
1616 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1618 uid = e_source_get_uid (E_SOURCE (source));
1619 server = e_server_side_source_get_server (source);
1621 /* We're exported if we can look ourselves up in the registry. */
1623 exported_source = e_source_registry_server_ref_source (server, uid);
1624 if (exported_source != NULL) {
1626 g_object_unref (exported_source);
1633 * e_server_side_source_get_write_directory:
1634 * @source: an #EServerSideSource
1636 * Returns the local directory path where changes to @source are written.
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.
1643 * Returns: the directory where changes are written
1648 e_server_side_source_get_write_directory (EServerSideSource *source)
1650 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1652 return source->priv->write_directory;
1656 * e_server_side_source_set_write_directory:
1657 * @source: an #EServerSideSource
1658 * @write_directory: the directory where changes are to be written
1660 * Sets the local directory path where changes to @source are to be written.
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.
1670 e_server_side_source_set_write_directory (EServerSideSource *source,
1671 const gchar *write_directory)
1673 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1674 g_return_if_fail (write_directory != NULL);
1676 if (g_strcmp0 (source->priv->write_directory, write_directory) == 0)
1679 g_free (source->priv->write_directory);
1680 source->priv->write_directory = g_strdup (write_directory);
1682 g_object_notify (G_OBJECT (source), "write-directory");
1686 * e_server_side_source_set_removable:
1687 * @source: an #EServerSideSource
1688 * @removable: whether to export the Removable interface
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.
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.
1702 e_server_side_source_set_removable (EServerSideSource *source,
1705 EDBusSourceRemovable *dbus_interface = NULL;
1706 GDBusObject *dbus_object;
1707 gboolean currently_removable;
1709 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1711 currently_removable = e_source_get_removable (E_SOURCE (source));
1713 if (removable == currently_removable)
1718 e_dbus_source_removable_skeleton_new ();
1721 dbus_interface, "handle-remove",
1722 G_CALLBACK (server_side_source_remove_cb), source);
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);
1730 if (dbus_interface != NULL)
1731 g_object_unref (dbus_interface);
1733 g_object_notify (G_OBJECT (source), "removable");
1737 * e_server_side_source_set_writable:
1738 * @source: an #EServerSideSource
1739 * @writable: whether to export the Writable interface
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.
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.
1753 e_server_side_source_set_writable (EServerSideSource *source,
1756 EDBusSourceWritable *dbus_interface = NULL;
1757 GDBusObject *dbus_object;
1758 gboolean currently_writable;
1760 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1762 currently_writable = e_source_get_writable (E_SOURCE (source));
1764 if (writable == currently_writable)
1769 e_dbus_source_writable_skeleton_new ();
1772 dbus_interface, "handle-write",
1773 G_CALLBACK (server_side_source_write_cb), source);
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);
1781 if (dbus_interface != NULL)
1782 g_object_unref (dbus_interface);
1784 g_object_notify (G_OBJECT (source), "writable");
1788 * e_server_side_source_set_remote_creatable:
1789 * @source: an #EServerSideSource
1790 * @remote_creatable: whether to export the RemoteCreatable interface
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.
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.
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.
1807 e_server_side_source_set_remote_creatable (EServerSideSource *source,
1808 gboolean remote_creatable)
1810 EDBusSourceRemoteCreatable *dbus_interface = NULL;
1811 GDBusObject *dbus_object;
1812 gboolean currently_remote_creatable;
1814 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1816 currently_remote_creatable =
1817 e_source_get_remote_creatable (E_SOURCE (source));
1819 if (remote_creatable == currently_remote_creatable)
1822 if (remote_creatable) {
1824 e_dbus_source_remote_creatable_skeleton_new ();
1827 dbus_interface, "handle-create",
1828 G_CALLBACK (server_side_source_remote_create_cb),
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);
1837 if (dbus_interface != NULL)
1838 g_object_unref (dbus_interface);
1840 g_object_notify (G_OBJECT (source), "remote-creatable");
1844 * e_server_side_source_set_remote_deletable:
1845 * @source: an #EServerSideSource
1846 * @remote_deletable: whether to export the RemoteDeletable interface
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.
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.
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.
1864 e_server_side_source_set_remote_deletable (EServerSideSource *source,
1865 gboolean remote_deletable)
1867 EDBusSourceRemoteDeletable *dbus_interface = NULL;
1868 GDBusObject *dbus_object;
1869 gboolean currently_remote_deletable;
1871 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1873 currently_remote_deletable =
1874 e_source_get_remote_deletable (E_SOURCE (source));
1876 if (remote_deletable == currently_remote_deletable)
1879 if (remote_deletable) {
1881 e_dbus_source_remote_deletable_skeleton_new ();
1884 dbus_interface, "handle-delete",
1885 G_CALLBACK (server_side_source_remote_delete_cb),
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);
1894 if (dbus_interface != NULL)
1895 g_object_unref (dbus_interface);
1897 g_object_notify (G_OBJECT (source), "remote-deletable");