ECalBackend: Remove internal_get_timezone() method.
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend.c
index debe316..a1a6dbf 100644 (file)
@@ -1,8 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 /* Evolution calendar - generic backend class
  *
- * Copyright (C) 2000 Ximian, Inc.
- * Copyright (C) 2000 Ximian, Inc.
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
  *
  * Authors: Federico Mena-Quintero <federico@ximian.com>
  *          JP Rosevear <jpr@ximian.com>
  */
 
 #include <config.h>
-#include <libxml/parser.h>
-#include <libxml/parserInternals.h>
-#include <libxml/xmlmemory.h>
+
+#include <glib/gi18n-lib.h>
 
 #include "e-cal-backend.h"
+#include "e-cal-backend-cache.h"
+
+#define E_CAL_BACKEND_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_CAL_BACKEND, ECalBackendPrivate))
 
-\f
+#define EDC_ERROR(_code)       e_data_cal_create_error (_code, NULL)
+#define EDC_OPENING_ERROR      e_data_cal_create_error (Busy, _("Cannot process, calendar backend is opening"))
+#define EDC_NOT_OPENED_ERROR   e_data_cal_create_error (NotOpened, NULL)
 
 /* Private part of the CalBackend structure */
 struct _ECalBackendPrivate {
-       /* The source for this backend */
-       ESource *source;
-
-       /* URI, from source. This is cached, since we return const. */
-       char *uri;
+       ESourceRegistry *registry;
 
        /* The kind of components for this backend */
        icalcomponent_kind kind;
 
+       gboolean opening, opened, readonly, removed;
+
+       gchar *cache_dir;
+
        /* List of Cal objects */
-       GMutex *clients_mutex;
+       GMutex clients_mutex;
        GList *clients;
 
-       GMutex *queries_mutex;
-       EList *queries;
+       GMutex views_mutex;
+       GList *views;
+
+       GHashTable *zone_cache;
+       GMutex zone_cache_lock;
 
        /* ECalBackend to pass notifications on to */
        ECalBackend *notification_proxy;
+
 };
 
 /* Property IDs */
-enum props {
+enum {
        PROP_0,
-       PROP_SOURCE,
-       PROP_URI,
-       PROP_KIND
+       PROP_CACHE_DIR,
+       PROP_KIND,
+       PROP_REGISTRY
 };
 
-/* Signal IDs */
-enum {
-       LAST_CLIENT_GONE,
-       OPENED,
-       REMOVED,
-       LAST_SIGNAL
-};
-static guint e_cal_backend_signals[LAST_SIGNAL];
+/* Forward Declarations */
+static void    e_cal_backend_remove_client_private
+                                       (ECalBackend *backend,
+                                        EDataCal *cal,
+                                        gboolean weak_unref);
+static void    e_cal_backend_timezone_cache_init
+                                       (ETimezoneCacheInterface *interface);
+
+G_DEFINE_TYPE_WITH_CODE (
+       ECalBackend,
+       e_cal_backend,
+       E_TYPE_BACKEND,
+       G_IMPLEMENT_INTERFACE (
+               E_TYPE_TIMEZONE_CACHE,
+               e_cal_backend_timezone_cache_init))
 
-static void e_cal_backend_class_init (ECalBackendClass *class);
-static void e_cal_backend_init (ECalBackend *backend);
-static void e_cal_backend_finalize (GObject *object);
+static void
+cal_backend_free_zone (icaltimezone *zone)
+{
+       icaltimezone_free (zone, 1);
+}
 
-#define CLASS(backend) (E_CAL_BACKEND_CLASS (G_OBJECT_GET_CLASS (backend)))
+static void
+cal_backend_set_default_cache_dir (ECalBackend *backend)
+{
+       ESource *source;
+       icalcomponent_kind kind;
+       const gchar *component_type;
+       const gchar *user_cache_dir;
+       const gchar *uid;
+       gchar *filename;
 
-static GObjectClass *parent_class;
+       user_cache_dir = e_get_user_cache_dir ();
 
-\f
+       kind = e_cal_backend_get_kind (backend);
+       source = e_backend_get_source (E_BACKEND (backend));
 
-/**
- * e_cal_backend_get_type:
- *
- * Registers the #ECalBackend class if necessary, and returns the type ID
- * associated to it.
- *
- * Return value: The type ID of the #ECalBackend class.
- **/
-GType
-e_cal_backend_get_type (void)
-{
-       static GType e_cal_backend_type = 0;
-
-       if (!e_cal_backend_type) {
-               static GTypeInfo info = {
-                        sizeof (ECalBackendClass),
-                        (GBaseInitFunc) NULL,
-                        (GBaseFinalizeFunc) NULL,
-                        (GClassInitFunc) e_cal_backend_class_init,
-                        NULL, NULL,
-                        sizeof (ECalBackend),
-                        0,
-                        (GInstanceInitFunc) e_cal_backend_init,
-                };
-               e_cal_backend_type = g_type_register_static (G_TYPE_OBJECT, "ECalBackend", &info, 0);
+       uid = e_source_get_uid (source);
+       g_return_if_fail (uid != NULL);
+
+       switch (kind) {
+               case ICAL_VEVENT_COMPONENT:
+                       component_type = "calendar";
+                       break;
+               case ICAL_VTODO_COMPONENT:
+                       component_type = "tasks";
+                       break;
+               case ICAL_VJOURNAL_COMPONENT:
+                       component_type = "memos";
+                       break;
+               default:
+                       g_return_if_reached ();
        }
 
-       return e_cal_backend_type;
+       filename = g_build_filename (
+               user_cache_dir, component_type, uid, NULL);
+       e_cal_backend_set_cache_dir (backend, filename);
+       g_free (filename);
 }
 
 static void
-e_cal_backend_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec)
+cal_backend_get_backend_property (ECalBackend *backend,
+                                  EDataCal *cal,
+                                  guint32 opid,
+                                  GCancellable *cancellable,
+                                  const gchar *prop_name)
 {
-       ECalBackend *backend;
-       ECalBackendPrivate *priv;
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (cal != NULL);
+       g_return_if_fail (prop_name != NULL);
+
+       if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENED)) {
+               e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opened (backend) ? "TRUE" : "FALSE");
+       } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_OPENING)) {
+               e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_opening (backend) ? "TRUE" : "FALSE");
+       } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_ONLINE)) {
+               e_data_cal_respond_get_backend_property (cal, opid, NULL, e_backend_get_online (E_BACKEND (backend)) ? "TRUE" : "FALSE");
+       } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_READONLY)) {
+               e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_is_readonly (backend) ? "TRUE" : "FALSE");
+       } else if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CACHE_DIR)) {
+               e_data_cal_respond_get_backend_property (cal, opid, NULL, e_cal_backend_get_cache_dir (backend));
+       } else {
+               e_data_cal_respond_get_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Unknown calendar property '%s'"), prop_name), NULL);
+       }
+}
 
-       backend = E_CAL_BACKEND (object);
-       priv = backend->priv;
+static void
+cal_backend_set_backend_property (ECalBackend *backend,
+                                  EDataCal *cal,
+                                  guint32 opid,
+                                  GCancellable *cancellable,
+                                  const gchar *prop_name,
+                                  const gchar *prop_value)
+{
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (cal != NULL);
+       g_return_if_fail (prop_name != NULL);
 
-       switch (property_id) {
-       case PROP_SOURCE:
-               {
-                       ESource *new_source;
+       e_data_cal_respond_set_backend_property (cal, opid, e_data_cal_create_error_fmt (NotSupported, _("Cannot change value of calendar property '%s'"), prop_name));
+}
+
+static void
+cal_backend_set_kind (ECalBackend *backend,
+                      icalcomponent_kind kind)
+{
+       backend->priv->kind = kind;
+}
 
-                       new_source = g_value_get_object (value);
-                       if (new_source)
-                               g_object_ref (new_source);
+static void
+cal_backend_set_registry (ECalBackend *backend,
+                          ESourceRegistry *registry)
+{
+       g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+       g_return_if_fail (backend->priv->registry == NULL);
 
-                       if (priv->source)
-                               g_object_unref (priv->source);
+       backend->priv->registry = g_object_ref (registry);
+}
 
-                       priv->source = new_source;
+static void
+cal_backend_set_property (GObject *object,
+                          guint property_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CACHE_DIR:
+                       e_cal_backend_set_cache_dir (
+                               E_CAL_BACKEND (object),
+                               g_value_get_string (value));
+                       return;
+
+               case PROP_KIND:
+                       cal_backend_set_kind (
+                               E_CAL_BACKEND (object),
+                               g_value_get_ulong (value));
+                       return;
+
+               case PROP_REGISTRY:
+                       cal_backend_set_registry (
+                               E_CAL_BACKEND (object),
+                               g_value_get_object (value));
+                       return;
+       }
 
-                       /* Cache the URI */
-                       if (new_source) {
-                               g_free (priv->uri);
-                               priv->uri = e_source_get_uri (priv->source);
-                       }
-               }
-               break;
-       case PROP_URI:
-               if (!priv->source) {
-                       g_free (priv->uri);
-                       priv->uri = g_value_dup_string (value);
-               }
-               break;
-       case PROP_KIND:
-               priv->kind = g_value_get_ulong (value);
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-               break;
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+cal_backend_get_property (GObject *object,
+                          guint property_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_CACHE_DIR:
+                       g_value_set_string (
+                               value, e_cal_backend_get_cache_dir (
+                               E_CAL_BACKEND (object)));
+                       return;
+
+               case PROP_KIND:
+                       g_value_set_ulong (
+                               value, e_cal_backend_get_kind (
+                               E_CAL_BACKEND (object)));
+                       return;
+
+               case PROP_REGISTRY:
+                       g_value_set_object (
+                               value, e_cal_backend_get_registry (
+                               E_CAL_BACKEND (object)));
+                       return;
        }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 }
 
 static void
