ECalBackend: Remove internal_get_timezone() method.
[platform/upstream/evolution-data-server.git] / calendar / libedata-cal / e-cal-backend.c
index a65051f..a1a6dbf 100644 (file)
 
 #include <glib/gi18n-lib.h>
 
-#include <libedataserver/e-data-server-util.h>
-
 #include "e-cal-backend.h"
 #include "e-cal-backend-cache.h"
 
-#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 E_CAL_BACKEND_GET_PRIVATE(obj) \
+       (G_TYPE_INSTANCE_GET_PRIVATE \
+       ((obj), E_TYPE_CAL_BACKEND, ECalBackendPrivate))
+
+#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;
+       ESourceRegistry *registry;
+
        /* The kind of components for this backend */
        icalcomponent_kind kind;
 
-       gboolean opening, opened, readonly, removed, online;
-
-       /* URI, from source. This is cached, since we return const. */
-       gchar *uri;
+       gboolean opening, opened, readonly, removed;
 
        gchar *cache_dir;
 
        /* List of Cal objects */
-       GMutex *clients_mutex;
-       GSList *clients;
+       GMutex clients_mutex;
+       GList *clients;
+
+       GMutex views_mutex;
+       GList *views;
 
-       GMutex *views_mutex;
-       GSList *views;
+       GHashTable *zone_cache;
+       GMutex zone_cache_lock;
 
        /* ECalBackend to pass notifications on to */
        ECalBackend *notification_proxy;
@@ -64,43 +67,29 @@ enum {
        PROP_0,
        PROP_CACHE_DIR,
        PROP_KIND,
-       PROP_SOURCE,
-       PROP_URI
-};
-
-/* Signal IDs */
-enum {
-       LAST_CLIENT_GONE,
-       LAST_SIGNAL
+       PROP_REGISTRY
 };
 
-static guint signals[LAST_SIGNAL];
-
-static void e_cal_backend_remove_client_private (ECalBackend *backend, EDataCal *cal, gboolean weak_unref);
-
-G_DEFINE_TYPE (ECalBackend, e_cal_backend, G_TYPE_OBJECT);
+/* 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
-source_changed_cb (ESource *source, ECalBackend *backend)
+cal_backend_free_zone (icaltimezone *zone)
 {
-       ECalBackendPrivate *priv;
-       gchar *suri;
-
-       g_return_if_fail (source != NULL);
-       g_return_if_fail (backend != NULL);
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-
-       priv = backend->priv;
-       g_return_if_fail (priv != NULL);
-       g_return_if_fail (priv->source == source);
-
-       suri = e_source_get_uri (priv->source);
-       if (!priv->uri || (suri && !g_str_equal (priv->uri, suri))) {
-               g_free (priv->uri);
-               priv->uri = suri;
-       } else {
-               g_free (suri);
-       }
+       icaltimezone_free (zone, 1);
 }
 
 static void
@@ -110,14 +99,16 @@ cal_backend_set_default_cache_dir (ECalBackend *backend)
        icalcomponent_kind kind;
        const gchar *component_type;
        const gchar *user_cache_dir;
-       gchar *mangled_uri;
+       const gchar *uid;
        gchar *filename;
 
        user_cache_dir = e_get_user_cache_dir ();
 
        kind = e_cal_backend_get_kind (backend);
-       source = e_cal_backend_get_source (backend);
-       g_return_if_fail (source != NULL);
+       source = e_backend_get_source (E_BACKEND (backend));
+
+       uid = e_source_get_uid (source);
+       g_return_if_fail (uid != NULL);
 
        switch (kind) {
                case ICAL_VEVENT_COMPONENT:
@@ -133,60 +124,18 @@ cal_backend_set_default_cache_dir (ECalBackend *backend)
                        g_return_if_reached ();
        }
 
-       /* Mangle the URI to not contain invalid characters. */
-       mangled_uri = g_strdelimit (e_source_get_uri (source), ":/", '_');
-
        filename = g_build_filename (
-               user_cache_dir, component_type, mangled_uri, NULL);
+               user_cache_dir, component_type, uid, NULL);
        e_cal_backend_set_cache_dir (backend, filename);
        g_free (filename);
-
-       g_free (mangled_uri);
-}
-
-static void
-cal_backend_set_source (ECalBackend *backend,
-                        ESource *source)
-{
-       if (backend->priv->source != NULL) {
-               g_signal_handlers_disconnect_matched (backend->priv->source, G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL, source_changed_cb, backend);
-               g_object_unref (backend->priv->source);
-       }
-
-       if (source != NULL)
-               g_signal_connect (
-                       g_object_ref (source), "changed",
-                       G_CALLBACK (source_changed_cb), backend);
-
-       backend->priv->source = source;
-
-       /* Cache the URI */
-       if (source != NULL) {
-               g_free (backend->priv->uri);
-               backend->priv->uri = e_source_get_uri (source);
-       }
-}
-
-static void
-cal_backend_set_uri (ECalBackend *backend,
-                     const gchar *uri)
-{
-       /* ESource's URI gets priority. */
-       if (backend->priv->source == NULL) {
-               g_free (backend->priv->uri);
-               backend->priv->uri = g_strdup (uri);
-       }
 }
 
 static void
-cal_backend_set_kind (ECalBackend *backend,
-                      icalcomponent_kind kind)
-{
-       backend->priv->kind = kind;
-}
-
-static void
-cal_backend_get_backend_property (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *prop_name)
+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));
@@ -198,7 +147,7 @@ cal_backend_get_backend_property (ECalBackend *backend, EDataCal *cal, guint32 o
        } 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_cal_backend_is_online (backend) ? "TRUE" : "FALSE");
+               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)) {
@@ -209,7 +158,12 @@ cal_backend_get_backend_property (ECalBackend *backend, EDataCal *cal, guint32 o
 }
 
 static void
-cal_backend_set_backend_property (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value)
+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));
@@ -220,6 +174,23 @@ cal_backend_set_backend_property (ECalBackend *backend, EDataCal *cal, guint32 o
 }
 
 static void
