/*
* e-collection-backend.c
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see .
*
*/
/**
* SECTION: e-collection-backend
* @include: libebackend/libebackend.h
* @short_description: A base class for a data source collection backend
*
* #ECollectionBackend is a base class for backends which manage a
* collection of data sources that collectively represent the resources
* on a remote server. The resources can include any number of private
* and shared email stores, calendars and address books.
*
* The backend's job is to synchronize local representations of remote
* resources by adding and removing #EServerSideSource instances in an
* #ESourceRegistryServer. If possible the backend should also listen
* for notifications of newly-added or deleted resources on the remote
* server or else poll the remote server at regular intervals and then
* update the data source collection accordingly.
*
* As most remote servers require authentication, the backend may also
* wish to implement the #ESourceAuthenticator interface so it can submit
* its own #EAuthenticationSession instances to the #ESourceRegistryServer.
**/
#include "e-collection-backend.h"
#include
#include
#include
#include
#include
#define E_COLLECTION_BACKEND_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_COLLECTION_BACKEND, ECollectionBackendPrivate))
struct _ECollectionBackendPrivate {
GWeakRef server;
/* Set of ESources */
GHashTable *children;
GMutex children_lock;
GMutex property_lock;
GProxyResolver *proxy_resolver;
gchar *cache_dir;
ESource *authentication_source;
gulong auth_source_changed_handler_id;
/* Resource ID -> ESource */
GHashTable *unclaimed_resources;
GMutex unclaimed_resources_lock;
gulong source_added_handler_id;
gulong source_removed_handler_id;
gulong notify_enabled_handler_id;
gulong notify_collection_handler_id;
gulong notify_online_handler_id;
guint scheduled_populate_idle_id;
};
enum {
PROP_0,
PROP_PROXY_RESOLVER,
PROP_SERVER
};
enum {
CHILD_ADDED,
CHILD_REMOVED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE (
ECollectionBackend,
e_collection_backend,
E_TYPE_BACKEND)
static void
collection_backend_children_insert (ECollectionBackend *backend,
ESource *source)
{
g_mutex_lock (&backend->priv->children_lock);
g_hash_table_add (backend->priv->children, g_object_ref (source));
g_mutex_unlock (&backend->priv->children_lock);
}
static gboolean
collection_backend_children_remove (ECollectionBackend *backend,
ESource *source)
{
gboolean removed;
g_mutex_lock (&backend->priv->children_lock);
removed = g_hash_table_remove (backend->priv->children, source);
g_mutex_unlock (&backend->priv->children_lock);
return removed;
}
static GList *
collection_backend_children_list (ECollectionBackend *backend)
{
GList *list, *link;
g_mutex_lock (&backend->priv->children_lock);
list = g_hash_table_get_keys (backend->priv->children);
for (link = list; link != NULL; link = g_list_next (link))
g_object_ref (link->data);
g_mutex_unlock (&backend->priv->children_lock);
return list;
}
static GFile *
collection_backend_new_user_file (ECollectionBackend *backend)
{
GFile *file;
gchar *safe_uid;
gchar *basename;
gchar *filename;
const gchar *cache_dir;
/* This is like e_server_side_source_new_user_file()
* except that it uses the backend's cache directory. */
safe_uid = e_uid_new ();
e_filename_make_safe (safe_uid);
cache_dir = e_collection_backend_get_cache_dir (backend);
basename = g_strconcat (safe_uid, ".source", NULL);
filename = g_build_filename (cache_dir, basename, NULL);
file = g_file_new_for_path (filename);
g_free (basename);
g_free (filename);
g_free (safe_uid);
return file;
}
static ESource *
collection_backend_new_source (ECollectionBackend *backend,
GFile *file,
GError **error)
{
ESourceRegistryServer *server;
ESource *child_source;
ESource *collection_source;
EServerSideSource *server_side_source;
const gchar *cache_dir;
const gchar *collection_uid;
server = e_collection_backend_ref_server (backend);
child_source = e_server_side_source_new (server, file, error);
g_object_unref (server);
if (child_source == NULL)
return NULL;
server_side_source = E_SERVER_SIDE_SOURCE (child_source);
/* Clients may change the source but may not remove it. */
e_server_side_source_set_writable (server_side_source, TRUE);
e_server_side_source_set_removable (server_side_source, FALSE);
/* Changes should be written back to the cache directory. */
cache_dir = e_collection_backend_get_cache_dir (backend);
e_server_side_source_set_write_directory (
server_side_source, cache_dir);
/* Configure the child source as a collection member. */
collection_source = e_backend_get_source (E_BACKEND (backend));
collection_uid = e_source_get_uid (collection_source);
e_source_set_parent (child_source, collection_uid);
return child_source;
}
static void
collection_backend_load_resources (ECollectionBackend *backend)
{
ESourceRegistryServer *server;
ECollectionBackendClass *class;
GDir *dir;
GFile *file;
const gchar *name;
const gchar *cache_dir;
GError *error = NULL;
/* This is based on e_source_registry_server_load_file()
* and e_source_registry_server_load_directory(). */
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_if_fail (class->dup_resource_id != NULL);
cache_dir = e_collection_backend_get_cache_dir (backend);
dir = g_dir_open (cache_dir, 0, &error);
if (error != NULL) {
g_warn_if_fail (dir == NULL);
g_warning ("%s: %s", G_STRFUNC, error->message);
g_error_free (error);
return;
}
g_return_if_fail (dir != NULL);
file = g_file_new_for_path (cache_dir);
server = e_collection_backend_ref_server (backend);
g_mutex_lock (&backend->priv->unclaimed_resources_lock);
while ((name = g_dir_read_name (dir)) != NULL) {
GFile *child;
ESource *source;
gchar *resource_id;
/* Ignore files with no ".source" suffix. */
if (!g_str_has_suffix (name, ".source"))
continue;
child = g_file_get_child (file, name);
source = collection_backend_new_source (backend, child, &error);
g_object_unref (child);
if (error != NULL) {
g_warn_if_fail (source == NULL);
g_warning ("%s: %s", G_STRFUNC, error->message);
g_clear_error (&error);
continue;
}
g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
resource_id = class->dup_resource_id (backend, source);
/* Hash table takes ownership of the resource ID. */
if (resource_id != NULL)
g_hash_table_insert (
backend->priv->unclaimed_resources,
resource_id, g_object_ref (source));
g_object_unref (source);
}
g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
g_object_unref (file);
g_object_unref (server);
g_dir_close (dir);
}
static ESource *
collection_backend_claim_resource (ECollectionBackend *backend,
const gchar *resource_id,
GError **error)
{
GHashTable *unclaimed_resources;
ESource *source;
g_mutex_lock (&backend->priv->unclaimed_resources_lock);
unclaimed_resources = backend->priv->unclaimed_resources;
source = g_hash_table_lookup (unclaimed_resources, resource_id);
if (source != NULL) {
g_object_ref (source);
g_hash_table_remove (unclaimed_resources, resource_id);
} else {
GFile *file = collection_backend_new_user_file (backend);
source = collection_backend_new_source (backend, file, error);
g_object_unref (file);
}
g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
return source;
}
static gboolean
collection_backend_child_is_calendar (ESource *child_source)
{
const gchar *extension_name;
extension_name = E_SOURCE_EXTENSION_CALENDAR;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
extension_name = E_SOURCE_EXTENSION_MEMO_LIST;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
extension_name = E_SOURCE_EXTENSION_TASK_LIST;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
return FALSE;
}
static gboolean
collection_backend_child_is_contacts (ESource *child_source)
{
const gchar *extension_name;
extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
return FALSE;
}
static gboolean
collection_backend_child_is_mail (ESource *child_source)
{
const gchar *extension_name;
extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
if (e_source_has_extension (child_source, extension_name))
return TRUE;
return FALSE;
}
static gboolean
include_master_source_enabled_transform (GBinding *binding,
const GValue *source_value,
GValue *target_value,
gpointer backend)
{
g_value_set_boolean (
target_value,
g_value_get_boolean (source_value) &&
e_source_get_enabled (e_backend_get_source (backend)));
return TRUE;
}
static void
collection_backend_bind_child_enabled (ECollectionBackend *backend,
ESource *child_source)
{
ESource *collection_source;
ESourceCollection *extension;
const gchar *extension_name;
/* See if the child source's "enabled" property can be
* bound to any ESourceCollection "enabled" properties. */
extension_name = E_SOURCE_EXTENSION_COLLECTION;
collection_source = e_backend_get_source (E_BACKEND (backend));
extension = e_source_get_extension (collection_source, extension_name);
if (collection_backend_child_is_calendar (child_source)) {
e_binding_bind_property_full (
extension, "calendar-enabled",
child_source, "enabled",
G_BINDING_SYNC_CREATE,
include_master_source_enabled_transform,
include_master_source_enabled_transform,
backend,
NULL);
return;
}
if (collection_backend_child_is_contacts (child_source)) {
e_binding_bind_property_full (
extension, "contacts-enabled",
child_source, "enabled",
G_BINDING_SYNC_CREATE,
include_master_source_enabled_transform,
include_master_source_enabled_transform,
backend,
NULL);
return;
}
if (collection_backend_child_is_mail (child_source)) {
e_binding_bind_property_full (
extension, "mail-enabled",
child_source, "enabled",
G_BINDING_SYNC_CREATE,
include_master_source_enabled_transform,
include_master_source_enabled_transform,
backend,
NULL);
return;
}
e_binding_bind_property (
collection_source, "enabled",
child_source, "enabled",
G_BINDING_SYNC_CREATE);
}
static void
collection_backend_source_added_cb (ESourceRegistryServer *server,
ESource *source,
ECollectionBackend *backend)
{
ESource *collection_source;
ESource *parent_source;
const gchar *uid;
/* If the newly-added source is our own child, emit "child-added". */
collection_source = e_backend_get_source (E_BACKEND (backend));
uid = e_source_get_parent (source);
if (uid == NULL)
return;
parent_source = e_source_registry_server_ref_source (server, uid);
g_return_if_fail (parent_source != NULL);
if (e_source_equal (collection_source, parent_source))
g_signal_emit (backend, signals[CHILD_ADDED], 0, source);
g_object_unref (parent_source);
}
static void
collection_backend_source_removed_cb (ESourceRegistryServer *server,
ESource *source,
ECollectionBackend *backend)
{
ESource *collection_source;
ESource *parent_source;
const gchar *uid;
/* If the removed source was our own child, emit "child-removed".
* Note that the source is already unlinked from the GNode tree. */
collection_source = e_backend_get_source (E_BACKEND (backend));
uid = e_source_get_parent (source);
if (uid == NULL)
return;
parent_source = e_source_registry_server_ref_source (server, uid);
g_return_if_fail (parent_source != NULL);
if (e_source_equal (collection_source, parent_source))
g_signal_emit (backend, signals[CHILD_REMOVED], 0, source);
g_object_unref (parent_source);
}
static void
collection_backend_source_enabled_cb (ESource *source,
GParamSpec *spec,
EBackend *backend)
{
ESource *collection_source;
GObject *collection;
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
collection_source = e_backend_get_source (E_BACKEND (backend));
collection = e_source_get_extension (collection_source, E_SOURCE_EXTENSION_COLLECTION);
/* Some child sources depend on both sub-part enabled and the main
ESource::enabled state, thus if the main's ESource::enabled
changes, then also notify the change of the sub-parts, thus
child's enabled property is properly recalculated. */
g_object_notify (collection, "calendar-enabled");
g_object_notify (collection, "contacts-enabled");
g_object_notify (collection, "mail-enabled");
}
static gboolean
collection_backend_populate_idle_cb (gpointer user_data)
{
ECollectionBackend *backend;
ECollectionBackendClass *class;
backend = E_COLLECTION_BACKEND (user_data);
backend->priv->scheduled_populate_idle_id = 0;
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->populate != NULL, FALSE);
class->populate (backend);
return FALSE;
}
static void
collection_backend_schedule_populate_idle (ECollectionBackend *backend)
{
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
if (!backend->priv->scheduled_populate_idle_id)
backend->priv->scheduled_populate_idle_id = g_idle_add_full (
G_PRIORITY_LOW,
collection_backend_populate_idle_cb,
g_object_ref (backend),
(GDestroyNotify) g_object_unref);
}
static void
collection_backend_online_cb (EBackend *backend,
GParamSpec *spec,
gpointer user_data)
{
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
if (e_backend_get_online (backend))
collection_backend_schedule_populate_idle (E_COLLECTION_BACKEND (backend));
}
static void
collection_backend_notify_collection_cb (ESourceCollection *collection_extension,
GParamSpec *param,
ECollectionBackend *collection_backend)
{
ESource *source;
g_return_if_fail (E_IS_SOURCE_COLLECTION (collection_extension));
g_return_if_fail (param != NULL);
g_return_if_fail (E_IS_COLLECTION_BACKEND (collection_backend));
source = e_backend_get_source (E_BACKEND (collection_backend));
if (!e_source_get_enabled (source) || (
g_strcmp0 (g_param_spec_get_name (param), "calendar-enabled") != 0 &&
g_strcmp0 (g_param_spec_get_name (param), "contacts-enabled") != 0 &&
g_strcmp0 (g_param_spec_get_name (param), "mail-enabled") != 0))
return;
collection_backend_schedule_populate_idle (collection_backend);
}
static void
collection_backend_update_proxy_resolver (ECollectionBackend *backend)
{
GProxyResolver *proxy_resolver = NULL;
ESourceAuthentication *extension;
ESource *source = NULL;
gboolean notify = FALSE;
gchar *uid;
extension = e_source_get_extension (
backend->priv->authentication_source,
E_SOURCE_EXTENSION_AUTHENTICATION);
uid = e_source_authentication_dup_proxy_uid (extension);
if (uid != NULL) {
ESourceRegistryServer *server;
server = e_collection_backend_ref_server (backend);
source = e_source_registry_server_ref_source (server, uid);
g_object_unref (server);
g_free (uid);
}
if (source != NULL) {
proxy_resolver = G_PROXY_RESOLVER (source);
if (!g_proxy_resolver_is_supported (proxy_resolver))
proxy_resolver = NULL;
}
g_mutex_lock (&backend->priv->property_lock);
/* Emitting a "notify" signal unnecessarily might have
* unwanted side effects like cancelling a SoupMessage.
* Only emit if we now have a different GProxyResolver. */
if (proxy_resolver != backend->priv->proxy_resolver) {
g_clear_object (&backend->priv->proxy_resolver);
backend->priv->proxy_resolver = proxy_resolver;
if (proxy_resolver != NULL)
g_object_ref (proxy_resolver);
notify = TRUE;
}
g_mutex_unlock (&backend->priv->property_lock);
if (notify)
g_object_notify (G_OBJECT (backend), "proxy-resolver");
g_clear_object (&source);
}
static void
collection_backend_auth_source_changed_cb (ESource *authentication_source,
GWeakRef *backend_weak_ref)
{
ECollectionBackend *backend;
backend = g_weak_ref_get (backend_weak_ref);
if (backend != NULL) {
collection_backend_update_proxy_resolver (backend);
g_object_unref (backend);
}
}
static void
collection_backend_set_server (ECollectionBackend *backend,
ESourceRegistryServer *server)
{
g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
g_weak_ref_set (&backend->priv->server, server);
}
static void
collection_backend_set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_SERVER:
collection_backend_set_server (
E_COLLECTION_BACKEND (object),
g_value_get_object (value));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
collection_backend_get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
switch (property_id) {
case PROP_PROXY_RESOLVER:
g_value_take_object (
value,
e_collection_backend_ref_proxy_resolver (
E_COLLECTION_BACKEND (object)));
return;
case PROP_SERVER:
g_value_take_object (
value,
e_collection_backend_ref_server (
E_COLLECTION_BACKEND (object)));
return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
}
static void
collection_backend_dispose (GObject *object)
{
ECollectionBackendPrivate *priv;
ESourceRegistryServer *server;
priv = E_COLLECTION_BACKEND_GET_PRIVATE (object);
server = g_weak_ref_get (&priv->server);
if (server != NULL) {
g_signal_handler_disconnect (
server, priv->source_added_handler_id);
g_signal_handler_disconnect (
server, priv->source_removed_handler_id);
g_weak_ref_set (&priv->server, NULL);
g_object_unref (server);
}
if (priv->notify_enabled_handler_id) {
ESource *source = e_backend_get_source (E_BACKEND (object));
if (source)
g_signal_handler_disconnect (source, priv->notify_enabled_handler_id);
priv->notify_enabled_handler_id = 0;
}
if (priv->notify_collection_handler_id) {
ESource *source = e_backend_get_source (E_BACKEND (object));
if (source) {
ESourceCollection *collection_extension;
collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
g_signal_handler_disconnect (collection_extension, priv->notify_collection_handler_id);
}
priv->notify_collection_handler_id = 0;
}
if (priv->notify_online_handler_id) {
g_signal_handler_disconnect (object, priv->notify_online_handler_id);
priv->notify_online_handler_id = 0;
}
g_mutex_lock (&priv->children_lock);
g_hash_table_remove_all (priv->children);
g_mutex_unlock (&priv->children_lock);
g_clear_object (&priv->proxy_resolver);
g_clear_object (&priv->authentication_source);
g_mutex_lock (&priv->unclaimed_resources_lock);
g_hash_table_remove_all (priv->unclaimed_resources);
g_mutex_unlock (&priv->unclaimed_resources_lock);
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_collection_backend_parent_class)->dispose (object);
}
static void
collection_backend_finalize (GObject *object)
{
ECollectionBackendPrivate *priv;
priv = E_COLLECTION_BACKEND_GET_PRIVATE (object);
g_hash_table_destroy (priv->children);
g_mutex_clear (&priv->children_lock);
g_mutex_clear (&priv->property_lock);
g_hash_table_destroy (priv->unclaimed_resources);
g_mutex_clear (&priv->unclaimed_resources_lock);
g_weak_ref_clear (&priv->server);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_collection_backend_parent_class)->finalize (object);
}
static void
collection_backend_constructed (GObject *object)
{
ECollectionBackend *backend;
ESourceRegistryServer *server;
ESource *source;
GNode *node;
const gchar *collection_uid;
const gchar *user_cache_dir;
gulong handler_id;
backend = E_COLLECTION_BACKEND (object);
/* Chain up to parent's constructed() method. */
G_OBJECT_CLASS (e_collection_backend_parent_class)->constructed (object);
server = e_collection_backend_ref_server (backend);
source = e_backend_get_source (E_BACKEND (backend));
/* Determine the backend's cache directory. */
user_cache_dir = e_get_user_cache_dir ();
collection_uid = e_source_get_uid (source);
backend->priv->cache_dir = g_build_filename (
user_cache_dir, "sources", collection_uid, NULL);
g_mkdir_with_parents (backend->priv->cache_dir, 0700);
/* Track the proxy resolver for this backend. */
backend->priv->authentication_source =
e_source_registry_server_find_extension (
server, source, E_SOURCE_EXTENSION_AUTHENTICATION);
if (backend->priv->authentication_source != NULL) {
gulong handler_id;
handler_id = g_signal_connect_data (
backend->priv->authentication_source, "changed",
G_CALLBACK (collection_backend_auth_source_changed_cb),
e_weak_ref_new (backend),
(GClosureNotify) e_weak_ref_free, 0);
backend->priv->auth_source_changed_handler_id = handler_id;
collection_backend_update_proxy_resolver (backend);
}
/* This requires the cache directory to be set. */
collection_backend_load_resources (backend);
/* Emit "child-added" signals for the children we already have. */
node = e_server_side_source_get_node (E_SERVER_SIDE_SOURCE (source));
node = g_node_first_child (node);
while (node != NULL) {
ESource *child = E_SOURCE (node->data);
g_signal_emit (backend, signals[CHILD_ADDED], 0, child);
node = g_node_next_sibling (node);
}
/* Listen for "source-added" and "source-removed" signals
* from the server, which may trigger our own "child-added"
* and "child-removed" signals. */
handler_id = g_signal_connect (
server, "source-added",
G_CALLBACK (collection_backend_source_added_cb), backend);
backend->priv->source_added_handler_id = handler_id;
handler_id = g_signal_connect (
server, "source-removed",
G_CALLBACK (collection_backend_source_removed_cb), backend);
backend->priv->source_removed_handler_id = handler_id;
g_object_unref (server);
backend->priv->notify_enabled_handler_id = g_signal_connect (source, "notify::enabled",
G_CALLBACK (collection_backend_source_enabled_cb), backend);
if (e_source_has_extension (source, E_SOURCE_EXTENSION_COLLECTION)) {
ESourceCollection *collection_extension;
collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
backend->priv->notify_collection_handler_id = g_signal_connect (collection_extension, "notify",
G_CALLBACK (collection_backend_notify_collection_cb), backend);
}
/* Populate the newly-added collection from an idle callback
* so persistent child sources have a chance to be added first. */
collection_backend_schedule_populate_idle (backend);
backend->priv->notify_online_handler_id = g_signal_connect (backend, "notify::online",
G_CALLBACK (collection_backend_online_cb), NULL);
}
static void
collection_backend_populate (ECollectionBackend *backend)
{
/* Placeholder so subclasses can safely chain up. */
}
static gchar *
collection_backend_dup_resource_id (ECollectionBackend *backend,
ESource *source)
{
const gchar *extension_name;
gchar *resource_id = NULL;
extension_name = E_SOURCE_EXTENSION_RESOURCE;
if (e_source_has_extension (source, extension_name)) {
ESourceResource *extension;
extension = e_source_get_extension (source, extension_name);
resource_id = e_source_resource_dup_identity (extension);
}
return resource_id;
}
static void
collection_backend_child_added (ECollectionBackend *backend,
ESource *child_source)
{
ESource *collection_source;
const gchar *extension_name;
gboolean is_mail = FALSE;
collection_backend_children_insert (backend, child_source);
collection_backend_bind_child_enabled (backend, child_source);
collection_source = e_backend_get_source (E_BACKEND (backend));
extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
is_mail |= e_source_has_extension (child_source, extension_name);
extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY;
is_mail |= e_source_has_extension (child_source, extension_name);
extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
is_mail |= e_source_has_extension (child_source, extension_name);
/* Synchronize mail-related display names with the collection. */
if (is_mail)
e_binding_bind_property (
collection_source, "display-name",
child_source, "display-name",
G_BINDING_SYNC_CREATE);
/* Collection children are not removable. */
e_server_side_source_set_removable (
E_SERVER_SIDE_SOURCE (child_source), FALSE);
/* Collection children inherit OAuth 2.0 support if available. */
e_binding_bind_property (
collection_source, "oauth2-support",
child_source, "oauth2-support",
G_BINDING_SYNC_CREATE);
}
static void
collection_backend_child_removed (ECollectionBackend *backend,
ESource *child_source)
{
collection_backend_children_remove (backend, child_source);
}
static gboolean
collection_backend_create_resource_sync (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GError **error)
{
EAsyncClosure *closure;
GAsyncResult *result;
gboolean success;
closure = e_async_closure_new ();
e_collection_backend_create_resource (
backend, source, cancellable,
e_async_closure_callback, closure);
result = e_async_closure_wait (closure);
success = e_collection_backend_create_resource_finish (
backend, result, error);
e_async_closure_free (closure);
return success;
}
static void
collection_backend_create_resource (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
simple = g_simple_async_result_new_error (
G_OBJECT (backend), callback, user_data,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("%s does not support creating remote resources"),
G_OBJECT_TYPE_NAME (backend));
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
}
static gboolean
collection_backend_create_resource_finish (ECollectionBackend *backend,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
simple = G_SIMPLE_ASYNC_RESULT (result);
/* Assume success unless a GError is set. */
return !g_simple_async_result_propagate_error (simple, error);
}
static gboolean
collection_backend_delete_resource_sync (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GError **error)
{
EAsyncClosure *closure;
GAsyncResult *result;
gboolean success;
closure = e_async_closure_new ();
e_collection_backend_delete_resource (
backend, source, cancellable,
e_async_closure_callback, closure);
result = e_async_closure_wait (closure);
success = e_collection_backend_delete_resource_finish (
backend, result, error);
e_async_closure_free (closure);
return success;
}
static void
collection_backend_delete_resource (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GSimpleAsyncResult *simple;
simple = g_simple_async_result_new_error (
G_OBJECT (backend), callback, user_data,
G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
_("%s does not support deleting remote resources"),
G_OBJECT_TYPE_NAME (backend));
g_simple_async_result_complete_in_idle (simple);
g_object_unref (simple);
}
static gboolean
collection_backend_delete_resource_finish (ECollectionBackend *backend,
GAsyncResult *result,
GError **error)
{
GSimpleAsyncResult *simple;
simple = G_SIMPLE_ASYNC_RESULT (result);
/* Assume success unless a GError is set. */
return !g_simple_async_result_propagate_error (simple, error);
}
static void
e_collection_backend_class_init (ECollectionBackendClass *class)
{
GObjectClass *object_class;
g_type_class_add_private (class, sizeof (ECollectionBackendPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->set_property = collection_backend_set_property;
object_class->get_property = collection_backend_get_property;
object_class->dispose = collection_backend_dispose;
object_class->finalize = collection_backend_finalize;
object_class->constructed = collection_backend_constructed;
class->populate = collection_backend_populate;
class->dup_resource_id = collection_backend_dup_resource_id;
class->child_added = collection_backend_child_added;
class->child_removed = collection_backend_child_removed;
class->create_resource_sync = collection_backend_create_resource_sync;
class->create_resource = collection_backend_create_resource;
class->create_resource_finish = collection_backend_create_resource_finish;
class->delete_resource_sync = collection_backend_delete_resource_sync;
class->delete_resource = collection_backend_delete_resource;
class->delete_resource_finish = collection_backend_delete_resource_finish;
g_object_class_install_property (
object_class,
PROP_PROXY_RESOLVER,
g_param_spec_object (
"proxy-resolver",
"Proxy Resolver",
"The proxy resolver for this backend",
G_TYPE_PROXY_RESOLVER,
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
g_object_class_install_property (
object_class,
PROP_SERVER,
g_param_spec_object (
"server",
"Server",
"The server to which the backend belongs",
E_TYPE_SOURCE_REGISTRY_SERVER,
G_PARAM_READWRITE |
G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS));
/**
* ECollectionBackend::child-added:
* @backend: the #ECollectionBackend which emitted the signal
* @child_source: the newly-added child #EServerSideSource
*
* Emitted when an #EServerSideSource is added to @backend's
* #ECollectionBackend:server as a child of @backend's collection
* #EBackend:source.
*
* You can think of this as a filtered version of
* #ESourceRegistryServer's #ESourceRegistryServer::source-added
* signal which only lets through sources relevant to @backend.
**/
signals[CHILD_ADDED] = g_signal_new (
"child-added",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ECollectionBackendClass, child_added),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
E_TYPE_SERVER_SIDE_SOURCE);
/**
* ECollectionBackend::child-removed:
* @backend: the #ECollectionBackend which emitted the signal
* @child_source: the child #EServerSideSource that got removed
*
* Emitted when an #EServerSideSource that is a child of
* @backend's collection #EBackend:source is removed from
* @backend's #ECollectionBackend:server.
*
* You can think of this as a filtered version of
* #ESourceRegistryServer's #ESourceRegistryServer::source-removed
* signal which only lets through sources relevant to @backend.
**/
signals[CHILD_REMOVED] = g_signal_new (
"child-removed",
G_OBJECT_CLASS_TYPE (object_class),
G_SIGNAL_RUN_LAST,
G_STRUCT_OFFSET (ECollectionBackendClass, child_removed),
NULL, NULL, NULL,
G_TYPE_NONE, 1,
E_TYPE_SERVER_SIDE_SOURCE);
}
static void
e_collection_backend_init (ECollectionBackend *backend)
{
GHashTable *children;
GHashTable *unclaimed_resources;
children = g_hash_table_new_full (
(GHashFunc) e_source_hash,
(GEqualFunc) e_source_equal,
(GDestroyNotify) g_object_unref,
(GDestroyNotify) NULL);
unclaimed_resources = g_hash_table_new_full (
(GHashFunc) g_str_hash,
(GEqualFunc) g_str_equal,
(GDestroyNotify) g_free,
(GDestroyNotify) g_object_unref);
backend->priv = E_COLLECTION_BACKEND_GET_PRIVATE (backend);
backend->priv->children = children;
g_mutex_init (&backend->priv->children_lock);
g_mutex_init (&backend->priv->property_lock);
backend->priv->unclaimed_resources = unclaimed_resources;
g_mutex_init (&backend->priv->unclaimed_resources_lock);
g_weak_ref_init (&backend->priv->server, NULL);
}
/**
* e_collection_backend_new_child:
* @backend: an #ECollectionBackend
* @resource_id: a stable and unique resource ID
*
* Creates a new #EServerSideSource as a child of the collection
* #EBackend:source owned by @backend. If possible, the #EServerSideSource
* is drawn from a cache of previously used sources indexed by @resource_id
* so that locally cached data from previous sessions can be reused.
*
* The returned data source should be passed to
* e_source_registry_server_add_source() to export it over D-Bus.
*
* Return: a newly-created data source
*
* Since: 3.6
**/
ESource *
e_collection_backend_new_child (ECollectionBackend *backend,
const gchar *resource_id)
{
ESource *collection_source;
ESource *child_source;
GError *error = NULL;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
g_return_val_if_fail (resource_id != NULL, NULL);
/* This being a newly-created or existing data source, claiming
* it should never fail but we'll check for errors just the same.
* It's unlikely enough that we don't need a GError parameter. */
child_source = collection_backend_claim_resource (
backend, resource_id, &error);
if (error != NULL) {
g_warn_if_fail (child_source == NULL);
g_warning ("%s: %s", G_STRFUNC, error->message);
g_error_free (error);
return NULL;
}
collection_source = e_backend_get_source (E_BACKEND (backend));
e_source_registry_debug_print (
"%s: Pairing %s with resource %s\n",
e_source_get_display_name (collection_source),
e_source_get_uid (child_source), resource_id);
return child_source;
}
/**
* e_collection_backend_ref_proxy_resolver:
* @backend: an #ECollectionBackend
*
* Returns the #GProxyResolver for @backend (if applicable), as indicated
* by the #ESourceAuthentication:proxy-uid of @backend's #EBackend:source
* or one of its ancestors.
*
* The returned #GProxyResolver is referenced for thread-safety and must
* be unreferenced with g_object_unref() when finished with it.
*
* Returns: a #GProxyResolver, or %NULL
*
* Since: 3.12
**/
GProxyResolver *
e_collection_backend_ref_proxy_resolver (ECollectionBackend *backend)
{
GProxyResolver *proxy_resolver = NULL;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
g_mutex_lock (&backend->priv->property_lock);
if (backend->priv->proxy_resolver != NULL)
proxy_resolver = g_object_ref (backend->priv->proxy_resolver);
g_mutex_unlock (&backend->priv->property_lock);
return proxy_resolver;
}
/**
* e_collection_backend_ref_server:
* @backend: an #ECollectionBackend
*
* Returns the #ESourceRegistryServer to which @backend belongs.
*
* The returned #ESourceRegistryServer is referenced for thread-safety.
* Unreference the #ESourceRegistryServer with g_object_unref() when
* finished with it.
*
* Returns: the #ESourceRegisterServer for @backend
*
* Since: 3.6
**/
ESourceRegistryServer *
e_collection_backend_ref_server (ECollectionBackend *backend)
{
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
return g_weak_ref_get (&backend->priv->server);
}
/**
* e_collection_backend_get_cache_dir:
* @backend: an #ECollectionBackend
*
* Returns the private cache directory path for @backend, which is named
* after the #ESource:uid of @backend's collection #EBackend:source.
*
* The cache directory is meant to store key files for backend-created
* data sources. See also: e_server_side_source_set_write_directory()
*
* Returns: the cache directory for @backend
*
* Since: 3.6
**/
const gchar *
e_collection_backend_get_cache_dir (ECollectionBackend *backend)
{
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
return backend->priv->cache_dir;
}
/**
* e_collection_backend_dup_resource_id:
* @backend: an #ECollectionBackend
* @child_source: an #ESource managed by @backend
*
* Extracts the resource ID for @child_source, which is supposed to be a
* stable and unique server-assigned identifier for the remote resource
* described by @child_source. If @child_source is not actually a child
* of the collection #EBackend:source owned by @backend, the function
* returns %NULL.
*
* The returned string should be freed with g_free() when no longer needed.
*
* Returns: a newly-allocated resource ID for @child_source, or %NULL
*
* Since: 3.6
**/
gchar *
e_collection_backend_dup_resource_id (ECollectionBackend *backend,
ESource *child_source)
{
ECollectionBackend *backend_for_child_source;
ECollectionBackendClass *class;
ESourceRegistryServer *server;
gboolean child_is_ours = FALSE;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
g_return_val_if_fail (E_IS_SOURCE (child_source), NULL);
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->dup_resource_id != NULL, NULL);
/* Make sure the ESource belongs to the ECollectionBackend to
* avoid accidentally creating a new extension while trying to
* extract a resource ID that isn't there. Better to test this
* up front than rely on ECollectionBackend subclasses to do it. */
server = e_collection_backend_ref_server (backend);
backend_for_child_source =
e_source_registry_server_ref_backend (server, child_source);
g_object_unref (server);
if (backend_for_child_source != NULL) {
child_is_ours = (backend_for_child_source == backend);
g_object_unref (backend_for_child_source);
}
if (!child_is_ours)
return NULL;
return class->dup_resource_id (backend, child_source);
}
/**
* e_collection_backend_claim_all_resources:
* @backend: an #ECollectionBackend
*
* Claims all previously used sources that have not yet been claimed by
* e_collection_backend_new_child() and returns them in a #GList. Note
* that previously used sources can only be claimed once, so subsequent
* calls to this function for @backend will return %NULL.
*
* The @backend is then expected to compare the returned list with a
* current list of resources from a remote server, create new #ESource
* instances as needed with e_collection_backend_new_child(), discard
* unneeded #ESource instances with e_source_remove(), and export the
* remaining instances with e_source_registry_server_add_source().
*
* The sources returned in the list are referenced for thread-safety.
* They must each be unreferenced with g_object_unref() when finished
* with them. Free the returned #GList itself with g_list_free().
*
* An easy way to free the list properly in one step is as follows:
*
* |[
* g_list_free_full (list, g_object_unref);
* ]|
*
* Returns: a list of previously used sources
*
* Since: 3.6
**/
GList *
e_collection_backend_claim_all_resources (ECollectionBackend *backend)
{
GHashTable *unclaimed_resources;
GList *resources;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
g_mutex_lock (&backend->priv->unclaimed_resources_lock);
unclaimed_resources = backend->priv->unclaimed_resources;
resources = g_hash_table_get_values (unclaimed_resources);
g_list_foreach (resources, (GFunc) g_object_ref, NULL);
g_hash_table_remove_all (unclaimed_resources);
g_mutex_unlock (&backend->priv->unclaimed_resources_lock);
return resources;
}
/**
* e_collection_backend_list_calendar_sources:
* @backend: an #ECollectionBackend
*
* Returns a list of calendar sources belonging to the data source
* collection managed by @backend.
*
* The sources returned in the list are referenced for thread-safety.
* They must each be unreferenced with g_object_unref() when finished
* with them. Free the returned #GList itself with g_list_free().
*
* An easy way to free the list properly in one step is as follows:
*
* |[
* g_list_free_full (list, g_object_unref);
* ]|
*
* Returns: a list of calendar sources
*
* Since: 3.6
**/
GList *
e_collection_backend_list_calendar_sources (ECollectionBackend *backend)
{
GList *result_list = NULL;
GList *list, *link;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
list = collection_backend_children_list (backend);
for (link = list; link != NULL; link = g_list_next (link)) {
ESource *child_source = E_SOURCE (link->data);
if (collection_backend_child_is_calendar (child_source))
result_list = g_list_prepend (
result_list, g_object_ref (child_source));
}
g_list_free_full (list, (GDestroyNotify) g_object_unref);
return g_list_reverse (result_list);
}
/**
* e_collection_backend_list_contacts_sources:
* @backend: an #ECollectionBackend
*
* Returns a list of address book sources belonging to the data source
* collection managed by @backend.
*
* The sources returned in the list are referenced for thread-safety.
* They must each be unreferenced with g_object_unref() when finished
* with them. Free the returned #GList itself with g_list_free().
*
* An easy way to free the list properly in one step is as follows:
*
* |[
* g_list_free_full (list, g_object_unref);
* ]|
*
* Returns: a list of address book sources
*
* Since: 3.6
**/
GList *
e_collection_backend_list_contacts_sources (ECollectionBackend *backend)
{
GList *result_list = NULL;
GList *list, *link;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
list = collection_backend_children_list (backend);
for (link = list; link != NULL; link = g_list_next (link)) {
ESource *child_source = E_SOURCE (link->data);
if (collection_backend_child_is_contacts (child_source))
result_list = g_list_prepend (
result_list, g_object_ref (child_source));
}
g_list_free_full (list, (GDestroyNotify) g_object_unref);
return g_list_reverse (result_list);
}
/**
* e_collection_backend_list_mail_sources:
* @backend: an #ECollectionBackend
*
* Returns a list of mail sources belonging to the data source collection
* managed by @backend.
*
* The sources returned in the list are referenced for thread-safety.
* They must each be unreferenced with g_object_unref() when finished
* with them. Free the returned #GList itself with g_list_free().
*
* An easy way to free the list properly in one step is as follows:
*
* |[
* g_list_free_full (list, g_object_unref);
* ]|
*
* Returns: a list of mail sources
*
* Since: 3.6
**/
GList *
e_collection_backend_list_mail_sources (ECollectionBackend *backend)
{
GList *result_list = NULL;
GList *list, *link;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), NULL);
list = collection_backend_children_list (backend);
for (link = list; link != NULL; link = g_list_next (link)) {
ESource *child_source = E_SOURCE (link->data);
if (collection_backend_child_is_mail (child_source))
result_list = g_list_prepend (
result_list, g_object_ref (child_source));
}
g_list_free_full (list, (GDestroyNotify) g_object_unref);
return g_list_reverse (result_list);
}
/**
* e_collection_backend_create_resource_sync
* @backend: an #ECollectionBackend
* @source: an #ESource
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Creates a server-side resource described by @source. For example, if
* @source describes a new calendar, an equivalent calendar is created on
* the server.
*
* It is the implementor's responsibility to examine @source and determine
* what the equivalent server-side resource would be. If this cannot be
* determined without ambiguity, the function must return an error.
*
* After the server-side resource is successfully created, the implementor
* must also add an #ESource to @backend's #ECollectionBackend:server. This
* can either be done immediately or in response to some "resource created"
* notification from the server. The added #ESource can be @source itself
* or a different #ESource instance that describes the new resource.
*
* If an error occurs, the function will set @error and return %FALSE.
*
* Returns: %TRUE on success, %FALSE on failure
*
* Since: 3.6
**/
gboolean
e_collection_backend_create_resource_sync (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GError **error)
{
ECollectionBackendClass *class;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->create_resource_sync != NULL, FALSE);
return class->create_resource_sync (
backend, source, cancellable, error);
}
/**
* e_collection_backend_create_resource:
* @backend: an #ECollectionBackend
* @source: an #ESource
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously creates a server-side resource described by @source.
* For example, if @source describes a new calendar, an equivalent calendar
* is created on the server.
*
* It is the implementor's responsibility to examine @source and determine
* what the equivalent server-side resource would be. If this cannot be
* determined without ambiguity, the function must return an error.
*
* After the server-side resource is successfully created, the implementor
* must also add an #ESource to @backend's #ECollectionBackend:server. This
* can either be done immediately or in response to some "resource created"
* notification from the server. The added #ESource can be @source itself
* or a different #ESource instance that describes the new resource.
*
* When the operation is finished, @callback will be called. You can then
* call e_collection_backend_create_resource_finish() to get the result of
* the operation.
*
* Since: 3.6
**/
void
e_collection_backend_create_resource (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
ECollectionBackendClass *class;
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
g_return_if_fail (E_IS_SOURCE (source));
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_if_fail (class->create_resource != NULL);
class->create_resource (
backend, source, cancellable, callback, user_data);
}
/**
* e_collection_backend_create_resource_finish:
* @backend: an #ECollectionBackend
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with e_collection_backend_create_resource().
*
* If an error occurred, the function will set @error and return %FALSE.
*
* Returns: %TRUE on success, %FALSE on failure
*
* Since: 3.6
**/
gboolean
e_collection_backend_create_resource_finish (ECollectionBackend *backend,
GAsyncResult *result,
GError **error)
{
ECollectionBackendClass *class;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->create_resource_finish != NULL, FALSE);
return class->create_resource_finish (backend, result, error);
}
/**
* e_collection_backend_delete_resource_sync:
* @backend: an #ECollectionBackend
* @source: an #ESource
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Deletes a server-side resource described by @source. The @source must
* be a child of @backend's collection #EBackend:source.
*
* After the server-side resource is successfully deleted, the implementor
* must also remove @source from the @backend's #ECollectionBackend:server.
* This can either be done immediately or in response to some "resource
* deleted" notification from the server.
*
* If an error occurs, the function will set @error and return %FALSE.
*
* Returns: %TRUE on success, %FALSE on failure
*
* Since: 3.6
**/
gboolean
e_collection_backend_delete_resource_sync (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GError **error)
{
ECollectionBackendClass *class;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->delete_resource_sync != NULL, FALSE);
return class->delete_resource_sync (
backend, source, cancellable, error);
}
/**
* e_collection_backend_delete_resource:
* @backend: an #ECollectionBackend
* @source: an #ESource
* @cancellable: optional #GCancellable object, or %NULL
* @callback: a #GAsyncReadyCallback to call when the request is satisfied
* @user_data: data to pass to the callback function
*
* Asynchronously deletes a server-side resource described by @source.
* The @source must be a child of @backend's collection #EBackend:source.
*
* After the server-side resource is successfully deleted, the implementor
* must also remove @source from the @backend's #ECollectionBackend:server.
* This can either be done immediately or in response to some "resource
* deleted" notification from the server.
*
* When the operation is finished, @callback will be called. You can then
* call e_collection_backend_delete_resource_finish() to get the result of
* the operation.
*
* Since: 3.6
**/
void
e_collection_backend_delete_resource (ECollectionBackend *backend,
ESource *source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
ECollectionBackendClass *class;
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
g_return_if_fail (E_IS_SOURCE (source));
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_if_fail (class->delete_resource != NULL);
return class->delete_resource (
backend, source, cancellable, callback, user_data);
}
/**
* e_collection_backend_delete_resource_finish:
* @backend: an #ECollectionBackend
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
* Finishes the operation started with e_collection_backend_delete_resource().
*
* If an error occurred, the function will set @error and return %FALSE.
*
* Returns: %TRUE on success, %FALSE on failure
*
* Since: 3.6
**/
gboolean
e_collection_backend_delete_resource_finish (ECollectionBackend *backend,
GAsyncResult *result,
GError **error)
{
ECollectionBackendClass *class;
g_return_val_if_fail (E_IS_COLLECTION_BACKEND (backend), FALSE);
g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
class = E_COLLECTION_BACKEND_GET_CLASS (backend);
g_return_val_if_fail (class->delete_resource_finish != NULL, FALSE);
return class->delete_resource_finish (backend, result, error);
}
static void
collection_backend_child_authenticate_done_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
ESource *source;
GError *error = NULL;
g_return_if_fail (E_IS_SOURCE (source_object));
source = E_SOURCE (source_object);
if (!e_source_invoke_authenticate_finish (source, result, &error) &&
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
g_warning ("%s: Failed to invoke authenticate for '%s': %s", G_STRFUNC,
e_source_get_uid (source), error ? error->message : "Unknown error");
}
g_clear_error (&error);
}
/**
* e_collection_backend_authenticate_children:
* @backend: an #ECollectionBackend
* @credentials: credentials to authenticate with
*
* Authenticates all enabled children sources with the given @crendetials.
* This is usually called when the collection source successfully used
* the @credentials to connect to the (possibly) remote data store, to
* open the childern too. Already connected child sources are skipped.
*
* Since: 3.16
**/
void
e_collection_backend_authenticate_children (ECollectionBackend *backend,
const ENamedParameters *credentials)
{
ESource *master_source, *child, *cred_source;
ESourceRegistryServer *registry_server;
ESourceCredentialsProvider *credentials_provider;
GList *sources, *link;
g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
master_source = e_backend_get_source (E_BACKEND (backend));
g_return_if_fail (master_source != NULL);
registry_server = e_collection_backend_ref_server (backend);
g_return_if_fail (registry_server != NULL);
credentials_provider = e_source_registry_server_ref_credentials_provider (registry_server);
sources = e_source_registry_server_list_sources (registry_server, NULL);
for (link = sources; link; link = g_list_next (link)) {
child = link->data;
if (child && !e_source_equal (child, master_source) && e_source_get_enabled (child) && (
e_source_get_connection_status (child) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS ||
e_source_get_connection_status (child) == E_SOURCE_CONNECTION_STATUS_DISCONNECTED)) {
cred_source = e_source_credentials_provider_ref_credentials_source (credentials_provider, child);
if (cred_source && e_source_equal (cred_source, master_source)) {
e_source_invoke_authenticate (child, credentials, NULL, collection_backend_child_authenticate_done_cb, NULL);
}
g_clear_object (&cred_source);
}
}
g_list_free_full (sources, g_object_unref);
g_clear_object (&credentials_provider);
g_clear_object (®istry_server);
}