-e_cal_backend_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec)
+cal_backend_dispose (GObject *object)
 {
-       ECalBackend *backend;
        ECalBackendPrivate *priv;
 
-       backend = E_CAL_BACKEND (object);
-       priv = backend->priv;
+       priv = E_CAL_BACKEND_GET_PRIVATE (object);
 
-       switch (property_id) {
-       case PROP_SOURCE:
-               g_value_set_object (value, e_cal_backend_get_source (backend));
-               break;
-       case PROP_URI:
-               g_value_set_string (value, e_cal_backend_get_uri (backend));
-               break;
-       case PROP_KIND:
-               g_value_set_ulong (value, e_cal_backend_get_kind (backend));
-               break;
-       default:
-               G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
-               break;
+       if (priv->registry != NULL) {
+               g_object_unref (priv->registry);
+               priv->registry = NULL;
        }
+
+       /* Chain up to parent's dispose() method. */
+       G_OBJECT_CLASS (e_cal_backend_parent_class)->dispose (object);
 }
 
-/* Class initialization function for the calendar backend */
 static void
-e_cal_backend_class_init (ECalBackendClass *class)
+cal_backend_finalize (GObject *object)
 {
-       GObjectClass *object_class;
+       ECalBackendPrivate *priv;
+
+       priv = E_CAL_BACKEND_GET_PRIVATE (object);
+
+       g_assert (priv->clients == NULL);
+
+       /* should be NULL, anyway */
+       g_list_free (priv->clients);
+       g_mutex_clear (&priv->clients_mutex);
+
+       g_list_free (priv->views);
+       g_mutex_clear (&priv->views_mutex);
+
+       g_hash_table_destroy (priv->zone_cache);
+       g_mutex_clear (&priv->zone_cache_lock);
+
+       g_free (priv->cache_dir);
+
+       /* Chain up to parent's finalize() method. */
+       G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
+}
 
-       parent_class = (GObjectClass *) g_type_class_peek_parent (class);
-
-       object_class = (GObjectClass *) class;
-
-       object_class->set_property = e_cal_backend_set_property;
-       object_class->get_property = e_cal_backend_get_property;
-       object_class->finalize = e_cal_backend_finalize;
-
-       g_object_class_install_property (object_class, PROP_SOURCE,
-                                        g_param_spec_object ("source", NULL, NULL, E_TYPE_SOURCE,
-                                                             G_PARAM_READABLE | G_PARAM_WRITABLE
-                                                             | G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property (object_class, PROP_URI,
-                                        g_param_spec_string ("uri", NULL, NULL, "",
-                                                             G_PARAM_READABLE | G_PARAM_WRITABLE
-                                                             | G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property (object_class, PROP_KIND,
-                                        g_param_spec_ulong ("kind", NULL, NULL,
-                                                            ICAL_NO_COMPONENT, ICAL_XLICMIMEPART_COMPONENT,
-                                                            ICAL_NO_COMPONENT,
-                                                            G_PARAM_READABLE | G_PARAM_WRITABLE
-                                                            | G_PARAM_CONSTRUCT_ONLY));
-       e_cal_backend_signals[LAST_CLIENT_GONE] =
-               g_signal_new ("last_client_gone",
-                             G_TYPE_FROM_CLASS (class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (ECalBackendClass, last_client_gone),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__VOID,
-                             G_TYPE_NONE, 0);
-       e_cal_backend_signals[OPENED] =
-               g_signal_new ("opened",
-                             G_TYPE_FROM_CLASS (class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (ECalBackendClass, opened),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__ENUM,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
-       e_cal_backend_signals[REMOVED] =
-               g_signal_new ("removed",
-                             G_TYPE_FROM_CLASS (class),
-                             G_SIGNAL_RUN_FIRST,
-                             G_STRUCT_OFFSET (ECalBackendClass, removed),
-                             NULL, NULL,
-                             g_cclosure_marshal_VOID__ENUM,
-                             G_TYPE_NONE, 1,
-                             G_TYPE_INT);
-
-       class->last_client_gone = NULL;
-       class->opened = NULL;
-       class->obj_updated = NULL;
-
-       class->get_cal_address = NULL;
-       class->get_alarm_email_address = NULL;
-       class->get_static_capabilities = NULL;
-       class->open = NULL;
-       class->is_loaded = NULL;
-       class->is_read_only = NULL;
-       class->start_query = NULL;
-       class->get_mode = NULL;
-       class->set_mode = NULL;
-       class->get_object = NULL;
-       class->get_default_object = NULL;
-       class->get_object_list = NULL;
-       class->get_free_busy = NULL;
-       class->get_changes = NULL;
-       class->discard_alarm = NULL;
-       class->create_object = NULL;
-       class->modify_object = NULL;
-       class->remove_object = NULL;
-       class->receive_objects = NULL;
-       class->send_objects = NULL;
-       class->get_timezone = NULL;
-       class->add_timezone = NULL;
-       class->set_default_timezone = NULL;
-}
-
-/* Object initialization func for the calendar backend */
 static void
-e_cal_backend_init (ECalBackend *backend)
+cal_backend_constructed (GObject *object)
 {
-       ECalBackendPrivate *priv;
+       cal_backend_set_default_cache_dir (E_CAL_BACKEND (object));
 
-       priv = g_new0 (ECalBackendPrivate, 1);
-       backend->priv = priv;
+       G_OBJECT_CLASS (e_cal_backend_parent_class)->constructed (object);
+}
+
+static gboolean
+cal_backend_authenticate_sync (EBackend *backend,
+                               ESourceAuthenticator *auth,
+                               GCancellable *cancellable,
+                               GError **error)
+{
+       ECalBackend *cal_backend;
+       ESourceRegistry *registry;
+       ESource *source;
 
-       priv->clients = NULL;
-       priv->clients_mutex = g_mutex_new ();
+       cal_backend = E_CAL_BACKEND (backend);
+       registry = e_cal_backend_get_registry (cal_backend);
+       source = e_backend_get_source (backend);
 
-       /* FIXME bonobo_object_ref/unref? */
-       priv->queries = e_list_new((EListCopyFunc) g_object_ref, (EListFreeFunc) g_object_unref, NULL);
-       priv->queries_mutex = g_mutex_new ();
+       return e_source_registry_authenticate_sync (
+               registry, source, auth, cancellable, error);
 }
 
 static void
-e_cal_backend_finalize (GObject *object)
+cal_backend_add_cached_timezone (ETimezoneCache *cache,
+                                 icaltimezone *zone)
 {
-       ECalBackend *backend = (ECalBackend *)object;
        ECalBackendPrivate *priv;
+       const gchar *tzid;
 
-       priv = backend->priv;
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
 
-       g_assert (priv->clients == NULL);
+       g_mutex_lock (&priv->zone_cache_lock);
 
-       g_object_unref (priv->queries);
+       tzid = icaltimezone_get_tzid (zone);
 
-       g_mutex_free (priv->clients_mutex);
-       g_mutex_free (priv->queries_mutex);
+       /* Avoid replacing an existing cache entry.  We don't want to
+        * invalidate any icaltimezone pointers that may have already
+        * been returned through e_timezone_cache_get_timezone(). */
+       if (!g_hash_table_contains (priv->zone_cache, tzid)) {
+               icalcomponent *icalcomp;
+               icaltimezone *cached_zone;
 
-       g_free (priv->uri);
-       g_object_unref (priv->source);
-       g_free (priv);
+               cached_zone = icaltimezone_new ();
+               icalcomp = icaltimezone_get_component (zone);
+               icalcomp = icalcomponent_new_clone (icalcomp);
+               icaltimezone_set_component (cached_zone, icalcomp);
 
-       G_OBJECT_CLASS (parent_class)->finalize (object);
-}
+               g_hash_table_insert (
+                       priv->zone_cache,
+                       g_strdup (tzid), cached_zone);
 
-\f
+               /* FIXME Should emit this from an idle GSource on
+                *       a stored GMainContext, but we don't have
+                *       a stored GMainContext.  Check back after
+                *       the D-Bus API rewrite. */
+               g_signal_emit_by_name (cache, "timezone-added", zone);
+       }
 
-/**
- * e_cal_backend_get_source:
- * @backend: An #ECalBackend object.
- *
- * Gets the #ESource associated with the given backend.
- *
- * Return value: The #ESource for the backend.
- */
-ESource *
-e_cal_backend_get_source (ECalBackend *backend)
+       g_mutex_unlock (&priv->zone_cache_lock);
+}
+
+static icaltimezone *
+cal_backend_get_cached_timezone (ETimezoneCache *cache,
+                                 const gchar *tzid)
 {
        ECalBackendPrivate *priv;
+       icaltimezone *zone = NULL;
+       icaltimezone *builtin_zone = NULL;
+       icalcomponent *icalcomp;
+       icalproperty *prop;
+       const gchar *builtin_tzid;
 
-       g_return_val_if_fail (backend != NULL, NULL);
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
 
-       priv = backend->priv;
+       if (g_str_equal (tzid, "UTC"))
+               return icaltimezone_get_utc_timezone ();
+
+       g_mutex_lock (&priv->zone_cache_lock);
+
+       /* See if we already have it in the cache. */
+       zone = g_hash_table_lookup (priv->zone_cache, tzid);
+
+       if (zone != NULL)
+               goto exit;
+
+       /* Try to replace the original time zone with a more complete
+        * and/or potentially updated built-in time zone.  Note this also
+        * applies to TZIDs which match built-in time zones exactly: they
+        * are extracted via icaltimezone_get_builtin_timezone_from_tzid(). */
+
+       builtin_tzid = e_cal_match_tzid (tzid);
+
+       if (builtin_tzid != NULL)
+               builtin_zone = icaltimezone_get_builtin_timezone_from_tzid (
+                       builtin_tzid);
+
+       if (builtin_zone == NULL)
+               goto exit;
+
+       /* Use the built-in time zone *and* rename it.  Likely the caller
+        * is asking for a specific TZID because it has an event with such
+        * a TZID.  Returning an icaltimezone with a different TZID would
+        * lead to broken VCALENDARs in the caller. */
+
+       icalcomp = icaltimezone_get_component (builtin_zone);
+       icalcomp = icalcomponent_new_clone (icalcomp);
+
+       prop = icalcomponent_get_first_property (
+               icalcomp, ICAL_ANY_PROPERTY);
+
+       while (prop != NULL) {
+               if (icalproperty_isa (prop) == ICAL_TZID_PROPERTY) {
+                       icalproperty_set_value_from_string (prop, tzid, "NO");
+                       break;
+               }
 
-       return priv->source;
+               prop = icalcomponent_get_next_property (
+                       icalcomp, ICAL_ANY_PROPERTY);
+       }
+
+       if (icalcomp != NULL) {
+               zone = icaltimezone_new ();
+               if (icaltimezone_set_component (zone, icalcomp)) {
+                       tzid = icaltimezone_get_tzid (zone);
+                       g_hash_table_insert (
+                               priv->zone_cache,
+                               g_strdup (tzid), zone);
+               } else {
+                       icalcomponent_free (icalcomp);
+                       icaltimezone_free (zone, 1);
+                       zone = NULL;
+               }
+       }
+
+exit:
+       g_mutex_unlock (&priv->zone_cache_lock);
+
+       return zone;
 }
 
-/**
- * e_cal_backend_get_uri:
- * @backend: A calendar backend.
- *
- * Queries the URI of a calendar backend, which must already have an open
- * calendar.
- *
- * Return value: The URI where the calendar is stored.
- **/
-const char *
-e_cal_backend_get_uri (ECalBackend *backend)
+static GList *
+cal_backend_list_cached_timezones (ETimezoneCache *cache)
 {
        ECalBackendPrivate *priv;
+       GList *list;
 
-       g_return_val_if_fail (backend != NULL, NULL);
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
 
-       priv = backend->priv;
+       g_mutex_lock (&priv->zone_cache_lock);
+
+       list = g_hash_table_get_values (priv->zone_cache);
+
+       g_mutex_unlock (&priv->zone_cache_lock);
 
-       return priv->uri;
+       return list;
+}
+
+static void
+e_cal_backend_class_init (ECalBackendClass *class)
+{
+       GObjectClass *object_class;
+       EBackendClass *backend_class;
+
+       g_type_class_add_private (class, sizeof (ECalBackendPrivate));
+
+       object_class = G_OBJECT_CLASS (class);
+       object_class->set_property = cal_backend_set_property;
+       object_class->get_property = cal_backend_get_property;
+       object_class->dispose = cal_backend_dispose;
+       object_class->finalize = cal_backend_finalize;
+       object_class->constructed = cal_backend_constructed;
+
+       backend_class = E_BACKEND_CLASS (class);
+       backend_class->authenticate_sync = cal_backend_authenticate_sync;
+
+       class->get_backend_property = cal_backend_get_backend_property;
+       class->set_backend_property = cal_backend_set_backend_property;
+
+       g_object_class_install_property (
+               object_class,
+               PROP_CACHE_DIR,
+               g_param_spec_string (
+                       "cache-dir",
+                       "Cache Dir",
+                       "The backend's cache directory",
+                       NULL,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_KIND,
+               g_param_spec_ulong (
+                       "kind",
+                       "Kind",
+                       "The kind of iCalendar components "
+                       "this backend manages",
+                       ICAL_NO_COMPONENT,
+                       ICAL_XLICMIMEPART_COMPONENT,
+                       ICAL_NO_COMPONENT,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       g_object_class_install_property (
+               object_class,
+               PROP_REGISTRY,
+               g_param_spec_object (
+                       "registry",
+                       "Registry",
+                       "Data source registry",
+                       E_TYPE_SOURCE_REGISTRY,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_cal_backend_timezone_cache_init (ETimezoneCacheInterface *interface)
+{
+       interface->add_timezone = cal_backend_add_cached_timezone;
+       interface->get_timezone = cal_backend_get_cached_timezone;
+       interface->list_timezones = cal_backend_list_cached_timezones;
+}
+
+static void
+e_cal_backend_init (ECalBackend *backend)
+{
+       GHashTable *zone_cache;
+
+       zone_cache = g_hash_table_new_full (
+               (GHashFunc) g_str_hash,
+               (GEqualFunc) g_str_equal,
+               (GDestroyNotify) g_free,
+               (GDestroyNotify) cal_backend_free_zone);
+
+       backend->priv = E_CAL_BACKEND_GET_PRIVATE (backend);
+
+       backend->priv->clients = NULL;
+       g_mutex_init (&backend->priv->clients_mutex);
+
+       backend->priv->views = NULL;
+       g_mutex_init (&backend->priv->views_mutex);
+
+       backend->priv->zone_cache = zone_cache;
+       g_mutex_init (&backend->priv->zone_cache_lock);
+
+       backend->priv->readonly = TRUE;
 }
 
 /**
  * e_cal_backend_get_kind:
- * @backend: An #ECalBackend object.
+ * @backend: an #ECalBackend
  *
  * Gets the kind of components the given backend stores.
  *
- * Return value: The kind of components for this backend.
+ * Returns: The kind of components for this backend.
  */
 icalcomponent_kind
 e_cal_backend_get_kind (ECalBackend *backend)
 {
-       ECalBackendPrivate *priv;
-
-       g_return_val_if_fail (backend != NULL, ICAL_NO_COMPONENT);
        g_return_val_if_fail (E_IS_CAL_BACKEND (backend), ICAL_NO_COMPONENT);
 
-       priv = backend->priv;
-
-       return priv->kind;
+       return backend->priv->kind;
 }
 
-static void
-cal_destroy_cb (gpointer data, GObject *where_cal_was)
+/**
+ * e_cal_backend_get_registry:
+ * @backend: an #ECalBackend
+ *
+ * Returns the data source registry to which #EBackend:source belongs.
+ *
+ * Returns: an #ESourceRegistry
+ *
+ * Since: 3.6
+ **/
+ESourceRegistry *
+e_cal_backend_get_registry (ECalBackend *backend)
 {
-       ECalBackend *backend = E_CAL_BACKEND (data);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
 
-       e_cal_backend_remove_client (backend, (EDataCal *) where_cal_was);
+       return backend->priv->registry;
 }
 
-static void
-listener_died_cb (gpointer cnx, gpointer data)
+/**
+ * e_cal_backend_is_opened:
+ * @backend: an #ECalBackend
+ *
+ * Checks if @backend's storage has been opened (and
+ * authenticated, if necessary) and the backend itself
+ * is ready for accessing. This property is changed automatically
+ * within call of e_cal_backend_notify_opened().
+ *
+ * Returns: %TRUE if fully opened, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_cal_backend_is_opened (ECalBackend *backend)
 {
-       EDataCal *cal = E_DATA_CAL (data);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
 
-       e_cal_backend_remove_client (e_data_cal_get_backend (cal), cal);
+       return backend->priv->opened;
 }
 
-static void
-last_client_gone (ECalBackend *backend)
+/**
+ * e_cal_backend_is_opening:
+ * @backend: an #ECalBackend
+ *
+ * Checks if @backend is processing its opening phase, which
+ * includes everything since the e_cal_backend_open() call,
+ * through authentication, up to e_cal_backend_notify_opened().
+ * This property is managed automatically and the backend deny
+ * every operation except of cancel and authenticate_user while
+ * it is being opening.
+ *
+ * Returns: %TRUE if opening phase is in the effect, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_cal_backend_is_opening (ECalBackend *backend)
 {
-       g_signal_emit (backend, e_cal_backend_signals[LAST_CLIENT_GONE], 0);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+
+       return backend->priv->opening;
 }
 
 /**
- * e_cal_backend_add_client:
- * @backend: An ECalBackend object.
- * @cal: An EDataCal object.
+ * e_cal_backend_is_readonly:
+ * @backend: an #ECalBackend
  *
- * Adds a new client to the given backend. For any event, the backend will
- * notify all clients added via this function.
- */
-void
-e_cal_backend_add_client (ECalBackend *backend, EDataCal *cal)
+ * Returns: Whether is backend read-only. This value is the last used
+ * in a call of e_cal_backend_notify_readonly().
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_cal_backend_is_readonly (ECalBackend *backend)
 {
-       ECalBackendPrivate *priv;
-
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (cal != NULL);
-       g_return_if_fail (E_IS_DATA_CAL (cal));
-
-       priv = backend->priv;
-
-       bonobo_object_set_immortal (BONOBO_OBJECT (cal), TRUE);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
 
-       g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
+       return backend->priv->readonly;
+}
 
-       ORBit_small_listen_for_broken (e_data_cal_get_listener (cal), G_CALLBACK (listener_died_cb), cal);
+/**
+ * e_cal_backend_is_removed:
+ * @backend: an #ECalBackend
+ *
+ * Checks if @backend has been removed from its physical storage.
+ *
+ * Returns: %TRUE if @backend has been removed, %FALSE otherwise.
+ *
+ * Since: 3.2
+ **/
+gboolean
+e_cal_backend_is_removed (ECalBackend *backend)
+{
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
 
-       g_mutex_lock (priv->clients_mutex);
-       priv->clients = g_list_append (priv->clients, cal);
-       g_mutex_unlock (priv->clients_mutex);
+       return backend->priv->removed;
 }
 
 /**
- * e_cal_backend_remove_client:
- * @backend: An #ECalBackend object.
- * @cal: An #EDataCal object.
+ * e_cal_backend_set_is_removed:
+ * @backend: an #ECalBackend
+ * @is_removed: A flag indicating whether the backend's storage was removed
  *
- * Removes a client from the list of connected clients to the given backend.
- */
+ * Sets the flag indicating whether @backend was removed to @is_removed.
+ * Meant to be used by backend implementations.
+ *
+ * Since: 3.2
+ **/
 void
-e_cal_backend_remove_client (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_set_is_removed (ECalBackend *backend,
+                              gboolean is_removed)
 {
-       ECalBackendPrivate *priv;
-
-       /* XXX this needs a bit more thinking wrt the mutex - we
-          should be holding it when we check to see if clients is
-          NULL */
-       g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (cal != NULL);
-       g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       priv = backend->priv;
+       backend->priv->removed = is_removed;
+}
 
-       /* Disconnect */
-       g_mutex_lock (priv->clients_mutex);
-       priv->clients = g_list_remove (priv->clients, cal);
-       g_mutex_unlock (priv->clients_mutex);
+/**
+ * e_cal_backend_get_cache_dir:
+ * @backend: an #ECalBackend
+ *
+ * Returns the cache directory for the given backend.
+ *
+ * Returns: the cache directory for the backend
+ *
+ * Since: 2.32
+ **/
+const gchar *
+e_cal_backend_get_cache_dir (ECalBackend *backend)
+{
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
 
-       /* When all clients go away, notify the parent factory about it so that
-        * it may decide whether to kill the backend or not.
-        */
-       if (!priv->clients)
-               last_client_gone (backend);
+       return backend->priv->cache_dir;
 }
 
 /**
- * e_cal_backend_add_query:
- * @backend: An #ECalBackend object.
- * @query: An #EDataCalView object.
+ * e_cal_backend_set_cache_dir:
+ * @backend: an #ECalBackend
+ * @cache_dir: a local cache directory
  *
- * Adds a query to the list of live queries being run by the given backend.
- * Doing so means that any listener on the query will get notified of any
- * change that affect the live query.
- */
+ * Sets the cache directory for the given backend.
+ *
+ * Note that #ECalBackend is initialized with a usable default based on
+ * #ECalBackend:source and #ECalBackend:kind properties.  Backends should
+ * not override the default without good reason.
+ *
+ * Since: 2.32
+ **/
 void
-e_cal_backend_add_query (ECalBackend *backend, EDataCalView *query)
+e_cal_backend_set_cache_dir (ECalBackend *backend,
+                             const gchar *cache_dir)
 {
-       g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (cache_dir != NULL);
 
-       g_mutex_lock (backend->priv->queries_mutex);
+       if (g_strcmp0 (backend->priv->cache_dir, cache_dir) == 0)
+               return;
 
-       e_list_append (backend->priv->queries, query);
+       g_free (backend->priv->cache_dir);
+       backend->priv->cache_dir = g_strdup (cache_dir);
 
-       g_mutex_unlock (backend->priv->queries_mutex);
+       g_object_notify (G_OBJECT (backend), "cache-dir");
 }
 
 /**
- * e_cal_backend_get_queries:
- * @backend: An #ECalBackend object.
+ * e_cal_backend_create_cache_filename:
+ * @backend: an #ECalBackend
+ * @uid: a component UID
+ * @filename: a filename to use; can be NULL
+ * @fileindex: index of a file; used only when @filename is NULL
  *
- * Gets the list of live queries being run on the given backend.
+ * Returns: a filename for an attachment in a local cache dir. Free returned
+ * pointer with a g_free().
  *
- * Return value: The list of live queries.
- */
-EList *
-e_cal_backend_get_queries (ECalBackend *backend)
+ * Since: 3.4
+ **/
+gchar *
+e_cal_backend_create_cache_filename (ECalBackend *backend,
+                                     const gchar *uid,
+                                     const gchar *filename,
+                                     gint fileindex)
 {
        g_return_val_if_fail (backend != NULL, NULL);
        g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
 
-       return backend->priv->queries;
+       return e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
 }
 
-
 /**
- * e_cal_backend_get_cal_address:
- * @backend: A calendar backend.
- *
- * Queries the cal address associated with a calendar backend, which
- * must already have an open calendar.
+ * e_cal_backend_get_backend_property:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @prop_name: property name to get value of; cannot be NULL
+ *
+ * Calls the get_backend_property method on the given backend.
+ * This might be finished with e_data_cal_respond_get_backend_property().
+ * Default implementation takes care of common properties and returns
+ * an 'unsupported' error for any unknown properties. The subclass may
+ * always call this default implementation for properties which fetching
+ * it doesn't overwrite.
+ *
+ * Since: 3.2
  **/
 void
-e_cal_backend_get_cal_address (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_get_backend_property (ECalBackend *backend,
+                                    EDataCal *cal,
+                                    guint32 opid,
+                                    GCancellable *cancellable,
+                                    const gchar *prop_name)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (prop_name != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property != NULL);
 
-       g_assert (CLASS (backend)->get_cal_address != NULL);
-       (* CLASS (backend)->get_cal_address) (backend, cal);
+       (* E_CAL_BACKEND_GET_CLASS (backend)->get_backend_property) (backend, cal, opid, cancellable, prop_name);
 }
 
+/**
+ * e_cal_backend_set_backend_property:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @prop_name: property name to change; cannot be NULL
+ * @prop_value: value to set to @prop_name; cannot be NULL
+ *
+ * Calls the set_backend_property method on the given backend.
+ * This might be finished with e_data_cal_respond_set_backend_property().
+ * Default implementation simply returns an 'unsupported' error.
+ * The subclass may always call this default implementation for properties
+ * which fetching it doesn't overwrite.
+ *
+ * Since: 3.2
+ **/
 void
-e_cal_backend_notify_readonly (ECalBackend *backend, gboolean read_only)
+e_cal_backend_set_backend_property (ECalBackend *backend,
+                                    EDataCal *cal,
+                                    guint32 opid,
+                                    GCancellable *cancellable,
+                                    const gchar *prop_name,
+                                    const gchar *prop_value)
 {
-       ECalBackendPrivate *priv;
-       GList *l;
-
-       priv = backend->priv;
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (prop_name != NULL);
+       g_return_if_fail (prop_value != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property != NULL);
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_readonly (priv->notification_proxy, read_only);
-               return;
-       }
-       for (l = priv->clients; l; l = l->next)
-               e_data_cal_notify_read_only (l->data, GNOME_Evolution_Calendar_Success, read_only);
+       (* E_CAL_BACKEND_GET_CLASS (backend)->set_backend_property) (backend, cal, opid, cancellable, prop_name, prop_value);
 }
 
-void
-e_cal_backend_notify_cal_address (ECalBackend *backend, char *address)
+static void
+cal_destroy_cb (gpointer data,
+                GObject *where_cal_was)
 {
-       ECalBackendPrivate *priv;
-       GList *l;
-
-       priv = backend->priv;
-
-       for (l = priv->clients; l; l = l->next)
-               e_data_cal_notify_cal_address (l->data, GNOME_Evolution_Calendar_Success, address);
+       e_cal_backend_remove_client_private (
+               E_CAL_BACKEND (data),
+               (EDataCal *) where_cal_was, FALSE);
 }
 
 /**
- * e_cal_backend_get_alarm_email_address:
- * @backend: An #ECalBackend object.
- * @cal: An #EDataCal object.
+ * e_cal_backend_add_client:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
  *
- * Calls the get_alarm_email_address method on the given backend.
+ * Adds a new client to the given backend. For any event, the backend will
+ * notify all clients added via this function.
  */
 void
-e_cal_backend_get_alarm_email_address (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_add_client (ECalBackend *backend,
+                          EDataCal *cal)
 {
+       ECalBackendPrivate *priv;
+
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (cal != NULL);
+       g_return_if_fail (E_IS_DATA_CAL (cal));
+
+       priv = backend->priv;
 
-       g_assert (CLASS (backend)->get_alarm_email_address != NULL);
-       (* CLASS (backend)->get_alarm_email_address) (backend, cal);
+       g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
+
+       g_mutex_lock (&priv->clients_mutex);
+       priv->clients = g_list_append (priv->clients, cal);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
-/**
- *e_cal_backend_get_alarm_email_address:
- * @backend: An #ECalBackend object.
- * @cal: An #EDataCal object.
- *
- * Calls the get_ldap_attribute method of the given backend.
- */
-void
-e_cal_backend_get_ldap_attribute (ECalBackend *backend, EDataCal *cal)
+static void
+e_cal_backend_remove_client_private (ECalBackend *backend,
+                                     EDataCal *cal,
+                                     gboolean weak_unref)
 {
-       g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (E_IS_DATA_CAL (cal));
 
-       g_assert (CLASS (backend)->get_ldap_attribute != NULL);
-       (* CLASS (backend)->get_ldap_attribute) (backend, cal);
+       if (weak_unref)
+               g_object_weak_unref (G_OBJECT (cal), cal_destroy_cb, backend);
+
+       /* Make sure the backend stays alive while holding the mutex. */
+       g_object_ref (backend);
+
+       /* Disconnect */
+       g_mutex_lock (&backend->priv->clients_mutex);
+       backend->priv->clients = g_list_remove (backend->priv->clients, cal);
+
+       if (backend->priv->clients == NULL)
+               backend->priv->opening = FALSE;
+
+       g_mutex_unlock (&backend->priv->clients_mutex);
+
+       g_object_unref (backend);
 }
 
 /**
- * e_cal_backend_get_alarm_email_address:
- * @backend: An #ECalBackend object.
- * @cal: An #EDataCal object.
+ * e_cal_backend_remove_client:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
  *
- * Calls the get_static_capabilities method on the given backend.
+ * Removes a client from the list of connected clients to the given backend.
  */
 void
-e_cal_backend_get_static_capabilities (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_remove_client (ECalBackend *backend,
+                             EDataCal *cal)
 {
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-
-       g_assert (CLASS (backend)->get_static_capabilities != NULL);
-       (* CLASS (backend)->get_static_capabilities) (backend, cal);
+       e_cal_backend_remove_client_private (backend, cal, TRUE);
 }
 
 /**
- * e_cal_backend_open:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @only_if_exists: Whether the calendar should be opened only if it already
- * exists.  If FALSE, a new calendar will be created when the specified @uri
- * does not exist.
- * @username: User name to use for authentication (if needed).
- * @password: Password for @username.
+ * e_cal_backend_add_view:
+ * @backend: an #ECalBackend
+ * @view: An #EDataCalView object.
  *
- * Opens a calendar backend with data from a calendar stored at the specified
- * URI.
+ * Adds a view to the list of live views being run by the given backend.
+ * Doing so means that any listener on the view will get notified of any
+ * change that affect the live view.
+ *
+ * Since: 3.2
  */
 void
-e_cal_backend_open (ECalBackend *backend, EDataCal *cal, gboolean only_if_exists,
-                   const char *username, const char *password)
+e_cal_backend_add_view (ECalBackend *backend,
+                        EDataCalView *view)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       g_assert (CLASS (backend)->open != NULL);
-       (* CLASS (backend)->open) (backend, cal, only_if_exists, username, password);
+       g_mutex_lock (&backend->priv->views_mutex);
+
+       backend->priv->views = g_list_append (backend->priv->views, view);
+
+       g_mutex_unlock (&backend->priv->views_mutex);
 }
 
 /**
- * e_cal_backend_remove:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * e_cal_backend_remove_view
+ * @backend: an #ECalBackend
+ * @view: An #EDataCalView object, previously added with @ref e_cal_backend_add_view.
  *
- * Removes the calendar being accessed by the given backend.
- */
+ * Removes view from the list of live views for the backend.
+ *
+ * Since: 3.2
+ **/
 void
-e_cal_backend_remove (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_remove_view (ECalBackend *backend,
+                           EDataCalView *view)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       g_assert (CLASS (backend)->remove != NULL);
-       (* CLASS (backend)->remove) (backend, cal);
+       g_mutex_lock (&backend->priv->views_mutex);
+
+       backend->priv->views = g_list_remove (backend->priv->views, view);
+
+       g_mutex_unlock (&backend->priv->views_mutex);
 }
 
 /**
- * e_cal_backend_is_loaded:
- * @backend: A calendar backend.
+ * e_cal_backend_list_views:
+ * @backend: an #ECalBackend
  *
- * Queries whether a calendar backend has been loaded yet.
+ * Returns a list of #ECalBookView instances added with
+ * e_cal_backend_add_view().
  *
- * Return value: TRUE if the backend has been loaded with data, FALSE
- * otherwise.
- */
-gboolean
-e_cal_backend_is_loaded (ECalBackend *backend)
+ * The views 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 list 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 cal views
+ *
+ * Since: 3.8
+ **/
+GList *
+e_cal_backend_list_views (ECalBackend *backend)
 {
-       gboolean result;
+       GList *list;
 
-       g_return_val_if_fail (backend != NULL, FALSE);
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+
+       g_mutex_lock (&backend->priv->views_mutex);
 
-       g_assert (CLASS (backend)->is_loaded != NULL);
-       result = (* CLASS (backend)->is_loaded) (backend);
+       /* XXX Use g_list_copy_deep() once we require GLib >= 2.34. */
+       list = g_list_copy (backend->priv->views);
+       g_list_foreach (list, (GFunc) g_object_ref, NULL);
 
-       return result;
+       g_mutex_unlock (&backend->priv->views_mutex);
+
+       return list;
 }
 
 /**
- * e_cal_backend_is_read_only
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * e_cal_backend_foreach_view:
+ * @backend: an #ECalBackend
+ * @callback: callback to call
+ * @user_data: user_data passed into the @callback
  *
- * Queries whether a calendar backend is read only or not.
+ * Calls @callback for each known calendar view of this @backend.
+ * @callback returns %FALSE to stop further processing.
  *
- */
+ * Since: 3.2
+ *
+ * Deprecated: 3.8: Use e_cal_backend_list_views() instead.
+ **/
 void
-e_cal_backend_is_read_only (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_foreach_view (ECalBackend *backend,
+                            gboolean (*callback) (EDataCalView *view,
+                                                  gpointer user_data),
+                            gpointer user_data)
 {
-       g_return_if_fail (backend != NULL);
+       GList *list, *link;
+
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (callback != NULL);
 
-       g_assert (CLASS (backend)->is_read_only != NULL);
-       (* CLASS (backend)->is_read_only) (backend, cal);
-}
+       list = e_cal_backend_list_views (backend);
 
-/**
- * e_cal_backend_start_query:
- * @backend: A calendar backend.
- * @query: The query to be started.
- *
- * Starts a new live query on the given backend.
- */
-void
-e_cal_backend_start_query (ECalBackend *backend, EDataCalView *query)
-{
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               if (!callback (E_DATA_CAL_VIEW (link->data), user_data))
+                       break;
+       }
 
-       g_assert (CLASS (backend)->start_query != NULL);
-       (* CLASS (backend)->start_query) (backend, query);
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
- * e_cal_backend_get_mode:
- * @backend: A calendar backend.
+ * e_cal_backend_set_notification_proxy:
+ * @backend: an #ECalBackend
+ * @proxy: The calendar backend to act as notification proxy.
  *
- * Queries whether a calendar backend is connected remotely.
+ * Sets the backend that will act as notification proxy for the given backend.
  *
- * Return value: The current mode the calendar is in
- **/
-CalMode
-e_cal_backend_get_mode (ECalBackend *backend)
+ * Since: 3.2
+ */
+void
+e_cal_backend_set_notification_proxy (ECalBackend *backend,
+                                      ECalBackend *proxy)
 {
-       CalMode result;
-
-       g_return_val_if_fail (backend != NULL, FALSE);
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
-
-       g_assert (CLASS (backend)->get_mode != NULL);
-       result = (* CLASS (backend)->get_mode) (backend);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       return result;
+       backend->priv->notification_proxy = proxy;
 }
 
-
 /**
- * e_cal_backend_set_mode:
- * @backend: A calendar backend
- * @mode: Mode to change to
+ * e_cal_backend_open:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @only_if_exists: Whether the calendar should be opened only if it already
+ * exists.  If FALSE, a new calendar will be created when the specified @uri
+ * does not exist.
  *
- * Sets the mode of the calendar
- */
+ * Opens a calendar backend with data from a calendar stored at the specified URI.
+ * This might be finished with e_data_cal_respond_open() or e_cal_backend_respond_opened(),
+ * though the overall opening phase finishes only after call
+ * of e_cal_backend_notify_opened() after which call the backend
+ * is either fully opened (including authentication against (remote)
+ * server/storage) or an error was encountered during this opening phase.
+ * 'opened' and 'opening' properties are updated automatically.
+ * The backend refuses all other operations until the opening phase is finished.
+ *
+ * The e_cal_backend_notify_opened() is called either from this function
+ * or from e_cal_backend_authenticate_user(), or after necessary steps
+ * initiated by these two functions.
+ *
+ * The opening phase usually works like this:
+ * 1) client requests open for the backend
+ * 2) server receives this request and calls e_cal_backend_open() - the opening phase begun
+ * 3) either the backend is opened during this call, and notifies client
+ *    with e_cal_backend_notify_opened() about that. This is usually
+ *    for local backends; their opening phase is finished
+ * 4) or the backend requires authentication, thus it notifies client
+ *    about that with e_cal_backend_notify_auth_required() and is
+ *    waiting for credentials, which will be received from client
+ *    by e_cal_backend_authenticate_user() call. Backend's opening
+ *    phase is still running in this case, thus it doesn't call
+ *    e_cal_backend_notify_opened() within e_cal_backend_open() call.
+ * 5) when backend receives credentials in e_cal_backend_authenticate_user()
+ *    then it tries to authenticate against a server/storage with them
+ *    and only after it knows result of the authentication, whether user
+ *    was or wasn't authenticated, it notifies client with the result
+ *    by e_cal_backend_notify_opened() and it's opening phase is
+ *    finished now. If there was no error returned then the backend is
+ *    considered opened, otherwise it's considered closed. Use AuthenticationFailed
+ *    error when the given credentials were rejected by the server/store, which
+ *    will result in a re-prompt on the client side, otherwise use AuthenticationRequired
+ *    if there was anything wrong with the given credentials. Set error's
+ *    message to a reason for a re-prompt, it'll be shown to a user.
+ * 6) client checks error returned from e_cal_backend_notify_opened() and
+ *    reprompts for a password if it was AuthenticationFailed. Otherwise
+ *    considers backend opened based on the error presence (no error means success).
+ *
+ * In any case, the call of e_cal_backend_open() should be always finished
+ * with e_data_cal_respond_open(), which has no influence on the opening phase,
+ * or alternatively with e_cal_backend_respond_opened(). Never use authentication
+ * errors in e_data_cal_respond_open() to notify the client the authentication is
+ * required, there is e_cal_backend_notify_auth_required() for this.
+ **/
 void
-e_cal_backend_set_mode (ECalBackend *backend, CalMode mode)
+e_cal_backend_open (ECalBackend *backend,
+                    EDataCal *cal,
+                    guint32 opid,
+                    GCancellable *cancellable,
+                    gboolean only_if_exists)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->open != NULL);
+
+       g_mutex_lock (&backend->priv->clients_mutex);
+
+       if (e_cal_backend_is_opened (backend)) {
+               gboolean online;
+
+               g_mutex_unlock (&backend->priv->clients_mutex);
 
-       g_assert (CLASS (backend)->set_mode != NULL);
-       (* CLASS (backend)->set_mode) (backend, mode);
+               e_data_cal_report_readonly (cal, backend->priv->readonly);
+
+               online = e_backend_get_online (E_BACKEND (backend));
+               e_data_cal_report_online (cal, online);
+
+               e_cal_backend_respond_opened (backend, cal, opid, NULL);
+       } else if (e_cal_backend_is_opening (backend)) {
+               g_mutex_unlock (&backend->priv->clients_mutex);
+
+               e_data_cal_respond_open (cal, opid, EDC_OPENING_ERROR);
+       } else {
+               backend->priv->opening = TRUE;
+               g_mutex_unlock (&backend->priv->clients_mutex);
+
+               (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
+       }
 }
 
 /**
- * e_cal_backend_get_default_object:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- *
- * Calls the get_default_object method on the given backend.
- */
+ * e_cal_backend_refresh:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ *
+ * Refreshes the calendar being accessed by the given backend.
+ * This might be finished with e_data_cal_respond_refresh(),
+ * and it might be called as soon as possible; it doesn't mean
+ * that the refreshing is done after calling that, the backend
+ * is only notifying client whether it started the refresh process
+ * or not.
+ *
+ * Since: 2.30
+ **/
 void
-e_cal_backend_get_default_object (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_refresh (ECalBackend *backend,
+                       EDataCal *cal,
+                       guint32 opid,
+                       GCancellable *cancellable)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       g_assert (CLASS (backend)->get_default_object != NULL);
-       (* CLASS (backend)->get_default_object) (backend, cal);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_refresh (cal, opid, EDC_OPENING_ERROR);
+       else if (!E_CAL_BACKEND_GET_CLASS (backend)->refresh)
+               e_data_cal_respond_refresh (cal, opid, EDC_ERROR (UnsupportedMethod));
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_refresh (cal, opid, EDC_NOT_OPENED_ERROR);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->refresh) (backend, cal, opid, cancellable);
 }
 
 /**
  * e_cal_backend_get_object:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
  * @uid: Unique identifier for a calendar object.
  * @rid: ID for the object's recurrence to get.
  *
  * Queries a calendar backend for a calendar object based on its unique
  * identifier and its recurrence ID (if a recurrent appointment).
- */
+ * This might be finished with e_data_cal_respond_get_object().
+ **/
 void
-e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid)
+e_cal_backend_get_object (ECalBackend *backend,
+                          EDataCal *cal,
+                          guint32 opid,
+                          GCancellable *cancellable,
+                          const gchar *uid,
+                          const gchar *rid)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (uid != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object != NULL);
 
-       g_assert (CLASS (backend)->get_object != NULL);
-       (* CLASS (backend)->get_object) (backend, cal, uid, rid);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_get_object (cal, opid, EDC_OPENING_ERROR, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_get_object (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->get_object) (backend, cal, opid, cancellable, uid, rid);
 }
 
 /**
  * e_cal_backend_get_object_list:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
  * @sexp: Expression to search for.
  *
  * Calls the get_object_list method on the given backend.
- */
-void
-e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, const char *sexp)
-{
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-
-       g_assert (CLASS (backend)->get_object_list != NULL);
-       (* CLASS (backend)->get_object_list) (backend, cal, sexp);
-}
-
-/**
- * e_cal_backend_get_attachment_list:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @uid: Unique identifier for a calendar object.
- * @rid: ID for the object's recurrence to get.
- *
- * Queries a calendar backend for attachments present in a calendar object based
- * on its unique identifier and its recurrence ID (if a recurrent appointment).
- */
+ * This might be finished with e_data_cal_respond_get_object_list().
+ **/
 void
-e_cal_backend_get_attachment_list (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid)
+e_cal_backend_get_object_list (ECalBackend *backend,
+                               EDataCal *cal,
+                               guint32 opid,
+                               GCancellable *cancellable,
+                               const gchar *sexp)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_object_list != NULL);
 
-       g_assert (CLASS (backend)->get_object != NULL);
-       (* CLASS (backend)->get_attachment_list) (backend, cal, uid, rid);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_get_object_list (cal, opid, EDC_OPENING_ERROR, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_get_object_list (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->get_object_list) (backend, cal, opid, cancellable, sexp);
 }
 
 /**
  * e_cal_backend_get_free_busy:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
  * @users: List of users to get free/busy information for.
  * @start: Start time for query.
  * @end: End time for query.
  *
- * Gets a free/busy object for the given time interval
- */
+ * Gets a free/busy object for the given time interval. Client side is
+ * notified about free/busy objects throug e_data_cal_report_free_busy_data().
+ * This might be finished with e_data_cal_respond_get_free_busy().
+ **/
 void
-e_cal_backend_get_free_busy (ECalBackend *backend, EDataCal *cal, GList *users, time_t start, time_t end)
+e_cal_backend_get_free_busy (ECalBackend *backend,
+                             EDataCal *cal,
+                             guint32 opid,
+                             GCancellable *cancellable,
+                             const GSList *users,
+                             time_t start,
+                             time_t end)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (start != -1 && end != -1);
        g_return_if_fail (start <= end);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy != NULL);
 
-       g_assert (CLASS (backend)->get_free_busy != NULL);
-       (* CLASS (backend)->get_free_busy) (backend, cal, users, start, end);
-}
-
-/**
- * e_cal_backend_get_changes:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @change_id: A unique uid for the callers change list
- *
- * Builds a sequence of objects and the type of change that occurred on them since
- * the last time the give change_id was seen
- */
-void
-e_cal_backend_get_changes (ECalBackend *backend, EDataCal *cal, const char *change_id)
-{
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (change_id != NULL);
-
-       g_assert (CLASS (backend)->get_changes != NULL);
-       (* CLASS (backend)->get_changes) (backend, cal, change_id);
-}
-
-/**
- * e_cal_backend_discard_alarm
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @uid: UID of the component to discard the alarm from.
- * @auid: Alarm ID.
- *
- * Discards an alarm from the given component. This allows the specific backend
- * to do whatever is needed to really discard the alarm.
- */
-void
-e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, const char *uid, const char *auid)
-{
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
-       g_return_if_fail (auid != NULL);
-
-       g_assert (CLASS (backend)->discard_alarm != NULL);
-       (* CLASS (backend)->discard_alarm) (backend, cal, uid, auid);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_get_free_busy (cal, opid, EDC_OPENING_ERROR);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_get_free_busy (cal, opid, EDC_NOT_OPENED_ERROR);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->get_free_busy) (backend, cal, opid, cancellable, users, start, end);
 }
 
 /**
- * e_cal_backend_create_object:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @calobj: The object to create.
+ * e_cal_backend_create_objects:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @calobjs: The objects to create (list of gchar *).
  *
  * Calls the create_object method on the given backend.
- */
+ * This might be finished with e_data_cal_respond_create_objects().
+ *
+ * Since: 3.6
+ **/
 void