+cal_backend_set_kind (ECalBackend *backend,
+                      icalcomponent_kind kind)
+{
+       backend->priv->kind = kind;
+}
+
+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);
+
+       backend->priv->registry = g_object_ref (registry);
+}
+
+static void
 cal_backend_set_property (GObject *object,
                           guint property_id,
                           const GValue *value,
@@ -231,21 +202,18 @@ cal_backend_set_property (GObject *object,
                                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_SOURCE:
-                       cal_backend_set_source (
+
+               case PROP_REGISTRY:
+                       cal_backend_set_registry (
                                E_CAL_BACKEND (object),
                                g_value_get_object (value));
                        return;
-               case PROP_URI:
-                       cal_backend_set_uri (
-                               E_CAL_BACKEND (object),
-                               g_value_get_string (value));
-                       return;
        }
 
        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -263,19 +231,16 @@ cal_backend_get_property (GObject *object,
                                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_SOURCE:
+
+               case PROP_REGISTRY:
                        g_value_set_object (
-                               value, e_cal_backend_get_source (
-                               E_CAL_BACKEND (object)));
-                       return;
-               case PROP_URI:
-                       g_value_set_string (
-                               value, e_cal_backend_get_uri (
+                               value, e_cal_backend_get_registry (
                                E_CAL_BACKEND (object)));
                        return;
        }
@@ -284,28 +249,41 @@ cal_backend_get_property (GObject *object,
 }
 
 static void
+cal_backend_dispose (GObject *object)
+{
+       ECalBackendPrivate *priv;
+
+       priv = E_CAL_BACKEND_GET_PRIVATE (object);
+
+       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);
+}
+
+static void
 cal_backend_finalize (GObject *object)
 {
        ECalBackendPrivate *priv;
 
-       priv = E_CAL_BACKEND (object)->priv;
+       priv = E_CAL_BACKEND_GET_PRIVATE (object);
 
        g_assert (priv->clients == NULL);
 
-       g_slist_free (priv->views);
        /* should be NULL, anyway */
-       g_slist_free (priv->clients);
+       g_list_free (priv->clients);
+       g_mutex_clear (&priv->clients_mutex);
 
-       g_mutex_free (priv->clients_mutex);
-       g_mutex_free (priv->views_mutex);
+       g_list_free (priv->views);
+       g_mutex_clear (&priv->views_mutex);
 
-       g_free (priv->uri);
-       g_free (priv->cache_dir);
+       g_hash_table_destroy (priv->zone_cache);
+       g_mutex_clear (&priv->zone_cache_lock);
 
-       if (priv->source) {
-               g_signal_handlers_disconnect_matched (priv->source, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, object);
-               g_object_unref (priv->source);
-       }
+       g_free (priv->cache_dir);
 
        /* Chain up to parent's finalize() method. */
        G_OBJECT_CLASS (e_cal_backend_parent_class)->finalize (object);
@@ -319,124 +297,250 @@ cal_backend_constructed (GObject *object)
        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;
+
+       cal_backend = E_CAL_BACKEND (backend);
+       registry = e_cal_backend_get_registry (cal_backend);
+       source = e_backend_get_source (backend);
+
+       return e_source_registry_authenticate_sync (
+               registry, source, auth, cancellable, error);
+}
+
 static void
-e_cal_backend_class_init (ECalBackendClass *klass)
+cal_backend_add_cached_timezone (ETimezoneCache *cache,
+                                 icaltimezone *zone)
+{
+       ECalBackendPrivate *priv;
+       const gchar *tzid;
+
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
+
+       g_mutex_lock (&priv->zone_cache_lock);
+
+       tzid = icaltimezone_get_tzid (zone);
+
+       /* 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;
+
+               cached_zone = icaltimezone_new ();
+               icalcomp = icaltimezone_get_component (zone);
+               icalcomp = icalcomponent_new_clone (icalcomp);
+               icaltimezone_set_component (cached_zone, icalcomp);
+
+               g_hash_table_insert (
+                       priv->zone_cache,
+                       g_strdup (tzid), cached_zone);
+
+               /* 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);
+       }
+
+       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;
+
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
+
+       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;
+               }
+
+               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;
+}
+
+static GList *
+cal_backend_list_cached_timezones (ETimezoneCache *cache)
+{
+       ECalBackendPrivate *priv;
+       GList *list;
+
+       priv = E_CAL_BACKEND_GET_PRIVATE (cache);
+
+       g_mutex_lock (&priv->zone_cache_lock);
+
+       list = g_hash_table_get_values (priv->zone_cache);
+
+       g_mutex_unlock (&priv->zone_cache_lock);
+
+       return list;
+}
+
+static void
+e_cal_backend_class_init (ECalBackendClass *class)
 {
        GObjectClass *object_class;
+       EBackendClass *backend_class;
 
-       g_type_class_add_private (klass, sizeof (ECalBackendPrivate));
+       g_type_class_add_private (class, sizeof (ECalBackendPrivate));
 
-       object_class = G_OBJECT_CLASS (klass);
+       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;
 
-       klass->get_backend_property = cal_backend_get_backend_property;
-       klass->set_backend_property = cal_backend_set_backend_property;
+       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,
-                       NULL,
-                       NULL,
-                       G_PARAM_READWRITE));
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
 
        g_object_class_install_property (
                object_class,
                PROP_KIND,
                g_param_spec_ulong (
                        "kind",
-                       NULL,
-                       NULL,
+                       "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_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
 
        g_object_class_install_property (
                object_class,
-               PROP_SOURCE,
+               PROP_REGISTRY,
                g_param_spec_object (
-                       "source",
-                       NULL,
-                       NULL,
-                       E_TYPE_SOURCE,
-                       G_PARAM_READWRITE |
-                       G_PARAM_CONSTRUCT_ONLY));
-
-       g_object_class_install_property (
-               object_class,
-               PROP_URI,
-               g_param_spec_string (
-                       "uri",
-                       NULL,
-                       NULL,
-                       "",
+                       "registry",
+                       "Registry",
+                       "Data source registry",
+                       E_TYPE_SOURCE_REGISTRY,
                        G_PARAM_READWRITE |
-                       G_PARAM_CONSTRUCT_ONLY));
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+}
 
-       signals[LAST_CLIENT_GONE] = g_signal_new (
-               "last_client_gone",
-               G_TYPE_FROM_CLASS (klass),
-               G_SIGNAL_RUN_FIRST,
-               G_STRUCT_OFFSET (ECalBackendClass, last_client_gone),
-               NULL, NULL,
-               g_cclosure_marshal_VOID__VOID,
-               G_TYPE_NONE, 0);
+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)
 {
-       backend->priv = G_TYPE_INSTANCE_GET_PRIVATE (
-               backend, E_TYPE_CAL_BACKEND, ECalBackendPrivate);
+       GHashTable *zone_cache;
 
-       backend->priv->clients = NULL;
-       backend->priv->clients_mutex = g_mutex_new ();
+       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->views = NULL;
-       backend->priv->views_mutex = g_mutex_new ();
-
-       backend->priv->readonly = TRUE;
-       backend->priv->online = FALSE;
-}
+       backend->priv = E_CAL_BACKEND_GET_PRIVATE (backend);
 
-/**
- * e_cal_backend_get_source:
- * @backend: an #ECalBackend
- *
- * Gets the #ESource associated with the given backend.
- *
- * Returns: The #ESource for the backend.
- */
-ESource *
-e_cal_backend_get_source (ECalBackend *backend)
-{
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       backend->priv->clients = NULL;
+       g_mutex_init (&backend->priv->clients_mutex);
 
-       return backend->priv->source;
-}
+       backend->priv->views = NULL;
+       g_mutex_init (&backend->priv->views_mutex);
 
-/**
- * e_cal_backend_get_uri:
- * @backend: an #ECalBackend
- *
- * Queries the URI of a calendar backend, which must already have an open
- * calendar.
- *
- * Returns: The URI where the calendar is stored.
- **/
-const gchar *
-e_cal_backend_get_uri (ECalBackend *backend)
-{
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+       backend->priv->zone_cache = zone_cache;
+       g_mutex_init (&backend->priv->zone_cache_lock);
 
-       return backend->priv->uri;
+       backend->priv->readonly = TRUE;
 }
 
 /**
@@ -456,17 +560,21 @@ e_cal_backend_get_kind (ECalBackend *backend)
 }
 
 /**
- * e_cal_backend_is_online:
+ * e_cal_backend_get_registry:
  * @backend: an #ECalBackend
  *
- * Returns: Whether is backend online.
+ * Returns the data source registry to which #EBackend:source belongs.
+ *
+ * Returns: an #ESourceRegistry
+ *
+ * Since: 3.6
  **/
-gboolean
-e_cal_backend_is_online (ECalBackend *backend)
+ESourceRegistry *
+e_cal_backend_get_registry (ECalBackend *backend)
 {
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), FALSE);
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
 
-       return backend->priv->online;
+       return backend->priv->registry;
 }
 
 /**
@@ -479,6 +587,8 @@ e_cal_backend_is_online (ECalBackend *backend)
  * 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)
@@ -489,7 +599,7 @@ e_cal_backend_is_opened (ECalBackend *backend)
 }
 
 /**
- * e_cal_backend_is_opening::
+ * e_cal_backend_is_opening:
  * @backend: an #ECalBackend
  *
  * Checks if @backend is processing its opening phase, which
@@ -500,6 +610,8 @@ e_cal_backend_is_opened (ECalBackend *backend)
  * 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)
@@ -515,6 +627,8 @@ e_cal_backend_is_opening (ECalBackend *backend)
  *
  * 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)
@@ -531,6 +645,8 @@ e_cal_backend_is_readonly (ECalBackend *backend)
  * 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)
@@ -547,9 +663,12 @@ e_cal_backend_is_removed (ECalBackend *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_set_is_removed (ECalBackend *backend, gboolean is_removed)
+e_cal_backend_set_is_removed (ECalBackend *backend,
+                              gboolean is_removed)
 {
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
 
@@ -594,6 +713,9 @@ e_cal_backend_set_cache_dir (ECalBackend *backend,
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (cache_dir != NULL);
 
+       if (g_strcmp0 (backend->priv->cache_dir, cache_dir) == 0)
+               return;
+
        g_free (backend->priv->cache_dir);
        backend->priv->cache_dir = g_strdup (cache_dir);
 
@@ -601,6 +723,30 @@ e_cal_backend_set_cache_dir (ECalBackend *backend,
 }
 
 /**
+ * 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
+ *
+ * Returns: a filename for an attachment in a local cache dir. Free returned
+ * pointer with a g_free().
+ *
+ * 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 e_filename_mkdir_encoded (e_cal_backend_get_cache_dir (backend), uid, filename, fileindex);
+}
+
+/**
  * e_cal_backend_get_backend_property:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
@@ -618,7 +764,11 @@ e_cal_backend_set_cache_dir (ECalBackend *backend,
  * Since: 3.2
  **/
 void
-e_cal_backend_get_backend_property (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *prop_name)
+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));
@@ -646,7 +796,12 @@ e_cal_backend_get_backend_property (ECalBackend *backend, EDataCal *cal, guint32
  * Since: 3.2
  **/
 void
-e_cal_backend_set_backend_property (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *prop_name, const gchar *prop_value)
+e_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));
@@ -658,10 +813,12 @@ e_cal_backend_set_backend_property (ECalBackend *backend, EDataCal *cal, guint32
 }
 
 static void
