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 */
56 gboolean allow_auth_prompt;
57 gchar *write_directory;
60 struct _AsyncContext {
61 EDBusSourceRemoteCreatable *remote_creatable;
62 EDBusSourceRemoteDeletable *remote_deletable;
63 GDBusMethodInvocation *invocation;
68 PROP_ALLOW_AUTH_PROMPT,
71 PROP_REMOTE_CREATABLE,
72 PROP_REMOTE_DELETABLE,
80 static GInitableIface *initable_parent_interface;
82 /* Forward Declarations */
83 static void e_server_side_source_initable_init
84 (GInitableIface *interface);
86 G_DEFINE_TYPE_WITH_CODE (
90 G_IMPLEMENT_INTERFACE (
92 e_server_side_source_initable_init))
95 async_context_free (AsyncContext *async_context)
97 if (async_context->remote_creatable != NULL)
98 g_object_unref (async_context->remote_creatable);
100 if (async_context->remote_deletable != NULL)
101 g_object_unref (async_context->remote_deletable);
103 if (async_context->invocation != NULL)
104 g_object_unref (async_context->invocation);
106 g_slice_free (AsyncContext, async_context);
110 server_side_source_parse_data (GKeyFile *key_file,
117 success = g_key_file_load_from_data (
118 key_file, data, length, G_KEY_FILE_NONE, error);
123 /* Make sure the key file has a [Data Source] group. */
124 if (!g_key_file_has_group (key_file, PRIMARY_GROUP_NAME)) {
126 error, G_KEY_FILE_ERROR,
127 G_KEY_FILE_ERROR_GROUP_NOT_FOUND,
128 _("Data source is missing a [%s] group"),
137 server_side_source_print_diff (ESource *source,
138 const gchar *old_data,
139 const gchar *new_data)
141 gchar **old_strv = NULL;
142 gchar **new_strv = NULL;
143 guint old_length = 0;
144 guint new_length = 0;
147 g_print ("Saving %s\n", e_source_get_uid (source));
149 if (old_data != NULL) {
150 old_strv = g_strsplit (old_data, "\n", 0);
151 old_length = g_strv_length (old_strv);
154 if (new_data != NULL) {
155 new_strv = g_strsplit (new_data, "\n", 0);
156 new_length = g_strv_length (new_strv);
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]);
164 g_print (" : %s\n", old_strv[ii]);
168 for (; ii < old_length; ii++)
169 g_print (" - : %s\n", old_strv[ii]);
171 for (; ii < new_length; ii++)
172 g_print (" + : %s\n", new_strv[ii]);
174 g_strfreev (old_strv);
175 g_strfreev (new_strv);
179 server_side_source_traverse_cb (GNode *node,
182 g_queue_push_tail (queue, g_object_ref (node->data));
188 server_side_source_allow_auth_prompt_cb (EDBusSource *interface,
189 GDBusMethodInvocation *invocation,
190 EServerSideSource *source)
192 e_server_side_source_set_allow_auth_prompt (source, TRUE);
194 e_dbus_source_complete_allow_auth_prompt (interface, invocation);
200 server_side_source_remove_cb (EDBusSourceRemovable *interface,
201 GDBusMethodInvocation *invocation,
202 EServerSideSource *source)
204 GError *error = NULL;
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. */
211 e_source_remove_sync (E_SOURCE (source), NULL, &error);
214 g_dbus_method_invocation_take_error (invocation, error);
216 e_dbus_source_removable_complete_remove (
217 interface, invocation);
223 server_side_source_write_cb (EDBusSourceWritable *interface,
224 GDBusMethodInvocation *invocation,
229 GDBusObject *dbus_object;
230 EDBusSource *dbus_source;
231 GError *error = NULL;
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. */
236 dbus_object = e_source_ref_dbus_object (source);
237 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
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);
244 /* Q: How does this trigger data being written to disk?
246 * A: Here's the sequence of events:
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.
258 e_dbus_source_set_data (dbus_source, data);
261 g_dbus_method_invocation_take_error (invocation, error);
263 e_dbus_source_writable_complete_write (
264 interface, invocation);
266 g_object_unref (dbus_source);
267 g_object_unref (dbus_object);
272 /* Helper for server_side_source_remote_create_cb() */
274 server_side_source_remote_create_done_cb (GObject *source_object,
275 GAsyncResult *result,
279 AsyncContext *async_context;
280 GError *error = NULL;
282 source = E_SOURCE (source_object);
283 async_context = (AsyncContext *) user_data;
285 e_source_remote_create_finish (source, result, &error);
288 g_dbus_method_invocation_take_error (
289 async_context->invocation, error);
291 e_dbus_source_remote_creatable_complete_create (
292 async_context->remote_creatable,
293 async_context->invocation);
295 async_context_free (async_context);
299 server_side_source_remote_create_cb (EDBusSourceRemoteCreatable *interface,
300 GDBusMethodInvocation *invocation,
305 EServerSideSource *server_side_source;
306 ESourceRegistryServer *server;
307 AsyncContext *async_context;
308 ESource *scratch_source;
309 GDBusObject *dbus_object;
310 EDBusSource *dbus_source;
313 GError *error = NULL;
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. */
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);
327 g_dbus_method_invocation_take_error (invocation, error);
331 server_side_source = E_SERVER_SIDE_SOURCE (source);
332 server = e_server_side_source_get_server (server_side_source);
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);
340 ((scratch_source != NULL) && (error == NULL)) ||
341 ((scratch_source == NULL) && (error != NULL)));
344 g_dbus_method_invocation_take_error (invocation, error);
348 dbus_object = e_source_ref_dbus_object (scratch_source);
349 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
351 e_dbus_source_set_data (dbus_source, data);
353 g_object_unref (dbus_object);
354 g_object_unref (dbus_source);
356 async_context = g_slice_new0 (AsyncContext);
357 async_context->remote_creatable = g_object_ref (interface);
358 async_context->invocation = g_object_ref (invocation);
360 e_source_remote_create (
361 source, scratch_source, NULL,
362 server_side_source_remote_create_done_cb,
365 g_object_unref (scratch_source);
370 /* Helper for server_side_source_remote_delete_cb() */
372 server_side_source_remote_delete_done_cb (GObject *source_object,
373 GAsyncResult *result,
377 AsyncContext *async_context;
378 GError *error = NULL;
380 source = E_SOURCE (source_object);
381 async_context = (AsyncContext *) user_data;
383 e_source_remote_delete_finish (source, result, &error);
386 g_dbus_method_invocation_take_error (
387 async_context->invocation, error);
389 e_dbus_source_remote_deletable_complete_delete (
390 async_context->remote_deletable,
391 async_context->invocation);
393 async_context_free (async_context);
397 server_side_source_remote_delete_cb (EDBusSourceRemoteDeletable *interface,
398 GDBusMethodInvocation *invocation,
401 AsyncContext *async_context;
403 async_context = g_slice_new0 (AsyncContext);
404 async_context->remote_deletable = g_object_ref (interface);
405 async_context->invocation = g_object_ref (invocation);
407 e_source_remote_delete (
409 server_side_source_remote_delete_done_cb,
416 server_side_source_set_file (EServerSideSource *source,
419 g_return_if_fail (file == NULL || G_IS_FILE (file));
420 g_return_if_fail (source->priv->file == NULL);
423 source->priv->file = g_object_ref (file);
427 server_side_source_set_server (EServerSideSource *source,
428 ESourceRegistryServer *server)
430 g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
431 g_return_if_fail (source->priv->server == NULL);
433 source->priv->server = server;
435 g_object_add_weak_pointer (
436 G_OBJECT (server), &source->priv->server);
440 server_side_source_set_uid (EServerSideSource *source,
443 g_return_if_fail (source->priv->uid == NULL);
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);
453 server_side_source_set_property (GObject *object,
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));
466 server_side_source_set_file (
467 E_SERVER_SIDE_SOURCE (object),
468 g_value_get_object (value));
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));
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));
484 e_server_side_source_set_removable (
485 E_SERVER_SIDE_SOURCE (object),
486 g_value_get_boolean (value));
490 server_side_source_set_server (
491 E_SERVER_SIDE_SOURCE (object),
492 g_value_get_object (value));
496 server_side_source_set_uid (
497 E_SERVER_SIDE_SOURCE (object),
498 g_value_get_string (value));
502 e_server_side_source_set_writable (
503 E_SERVER_SIDE_SOURCE (object),
504 g_value_get_boolean (value));
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));
514 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
518 server_side_source_get_property (GObject *object,
523 switch (property_id) {
524 case PROP_ALLOW_AUTH_PROMPT:
525 g_value_set_boolean (
527 e_server_side_source_get_allow_auth_prompt (
528 E_SERVER_SIDE_SOURCE (object)));
532 g_value_set_boolean (
534 e_server_side_source_get_exported (
535 E_SERVER_SIDE_SOURCE (object)));
541 e_server_side_source_get_file (
542 E_SERVER_SIDE_SOURCE (object)));
545 case PROP_REMOTE_CREATABLE:
546 g_value_set_boolean (
548 e_source_get_remote_creatable (
552 case PROP_REMOTE_DELETABLE:
553 g_value_set_boolean (
555 e_source_get_remote_deletable (
560 g_value_set_boolean (
562 e_source_get_removable (
569 e_server_side_source_get_server (
570 E_SERVER_SIDE_SOURCE (object)));
574 g_value_take_string (
581 g_value_set_boolean (
583 e_source_get_writable (
587 case PROP_WRITE_DIRECTORY:
590 e_server_side_source_get_write_directory (
591 E_SERVER_SIDE_SOURCE (object)));
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
599 server_side_source_dispose (GObject *object)
601 EServerSideSourcePrivate *priv;
603 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
605 if (priv->server != NULL) {
606 g_object_remove_weak_pointer (
607 G_OBJECT (priv->server), &priv->server);
611 if (priv->file != NULL) {
612 g_object_unref (priv->file);
616 /* Chain up to parent's dispose() method. */
617 G_OBJECT_CLASS (e_server_side_source_parent_class)->dispose (object);
621 server_side_source_finalize (GObject *object)
623 EServerSideSourcePrivate *priv;
625 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
627 g_node_unlink (&priv->node);
630 g_free (priv->file_contents);
631 g_free (priv->write_directory);
633 /* Chain up to parent's finalize() method. */
634 G_OBJECT_CLASS (e_server_side_source_parent_class)->finalize (object);
638 server_side_source_changed (ESource *source)
640 GDBusObject *dbus_object;
641 EDBusSource *dbus_source;
644 GError *error = NULL;
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)))
650 dbus_object = e_source_ref_dbus_object (source);
651 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
653 old_data = e_dbus_source_dup_data (dbus_source);
654 new_data = e_source_to_string (source, NULL);
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);
665 g_object_unref (dbus_source);
666 g_object_unref (dbus_object);
668 /* This writes the "data" property to disk. */
669 e_source_write_sync (source, NULL, &error);
672 g_warning ("%s: %s", G_STRFUNC, error->message);
673 g_error_free (error);
678 server_side_source_remove_sync (ESource *source,
679 GCancellable *cancellable,
682 EAsyncClosure *closure;
683 GAsyncResult *result;
686 closure = e_async_closure_new ();
689 source, cancellable, e_async_closure_callback, closure);
691 result = e_async_closure_wait (closure);
693 success = e_source_remove_finish (source, result, error);
695 e_async_closure_free (closure);
701 server_side_source_remove (ESource *source,
702 GCancellable *cancellable,
703 GAsyncReadyCallback callback,
706 EServerSideSourcePrivate *priv;
707 GSimpleAsyncResult *simple;
708 ESourceRegistryServer *server;
709 GQueue queue = G_QUEUE_INIT;
711 GError *error = NULL;
713 /* XXX Yes we block here. We do this operation
714 * synchronously to keep the server code simple. */
716 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
718 simple = g_simple_async_result_new (
719 G_OBJECT (source), callback, user_data,
720 server_side_source_remove);
722 g_simple_async_result_set_check_cancellable (simple, cancellable);
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. */
728 &priv->node, G_POST_ORDER, G_TRAVERSE_ALL, -1,
729 (GNodeTraverseFunc) server_side_source_traverse_cb, &queue);
731 /* Unexport the object and its descendants. */
732 server = E_SOURCE_REGISTRY_SERVER (priv->server);
733 e_source_registry_server_remove_source (server, source);
735 list = g_queue_peek_head_link (&queue);
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;
742 child = E_SERVER_SIDE_SOURCE (link->data);
743 file = e_server_side_source_get_file (child);
746 g_file_delete (file, cancellable, &error);
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.
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);
765 while (!g_queue_is_empty (&queue))
766 g_object_unref (g_queue_pop_head (&queue));
769 g_simple_async_result_take_error (simple, error);
771 g_simple_async_result_complete_in_idle (simple);
772 g_object_unref (simple);
776 server_side_source_remove_finish (ESource *source,
777 GAsyncResult *result,
780 GSimpleAsyncResult *simple;
782 g_return_val_if_fail (
783 g_simple_async_result_is_valid (
784 result, G_OBJECT (source),
785 server_side_source_remove), FALSE);
787 simple = G_SIMPLE_ASYNC_RESULT (result);
789 /* Assume success unless a GError is set. */
790 return !g_simple_async_result_propagate_error (simple, error);
794 server_side_source_write_sync (ESource *source,
795 GCancellable *cancellable,
798 EAsyncClosure *closure;
799 GAsyncResult *result;
802 closure = e_async_closure_new ();
805 source, cancellable, e_async_closure_callback, closure);
807 result = e_async_closure_wait (closure);
809 success = e_source_write_finish (source, result, error);
811 e_async_closure_free (closure);
817 server_side_source_write (ESource *source,
818 GCancellable *cancellable,
819 GAsyncReadyCallback callback,
822 EServerSideSourcePrivate *priv;
823 GSimpleAsyncResult *simple;
824 GDBusObject *dbus_object;
825 EDBusSource *dbus_source;
826 gboolean replace_file;
827 const gchar *old_data;
829 GError *error = NULL;
831 /* XXX Yes we block here. We do this operation
832 * synchronously to keep the server code simple. */
834 priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
836 simple = g_simple_async_result_new (
837 G_OBJECT (source), callback, user_data,
838 server_side_source_write);
840 g_simple_async_result_set_check_cancellable (simple, cancellable);
842 dbus_object = e_source_ref_dbus_object (source);
843 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
845 old_data = priv->file_contents;
846 new_data = e_source_to_string (source, NULL);
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. */
856 G_IS_FILE (priv->file) &&
857 (g_strcmp0 (old_data, new_data) != 0);
864 g_warn_if_fail (priv->write_directory != NULL);
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);
873 if (!g_file_equal (file, priv->file)) {
874 g_object_unref (priv->file);
875 priv->file = g_object_ref (file);
878 server_side_source_print_diff (source, old_data, new_data);
880 g_file_replace_contents (
881 file, new_data, strlen (new_data), NULL, FALSE,
882 G_FILE_CREATE_NONE, NULL, cancellable, &error);
885 g_free (priv->file_contents);
886 priv->file_contents = new_data;
890 g_object_unref (file);
895 g_object_unref (dbus_source);
896 g_object_unref (dbus_object);
899 g_simple_async_result_take_error (simple, error);
901 g_simple_async_result_complete_in_idle (simple);
902 g_object_unref (simple);
906 server_side_source_write_finish (ESource *source,
907 GAsyncResult *result,
910 GSimpleAsyncResult *simple;
912 g_return_val_if_fail (
913 g_simple_async_result_is_valid (
914 result, G_OBJECT (source),
915 server_side_source_write), FALSE);
917 simple = G_SIMPLE_ASYNC_RESULT (result);
919 /* Assume success unless a GError is set. */
920 return !g_simple_async_result_propagate_error (simple, error);
924 server_side_source_remote_create_sync (ESource *source,
925 ESource *scratch_source,
926 GCancellable *cancellable,
929 ECollectionBackend *backend;
930 ESourceRegistryServer *server;
931 EServerSideSource *server_side_source;
934 if (!e_source_get_remote_creatable (source)) {
937 G_IO_ERROR_NOT_SUPPORTED,
938 _("Data source '%s' does not "
939 "support creating remote resources"),
940 e_source_get_display_name (source));
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);
948 if (backend == NULL) {
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));
958 success = e_collection_backend_create_resource_sync (
959 backend, scratch_source, cancellable, error);
961 g_object_unref (backend);
967 server_side_source_remote_delete_sync (ESource *source,
968 GCancellable *cancellable,
971 ECollectionBackend *backend;
972 ESourceRegistryServer *server;
973 EServerSideSource *server_side_source;
976 if (!e_source_get_remote_deletable (source)) {
979 G_IO_ERROR_NOT_SUPPORTED,
980 _("Data source '%s' does not "
981 "support deleting remote resources"),
982 e_source_get_display_name (source));
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);
990 if (backend == NULL) {
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));
1000 success = e_collection_backend_delete_resource_sync (
1001 backend, source, cancellable, error);
1003 g_object_unref (backend);
1009 server_side_source_initable_init (GInitable *initable,
1010 GCancellable *cancellable,
1013 EServerSideSource *source;
1014 GDBusObject *dbus_object;
1015 EDBusSource *dbus_source;
1018 source = E_SERVER_SIDE_SOURCE (initable);
1019 file = e_server_side_source_get_file (source);
1022 g_warn_if_fail (source->priv->uid == NULL);
1024 e_server_side_source_uid_from_file (file, error);
1025 if (source->priv->uid == NULL)
1029 if (source->priv->uid == NULL)
1030 source->priv->uid = e_uid_new ();
1032 dbus_source = e_dbus_source_skeleton_new ();
1034 e_dbus_source_set_uid (dbus_source, source->priv->uid);
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);
1042 dbus_source, "handle-allow-auth-prompt",
1043 G_CALLBACK (server_side_source_allow_auth_prompt_cb), source);
1045 g_object_unref (dbus_source);
1047 if (!e_server_side_source_load (source, cancellable, error))
1050 /* Chain up to parent interface's init() method. */
1051 return initable_parent_interface->init (initable, cancellable, error);
1055 e_server_side_source_class_init (EServerSideSourceClass *class)
1057 GObjectClass *object_class;
1058 ESourceClass *source_class;
1060 g_type_class_add_private (class, sizeof (EServerSideSourcePrivate));
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;
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;
1079 g_object_class_install_property (
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",
1090 G_PARAM_STATIC_STRINGS));
1092 g_object_class_install_property (
1095 g_param_spec_boolean (
1098 "Whether the source has been exported over D-Bus",
1101 G_PARAM_STATIC_STRINGS));
1103 g_object_class_install_property (
1106 g_param_spec_object (
1109 "The key file for the data source",
1112 G_PARAM_CONSTRUCT_ONLY |
1113 G_PARAM_STATIC_STRINGS));
1115 /* This overrides the "remote-creatable" property
1116 * in ESourceClass with a writable version. */
1117 g_object_class_install_property (
1119 PROP_REMOTE_CREATABLE,
1120 g_param_spec_boolean (
1123 "Whether the data source "
1124 "can create remote resources",
1127 G_PARAM_STATIC_STRINGS));
1129 /* This overrides the "remote-deletable" property
1130 * in ESourceClass with a writable version. */
1131 g_object_class_install_property (
1133 PROP_REMOTE_DELETABLE,
1134 g_param_spec_boolean (
1137 "Whether the data source "
1138 "can delete remote resources",
1141 G_PARAM_STATIC_STRINGS));
1143 /* This overrides the "removable" property
1144 * in ESourceClass with a writable version. */
1145 g_object_class_install_property (
1148 g_param_spec_boolean (
1151 "Whether the data source is removable",
1154 G_PARAM_STATIC_STRINGS));
1156 g_object_class_install_property (
1159 g_param_spec_object (
1162 "The server to which the data source belongs",
1163 E_TYPE_SOURCE_REGISTRY_SERVER,
1165 G_PARAM_CONSTRUCT_ONLY |
1166 G_PARAM_STATIC_STRINGS));
1168 /* This overrides the "uid" property in
1169 * ESourceClass with a construct-only version. */
1170 g_object_class_install_property (
1173 g_param_spec_string (
1176 "The unique identity of the data source",
1179 G_PARAM_CONSTRUCT_ONLY |
1180 G_PARAM_STATIC_STRINGS));
1182 /* This overrides the "writable" property
1183 * in ESourceClass with a writable version. */
1184 g_object_class_install_property (
1187 g_param_spec_boolean (
1190 "Whether the data source is writable",
1193 G_PARAM_STATIC_STRINGS));
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 (
1199 PROP_WRITE_DIRECTORY,
1200 g_param_spec_string (
1203 "Directory in which to write changes to disk",
1206 G_PARAM_STATIC_STRINGS));
1210 e_server_side_source_initable_init (GInitableIface *interface)
1212 initable_parent_interface = g_type_interface_peek_parent (interface);
1214 interface->init = server_side_source_initable_init;
1218 e_server_side_source_init (EServerSideSource *source)
1220 const gchar *user_dir;
1222 source->priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (source);
1224 source->priv->node.data = source;
1226 user_dir = e_server_side_source_get_user_dir ();
1227 source->priv->write_directory = g_strdup (user_dir);
1231 * e_server_side_source_get_user_dir:
1233 * Returns the directory where user-specific data source files are stored.
1235 * Returns: the user-specific data source directory
1240 e_server_side_source_get_user_dir (void)
1242 static gchar *dirname = NULL;
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);
1254 * e_server_side_source_new_user_file:
1255 * @uid: unique identifier for a data source, or %NULL
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().
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.
1264 * Note the data source file itself is not created here, only its name.
1266 * Returns: the #GFile for a new data source
1271 e_server_side_source_new_user_file (const gchar *uid)
1277 const gchar *user_dir;
1280 safe_uid = e_uid_new ();
1282 safe_uid = g_strdup (uid);
1283 e_filename_make_safe (safe_uid);
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);
1289 file = g_file_new_for_path (filename);
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
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.
1307 * Returns: the unique identity string for @file, or %NULL
1312 e_server_side_source_uid_from_file (GFile *file,
1318 g_return_val_if_fail (G_IS_FILE (file), FALSE);
1320 basename = g_file_get_basename (file);
1322 if (g_str_has_suffix (basename, ".source")) {
1323 /* strlen(".source") --> 7 */
1324 uid = g_strndup (basename, strlen (basename) - 7);
1328 G_IO_ERROR_INVALID_FILENAME,
1329 _("File must have a '.source' extension"));
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
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.
1348 * Returns: a new #EServerSideSource, or %NULL
1353 e_server_side_source_new (ESourceRegistryServer *server,
1357 EDBusObjectSkeleton *dbus_object;
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);
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);
1367 source = g_initable_new (
1368 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1369 "dbus-object", dbus_object,
1370 "file", file, "server", server, NULL);
1372 g_object_unref (dbus_object);
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
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.
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.
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.
1396 * Returns: a new memory-only #EServerSideSource, or %NULL
1401 e_server_side_source_new_memory_only (ESourceRegistryServer *server,
1405 EDBusObjectSkeleton *dbus_object;
1408 g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
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);
1414 source = g_initable_new (
1415 E_TYPE_SERVER_SIDE_SOURCE, NULL, error,
1416 "dbus-object", dbus_object,
1417 "server", server, "uid", uid, NULL);
1419 g_object_unref (dbus_object);
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
1430 * Reloads data source content from the file pointed to by the
1431 * #EServerSideSource:file property.
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.
1436 * If a read error occurs or the file contains syntax errors, the function
1437 * sets @error and returns %FALSE.
1439 * Returns: %TRUE on success, %FALSE on failure
1444 e_server_side_source_load (EServerSideSource *source,
1445 GCancellable *cancellable,
1448 GDBusObject *dbus_object;
1449 EDBusSource *dbus_source;
1455 GError *local_error = NULL;
1457 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1459 file = e_server_side_source_get_file (source);
1462 g_file_load_contents (
1463 file, cancellable, &data,
1464 &length, NULL, &local_error);
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);
1470 } else if (local_error != NULL) {
1471 g_propagate_error (error, local_error);
1475 source->priv->file_contents = g_strdup (data);
1479 /* Create the bare minimum to pass parse_data(). */
1480 data = g_strdup_printf ("[%s]", PRIMARY_GROUP_NAME);
1481 length = strlen (data);
1484 key_file = g_key_file_new ();
1486 success = server_side_source_parse_data (
1487 key_file, data, length, error);
1489 g_key_file_free (key_file);
1496 /* Update the D-Bus interface properties. */
1498 dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
1499 dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
1501 e_dbus_source_set_data (dbus_source, data);
1503 g_object_unref (dbus_source);
1504 g_object_unref (dbus_object);
1512 * e_server_side_source_get_file:
1513 * @source: an #EServerSideSource
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.
1518 * Returns: the #GFile for @source, or %NULL
1523 e_server_side_source_get_file (EServerSideSource *source)
1525 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1527 return source->priv->file;
1531 * e_server_side_source_get_node:
1532 * @source: an #EServerSideSource
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.
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.
1542 * Returns: a #GNode, or %NULL
1547 e_server_side_source_get_node (EServerSideSource *source)
1549 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1551 return &source->priv->node;
1555 * e_server_side_source_get_server:
1556 * @source: an #EServerSideSource
1558 * Returns the #ESourceRegistryServer to which @source belongs.
1560 * Returns: the #ESourceRegistryServer for @source
1564 ESourceRegistryServer *
1565 e_server_side_source_get_server (EServerSideSource *source)
1567 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1569 return source->priv->server;
1573 * e_server_side_source_get_allow_auth_prompt:
1574 * @source: an #EServerSideSource
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.
1580 * See e_server_side_source_set_allow_auth_prompt() for further
1583 * Returns: whether auth prompts are allowed for @source
1588 e_server_side_source_get_allow_auth_prompt (EServerSideSource *source)
1590 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1592 return source->priv->allow_auth_prompt;
1596 * e_server_side_source_set_allow_auth_prompt:
1597 * @source: an #EServerSideSource
1598 * @allow_auth_prompt: whether auth prompts are allowed for @source
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.
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.
1615 e_server_side_source_set_allow_auth_prompt (EServerSideSource *source,
1616 gboolean allow_auth_prompt)
1618 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1620 if ((source->priv->allow_auth_prompt ? 1 : 0) == (allow_auth_prompt ? 1 : 0))
1623 source->priv->allow_auth_prompt = allow_auth_prompt;
1625 g_object_notify (G_OBJECT (source), "allow-auth-prompt");
1629 * e_server_side_source_get_exported:
1630 * @source: an #EServerSideSource
1632 * Returns whether @source has been exported over D-Bus.
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().
1639 * Returns: whether @source has been exported
1644 e_server_side_source_get_exported (EServerSideSource *source)
1646 ESourceRegistryServer *server;
1647 ESource *exported_source;
1648 gboolean exported = FALSE;
1651 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
1653 uid = e_source_get_uid (E_SOURCE (source));
1654 server = e_server_side_source_get_server (source);
1656 /* We're exported if we can look ourselves up in the registry. */
1658 exported_source = e_source_registry_server_ref_source (server, uid);
1659 if (exported_source != NULL) {
1661 g_object_unref (exported_source);
1668 * e_server_side_source_get_write_directory:
1669 * @source: an #EServerSideSource
1671 * Returns the local directory path where changes to @source are written.
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.
1678 * Returns: the directory where changes are written
1683 e_server_side_source_get_write_directory (EServerSideSource *source)
1685 g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), NULL);
1687 return source->priv->write_directory;
1691 * e_server_side_source_set_write_directory:
1692 * @source: an #EServerSideSource
1693 * @write_directory: the directory where changes are to be written
1695 * Sets the local directory path where changes to @source are to be written.
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.
1705 e_server_side_source_set_write_directory (EServerSideSource *source,
1706 const gchar *write_directory)
1708 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1709 g_return_if_fail (write_directory != NULL);
1711 if (g_strcmp0 (source->priv->write_directory, write_directory) == 0)
1714 g_free (source->priv->write_directory);
1715 source->priv->write_directory = g_strdup (write_directory);
1717 g_object_notify (G_OBJECT (source), "write-directory");
1721 * e_server_side_source_set_removable:
1722 * @source: an #EServerSideSource
1723 * @removable: whether to export the Removable interface
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.
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.
1737 e_server_side_source_set_removable (EServerSideSource *source,
1740 EDBusSourceRemovable *dbus_interface = NULL;
1741 GDBusObject *dbus_object;
1742 gboolean currently_removable;
1744 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1746 currently_removable = e_source_get_removable (E_SOURCE (source));
1748 if (removable == currently_removable)
1753 e_dbus_source_removable_skeleton_new ();
1756 dbus_interface, "handle-remove",
1757 G_CALLBACK (server_side_source_remove_cb), source);
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);
1765 if (dbus_interface != NULL)
1766 g_object_unref (dbus_interface);
1768 g_object_notify (G_OBJECT (source), "removable");
1772 * e_server_side_source_set_writable:
1773 * @source: an #EServerSideSource
1774 * @writable: whether to export the Writable interface
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.
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.
1788 e_server_side_source_set_writable (EServerSideSource *source,
1791 EDBusSourceWritable *dbus_interface = NULL;
1792 GDBusObject *dbus_object;
1793 gboolean currently_writable;
1795 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1797 currently_writable = e_source_get_writable (E_SOURCE (source));
1799 if (writable == currently_writable)
1804 e_dbus_source_writable_skeleton_new ();
1807 dbus_interface, "handle-write",
1808 G_CALLBACK (server_side_source_write_cb), source);
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);
1816 if (dbus_interface != NULL)
1817 g_object_unref (dbus_interface);
1819 g_object_notify (G_OBJECT (source), "writable");
1823 * e_server_side_source_set_remote_creatable:
1824 * @source: an #EServerSideSource
1825 * @remote_creatable: whether to export the RemoteCreatable interface
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.
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.
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.
1842 e_server_side_source_set_remote_creatable (EServerSideSource *source,
1843 gboolean remote_creatable)
1845 EDBusSourceRemoteCreatable *dbus_interface = NULL;
1846 GDBusObject *dbus_object;
1847 gboolean currently_remote_creatable;
1849 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1851 currently_remote_creatable =
1852 e_source_get_remote_creatable (E_SOURCE (source));
1854 if (remote_creatable == currently_remote_creatable)
1857 if (remote_creatable) {
1859 e_dbus_source_remote_creatable_skeleton_new ();
1862 dbus_interface, "handle-create",
1863 G_CALLBACK (server_side_source_remote_create_cb),
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);
1872 if (dbus_interface != NULL)
1873 g_object_unref (dbus_interface);
1875 g_object_notify (G_OBJECT (source), "remote-creatable");
1879 * e_server_side_source_set_remote_deletable:
1880 * @source: an #EServerSideSource
1881 * @remote_deletable: whether to export the RemoteDeletable interface
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.
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.
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.
1899 e_server_side_source_set_remote_deletable (EServerSideSource *source,
1900 gboolean remote_deletable)
1902 EDBusSourceRemoteDeletable *dbus_interface = NULL;
1903 GDBusObject *dbus_object;
1904 gboolean currently_remote_deletable;
1906 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
1908 currently_remote_deletable =
1909 e_source_get_remote_deletable (E_SOURCE (source));
1911 if (remote_deletable == currently_remote_deletable)
1914 if (remote_deletable) {
1916 e_dbus_source_remote_deletable_skeleton_new ();
1919 dbus_interface, "handle-delete",
1920 G_CALLBACK (server_side_source_remote_delete_cb),
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);
1929 if (dbus_interface != NULL)
1930 g_object_unref (dbus_interface);
1932 g_object_notify (G_OBJECT (source), "remote-deletable");