-e_cal_backend_create_object (ECalBackend *backend, EDataCal *cal, const char *calobj)
+e_cal_backend_create_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *calobjs)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobj != NULL);
-
-       if (CLASS (backend)->create_object)
-               (* CLASS (backend)->create_object) (backend, cal, calobj);
+       g_return_if_fail (calobjs != NULL);
+
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_create_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
+       else if (!E_CAL_BACKEND_GET_CLASS (backend)->create_objects)
+               e_data_cal_respond_create_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_create_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
        else
-               e_data_cal_notify_object_created (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL);
+               (* E_CAL_BACKEND_GET_CLASS (backend)->create_objects) (backend, cal, opid, cancellable, calobjs);
 }
 
 /**
- * e_cal_backend_modify_object:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @calobj: Object to be modified.
+ * e_cal_backend_modify_objects:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @calobjs: Objects to be modified (list of gchar *).
  * @mod: Type of modification.
  *
- * Calls the modify_object method on the given backend.
- */
+ * Calls the modify_objects method on the given backend.
+ * This might be finished with e_data_cal_respond_modify_objects().
+ *
+ * Since: 3.6
+ **/
 void
-e_cal_backend_modify_object (ECalBackend *backend, EDataCal *cal, const char *calobj, CalObjModType mod)
+e_cal_backend_modify_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *calobjs,
+                              CalObjModType mod)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (calobj != NULL);
-
-       if (CLASS (backend)->modify_object)
-               (* CLASS (backend)->modify_object) (backend, cal, calobj, mod);
+       g_return_if_fail (calobjs != NULL);
+
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_modify_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
+       else if (!E_CAL_BACKEND_GET_CLASS (backend)->modify_objects)
+               e_data_cal_respond_modify_objects (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_modify_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
        else
-               e_data_cal_notify_object_removed (cal, GNOME_Evolution_Calendar_PermissionDenied, NULL, NULL, NULL);
+               (* E_CAL_BACKEND_GET_CLASS (backend)->modify_objects) (backend, cal, opid, cancellable, calobjs, mod);
 }
 
 /**
- * e_cal_backend_remove_object:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @uid: Unique identifier of the object to remove.
- * @rid: A recurrence ID.
+ * e_cal_backend_remove_objects:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @ids: List of #ECalComponentId objects identifying the objects to remove
  * @mod: Type of removal.
  *
- * Removes an object in a calendar backend.  The backend will notify all of its
+ * Removes objects in a calendar backend.  The backend will notify all of its
  * clients about the change.
- */
+ * This might be finished with e_data_cal_respond_remove_objects().
+ *
+ * Since: 3.6
+ **/
 void