-cal_destroy_cb (gpointer data, GObject *where_cal_was)
+cal_destroy_cb (gpointer data,
+                GObject *where_cal_was)
 {
-       e_cal_backend_remove_client_private (E_CAL_BACKEND (data),
-                                            (EDataCal *) where_cal_was, FALSE);
+       e_cal_backend_remove_client_private (
+               E_CAL_BACKEND (data),
+               (EDataCal *) where_cal_was, FALSE);
 }
 
 /**
@@ -673,7 +830,8 @@ cal_destroy_cb (gpointer data, GObject *where_cal_was)
  * notify all clients added via this function.
  */
 void
-e_cal_backend_add_client (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_add_client (ECalBackend *backend,
+                          EDataCal *cal)
 {
        ECalBackendPrivate *priv;
 
@@ -686,39 +844,35 @@ e_cal_backend_add_client (ECalBackend *backend, EDataCal *cal)
 
        g_object_weak_ref (G_OBJECT (cal), cal_destroy_cb, backend);
 
-       g_mutex_lock (priv->clients_mutex);
-       priv->clients = g_slist_append (priv->clients, cal);
-       g_mutex_unlock (priv->clients_mutex);
+       g_mutex_lock (&priv->clients_mutex);
+       priv->clients = g_list_append (priv->clients, cal);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 static void
-e_cal_backend_remove_client_private (ECalBackend *backend, EDataCal *cal, gboolean weak_unref)
+e_cal_backend_remove_client_private (ECalBackend *backend,
+                                     EDataCal *cal,
+                                     gboolean weak_unref)
 {
-       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;
-
        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 (priv->clients_mutex);
-       priv->clients = g_slist_remove (priv->clients, cal);
-       g_mutex_unlock (priv->clients_mutex);
+       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);
 
-       /* 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)
-               g_signal_emit (backend, signals[LAST_CLIENT_GONE], 0);
+       g_object_unref (backend);
 }
 
 /**
@@ -729,7 +883,8 @@ e_cal_backend_remove_client_private (ECalBackend *backend, EDataCal *cal, gboole
  * Removes a client from the list of connected clients to the given backend.
  */
 void
-e_cal_backend_remove_client (ECalBackend *backend, EDataCal *cal)
+e_cal_backend_remove_client (ECalBackend *backend,
+                             EDataCal *cal)
 {
        e_cal_backend_remove_client_private (backend, cal, TRUE);
 }
@@ -742,18 +897,21 @@ e_cal_backend_remove_client (ECalBackend *backend, EDataCal *cal)
  * 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_add_view (ECalBackend *backend, EDataCalView *view)
+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_mutex_lock (backend->priv->views_mutex);
+       g_mutex_lock (&backend->priv->views_mutex);
 
-       backend->priv->views = g_slist_append (backend->priv->views, view);
+       backend->priv->views = g_list_append (backend->priv->views, view);
 
-       g_mutex_unlock (backend->priv->views_mutex);
+       g_mutex_unlock (&backend->priv->views_mutex);
 }
 
 /**
@@ -763,19 +921,59 @@ e_cal_backend_add_view (ECalBackend *backend, EDataCalView *view)
  *
  * Removes view from the list of live views for the backend.
  *
- * Since: 2.24
+ * Since: 3.2
  **/
 void
-e_cal_backend_remove_view (ECalBackend *backend, EDataCalView *view)
+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_mutex_lock (backend->priv->views_mutex);
+       g_mutex_lock (&backend->priv->views_mutex);
 
-       backend->priv->views = g_slist_remove (backend->priv->views, view);
+       backend->priv->views = g_list_remove (backend->priv->views, view);
 
-       g_mutex_unlock (backend->priv->views_mutex);
+       g_mutex_unlock (&backend->priv->views_mutex);
+}
+
+/**
+ * e_cal_backend_list_views:
+ * @backend: an #ECalBackend
+ *
+ * Returns a list of #ECalBookView instances added with
+ * e_cal_backend_add_view().
+ *
+ * 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)
+{
+       GList *list;
+
+       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
+
+       g_mutex_lock (&backend->priv->views_mutex);
+
+       /* 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);
+
+       g_mutex_unlock (&backend->priv->views_mutex);
+
+       return list;
 }
 
 /**
@@ -786,28 +984,30 @@ e_cal_backend_remove_view (ECalBackend *backend, EDataCalView *view)
  *
  * 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_foreach_view (ECalBackend *backend, gboolean (* callback) (EDataCalView *view, gpointer user_data), gpointer user_data)
+e_cal_backend_foreach_view (ECalBackend *backend,
+                            gboolean (*callback) (EDataCalView *view,
+                                                  gpointer user_data),
+                            gpointer user_data)
 {
-       const GSList *views;
-       EDataCalView *view;
-       gboolean stop = FALSE;
+       GList *list, *link;
 
-       g_return_if_fail (backend != NULL);
+       g_return_if_fail (E_IS_CAL_BACKEND (backend));
        g_return_if_fail (callback != NULL);
 
-       g_mutex_lock (backend->priv->views_mutex);
-
-       for (views = backend->priv->views; views && !stop; views = views->next) {
-               view = E_DATA_CAL_VIEW (views->data);
+       list = e_cal_backend_list_views (backend);
 
-               g_object_ref (view);
-               stop = !callback (view, user_data);
-               g_object_unref (view);
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               if (!callback (E_DATA_CAL_VIEW (link->data), user_data))
+                       break;
        }
 
-       g_mutex_unlock (backend->priv->views_mutex);
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -816,30 +1016,16 @@ e_cal_backend_foreach_view (ECalBackend *backend, gboolean (* callback) (EDataCa
  * @proxy: The calendar backend to act as notification proxy.
  *
  * Sets the backend that will act as notification proxy for the given backend.
- */
-void
-e_cal_backend_set_notification_proxy (ECalBackend *backend, ECalBackend *proxy)
-{
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-
-       backend->priv->notification_proxy = proxy;
-}
-
-/**
- * e_cal_backend_set_online:
- * @backend: A calendar backend
- * @is_online: Whether is online
  *
- * Sets the online mode of the calendar
+ * Since: 3.2
  */
 void
