2 * e-collection-backend.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-collection-backend
21 * @include: libebackend/libebackend.h
22 * @short_description: A base class for a data source collection backend
24 * #ECollectionBackend is a base class for backends which manage a
25 * collection of data sources that collectively represent the resources
26 * on a remote server. The resources can include any number of private
27 * and shared email stores, calendars and address books.
29 * The backend's job is to synchronize local representations of remote
30 * resources by adding and removing #EServerSideSource instances in an
31 * #ESourceRegistryServer. If possible the backend should also listen
32 * for notifications of newly-added or deleted resources on the remote
33 * server or else poll the remote server at regular intervals and then
34 * update the data source collection accordingly.
36 * As most remote servers require authentication, the backend may also
37 * wish to implement the #ESourceAuthenticator interface so it can submit
38 * its own #EAuthenticationSession instances to the #ESourceRegistryServer.
41 #include "e-collection-backend.h"
44 #include <glib/gi18n-lib.h>
46 #include <libedataserver/libedataserver.h>
48 #include <libebackend/e-server-side-source.h>
49 #include <libebackend/e-source-registry-server.h>
51 #define E_COLLECTION_BACKEND_GET_PRIVATE(obj) \
52 (G_TYPE_INSTANCE_GET_PRIVATE \
53 ((obj), E_TYPE_COLLECTION_BACKEND, ECollectionBackendPrivate))
55 struct _ECollectionBackendPrivate {
64 /* Resource ID -> ESource */
65 GHashTable *unclaimed_resources;
66 GMutex unclaimed_resources_lock;
68 gulong source_added_handler_id;
69 gulong source_removed_handler_id;
83 static guint signals[LAST_SIGNAL];
91 collection_backend_children_insert (ECollectionBackend *backend,
94 g_mutex_lock (&backend->priv->children_lock);
96 g_hash_table_add (backend->priv->children, g_object_ref (source));
98 g_mutex_unlock (&backend->priv->children_lock);
102 collection_backend_children_remove (ECollectionBackend *backend,
107 g_mutex_lock (&backend->priv->children_lock);
109 removed = g_hash_table_remove (backend->priv->children, source);
111 g_mutex_unlock (&backend->priv->children_lock);
117 collection_backend_children_list (ECollectionBackend *backend)
121 g_mutex_lock (&backend->priv->children_lock);
123 list = g_hash_table_get_keys (backend->priv->children);
125 for (link = list; link != NULL; link = g_list_next (link))
126 g_object_ref (link->data);
128 g_mutex_unlock (&backend->priv->children_lock);
134 collection_backend_new_user_file (ECollectionBackend *backend)
140 const gchar *cache_dir;
142 /* This is like e_server_side_source_new_user_file()
143 * except that it uses the backend's cache directory. */
145 safe_uid = e_uid_new ();
146 e_filename_make_safe (safe_uid);
148 cache_dir = e_collection_backend_get_cache_dir (backend);
149 basename = g_strconcat (safe_uid, ".source", NULL);
150 filename = g_build_filename (cache_dir, basename, NULL);
152 file = g_file_new_for_path (filename);
162 collection_backend_new_source (ECollectionBackend *backend,
166 ESourceRegistryServer *server;
167 ESource *child_source;
168 ESource *collection_source;
169 EServerSideSource *server_side_source;
170 const gchar *cache_dir;
171 const gchar *collection_uid;
173 server = e_collection_backend_ref_server (backend);
174 child_source = e_server_side_source_new (server, file, error);
175 g_object_unref (server);
177 if (child_source == NULL)
180 server_side_source = E_SERVER_SIDE_SOURCE (child_source);
182 /* Clients may change the source but may not remove it. */
183 e_server_side_source_set_writable (server_side_source, TRUE);
184 e_server_side_source_set_removable (server_side_source, FALSE);
186 /* Changes should be written back to the cache directory. */
187 cache_dir = e_collection_backend_get_cache_dir (backend);
188 e_server_side_source_set_write_directory (
189 server_side_source, cache_dir);
191 /* Configure the child source as a collection member. */
192 collection_source = e_backend_get_source (E_BACKEND (backend));
193 collection_uid = e_source_get_uid (collection_source);
194 e_source_set_parent (child_source, collection_uid);
200 collection_backend_load_resources (ECollectionBackend *backend)
202 ESourceRegistryServer *server;
203 ECollectionBackendClass *class;
207 const gchar *cache_dir;
208 GError *error = NULL;
210 /* This is based on e_source_registry_server_load_file()
211 * and e_source_registry_server_load_directory(). */
213 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
214 g_return_if_fail (class->dup_resource_id != NULL);
216 cache_dir = e_collection_backend_get_cache_dir (backend);
218 dir = g_dir_open (cache_dir, 0, &error);
220 g_warn_if_fail (dir == NULL);
221 g_warning ("%s: %s", G_STRFUNC, error->message);
222 g_error_free (error);
226 g_return_if_fail (dir != NULL);
228 file = g_file_new_for_path (cache_dir);
229 server = e_collection_backend_ref_server (backend);
231 g_mutex_lock (&backend->priv->unclaimed_resources_lock);
233 while ((name = g_dir_read_name (dir)) != NULL) {
238 /* Ignore files with no ".source" suffix. */
239 if (!g_str_has_suffix (name, ".source"))
242 child = g_file_get_child (file, name);
243 source = collection_backend_new_source (backend, child, &error);
244 g_object_unref (child);
247 g_warn_if_fail (source == NULL);
248 g_warning ("%s: %s", G_STRFUNC, error->message);
249 g_error_free (error);
253 g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
255 resource_id = class->dup_resource_id (backend, source);
257 /* Hash table takes ownership of the resource ID. */
258 if (resource_id != NULL)
259 g_hash_table_insert (
260 backend->priv->unclaimed_resources,
261 resource_id, g_object_ref (source));
263 g_object_unref (source);
266 g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
268 g_object_unref (file);
269 g_object_unref (server);
274 collection_backend_claim_resource (ECollectionBackend *backend,
275 const gchar *resource_id,
278 GHashTable *unclaimed_resources;
281 g_mutex_lock (&backend->priv->unclaimed_resources_lock);
283 unclaimed_resources = backend->priv->unclaimed_resources;
284 source = g_hash_table_lookup (unclaimed_resources, resource_id);
286 if (source != NULL) {
287 g_object_ref (source);
288 g_hash_table_remove (unclaimed_resources, resource_id);
290 GFile *file = collection_backend_new_user_file (backend);
291 source = collection_backend_new_source (backend, file, error);
292 g_object_unref (file);
295 g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
301 collection_backend_child_is_calendar (ESource *child_source)
303 const gchar *extension_name;
305 extension_name = E_SOURCE_EXTENSION_CALENDAR;
306 if (e_source_has_extension (child_source, extension_name))
309 extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
310 if (e_source_has_extension (child_source, extension_name))
313 extension_name = E_SOURCE_EXTENSION_TASK_LIST;
314 if (e_source_has_extension (child_source, extension_name))
321 collection_backend_child_is_contacts (ESource *child_source)
323 const gchar *extension_name;
325 extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
326 if (e_source_has_extension (child_source, extension_name))
333 collection_backend_child_is_mail (ESource *child_source)
335 const gchar *extension_name;
337 extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
338 if (e_source_has_extension (child_source, extension_name))
341 extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
342 if (e_source_has_extension (child_source, extension_name))
345 extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
346 if (e_source_has_extension (child_source, extension_name))
353 include_master_source_enabled_transform (GBinding *binding,
354 const GValue *source_value,
355 GValue *target_value,
358 g_value_set_boolean (
360 g_value_get_boolean (source_value) &&
361 e_source_get_enabled (e_backend_get_source (backend)));
367 collection_backend_bind_child_enabled (ECollectionBackend *backend,
368 ESource *child_source)
370 ESource *collection_source;
371 ESourceCollection *extension;
372 const gchar *extension_name;
374 /* See if the child source's "enabled" property can be
375 * bound to any ESourceCollection "enabled" properties. */
377 extension_name = E_SOURCE_EXTENSION_COLLECTION;
378 collection_source = e_backend_get_source (E_BACKEND (backend));
379 extension = e_source_get_extension (collection_source, extension_name);
381 if (collection_backend_child_is_calendar (child_source)) {
382 g_object_bind_property_full (
383 extension, "calendar-enabled",
384 child_source, "enabled",
385 G_BINDING_SYNC_CREATE,
386 include_master_source_enabled_transform,
387 include_master_source_enabled_transform,
393 if (collection_backend_child_is_contacts (child_source)) {
394 g_object_bind_property_full (
395 extension, "contacts-enabled",
396 child_source, "enabled",
397 G_BINDING_SYNC_CREATE,
398 include_master_source_enabled_transform,
399 include_master_source_enabled_transform,
405 if (collection_backend_child_is_mail (child_source)) {
406 g_object_bind_property_full (
407 extension, "mail-enabled",
408 child_source, "enabled",
409 G_BINDING_SYNC_CREATE,
410 include_master_source_enabled_transform,
411 include_master_source_enabled_transform,
417 g_object_bind_property (
418 collection_source, "enabled",
419 child_source, "enabled",
420 G_BINDING_SYNC_CREATE);
424 collection_backend_source_added_cb (ESourceRegistryServer *server,
426 ECollectionBackend *backend)
428 ESource *collection_source;
429 ESource *parent_source;
432 /* If the newly-added source is our own child, emit "child-added". */
434 collection_source = e_backend_get_source (E_BACKEND (backend));
436 uid = e_source_get_parent (source);
440 parent_source = e_source_registry_server_ref_source (server, uid);
441 g_return_if_fail (parent_source != NULL);
443 if (e_source_equal (collection_source, parent_source))
444 g_signal_emit (backend, signals[CHILD_ADDED], 0, source);
446 g_object_unref (parent_source);
450 collection_backend_source_removed_cb (ESourceRegistryServer *server,
452 ECollectionBackend *backend)
454 ESource *collection_source;
455 ESource *parent_source;
458 /* If the removed source was our own child, emit "child-removed".
459 * Note that the source is already unlinked from the GNode tree. */
461 collection_source = e_backend_get_source (E_BACKEND (backend));
463 uid = e_source_get_parent (source);
467 parent_source = e_source_registry_server_ref_source (server, uid);
468 g_return_if_fail (parent_source != NULL);
470 if (e_source_equal (collection_source, parent_source))
471 g_signal_emit (backend, signals[CHILD_REMOVED], 0, source);
473 g_object_unref (parent_source);
477 collection_backend_populate_idle_cb (gpointer user_data)
479 ECollectionBackend *backend;
480 ECollectionBackendClass *class;
482 backend = E_COLLECTION_BACKEND (user_data);
484 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
485 g_return_val_if_fail (class->populate != NULL, FALSE);
487 class->populate (backend);
493 collection_backend_set_server (ECollectionBackend *backend,
494 ESourceRegistryServer *server)
496 g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
498 g_weak_ref_set (&backend->priv->server, server);
502 collection_backend_set_property (GObject *object,
507 switch (property_id) {
509 collection_backend_set_server (
510 E_COLLECTION_BACKEND (object),
511 g_value_get_object (value));
515 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
519 collection_backend_get_property (GObject *object,
524 switch (property_id) {
526 g_value_take_object (
528 e_collection_backend_ref_server (
529 E_COLLECTION_BACKEND (object)));
533 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
537 collection_backend_dispose (GObject *object)
539 ECollectionBackendPrivate *priv;
540 ESourceRegistryServer *server;
542 priv = E_COLLECTION_BACKEND_GET_PRIVATE (object);
544 server = g_weak_ref_get (&priv->server);
545 if (server != NULL) {
546 g_signal_handler_disconnect (
547 server, priv->source_added_handler_id);
548 g_signal_handler_disconnect (
549 server, priv->source_removed_handler_id);
550 g_weak_ref_set (&priv->server, NULL);
551 g_object_unref (server);
554 g_mutex_lock (&priv->children_lock);
555 g_hash_table_remove_all (priv->children);
556 g_mutex_unlock (&priv->children_lock);
558 g_mutex_lock (&priv->unclaimed_resources_lock);
559 g_hash_table_remove_all (priv->unclaimed_resources);
560 g_mutex_unlock (&priv->unclaimed_resources_lock);
562 /* Chain up to parent's dispose() method. */
563 G_OBJECT_CLASS (e_collection_backend_parent_class)->dispose (object);
567 collection_backend_finalize (GObject *object)
569 ECollectionBackendPrivate *priv;
571 priv = E_COLLECTION_BACKEND_GET_PRIVATE (object);
573 g_hash_table_destroy (priv->children);
574 g_mutex_clear (&priv->children_lock);
576 g_hash_table_destroy (priv->unclaimed_resources);
577 g_mutex_clear (&priv->unclaimed_resources_lock);
579 /* Chain up to parent's finalize() method. */
580 G_OBJECT_CLASS (e_collection_backend_parent_class)->finalize (object);
584 collection_backend_constructed (GObject *object)
586 ECollectionBackend *backend;
587 ESourceRegistryServer *server;
590 const gchar *collection_uid;
591 const gchar *user_cache_dir;
594 backend = E_COLLECTION_BACKEND (object);
596 /* Chain up to parent's constructed() method. */
597 G_OBJECT_CLASS (e_collection_backend_parent_class)->
598 constructed (object);
600 source = e_backend_get_source (E_BACKEND (backend));
602 /* Determine the backend's cache directory. */
604 user_cache_dir = e_get_user_cache_dir ();
605 collection_uid = e_source_get_uid (source);
606 backend->priv->cache_dir = g_build_filename (
607 user_cache_dir, "sources", collection_uid, NULL);
608 g_mkdir_with_parents (backend->priv->cache_dir, 0700);
610 /* This requires the cache directory to be set. */
611 collection_backend_load_resources (backend);
613 /* Emit "child-added" signals for the children we already have. */
615 node = e_server_side_source_get_node (E_SERVER_SIDE_SOURCE (source));
616 node = g_node_first_child (node);
618 while (node != NULL) {
619 ESource *child = E_SOURCE (node->data);
620 g_signal_emit (backend, signals[CHILD_ADDED], 0, child);
621 node = g_node_next_sibling (node);
624 /* Listen for "source-added" and "source-removed" signals
625 * from the server, which may trigger our own "child-added"
626 * and "child-removed" signals. */
628 server = e_collection_backend_ref_server (backend);
630 handler_id = g_signal_connect (
631 server, "source-added",
632 G_CALLBACK (collection_backend_source_added_cb), backend);
634 backend->priv->source_added_handler_id = handler_id;
636 handler_id = g_signal_connect (
637 server, "source-removed",
638 G_CALLBACK (collection_backend_source_removed_cb), backend);
640 backend->priv->source_removed_handler_id = handler_id;
642 g_object_unref (server);
644 /* Populate the newly-added collection from an idle callback
645 * so persistent child sources have a chance to be added first. */
649 collection_backend_populate_idle_cb,
650 g_object_ref (backend),
651 (GDestroyNotify) g_object_unref);
655 collection_backend_authenticate_sync (EBackend *backend,
656 ESourceAuthenticator *authenticator,
657 GCancellable *cancellable,
660 ECollectionBackend *collection_backend;
661 ESourceRegistryServer *server;
662 EAuthenticationSession *session;
664 const gchar *source_uid;
667 source = e_backend_get_source (backend);
668 source_uid = e_source_get_uid (source);
670 collection_backend = E_COLLECTION_BACKEND (backend);
671 server = e_collection_backend_ref_server (collection_backend);
672 session = e_source_registry_server_new_auth_session (
673 server, authenticator, source_uid);
675 success = e_source_registry_server_authenticate_sync (
676 server, session, cancellable, error);
678 g_object_unref (session);
679 g_object_unref (server);
685 collection_backend_populate (ECollectionBackend *backend)
687 /* Placeholder so subclasses can safely chain up. */
691 collection_backend_dup_resource_id (ECollectionBackend *backend,
694 const gchar *extension_name;
695 gchar *resource_id = NULL;
697 extension_name = E_SOURCE_EXTENSION_RESOURCE;
699 if (e_source_has_extension (source, extension_name)) {
700 ESourceResource *extension;
702 extension = e_source_get_extension (source, extension_name);
703 resource_id = e_source_resource_dup_identity (extension);
710 collection_backend_child_added (ECollectionBackend *backend,
711 ESource *child_source)
713 ESource *collection_source;
715 collection_backend_children_insert (backend, child_source);
716 collection_backend_bind_child_enabled (backend, child_source);
718 collection_source = e_backend_get_source (E_BACKEND (backend));
720 /* Collection children are not removable. */
721 e_server_side_source_set_removable (
722 E_SERVER_SIDE_SOURCE (child_source), FALSE);
724 /* Collection children inherit the authentication session type. */
725 g_object_bind_property (
726 collection_source, "auth-session-type",
727 child_source, "auth-session-type",
728 G_BINDING_SYNC_CREATE);
730 /* Collection children inherit OAuth 2.0 support if available. */
731 g_object_bind_property (
732 collection_source, "oauth2-support",
733 child_source, "oauth2-support",
734 G_BINDING_SYNC_CREATE);
738 collection_backend_child_removed (ECollectionBackend *backend,
739 ESource *child_source)
741 collection_backend_children_remove (backend, child_source);
745 collection_backend_create_resource_sync (ECollectionBackend *backend,
747 GCancellable *cancellable,
750 EAsyncClosure *closure;
751 GAsyncResult *result;
754 closure = e_async_closure_new ();
756 e_collection_backend_create_resource (
757 backend, source, cancellable,
758 e_async_closure_callback, closure);
760 result = e_async_closure_wait (closure);
762 success = e_collection_backend_create_resource_finish (
763 backend, result, error);
765 e_async_closure_free (closure);
771 collection_backend_create_resource (ECollectionBackend *backend,
773 GCancellable *cancellable,
774 GAsyncReadyCallback callback,
777 GSimpleAsyncResult *simple;
779 simple = g_simple_async_result_new_error (
780 G_OBJECT (backend), callback, user_data,
781 G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
782 _("%s does not support creating remote resources"),
783 G_OBJECT_TYPE_NAME (backend));
785 g_simple_async_result_complete_in_idle (simple);
787 g_object_unref (simple);
791 collection_backend_create_resource_finish (ECollectionBackend *backend,
792 GAsyncResult *result,
795 GSimpleAsyncResult *simple;
797 simple = G_SIMPLE_ASYNC_RESULT (result);
799 /* Assume success unless a GError is set. */
800 return !g_simple_async_result_propagate_error (simple, error);
804 collection_backend_delete_resource_sync (ECollectionBackend *backend,
806 GCancellable *cancellable,
809 EAsyncClosure *closure;
810 GAsyncResult *result;
813 closure = e_async_closure_new ();
815 e_collection_backend_delete_resource (
816 backend, source, cancellable,
817 e_async_closure_callback, closure);
819 result = e_async_closure_wait (closure);
821 success = e_collection_backend_delete_resource_finish (
822 backend, result, error);
824 e_async_closure_free (closure);
830 collection_backend_delete_resource (ECollectionBackend *backend,
832 GCancellable *cancellable,
833 GAsyncReadyCallback callback,
836 GSimpleAsyncResult *simple;
838 simple = g_simple_async_result_new_error (
839 G_OBJECT (backend), callback, user_data,
840 G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
841 _("%s does not support deleting remote resources"),
842 G_OBJECT_TYPE_NAME (backend));
844 g_simple_async_result_complete_in_idle (simple);
846 g_object_unref (simple);
850 collection_backend_delete_resource_finish (ECollectionBackend *backend,
851 GAsyncResult *result,
854 GSimpleAsyncResult *simple;
856 simple = G_SIMPLE_ASYNC_RESULT (result);
858 /* Assume success unless a GError is set. */
859 return !g_simple_async_result_propagate_error (simple, error);
863 e_collection_backend_class_init (ECollectionBackendClass *class)
865 GObjectClass *object_class;
866 EBackendClass *backend_class;
868 g_type_class_add_private (class, sizeof (ECollectionBackendPrivate));
870 object_class = G_OBJECT_CLASS (class);
871 object_class->set_property = collection_backend_set_property;
872 object_class->get_property = collection_backend_get_property;
873 object_class->dispose = collection_backend_dispose;
874 object_class->finalize = collection_backend_finalize;
875 object_class->constructed = collection_backend_constructed;
877 backend_class = E_BACKEND_CLASS (class);
878 backend_class->authenticate_sync = collection_backend_authenticate_sync;
880 class->populate = collection_backend_populate;
881 class->dup_resource_id = collection_backend_dup_resource_id;
882 class->child_added = collection_backend_child_added;
883 class->child_removed = collection_backend_child_removed;
884 class->create_resource_sync = collection_backend_create_resource_sync;
885 class->create_resource = collection_backend_create_resource;
886 class->create_resource_finish = collection_backend_create_resource_finish;
887 class->delete_resource_sync = collection_backend_delete_resource_sync;
888 class->delete_resource = collection_backend_delete_resource;
889 class->delete_resource_finish = collection_backend_delete_resource_finish;
891 g_object_class_install_property (
894 g_param_spec_object (
897 "The server to which the backend belongs",
898 E_TYPE_SOURCE_REGISTRY_SERVER,
900 G_PARAM_CONSTRUCT_ONLY |
901 G_PARAM_STATIC_STRINGS));
904 * ECollectionBackend::child-added:
905 * @backend: the #ECollectionBackend which emitted the signal
906 * @child_source: the newly-added child #EServerSideSource
908 * Emitted when an #EServerSideSource is added to @backend's
909 * #ECollectionBackend:server as a child of @backend's collection
912 * You can think of this as a filtered version of
913 * #ESourceRegistryServer's #ESourceRegistryServer::source-added
914 * signal which only lets through sources relevant to @backend.
916 signals[CHILD_ADDED] = g_signal_new (
918 G_OBJECT_CLASS_TYPE (object_class),
920 G_STRUCT_OFFSET (ECollectionBackendClass, child_added),
922 g_cclosure_marshal_VOID__OBJECT,
924 E_TYPE_SERVER_SIDE_SOURCE);
927 * ECollectionBackend::child-removed:
928 * @backend: the #ECollectionBackend which emitted the signal
929 * @child_source: the child #EServerSideSource that got removed
931 * Emitted when an #EServerSideSource that is a child of
932 * @backend's collection #EBackend:source is removed from
933 * @backend's #ECollectionBackend:server.
935 * You can think of this as a filtered version of
936 * #ESourceRegistryServer's #ESourceRegistryServer::source-removed
937 * signal which only lets through sources relevant to @backend.
939 signals[CHILD_REMOVED] = g_signal_new (
941 G_OBJECT_CLASS_TYPE (object_class),
943 G_STRUCT_OFFSET (ECollectionBackendClass, child_removed),
945 g_cclosure_marshal_VOID__OBJECT,
947 E_TYPE_SERVER_SIDE_SOURCE);
951 e_collection_backend_init (ECollectionBackend *backend)
953 GHashTable *children;
954 GHashTable *unclaimed_resources;
956 children = g_hash_table_new_full (
957 (GHashFunc) e_source_hash,
958 (GEqualFunc) e_source_equal,
959 (GDestroyNotify) g_object_unref,
960 (GDestroyNotify) NULL);
962 unclaimed_resources = g_hash_table_new_full (
963 (GHashFunc) g_str_hash,
964 (GEqualFunc) g_str_equal,
965 (GDestroyNotify) g_free,
966 (GDestroyNotify) g_object_unref);
968 backend->priv = E_COLLECTION_BACKEND_GET_PRIVATE (backend);
969 backend->priv->children = children;
970 g_mutex_init (&backend->priv->children_lock);
971 backend->priv->unclaimed_resources = unclaimed_resources;
972 g_mutex_init (&backend->priv->unclaimed_resources_lock);
976 * e_collection_backend_new_child:
977 * @backend: an #ECollectionBackend
978 * @resource_id: a stable and unique resource ID
980 * Creates a new #EServerSideSource as a child of the collection
981 * #EBackend:source owned by @backend. If possible, the #EServerSideSource
982 * is drawn from a cache of previously used sources indexed by @resource_id
983 * so that locally cached data from previous sessions can be reused.
985 * The returned data source should be passed to
986 * e_source_registry_server_add_source() to export it over D-Bus.
988 * Return: a newly-created data source
993 e_collection_backend_new_child (ECollectionBackend *backend,
994 const gchar *resource_id)
996 ESource *collection_source;
997 ESource *child_source;
998 GError *error = NULL;
1000 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1001 g_return_val_if_fail (resource_id != NULL, NULL);
1003 /* This being a newly-created or existing data source, claiming
1004 * it should never fail but we'll check for errors just the same.
1005 * It's unlikely enough that we don't need a GError parameter. */
1006 child_source = collection_backend_claim_resource (
1007 backend, resource_id, &error);
1009 if (error != NULL) {
1010 g_warn_if_fail (child_source == NULL);
1011 g_warning ("%s: %s", G_STRFUNC, error->message);
1012 g_error_free (error);
1016 collection_source = e_backend_get_source (E_BACKEND (backend));
1019 "%s: Pairing %s with resource %s\n",
1020 e_source_get_display_name (collection_source),
1021 e_source_get_uid (child_source), resource_id);
1023 return child_source;
1027 * e_collection_backend_ref_server:
1028 * @backend: an #ECollectionBackend
1030 * Returns the #ESourceRegistryServer to which @backend belongs.
1032 * The returned #ESourceRegistryServer is referenced for thread-safety.
1033 * Unreference the #ESourceRegistryServer with g_object_unref() when
1036 * Returns: the #ESourceRegisterServer for @backend
1040 ESourceRegistryServer *
1041 e_collection_backend_ref_server (ECollectionBackend *backend)
1043 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1045 return g_weak_ref_get (&backend->priv->server);
1049 * e_collection_backend_get_cache_dir:
1050 * @backend: an #ECollectionBackend
1052 * Returns the private cache directory path for @backend, which is named
1053 * after the #ESource:uid of @backend's collection #EBackend:source.
1055 * The cache directory is meant to store key files for backend-created
1056 * data sources. See also: e_server_side_source_set_write_directory()
1058 * Returns: the cache directory for @backend
1063 e_collection_backend_get_cache_dir (ECollectionBackend *backend)
1065 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1067 return backend->priv->cache_dir;
1071 * e_collection_backend_dup_resource_id:
1072 * @backend: an #ECollectionBackend
1073 * @child_source: an #ESource managed by @backend
1075 * Extracts the resource ID for @child_source, which is supposed to be a
1076 * stable and unique server-assigned identifier for the remote resource
1077 * described by @child_source. If @child_source is not actually a child
1078 * of the collection #EBackend:source owned by @backend, the function
1081 * The returned string should be freed with g_free() when no longer needed.
1083 * Returns: a newly-allocated resource ID for @child_source, or %NULL
1088 e_collection_backend_dup_resource_id (ECollectionBackend *backend,
1089 ESource *child_source)
1091 ECollectionBackend *backend_for_child_source;
1092 ECollectionBackendClass *class;
1093 ESourceRegistryServer *server;
1094 gboolean child_is_ours = FALSE;
1096 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1097 g_return_val_if_fail (E_IS_SOURCE (child_source), NULL);
1099 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1100 g_return_val_if_fail (class->dup_resource_id != NULL, NULL);
1102 /* Make sure the ESource belongs to the ECollectionBackend to
1103 * avoid accidentally creating a new extension while trying to
1104 * extract a resource ID that isn't there. Better to test this
1105 * up front than rely on ECollectionBackend subclasses to do it. */
1106 server = e_collection_backend_ref_server (backend);
1107 backend_for_child_source =
1108 e_source_registry_server_ref_backend (server, child_source);
1109 g_object_unref (server);
1111 if (backend_for_child_source != NULL) {
1112 child_is_ours = (backend_for_child_source == backend);
1113 g_object_unref (backend_for_child_source);
1119 return class->dup_resource_id (backend, child_source);
1123 * e_collection_backend_claim_all_resources:
1124 * @backend: an #ECollectionBackend
1126 * Claims all previously used sources that have not yet been claimed by
1127 * e_collection_backend_new_child() and returns them in a #GList. Note
1128 * that previously used sources can only be claimed once, so subsequent
1129 * calls to this function for @backend will return %NULL.
1131 * The @backend is then expected to compare the returned list with a
1132 * current list of resources from a remote server, create new #ESource
1133 * instances as needed with e_collection_backend_new_child(), discard
1134 * unneeded #ESource instances with e_source_remove(), and export the
1135 * remaining instances with e_source_registry_server_add_source().
1137 * The sources returned in the list are referenced for thread-safety.
1138 * They must each be unreferenced with g_object_unref() when finished
1139 * with them. Free the returned #GList itself with g_list_free().
1141 * An easy way to free the list properly in one step is as follows:
1144 * g_list_free_full (list, g_object_unref);
1147 * Returns: a list of previously used sources
1152 e_collection_backend_claim_all_resources (ECollectionBackend *backend)
1154 GHashTable *unclaimed_resources;
1157 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1159 g_mutex_lock (&backend->priv->unclaimed_resources_lock);
1161 unclaimed_resources = backend->priv->unclaimed_resources;
1162 resources = g_hash_table_get_values (unclaimed_resources);
1163 g_list_foreach (resources, (GFunc) g_object_ref, NULL);
1164 g_hash_table_remove_all (unclaimed_resources);
1166 g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
1172 * e_collection_backend_list_calendar_sources:
1173 * @backend: an #ECollectionBackend
1175 * Returns a list of calendar sources belonging to the data source
1176 * collection managed by @backend.
1178 * The sources returned in the list are referenced for thread-safety.
1179 * They must each be unreferenced with g_object_unref() when finished
1180 * with them. Free the returned #GList itself with g_list_free().
1182 * An easy way to free the list properly in one step is as follows:
1185 * g_list_free_full (list, g_object_unref);
1188 * Returns: a list of calendar sources
1193 e_collection_backend_list_calendar_sources (ECollectionBackend *backend)
1195 GList *result_list = NULL;
1198 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1200 list = collection_backend_children_list (backend);
1202 for (link = list; link != NULL; link = g_list_next (link)) {
1203 ESource *child_source = E_SOURCE (link->data);
1204 if (collection_backend_child_is_calendar (child_source))
1205 result_list = g_list_prepend (
1206 result_list, g_object_ref (child_source));
1209 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1211 return g_list_reverse (result_list);
1215 * e_collection_backend_list_contacts_sources:
1216 * @backend: an #ECollectionBackend
1218 * Returns a list of address book sources belonging to the data source
1219 * collection managed by @backend.
1221 * The sources returned in the list are referenced for thread-safety.
1222 * They must each be unreferenced with g_object_unref() when finished
1223 * with them. Free the returned #GList itself with g_list_free().
1225 * An easy way to free the list properly in one step is as follows:
1228 * g_list_free_full (list, g_object_unref);
1231 * Returns: a list of address book sources
1236 e_collection_backend_list_contacts_sources (ECollectionBackend *backend)
1238 GList *result_list = NULL;
1241 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1243 list = collection_backend_children_list (backend);
1245 for (link = list; link != NULL; link = g_list_next (link)) {
1246 ESource *child_source = E_SOURCE (link->data);
1247 if (collection_backend_child_is_contacts (child_source))
1248 result_list = g_list_prepend (
1249 result_list, g_object_ref (child_source));
1252 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1254 return g_list_reverse (result_list);
1258 * e_collection_backend_list_mail_sources:
1259 * @backend: an #ECollectionBackend
1261 * Returns a list of mail sources belonging to the data source collection
1262 * managed by @backend.
1264 * The sources returned in the list are referenced for thread-safety.
1265 * They must each be unreferenced with g_object_unref() when finished
1266 * with them. Free the returned #GList itself with g_list_free().
1268 * An easy way to free the list properly in one step is as follows:
1271 * g_list_free_full (list, g_object_unref);
1274 * Returns: a list of mail sources
1279 e_collection_backend_list_mail_sources (ECollectionBackend *backend)
1281 GList *result_list = NULL;
1284 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
1286 list = collection_backend_children_list (backend);
1288 for (link = list; link != NULL; link = g_list_next (link)) {
1289 ESource *child_source = E_SOURCE (link->data);
1290 if (collection_backend_child_is_mail (child_source))
1291 result_list = g_list_prepend (
1292 result_list, g_object_ref (child_source));
1295 g_list_free_full (list, (GDestroyNotify) g_object_unref);
1297 return g_list_reverse (result_list);
1301 * e_collection_backend_create_resource_sync
1302 * @backend: an #ECollectionBackend
1303 * @source: an #ESource
1304 * @cancellable: optional #GCancellable object, or %NULL
1305 * @error: return location for a #GError, or %NULL
1307 * Creates a server-side resource described by @source. For example, if
1308 * @source describes a new calendar, an equivalent calendar is created on
1311 * It is the implementor's responsibility to examine @source and determine
1312 * what the equivalent server-side resource would be. If this cannot be
1313 * determined without ambiguity, the function must return an error.
1315 * After the server-side resource is successfully created, the implementor
1316 * must also add an #ESource to @backend's #ECollectionBackend:server. This
1317 * can either be done immediately or in response to some "resource created"
1318 * notification from the server. The added #ESource can be @source itself
1319 * or a different #ESource instance that describes the new resource.
1321 * If an error occurs, the function will set @error and return %FALSE.
1323 * Returns: %TRUE on success, %FALSE on failure
1328 e_collection_backend_create_resource_sync (ECollectionBackend *backend,
1330 GCancellable *cancellable,
1333 ECollectionBackendClass *class;
1335 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
1336 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1338 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1339 g_return_val_if_fail (class->create_resource_sync != NULL, FALSE);
1341 return class->create_resource_sync (
1342 backend, source, cancellable, error);
1346 * e_collection_backend_create_resource:
1347 * @backend: an #ECollectionBackend
1348 * @source: an #ESource
1349 * @cancellable: optional #GCancellable object, or %NULL
1350 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1351 * @user_data: data to pass to the callback function
1353 * Asynchronously creates a server-side resource described by @source.
1354 * For example, if @source describes a new calendar, an equivalent calendar
1355 * is created on the server.
1357 * It is the implementor's responsibility to examine @source and determine
1358 * what the equivalent server-side resource would be. If this cannot be
1359 * determined without ambiguity, the function must return an error.
1361 * After the server-side resource is successfully created, the implementor
1362 * must also add an #ESource to @backend's #ECollectionBackend:server. This
1363 * can either be done immediately or in response to some "resource created"
1364 * notification from the server. The added #ESource can be @source itself
1365 * or a different #ESource instance that describes the new resource.
1367 * When the operation is finished, @callback will be called. You can then
1368 * call e_collection_backend_create_resource_finish() to get the result of
1374 e_collection_backend_create_resource (ECollectionBackend *backend,
1376 GCancellable *cancellable,
1377 GAsyncReadyCallback callback,
1380 ECollectionBackendClass *class;
1382 g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
1383 g_return_if_fail (E_IS_SOURCE (source));
1385 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1386 g_return_if_fail (class->create_resource != NULL);
1388 class->create_resource (
1389 backend, source, cancellable, callback, user_data);
1393 * e_collection_backend_create_resource_finish:
1394 * @backend: an #ECollectionBackend
1395 * @result: a #GAsyncResult
1396 * @error: return location for a #GError, or %NULL
1398 * Finishes the operation started with e_collection_backend_create_resource().
1400 * If an error occurred, the function will set @error and return %FALSE.
1402 * Returns: %TRUE on success, %FALSE on failure
1407 e_collection_backend_create_resource_finish (ECollectionBackend *backend,
1408 GAsyncResult *result,
1411 ECollectionBackendClass *class;
1413 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
1414 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
1416 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1417 g_return_val_if_fail (class->create_resource_finish != NULL, FALSE);
1419 return class->create_resource_finish (backend, result, error);
1423 * e_collection_backend_delete_resource_sync:
1424 * @backend: an #ECollectionBackend
1425 * @source: an #ESource
1426 * @cancellable: optional #GCancellable object, or %NULL
1427 * @error: return location for a #GError, or %NULL
1429 * Deletes a server-side resource described by @source. The @source must
1430 * be a child of @backend's collection #EBackend:source.
1432 * After the server-side resource is successfully deleted, the implementor
1433 * must also remove @source from the @backend's #ECollectionBackend:server.
1434 * This can either be done immediately or in response to some "resource
1435 * deleted" notification from the server.
1437 * If an error occurs, the function will set @error and return %FALSE.
1439 * Returns: %TRUE on success, %FALSE on failure
1444 e_collection_backend_delete_resource_sync (ECollectionBackend *backend,
1446 GCancellable *cancellable,
1449 ECollectionBackendClass *class;
1451 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
1452 g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
1454 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1455 g_return_val_if_fail (class->delete_resource_sync != NULL, FALSE);
1457 return class->delete_resource_sync (
1458 backend, source, cancellable, error);
1462 * e_collection_backend_delete_resource:
1463 * @backend: an #ECollectionBackend
1464 * @source: an #ESource
1465 * @cancellable: optional #GCancellable object, or %NULL
1466 * @callback: a #GAsyncReadyCallback to call when the request is satisfied
1467 * @user_data: data to pass to the callback function
1469 * Asynchronously deletes a server-side resource described by @source.
1470 * The @source must be a child of @backend's collection #EBackend:source.
1472 * After the server-side resource is successfully deleted, the implementor
1473 * must also remove @source from the @backend's #ECollectionBackend:server.
1474 * This can either be done immediately or in response to some "resource
1475 * deleted" notification from the server.
1477 * When the operation is finished, @callback will be called. You can then
1478 * call e_collection_backend_delete_resource_finish() to get the result of
1484 e_collection_backend_delete_resource (ECollectionBackend *backend,
1486 GCancellable *cancellable,
1487 GAsyncReadyCallback callback,
1490 ECollectionBackendClass *class;
1492 g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
1493 g_return_if_fail (E_IS_SOURCE (source));
1495 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1496 g_return_if_fail (class->delete_resource != NULL);
1498 return class->delete_resource (
1499 backend, source, cancellable, callback, user_data);
1503 * e_collection_backend_delete_resource_finish:
1504 * @backend: an #ECollectionBackend
1505 * @result: a #GAsyncResult
1506 * @error: return location for a #GError, or %NULL
1508 * Finishes the operation started with e_collection_backend_delete_resource().
1510 * If an error occurred, the function will set @error and return %FALSE.
1512 * Returns: %TRUE on success, %FALSE on failure
1517 e_collection_backend_delete_resource_finish (ECollectionBackend *backend,
1518 GAsyncResult *result,
1521 ECollectionBackendClass *class;
1523 g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
1524 g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
1526 class = E_COLLECTION_BACKEND_GET_CLASS (backend);
1527 g_return_val_if_fail (class->delete_resource_finish != NULL, FALSE);
1529 return class->delete_resource_finish (backend, result, error);