-e_cal_backend_remove_object (ECalBackend *backend, EDataCal *cal, const char *uid, const char *rid, CalObjModType mod)
+e_cal_backend_remove_objects (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GCancellable *cancellable,
+                              const GSList *ids,
+                              CalObjModType mod)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (uid != NULL);
+       g_return_if_fail (ids != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_objects != NULL);
 
-       g_assert (CLASS (backend)->remove_object != NULL);
-       (* CLASS (backend)->remove_object) (backend, cal, uid, rid, mod);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_remove_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_remove_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->remove_objects) (backend, cal, opid, cancellable, ids, mod);
 }
 
 /**
  * e_cal_backend_receive_objects:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
  * @calobj: iCalendar object.
  *
  * Calls the receive_objects method on the given backend.
- */
+ * This might be finished with e_data_cal_respond_receive_objects().
+ **/
 void
-e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
+e_cal_backend_receive_objects (ECalBackend *backend,
+                               EDataCal *cal,
+                               guint32 opid,
+                               GCancellable *cancellable,
+                               const gchar *calobj)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (calobj != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->receive_objects != NULL);
 
-       g_assert (CLASS (backend)->receive_objects != NULL);
-       (* CLASS (backend)->receive_objects) (backend, cal, calobj);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_receive_objects (cal, opid, EDC_OPENING_ERROR);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_receive_objects (cal, opid, EDC_NOT_OPENED_ERROR);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->receive_objects) (backend, cal, opid, cancellable, calobj);
 }
 
 /**
  * e_cal_backend_send_objects:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
  * @calobj: iCalendar object to be sent.
  *
  * Calls the send_objects method on the given backend.
- */
+ * This might be finished with e_data_cal_respond_send_objects().
+ **/
 void