-e_cal_backend_set_online (ECalBackend *backend, gboolean is_online)
+e_cal_backend_set_notification_proxy (ECalBackend *backend,
+                                      ECalBackend *proxy)
 {
-       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)->set_online != NULL);
 
-       (* E_CAL_BACKEND_GET_CLASS (backend)->set_online) (backend, is_online);
+       backend->priv->notification_proxy = proxy;
 }
 
 /**
@@ -899,88 +1085,42 @@ e_cal_backend_set_online (ECalBackend *backend, gboolean is_online)
  * required, there is e_cal_backend_notify_auth_required() for this.
  **/
 void
-e_cal_backend_open (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, gboolean only_if_exists)
+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);
+       g_mutex_lock (&backend->priv->clients_mutex);
 
        if (e_cal_backend_is_opened (backend)) {
-               g_mutex_unlock (backend->priv->clients_mutex);
+               gboolean online;
+
+               g_mutex_unlock (&backend->priv->clients_mutex);
 
                e_data_cal_report_readonly (cal, backend->priv->readonly);
-               e_data_cal_report_online (cal, backend->priv->online);
+
+               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);
+               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);
+               g_mutex_unlock (&backend->priv->clients_mutex);
 
                (* E_CAL_BACKEND_GET_CLASS (backend)->open) (backend, cal, opid, cancellable, only_if_exists);
        }
 }
 
 /**
- * e_cal_backend_authenticate_user:
- * @backend: an #ECalBackend
- * @cancellable: a #GCancellable for the operation
- * @credentials: #ECredentials to use for authentication
- *
- * Notifies @backend about @credentials provided by user to use
- * for authentication. This notification is usually called during
- * opening phase as a response to e_cal_backend_notify_auth_required()
- * on the client side and it results in setting property 'opening' to %TRUE
- * unless the backend is already opened. This function finishes opening
- * phase, thus it should be finished with e_cal_backend_notify_opened().
- *
- * See information at e_cal_backend_open() for more details
- * how the opening phase works.
- **/
-void
-e_cal_backend_authenticate_user (ECalBackend  *backend,
-                                GCancellable *cancellable,
-                                ECredentials *credentials)
-{
-       g_return_if_fail (E_IS_CAL_BACKEND (backend));
-       g_return_if_fail (credentials != NULL);
-       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user);
-
-       if (!e_cal_backend_is_opened (backend))
-               backend->priv->opening = TRUE;
-
-       (* E_CAL_BACKEND_GET_CLASS (backend)->authenticate_user) (backend, cancellable, credentials);
-}
-
-/**
- * e_cal_backend_remove:
- * @backend: an #ECalBackend
- * @cal: an #EDataCal
- * @opid: the ID to use for this operation
- * @cancellable: a #GCancellable for the operation
- *
- * Removes the calendar being accessed by the given backend.
- * This might be finished with e_data_cal_respond_remove().
- **/
-void
-e_cal_backend_remove (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable)
-{
-       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)->remove != NULL);
-
-       if (e_cal_backend_is_opening (backend))
-               e_data_cal_respond_remove (cal, opid, EDC_OPENING_ERROR);
-       else
-               (* E_CAL_BACKEND_GET_CLASS (backend)->remove) (backend, cal, opid, cancellable);
-}
-
-/**
  * e_cal_backend_refresh:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
@@ -997,7 +1137,10 @@ e_cal_backend_remove (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancel
  * Since: 2.30
  **/
 void
-e_cal_backend_refresh (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable)
+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));
@@ -1006,6 +1149,8 @@ e_cal_backend_refresh (ECalBackend *backend, EDataCal *cal, guint32 opid, GCance
                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);
 }
@@ -1024,7 +1169,12 @@ e_cal_backend_refresh (ECalBackend *backend, EDataCal *cal, guint32 opid, GCance
  * This might be finished with e_data_cal_respond_get_object().
  **/
 void
-e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *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));
@@ -1033,6 +1183,8 @@ e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, guint32 opid, GCa
 
        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);
 }
@@ -1049,7 +1201,11 @@ e_cal_backend_get_object (ECalBackend *backend, EDataCal *cal, guint32 opid, GCa
  * This might be finished with e_data_cal_respond_get_object_list().
  **/
 void
-e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *sexp)
+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));
@@ -1057,6 +1213,8 @@ e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, guint32 opid
 
        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);
 }
@@ -1076,7 +1234,13 @@ e_cal_backend_get_object_list (ECalBackend *backend, EDataCal *cal, guint32 opid
  * This might be finished with e_data_cal_respond_get_free_busy().
  **/
 void
-e_cal_backend_get_free_busy (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const GSList *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));
@@ -1086,89 +1250,116 @@ e_cal_backend_get_free_busy (ECalBackend *backend, EDataCal *cal, guint32 opid,
 
        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:
+ * 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
- * @calobj: The object to create.
+ * @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_object().
+ * This might be finished with e_data_cal_respond_create_objects().
+ *
+ * Since: 3.6
  **/
 void
-e_cal_backend_create_object (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *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);
+       g_return_if_fail (calobjs != NULL);
 
        if (e_cal_backend_is_opening (backend))
-               e_data_cal_respond_create_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
-       else if (E_CAL_BACKEND_GET_CLASS (backend)->create_object)
-               (* E_CAL_BACKEND_GET_CLASS (backend)->create_object) (backend, cal, opid, cancellable, calobj);
+               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_respond_create_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+               (* E_CAL_BACKEND_GET_CLASS (backend)->create_objects) (backend, cal, opid, cancellable, calobjs);
 }
 
 /**
- * e_cal_backend_modify_object:
+ * 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
- * @calobj: Object to be modified.
+ * @calobjs: Objects to be modified (list of gchar *).
  * @mod: Type of modification.
  *
- * Calls the modify_object method on the given backend.
- * This might be finished with e_data_cal_respond_modify_object().
+ * 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, guint32 opid, GCancellable *cancellable, const gchar *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);
+       g_return_if_fail (calobjs != NULL);
 
        if (e_cal_backend_is_opening (backend))
-               e_data_cal_respond_modify_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL);
-       else if (E_CAL_BACKEND_GET_CLASS (backend)->modify_object)
-               (* E_CAL_BACKEND_GET_CLASS (backend)->modify_object) (backend, cal, opid, cancellable, calobj, mod);
+               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_respond_modify_object (cal, opid, EDC_ERROR (UnsupportedMethod), NULL, NULL);
+               (* E_CAL_BACKEND_GET_CLASS (backend)->modify_objects) (backend, cal, opid, cancellable, calobjs, mod);
 }
 
 /**
- * e_cal_backend_remove_object:
+ * 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
- * @uid: Unique identifier of the object to remove.
- * @rid: A recurrence ID.
+ * @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_object().
+ * This might be finished with e_data_cal_respond_remove_objects().
+ *
+ * Since: 3.6
  **/
 void
-e_cal_backend_remove_object (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *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 (E_CAL_BACKEND_GET_CLASS (backend)->remove_object != NULL);
+       g_return_if_fail (ids != NULL);
+       g_return_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->remove_objects != NULL);
 
        if (e_cal_backend_is_opening (backend))