-e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, const char *calobj)
+e_cal_backend_send_objects (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const gchar *calobj)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (calobj != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->send_objects != NULL);
 
-       g_assert (CLASS (backend)->send_objects != NULL);
-       (* CLASS (backend)->send_objects) (backend, cal, calobj);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_send_objects (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_send_objects (cal, opid, EDC_NOT_OPENED_ERROR, NULL, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->send_objects) (backend, cal, opid, cancellable, calobj);
 }
 
 /**
- * e_cal_backend_get_timezone:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
- * NULL.
+ * e_cal_backend_get_attachment_uris:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @uid: Unique identifier for a calendar object.
+ * @rid: ID for the object's recurrence to get.
  *
- * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
- * can't be found.
- */
+ * Queries a calendar backend for attachments present in a calendar object based
+ * on its unique identifier and its recurrence ID (if a recurrent appointment).
+ * This might be finished with e_data_cal_respond_get_attachment_uris().
+ *
+ * Since: 3.2
+ **/
 void
-e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
+e_cal_backend_get_attachment_uris (ECalBackend *backend,
+                                   EDataCal *cal,
+                                   guint32 opid,
+                                   GCancellable *cancellable,
+                                   const gchar *uid,
+                                   const gchar *rid)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (tzid != NULL);
+       g_return_if_fail (uid != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris != NULL);
 
-       g_assert (CLASS (backend)->get_timezone != NULL);
-       (* CLASS (backend)->get_timezone) (backend, cal, tzid);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_get_attachment_uris (cal, opid, EDC_OPENING_ERROR, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_get_attachment_uris (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->get_attachment_uris) (backend, cal, opid, cancellable, uid, rid);
 }
 
 /**
- * e_cal_backend_set_default_zone:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @tzobj: The timezone object, in a string.
+ * e_cal_backend_discard_alarm:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @uid: Unique identifier for a calendar object.
+ * @rid: ID for the object's recurrence to discard alarm in.
+ * @auid: Unique identifier of the alarm itself.
  *
- * Sets the default timezone for the calendar, which is used to resolve
- * DATE and floating DATE-TIME values.
- */
+ * Discards alarm @auid from the object identified by @uid and @rid.
+ * This might be finished with e_data_cal_respond_discard_alarm().
+ * Default implementation of this method returns Not Supported error.
+ **/
 void
-e_cal_backend_set_default_zone (ECalBackend *backend, EDataCal *cal, const char *tzobj)
+e_cal_backend_discard_alarm (ECalBackend *backend,
+                             EDataCal *cal,
+                             guint32 opid,
+                             GCancellable *cancellable,
+                             const gchar *uid,
+                             const gchar *rid,
+                             const gchar *auid)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (tzobj != NULL);
+       g_return_if_fail (uid != NULL);
+       g_return_if_fail (auid != NULL);
 
-       (* CLASS (backend)->set_default_zone) (backend, cal, tzobj);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_discard_alarm (cal, opid, EDC_OPENING_ERROR);
+       else if (!E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm)
+               e_data_cal_respond_discard_alarm (cal, opid, e_data_cal_create_error (NotSupported, NULL));
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_discard_alarm (cal, opid, EDC_NOT_OPENED_ERROR);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, rid, auid);
 }
 
 /**
- * @deprecated This virual function should not be used in the backends, use
- * e_cal_backend_set_zone instead. This function restricts the default timezone
- * to be libical builtin timezone.
- *
- * e_cal_backend_set_default_timezone:
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @tzid: The TZID identifying the timezone.
- *
- * Sets the default timezone for the calendar, which is used to resolve
- * DATE and floating DATE-TIME values.
+ * e_cal_backend_get_timezone:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @tzid: Unique identifier of a VTIMEZONE object. Note that this must not be
+ * NULL.
  *
- */
+ * Returns the icaltimezone* corresponding to the TZID, or NULL if the TZID
+ * can't be found.
+ * This might be finished with e_data_cal_respond_get_timezone().
+ **/
 void
-e_cal_backend_set_default_timezone (ECalBackend *backend, EDataCal *cal, const char *tzid)
+e_cal_backend_get_timezone (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const gchar *tzid)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (tzid != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->get_timezone != NULL);
 
-       (* CLASS (backend)->set_default_timezone) (backend, cal, tzid);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_get_timezone (cal, opid, EDC_OPENING_ERROR, NULL);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_get_timezone (cal, opid, EDC_NOT_OPENED_ERROR, NULL);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->get_timezone) (backend, cal, opid, cancellable, tzid);
 }
 
 /**
  * e_cal_backend_add_timezone
- * @backend: A calendar backend.
- * @cal: An #EDataCal object.
- * @tzobj: The timezone object, in a string.
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: the ID to use for this operation
+ * @cancellable: a #GCancellable for the operation
+ * @tzobject: The timezone object, in a string.
  *
  * Add a timezone object to the given backend.
- */
+ * This might be finished with e_data_cal_respond_add_timezone().
+ **/
 void
-e_cal_backend_add_timezone (ECalBackend *backend, EDataCal *cal, const char *tzobj)
+e_cal_backend_add_timezone (ECalBackend *backend,
+                            EDataCal *cal,
+                            guint32 opid,
+                            GCancellable *cancellable,
+                            const gchar *tzobject)
 {
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (tzobj != NULL);
-       g_return_if_fail (CLASS (backend)->add_timezone != NULL);
+       g_return_if_fail (tzobject != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->add_timezone != NULL);
 
-       (* CLASS (backend)->add_timezone) (backend, cal, tzobj);
+       if (e_cal_backend_is_opening (backend))
+               e_data_cal_respond_add_timezone (cal, opid, EDC_OPENING_ERROR);
+       else if (!e_cal_backend_is_opened (backend))
+               e_data_cal_respond_add_timezone (cal, opid, EDC_NOT_OPENED_ERROR);
+       else
+               (* E_CAL_BACKEND_GET_CLASS (backend)->add_timezone) (backend, cal, opid, cancellable, tzobject);
 }
 
 /**
- * e_cal_backend_internal_get_default_timezone:
- * @backend: A calendar backend.
+ * e_cal_backend_start_view:
+ * @backend: an #ECalBackend
+ * @view: The view to be started.
  *
- * Calls the internal_get_default_timezone method on the given backend.
- */
-icaltimezone *
-e_cal_backend_internal_get_default_timezone (ECalBackend *backend)
-{
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
-       g_return_val_if_fail (CLASS (backend)->internal_get_default_timezone != NULL, NULL);
-
-       return (* CLASS (backend)->internal_get_default_timezone) (backend);
-}
-
-/**
- * e_cal_backend_internal_get_timezone:
- * @backend: A calendar backend.
- * @tzid: ID of the timezone to get.
+ * Starts a new live view on the given backend.
  *
- * Calls the internal_get_timezone method on the given backend.
+ * Since: 3.2
  */