-               e_data_cal_respond_remove_object (cal, opid, EDC_OPENING_ERROR, NULL, NULL, NULL);
+               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_object) (backend, cal, opid, cancellable, uid, rid, mod);
+               (* E_CAL_BACKEND_GET_CLASS (backend)->remove_objects) (backend, cal, opid, cancellable, ids, mod);
 }
 
 /**
@@ -1183,7 +1374,11 @@ e_cal_backend_remove_object (ECalBackend *backend, EDataCal *cal, guint32 opid,
  * This might be finished with e_data_cal_respond_receive_objects().
  **/
 void
-e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *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));
@@ -1192,6 +1387,8 @@ e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, guint32 opid
 
        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);
 }
@@ -1208,7 +1405,11 @@ e_cal_backend_receive_objects (ECalBackend *backend, EDataCal *cal, guint32 opid
  * This might be finished with e_data_cal_respond_send_objects().
  **/
 void
-e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *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));
@@ -1217,6 +1418,8 @@ e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, guint32 opid, G
 
        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);
 }
@@ -1233,9 +1436,16 @@ e_cal_backend_send_objects (ECalBackend *backend, EDataCal *cal, guint32 opid, G
  * 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_attachment_uris (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid)
+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));
@@ -1244,6 +1454,8 @@ e_cal_backend_get_attachment_uris (ECalBackend *backend, EDataCal *cal, guint32
 
        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);
 }
@@ -1263,7 +1475,13 @@ e_cal_backend_get_attachment_uris (ECalBackend *backend, EDataCal *cal, guint32
  * Default implementation of this method returns Not Supported error.
  **/
 void
-e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *uid, const gchar *rid, const gchar *auid)
+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));
@@ -1272,10 +1490,12 @@ e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, guint32 opid,
 
        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_CAL_BACKEND_GET_CLASS (backend)->discard_alarm) (backend, cal, opid, cancellable, uid, rid, auid);
-       else
+       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);
 }
 
 /**
@@ -1292,7 +1512,11 @@ e_cal_backend_discard_alarm (ECalBackend *backend, EDataCal *cal, guint32 opid,
  * This might be finished with e_data_cal_respond_get_timezone().
  **/
 void
-e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, guint32 opid, GCancellable *cancellable, const gchar *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));
@@ -1301,6 +1525,8 @@ e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, guint32 opid, G
 
        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);
 }
@@ -1311,13 +1537,17 @@ e_cal_backend_get_timezone (ECalBackend *backend, EDataCal *cal, guint32 opid, G
  * @cal: an #EDataCal
  * @opid: the ID to use for this operation
  * @cancellable: a #GCancellable for the operation
- * @tzobj: The timezone object, in a string.
+ * @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, guint32 opid, GCancellable *cancellable, const gchar *tzobject)
+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 (tzobject != NULL);
@@ -1325,36 +1555,24 @@ e_cal_backend_add_timezone (ECalBackend *backend, EDataCal *cal, guint32 opid, G
 
        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_timezone:
- * @backend: an #ECalBackend
- * @tzid: ID of the timezone to get.
- *
- * Calls the internal_get_timezone method on the given backend.
- */
-icaltimezone *
-e_cal_backend_internal_get_timezone (ECalBackend *backend, const gchar *tzid)
-{
-       g_return_val_if_fail (E_IS_CAL_BACKEND (backend), NULL);
-       g_return_val_if_fail (tzid != NULL, NULL);
-       g_return_val_if_fail (E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone != NULL, NULL);
-
-       return (* E_CAL_BACKEND_GET_CLASS (backend)->internal_get_timezone) (backend, tzid);
-}
-
-/**
  * e_cal_backend_start_view:
  * @backend: an #ECalBackend
  * @view: The view to be started.
  *
  * Starts a new live view on the given backend.
+ *
+ * Since: 3.2
  */
 void
-e_cal_backend_start_view (ECalBackend *backend, EDataCalView *view)
+e_cal_backend_start_view (ECalBackend *backend,
+                          EDataCalView *view)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
@@ -1373,7 +1591,8 @@ e_cal_backend_start_view (ECalBackend *backend, EDataCalView *view)
  * Since: 3.2
  */
 void
-e_cal_backend_stop_view (ECalBackend *backend, EDataCalView *view)
+e_cal_backend_stop_view (ECalBackend *backend,
+                         EDataCalView *view)
 {
        g_return_if_fail (backend != NULL);
        g_return_if_fail (E_IS_CAL_BACKEND (backend));
@@ -1385,204 +1604,175 @@ e_cal_backend_stop_view (ECalBackend *backend, EDataCalView *view)
        (* E_CAL_BACKEND_GET_CLASS (backend)->stop_view) (backend, view);
 }
 
-static gboolean
-object_created_cb (EDataCalView *view, gpointer calobj)
-{
-       if (e_data_cal_view_object_matches (view, calobj))
-               e_data_cal_view_notify_objects_added_1 (view, calobj);
-
-       return TRUE;
-}
-
 /**
- * e_cal_backend_notify_object_created:
+ * e_cal_backend_notify_component_created:
  * @backend: an #ECalBackend
- * @calobj: iCalendar representation of new object
+ * @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 gchar *calobj)
+e_cal_backend_notify_component_created (ECalBackend *backend,
+                                        ECalComponent *component)
 {
-       ECalBackendPrivate *priv;
+       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;
        }
 
-       e_cal_backend_foreach_view (backend, object_created_cb, (gpointer) calobj);
-}
+       list = e_cal_backend_list_views (backend);
 
-/**
- * e_cal_backend_notify_objects_added:
- *
- * Since: 2.24
- **/
-void
-e_cal_backend_notify_objects_added (ECalBackend *backend, EDataCalView *view, const GSList *objects)
-{
-       e_data_cal_view_notify_objects_added (view, objects);
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               EDataCalView *view = E_DATA_CAL_VIEW (link->data);
+
+               if (e_data_cal_view_component_matches (view, component))
+                       e_data_cal_view_notify_components_added_1 (view, component);
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 static void
-match_view_and_notify (EDataCalView *view, const gchar *old_object, const gchar *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 (view, 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 (view, object);
        if (old_match && new_match)
-               e_data_cal_view_notify_objects_modified_1 (view, object);
+               e_data_cal_view_notify_components_modified_1 (view, new_component);
        else if (new_match)
-               e_data_cal_view_notify_objects_added_1 (view, 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 (view, 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);
        }
 }
 
-struct call_data {
-       const gchar *old_object;
-       const gchar *object;
-       const ECalComponentId *id;
-};
-
-static gboolean
-call_match_and_notify (EDataCalView *view, gpointer user_data)
-{
-       struct call_data *cd = user_data;
-
-       g_return_val_if_fail (user_data != NULL, FALSE);
-
-       match_view_and_notify (view, cd->old_object, cd->object);
-
-       return TRUE;
-}
-
 /**
- * e_cal_backend_notify_object_modified:
+ * e_cal_backend_notify_component_modified:
  * @backend: an #ECalBackend
- * @old_object: iCalendar representation of the original form of the object
- * @object: iCalendar representation of the new form of the object
+ * @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.
  *
- * #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.
+ * 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_object_modified (ECalBackend *backend, const gchar *old_object, const gchar *object)
+e_cal_backend_notify_component_modified (ECalBackend *backend,
+                                         ECalComponent *old_component,
+                                         ECalComponent *new_component)
 {
-       ECalBackendPrivate *priv;
-       struct call_data cd;
+       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_object_modified (priv->notification_proxy, old_object, object);
+       if (backend->priv->notification_proxy != NULL) {
+               e_cal_backend_notify_component_modified (
+                       backend->priv->notification_proxy,
+                       old_component, new_component);
                return;
        }
 
-       cd.old_object = old_object;
-       cd.object = object;
-       cd.id = NULL;
+       list = e_cal_backend_list_views (backend);
 
-       e_cal_backend_foreach_view (backend, call_match_and_notify, &cd);
-}
+       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_cal_backend_notify_objects_modified:
- *
- * Since: 2.24
- **/
-void
-e_cal_backend_notify_objects_modified (ECalBackend *backend, EDataCalView *view, const GSList *objects)
-{
-       e_data_cal_view_notify_objects_modified (view, objects);
-}
-
-static gboolean
-object_removed_cb (EDataCalView *view, gpointer user_data)
-{
-       struct call_data *cd = user_data;
-
-       g_return_val_if_fail (user_data != NULL, FALSE);
-
-       if (cd->object == NULL) {
-               /* if object == NULL, it means the object has been completely
-                  removed from the backend */
-               if (!cd->old_object || e_data_cal_view_object_matches (view, cd->old_object))
-                       e_data_cal_view_notify_objects_removed_1 (view, cd->id);
-       } else
-               match_view_and_notify (view, cd->old_object, cd->object);
-
-       return TRUE;
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
- * e_cal_backend_notify_object_removed:
+ * e_cal_backend_notify_component_removed:
  * @backend: an #ECalBackend
  * @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.
+ * @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.
  *
- * 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.
+ * 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_object_removed (ECalBackend *backend, const ECalComponentId *id,
-                                    const gchar *old_object, const gchar *object)
+e_cal_backend_notify_component_removed (ECalBackend *backend,
+                                        const ECalComponentId *id,
+                                        ECalComponent *old_component,
+                                        ECalComponent *new_component)
 {
-       ECalBackendPrivate *priv;
-       struct call_data cd;
+       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_object_removed (priv->notification_proxy, id, old_object, object);
+       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;
        }
 
-       cd.old_object = old_object;
-       cd.object = object;
-       cd.id = id;
+       list = e_cal_backend_list_views (backend);
 
-       e_cal_backend_foreach_view (backend, object_removed_cb, &cd);
-}
+       for (link = list; link != NULL; link = g_list_next (link)) {
+               EDataCalView *view = E_DATA_CAL_VIEW (link->data);
 
-/**
- * e_cal_backend_notify_objects_removed:
- *
- * Since: 2.24
- **/
-void
-e_cal_backend_notify_objects_removed (ECalBackend *backend, EDataCalView *view, const GSList *ids)
-{
-       e_data_cal_view_notify_objects_removed (view, ids);
+               if (new_component != NULL)
+                       match_view_and_notify_component (
+                               view, old_component, new_component);
+
+               else if (old_component == NULL)
+                       e_data_cal_view_notify_objects_removed_1 (view, id);
+
+               else if (e_data_cal_view_component_matches (view, old_component))
+                       e_data_cal_view_notify_objects_removed_1 (view, id);
+       }
+
+       g_list_free_full (list, (GDestroyNotify) g_object_unref);
 }
 
 /**
@@ -1593,22 +1783,23 @@ e_cal_backend_notify_objects_removed (ECalBackend *backend, EDataCalView *view,
  * Notifies each of the backend's listeners about an error
  **/
 void
-e_cal_backend_notify_error (ECalBackend *backend, const gchar *message)
+e_cal_backend_notify_error (ECalBackend *backend,
+                            const gchar *message)
 {
        ECalBackendPrivate *priv = backend->priv;
-       GSList *l;
+       GList *l;
 
        if (priv->notification_proxy) {
                e_cal_backend_notify_error (priv->notification_proxy, message);
                return;
        }
 
-       g_mutex_lock (priv->clients_mutex);
+       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);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 /**
@@ -1620,10 +1811,11 @@ e_cal_backend_notify_error (ECalBackend *backend, const gchar *message)
  * Meant to be used by backend implementations.
  **/
 void
-e_cal_backend_notify_readonly (ECalBackend *backend, gboolean is_readonly)
+e_cal_backend_notify_readonly (ECalBackend *backend,
+                               gboolean is_readonly)
 {
        ECalBackendPrivate *priv;
-       GSList *l;
+       GList *l;
 
        priv = backend->priv;
        priv->readonly = is_readonly;
@@ -1633,12 +1825,12 @@ e_cal_backend_notify_readonly (ECalBackend *backend, gboolean is_readonly)
                return;
        }
 
-       g_mutex_lock (priv->clients_mutex);
+       g_mutex_lock (&priv->clients_mutex);
 
        for (l = priv->clients; l; l = l->next)
                e_data_cal_report_readonly (l->data, is_readonly);
 
-       g_mutex_unlock (priv->clients_mutex);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 /**
@@ -1648,73 +1840,29 @@ e_cal_backend_notify_readonly (ECalBackend *backend, gboolean is_readonly)
  *
  * Notifies clients of @backend's connection status indicated by @is_online.
  * Meant to be used by backend implementations.
+ *
+ * Since: 3.2
  **/
 void
-e_cal_backend_notify_online (ECalBackend *backend, gboolean is_online)
+e_cal_backend_notify_online (ECalBackend *backend,
+                             gboolean is_online)
 {
        ECalBackendPrivate *priv;
-       GSList *clients;
+       GList *clients;
 
        priv = backend->priv;
-       priv->online = is_online;
 
        if (priv->notification_proxy) {
                e_cal_backend_notify_online (priv->notification_proxy, is_online);
                return;
        }
 
-       g_mutex_lock (priv->clients_mutex);
+       g_mutex_lock (&priv->clients_mutex);
 
-       for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
+       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_auth_required:
- * @backend: an #ECalBackend
- * @is_self: Use %TRUE to indicate the authentication is required
- *    for the @backend, otheriwse the authentication is for any
- *    other source. Having @credentials %NULL means @is_self
- *    automatically.
- * @credentials: an #ECredentials that contains extra information for
- *    a source for which authentication is requested.
- *    This parameter can be NULL to indicate "for this calendar".
- *
- * Notifies clients that @backend requires authentication in order to
- * connect. This function call does not influence 'opening', but 
- * influences 'opened' property, which is set to %FALSE when @is_self
- * is %TRUE or @credentials is %NULL. Opening phase is finished
- * by e_cal_backend_notify_opened() if this is requested for @backend.
- *
- * See e_cal_backend_open() for a description how the whole opening
- * phase works.
- *
- * Meant to be used by backend implementations.
- **/
-void
-e_cal_backend_notify_auth_required (ECalBackend *backend, gboolean is_self, const ECredentials *credentials)
-{
-       ECalBackendPrivate *priv;
-       GSList *clients;
-
-       priv = backend->priv;
-
-       if (priv->notification_proxy) {
-               e_cal_backend_notify_auth_required (priv->notification_proxy, is_self, credentials);
-               return;
-       }
-
-       g_mutex_lock (priv->clients_mutex);
-
-       if (is_self || !credentials)
-               priv->opened = FALSE;
-
-       for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
-               e_data_cal_report_auth_required (E_DATA_CAL (clients->data), credentials);
-
-       g_mutex_unlock (priv->clients_mutex);
+       g_mutex_unlock (&priv->clients_mutex);
 }
 
 /**
@@ -1735,29 +1883,64 @@ e_cal_backend_notify_auth_required (ECalBackend *backend, gboolean is_self, cons
  * 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)
+e_cal_backend_notify_opened (ECalBackend *backend,
+                             GError *error)
 {
        ECalBackendPrivate *priv;
-       GSList *clients;
+       GList *clients;
 
        priv = backend->priv;
-       g_mutex_lock (priv->clients_mutex);
+       g_mutex_lock (&priv->clients_mutex);
 
        priv->opening = FALSE;
        priv->opened = error == NULL;
 
-       for (clients = priv->clients; clients != NULL; clients = g_slist_next (clients))
+       for (clients = priv->clients; clients != NULL; clients = g_list_next (clients))
                e_data_cal_report_opened (E_DATA_CAL (clients->data), error);
 
-       g_mutex_unlock (priv->clients_mutex);
+       g_mutex_unlock (&priv->clients_mutex);
 
        if (error)
                g_error_free (error);
 }
 
 /**
+ * e_cal_backend_notify_property_changed:
+ * @backend: an #ECalBackend
+ * @prop_name: property name, which changed
+ * @prop_value: new property value
+ *
+ * Notifies client about property value change.
+ *
+ * Since: 3.2
+ **/
+void
+e_cal_backend_notify_property_changed (ECalBackend *backend,
+                                       const gchar *prop_name,
+                                       const gchar *prop_value)
+{
+       ECalBackendPrivate *priv;
+       GList *clients;
+
+       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);
+
+       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_respond_opened:
  * @backend: an #ECalBackend
  * @cal: an #EDataCal
@@ -1766,13 +1949,18 @@ e_cal_backend_notify_opened (ECalBackend *backend, GError *error)
  *
  * 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_data_cal_respond_open() and
- * e_cal_backend_notify_opened() with the same @error.
+ * 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_respond_opened (ECalBackend *backend, EDataCal *cal, guint32 opid, GError *error)
+e_cal_backend_respond_opened (ECalBackend *backend,
+                              EDataCal *cal,
+                              guint32 opid,
+                              GError *error)
 {
        GError *copy = NULL;
 
@@ -1784,8 +1972,8 @@ e_cal_backend_respond_opened (ECalBackend *backend, EDataCal *cal, guint32 opid,
        if (error)
                copy = g_error_copy (error);
 
-       e_data_cal_respond_open (cal, opid, error);
        e_cal_backend_notify_opened (backend, copy);
+       e_data_cal_respond_open (cal, opid, error);
 }
 
 /**
@@ -1799,7 +1987,8 @@ e_cal_backend_respond_opened (ECalBackend *backend, EDataCal *cal, guint32 opid,
  * Since: 2.28
  **/
 void
-e_cal_backend_empty_cache (ECalBackend *backend, ECalBackendCache *cache)
+e_cal_backend_empty_cache (ECalBackend *backend,
+                           ECalBackendCache *cache)
 {
        GList *comps_in_cache;
 
@@ -1816,17 +2005,15 @@ e_cal_backend_empty_cache (ECalBackend *backend, ECalBackendCache *cache)
        for (comps_in_cache = e_cal_backend_cache_get_components (cache);
             comps_in_cache;
             comps_in_cache = comps_in_cache->next) {
-               gchar *comp_str;
                ECalComponentId *id;
                ECalComponent *comp = comps_in_cache->data;
 
                id = e_cal_component_get_id (comp);
-               comp_str = e_cal_component_get_as_string (comp);
 
                e_cal_backend_cache_remove_component (cache, id->uid, id->rid);
-               e_cal_backend_notify_object_removed (backend, id, comp_str, NULL);
 
-               g_free (comp_str);
+               e_cal_backend_notify_component_removed (backend, id, comp, NULL);
+
                e_cal_component_free_id (id);
                g_object_unref (comp);
        }