-icaltimezone *
-e_cal_backend_internal_get_timezone (ECalBackend *backend, const char *tzid)
+void
+e_cal_backend_start_view (ECalBackend *backend,
+                          EDataCalView *view)
 {
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
-       g_return_val_if_fail (tzid != NULL, NULL);
-       g_return_val_if_fail (CLASS (backend)->internal_get_timezone != NULL, NULL);
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->start_view != NULL);
 
-       return (* CLASS (backend)->internal_get_timezone) (backend, tzid);
+       (* E_CAL_BACKEND_GET_CLASS (backend)->start_view) (backend, view);
 }
 
 /**
- * e_cal_backend_set_notification_proxy:
- * @backend: A calendar backend.
- * @proxy: The calendar backend to act as notification proxy.
+ * e_cal_backend_stop_view:
+ * @backend: an #ECalBackend
+ * @view: The view to be stopped.
  *
- * Sets the backend that will act as notification proxy for the given backend.
+ * Stops a previously started live view on the given backend.
+ *
+ * Since: 3.2
  */
 void
-e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy)
+e_cal_backend_stop_view (ECalBackend *backend,
+                         EDataCalView *view)
 {
-       ECalBackendPrivate *priv;
-
+       g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
-       priv = backend->priv;
+       /* backward compatibility, do not force each backend define this function */
+       if (!E_CAL_BACKEND_GET_CLASS (backend)->stop_view)
+               return;
 
-       priv->notification_proxy = proxy;
+       (* E_CAL_BACKEND_GET_CLASS (backend)->stop_view) (backend, view);
 }
 
 /**
- * e_cal_backend_notify_object_created:
- * @backend: A calendar backend.
- * @calobj: iCalendar representation of new object
+ * e_cal_backend_notify_component_created:
+ * @backend: an #ECalBackend
+ * @component: the newly created #ECalComponent
  *
  * Notifies each of the backend's listeners about a new object.
  *
- * #e_data_cal_notify_object_created() calls this for you. You only need to
- * call e_cal_backend_notify_object_created() yourself to report objects
- * created by non-EDS clients.
+ * Like e_cal_backend_notify_object_created() except takes an #ECalComponent
+ * instead of an ical string representation and uses the #EDataCalView's
+ * fields-of-interest to filter out unwanted information from ical strings
+ * sent over the bus.
+ *
+ * Since: 3.4
  **/
 void
-e_cal_backend_notify_object_created (ECalBackend *backend, const char *calobj)
+e_cal_backend_notify_component_created (ECalBackend *backend,
+                                        ECalComponent *component)
 {
-       ECalBackendPrivate *priv;
-       EList *queries;
-       EIterator *iter;
-       EDataCalView *query;
+       GList *list, *link;
 
-       priv = backend->priv;
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (E_IS_CAL_COMPONENT (component));
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_object_created (priv->notification_proxy, calobj);
+       if (backend->priv->notification_proxy != NULL) {
+               e_cal_backend_notify_component_created (
+                       backend->priv->notification_proxy, component);
                return;
        }
 
-       queries = e_cal_backend_get_queries (backend);
-       iter = e_list_get_iterator (queries);
+       list = e_cal_backend_list_views (backend);
 
-       while (e_iterator_is_valid (iter)) {
-               query = QUERY (e_iterator_get (iter));
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               EDataCalView *view = E_DATA_CAL_VIEW (link->data);
 
-               bonobo_object_ref (query);
-               if (e_data_cal_view_object_matches (query, calobj))
-                       e_data_cal_view_notify_objects_added_1 (query, calobj);
-               bonobo_object_unref (query);
-
-               e_iterator_next (iter);
+               if (e_data_cal_view_component_matches (view, component))
+                       e_data_cal_view_notify_components_added_1 (view, component);
        }
-       g_object_unref (iter);
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 static void
-match_query_and_notify (EDataCalView *query, const char *old_object, const char *object)
+match_view_and_notify_component (EDataCalView *view,
+                                 ECalComponent *old_component,
+                                 ECalComponent *new_component)
 {
        gboolean old_match = FALSE, new_match = FALSE;
 
-       if (old_object)
-               old_match = e_data_cal_view_object_matches (query, old_object);
+       if (old_component)
+               old_match = e_data_cal_view_component_matches (view, old_component);
+
+       new_match = e_data_cal_view_component_matches (view, new_component);
 
-       new_match = e_data_cal_view_object_matches (query, object);
        if (old_match && new_match)
-               e_data_cal_view_notify_objects_modified_1 (query, object);
+               e_data_cal_view_notify_components_modified_1 (view, new_component);
        else if (new_match)
-               e_data_cal_view_notify_objects_added_1 (query, object);
+               e_data_cal_view_notify_components_added_1 (view, new_component);
        else if (old_match) {
-               ECalComponent *comp = NULL;
 
-               comp = e_cal_component_new_from_string (old_object);
-               if (comp) {
-                       ECalComponentId *id = e_cal_component_get_id (comp);
+               ECalComponentId *id = e_cal_component_get_id (old_component);
 
-                       e_data_cal_view_notify_objects_removed_1 (query, id);
+               e_data_cal_view_notify_objects_removed_1 (view, id);
 
-                       e_cal_component_free_id (id);
-                       g_object_unref (comp);
-               }
+               e_cal_component_free_id (id);
        }
 }
 
 /**
- * e_cal_backend_notify_view_progress:
- * @backend: A calendar backend.
- * @message: the UID of the removed object
- * @percent: percentage of the objects loaded in the view
+ * e_cal_backend_notify_component_modified:
+ * @backend: an #ECalBackend
+ * @old_component: the #ECalComponent before the modification
+ * @new_component: the #ECalComponent after the modification
+ *
+ * Notifies each of the backend's listeners about a modified object.
  *
- * Notifies each of the backend's listeners about the view_progress in downloading the items.
+ * Like e_cal_backend_notify_object_modified() except takes an #ECalComponent
+ * instead of an ical string representation and uses the #EDataCalView's
+ * fields-of-interest to filter out unwanted information from ical strings
+ * sent over the bus.
+ *
+ * Since: 3.4
  **/
 void
-e_cal_backend_notify_view_progress (ECalBackend *backend, const char *message, int percent)
+e_cal_backend_notify_component_modified (ECalBackend *backend,
+                                         ECalComponent *old_component,
+                                         ECalComponent *new_component)
 {
-       ECalBackendPrivate *priv;
-       EList *queries;
-       EIterator *iter;
-       EDataCalView *query;
+       GList *list, *link;
 
-       priv = backend->priv;
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (E_IS_CAL_COMPONENT (old_component));
+       g_return_if_fail (E_IS_CAL_COMPONENT (new_component));
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_view_progress (priv->notification_proxy, message, percent);
+       if (backend->priv->notification_proxy != NULL) {
+               e_cal_backend_notify_component_modified (
+                       backend->priv->notification_proxy,
+                       old_component, new_component);
                return;
        }
 
-       queries = e_cal_backend_get_queries (backend);
-       iter = e_list_get_iterator (queries);
-
-       while (e_iterator_is_valid (iter)) {
-               query = QUERY (e_iterator_get (iter));
-
-               bonobo_object_ref (query);
-
-               e_data_cal_view_notify_progress (query, message, percent);
+       list = e_cal_backend_list_views (backend);
 
-               bonobo_object_unref (query);
+       for (link = list; link != NULL; link = g_list_next (link))
+               match_view_and_notify_component (
+                       E_DATA_CAL_VIEW (link->data),
+                       old_component, new_component);
 
-               e_iterator_next (iter);
-       }
-       g_object_unref (iter);
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
- * e_cal_backend_notify_view_done:
- * @backend: A calendar backend.
- * @status: returns the status once the view is fully populated.
+ * e_cal_backend_notify_component_removed:
+ * @backend: an #ECalBackend
+ * @id: the Id of the removed object
+ * @old_component: the removed component
+ * @new_component: the component after the removal. This only applies to recurrent 
+ * appointments that had an instance removed. In that case, this function
+ * notifies a modification instead of a removal.
+ *
+ * Notifies each of the backend's listeners about a removed object.
  *
- * Notifies each of the backend's listeners about the view_done in downloading the items.
+ * Like e_cal_backend_notify_object_removed() except takes an #ECalComponent
+ * instead of an ical string representation and uses the #EDataCalView's
+ * fields-of-interest to filter out unwanted information from ical strings
+ * sent over the bus.
+ *
+ * Since: 3.4
  **/
 void
-e_cal_backend_notify_view_done (ECalBackend *backend, GNOME_Evolution_Calendar_CallStatus status)
+e_cal_backend_notify_component_removed (ECalBackend *backend,
+                                        const ECalComponentId *id,
+                                        ECalComponent *old_component,
+                                        ECalComponent *new_component)
 {
-       ECalBackendPrivate *priv;
-       EList *queries;
-       EIterator *iter;
-       EDataCalView *query;
+       GList *list, *link;
 
-       priv = backend->priv;
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (id != NULL);
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_view_done (priv->notification_proxy, status);
+       if (old_component != NULL)
+               g_return_if_fail (E_IS_CAL_COMPONENT (old_component));
+
+       if (new_component != NULL)
+               g_return_if_fail (E_IS_CAL_COMPONENT (new_component));
+
+       if (backend->priv->notification_proxy != NULL) {
+               e_cal_backend_notify_component_removed (
+                       backend->priv->notification_proxy,
+                       id, old_component, new_component);
                return;
        }
 
-       queries = e_cal_backend_get_queries (backend);
-       iter = e_list_get_iterator (queries);
-
-       while (e_iterator_is_valid (iter)) {
-               query = QUERY (e_iterator_get (iter));
+       list = e_cal_backend_list_views (backend);
 
-               bonobo_object_ref (query);
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               EDataCalView *view = E_DATA_CAL_VIEW (link->data);
 
-               e_data_cal_view_notify_done (query, status);
+               if (new_component != NULL)
+                       match_view_and_notify_component (
+                               view, old_component, new_component);
 
-               bonobo_object_unref (query);
+               else if (old_component == NULL)
+                       e_data_cal_view_notify_objects_removed_1 (view, id);
 
-               e_iterator_next (iter);
+               else if (e_data_cal_view_component_matches (view, old_component))
+                       e_data_cal_view_notify_objects_removed_1 (view, id);
        }
-       g_object_unref (iter);
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
- * e_cal_backend_notify_object_modified:
- * @backend: A calendar backend.
- * @old_object: iCalendar representation of the original form of the object
- * @object: iCalendar representation of the new form of the object
+ * e_cal_backend_notify_error:
+ * @backend: an #ECalBackend
+ * @message: Error message
  *
- * Notifies each of the backend's listeners about a modified object.
+ * Notifies each of the backend's listeners about an error
+ **/
+void
+e_cal_backend_notify_error (ECalBackend *backend,
+                            const gchar *message)
+{
+       ECalBackendPrivate *priv = backend->priv;
+       GList *l;
+
+       if (priv->notification_proxy) {
+               e_cal_backend_notify_error (priv->notification_proxy, message);
+               return;
+       }
+
+       g_mutex_lock (&priv->clients_mutex);
+
+       for (l = priv->clients; l; l = l->next)
+               e_data_cal_report_error (l->data, message);
+
+       g_mutex_unlock (&priv->clients_mutex);
+}
+
+/**
+ * e_cal_backend_notify_readonly:
+ * @backend: an #ECalBackend
+ * @is_readonly: flag indicating readonly status
  *
- * #e_data_cal_notify_object_modified() calls this for you. You only need to
- * call e_cal_backend_notify_object_modified() yourself to report objects
- * modified by non-EDS clients.
+ * Notifies all backend's clients about the current readonly state.
+ * Meant to be used by backend implementations.
  **/
 void
-e_cal_backend_notify_object_modified (ECalBackend *backend,
-                                     const char *old_object, const char *object)
+e_cal_backend_notify_readonly (ECalBackend *backend,
+                               gboolean is_readonly)
 {
        ECalBackendPrivate *priv;
-       EList *queries;
-       EIterator *iter;
-       EDataCalView *query;
+       GList *l;
 
        priv = backend->priv;
+       priv->readonly = is_readonly;
 
        if (priv->notification_proxy) {
-               e_cal_backend_notify_object_modified (priv->notification_proxy, old_object, object);
+               e_cal_backend_notify_readonly (priv->notification_proxy, is_readonly);
                return;
        }
 
-       queries = e_cal_backend_get_queries (backend);
-       iter = e_list_get_iterator (queries);
-
-       while (e_iterator_is_valid (iter)) {
-               query = QUERY (e_iterator_get (iter));
+       g_mutex_lock (&priv->clients_mutex);
 
-               bonobo_object_ref (query);
-               match_query_and_notify (query, old_object, object);
-               bonobo_object_unref (query);
+       for (l = priv->clients; l; l = l->next)
+               e_data_cal_report_readonly (l->data, is_readonly);
 
-               e_iterator_next (iter);
-       }
-       g_object_unref (iter);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 /**
- * e_cal_backend_notify_object_removed:
- * @backend: A calendar backend.
- * @id: the Id of the removed object
- * @old_object: iCalendar representation of the removed object
- * @new_object: iCalendar representation of the object after the removal. This
- * only applies to recurrent appointments that had an instance removed. In that
- * case, this function notifies a modification instead of a removal.
+ * e_cal_backend_notify_online:
+ * @backend: an #ECalBackend
+ * @is_online: flag indicating whether @backend is connected and online
  *
- * Notifies each of the backend's listeners about a removed object.
+ * Notifies clients of @backend's connection status indicated by @is_online.
+ * Meant to be used by backend implementations.
  *
- * e_data_cal_notify_object_removed() calls this for you. You only need to
- * call e_cal_backend_notify_object_removed() yourself to report objects
- * removed by non-EDS clients.
+ * Since: 3.2
  **/
 void
-e_cal_backend_notify_object_removed (ECalBackend *backend, const ECalComponentId *id,
-                                    const char *old_object, const char *object)
+e_cal_backend_notify_online (ECalBackend *backend,
+                             gboolean is_online)
 {
        ECalBackendPrivate *priv;
-       EList *queries;
-       EIterator *iter;
-       EDataCalView *query;
+       GList *clients;
 
        priv = backend->priv;
 
        if (priv->notification_proxy) {
-               e_cal_backend_notify_object_removed (priv->notification_proxy, id, old_object, object);
+               e_cal_backend_notify_online (priv->notification_proxy, is_online);
                return;
        }
 
-       queries = e_cal_backend_get_queries (backend);
-       iter = e_list_get_iterator (queries);
+       g_mutex_lock (&priv->clients_mutex);
+
+       for (clients = priv->clients; clients != NULL; clients = g_list_next (clients))
+               e_data_cal_report_online (E_DATA_CAL (clients->data), is_online);
+
+       g_mutex_unlock (&priv->clients_mutex);
+}
+
+/**
+ * e_cal_backend_notify_opened:
+ * @backend: an #ECalBackend
+ * @error: a #GError corresponding to the error encountered during
+ *    the opening phase. Use %NULL for success. The @error is freed
+ *    automatically if not %NULL.
+ *
+ * Notifies clients that @backend finished its opening phase.
+ * See e_cal_backend_open() for more information how the opening
+ * phase works. Calling this function changes 'opening' property,
+ * same as 'opened'. 'opening' is set to %FALSE and the backend
+ * is considered 'opened' only if the @error is %NULL.
+ *
+ * See also: e_cal_backend_respond_opened()
+ *
+ * Note: The @error is freed automatically if not %NULL.
+ *
+ * Meant to be used by backend implementations.
+ *
+ * Since: 3.2
+ **/
+void
+e_cal_backend_notify_opened (ECalBackend *backend,
+                             GError *error)
+{
+       ECalBackendPrivate *priv;
+       GList *clients;
 
-       while (e_iterator_is_valid (iter)) {
-               query = QUERY (e_iterator_get (iter));
+       priv = backend->priv;
+       g_mutex_lock (&priv->clients_mutex);
 
-               bonobo_object_ref (query);
+       priv->opening = FALSE;
+       priv->opened = error == NULL;
 
-               if (object == NULL) {
-                       /* if object == NULL, it means the object has been completely
-                          removed from the backend */
-                       if (e_data_cal_view_object_matches (query, old_object))
-                               e_data_cal_view_notify_objects_removed_1 (query, id);
-               } else
-                       match_query_and_notify (query, old_object, object);
+       for (clients = priv->clients; clients != NULL; clients = g_list_next (clients))
+               e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
 
-               bonobo_object_unref (query);
+       g_mutex_unlock (&priv->clients_mutex);
 
-               e_iterator_next (iter);
-       }
-       g_object_unref (iter);
+       if (error)
+               g_error_free (error);
 }
 
 /**
- * e_cal_backend_notify_mode:
- * @backend: A calendar backend.
- * @status: Status of the mode set
- * @mode: the current mode
+ * e_cal_backend_notify_property_changed:
+ * @backend: an #ECalBackend
+ * @prop_name: property name, which changed
+ * @prop_value: new property value
  *
- * Notifies each of the backend's listeners about the results of a
- * setMode call.
+ * Notifies client about property value change.
+ *
+ * Since: 3.2
  **/
 void
-e_cal_backend_notify_mode (ECalBackend *backend,
-                          GNOME_Evolution_Calendar_CalListener_SetModeStatus status,
-                          GNOME_Evolution_Calendar_CalMode mode)
+e_cal_backend_notify_property_changed (ECalBackend *backend,
+                                       const gchar *prop_name,
+                                       const gchar *prop_value)
 {
-       ECalBackendPrivate *priv = backend->priv;
-       GList *l;
+       ECalBackendPrivate *priv;
+       GList *clients;
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_mode (priv->notification_proxy, status, mode);
-               return;
-       }
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (prop_name != NULL);
+       g_return_if_fail (*prop_name != '\0');
+       g_return_if_fail (prop_value != NULL);
 
-       for (l = priv->clients; l; l = l->next)
-               e_data_cal_notify_mode (l->data, status, mode);
+       priv = backend->priv;
+       g_mutex_lock (&priv->clients_mutex);
+
+       for (clients = priv->clients; clients != NULL; clients = g_list_next (clients))
+               e_data_cal_report_backend_property_changed (E_DATA_CAL (clients->data), prop_name, prop_value);
+
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 /**
- * e_cal_backend_notify_auth_required:
- * @backend: A calendar backend.
+ * e_cal_backend_respond_opened:
+ * @backend: an #ECalBackend
+ * @cal: an #EDataCal
+ * @opid: an operation ID
+ * @error: result error; can be %NULL, if it isn't then it's automatically freed
  *
- * Notifies each of the backend's listeners that authentication is required to
- * open the calendar.
- */
+ * This is a replacement for e_data_cal_respond_open() for cases where
+ * the finish of 'open' method call also finishes backend opening phase.
+ * This function covers calling of both e_cal_backend_notify_opened() and
+ * e_data_cal_respond_open() with the same @error.
+ *
+ * See e_cal_backend_open() for more details how the opening phase works.
+ *
+ * Since: 3.2
+ **/
 void
-e_cal_backend_notify_auth_required (ECalBackend *backend)
+e_cal_backend_respond_opened (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GError *error)
 {
-        ECalBackendPrivate *priv = backend->priv;
-        GList *l;
+       GError *copy = NULL;
+
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+       g_return_if_fail (cal != NULL);
+       g_return_if_fail (opid != 0);
+
+       if (error)
+               copy = g_error_copy (error);
 
-        for (l = priv->clients; l; l = l->next)
-                e_data_cal_notify_auth_required (l->data);
+       e_cal_backend_notify_opened (backend, copy);
+       e_data_cal_respond_open (cal, opid, error);
 }
 
 /**
- * e_cal_backend_notify_error:
- * @backend: A calendar backend.
- * @message: Error message
+ * e_cal_backend_empty_cache:
+ * @backend: an #ECalBackend
+ * @cache: Backend's cache to empty.
  *
- * Notifies each of the backend's listeners about an error
+ * Empties backend's cache with all notifications and so on, thus all listening
+ * will know there is nothing in this backend.
+ *
+ * Since: 2.28
  **/
 void
-e_cal_backend_notify_error (ECalBackend *backend, const char *message)
+e_cal_backend_empty_cache (ECalBackend *backend,
+                           ECalBackendCache *cache)
 {
-       ECalBackendPrivate *priv = backend->priv;
-       GList *l;
+       GList *comps_in_cache;
 
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_error (priv->notification_proxy, message);
+       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
+
+       if (!cache)
                return;
+
+       g_return_if_fail (E_IS_CAL_BACKEND_CACHE (cache));
+
+       e_file_cache_freeze_changes (E_FILE_CACHE (cache));
+
+       for (comps_in_cache = e_cal_backend_cache_get_components (cache);
+            comps_in_cache;
+            comps_in_cache = comps_in_cache->next) {
+               ECalComponentId *id;
+               ECalComponent *comp = comps_in_cache->data;
+
+               id = e_cal_component_get_id (comp);
+
+               e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
+
+               e_cal_backend_notify_component_removed (backend, id, comp, NULL);
+
+               e_cal_component_free_id (id);
+               g_object_unref (comp);
        }
 
-       for (l = priv->clients; l; l = l->next)
-               e_data_cal_notify_error (l->data, message);
+       g_list_free (comps_in_cache);
+
+       e_file_cache_thaw_changes (E_FILE_CACHE (cache));
 }