[evolution-data-server/wip/offline-cache] Make CalDAV backend derive from ECalMetaBackend + some other semi-related changes
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/wip/offline-cache] Make CalDAV backend derive from ECalMetaBackend + some other semi-related changes
- Date: Wed, 12 Apr 2017 09:53:58 +0000 (UTC)
commit d963caf8f546071a9b46be00b48501d908166412
Author: Milan Crha <mcrha redhat com>
Date: Wed Apr 12 11:53:07 2017 +0200
Make CalDAV backend derive from ECalMetaBackend + some other semi-related changes
.../backends/caldav/e-cal-backend-caldav.c | 6170 ++++----------------
.../backends/caldav/e-cal-backend-caldav.h | 4 +-
.../backends/gtasks/e-cal-backend-gtasks.c | 36 +-
src/calendar/backends/http/e-cal-backend-http.c | 75 +-
src/calendar/libedata-cal/e-cal-meta-backend.c | 435 ++-
src/calendar/libedata-cal/e-cal-meta-backend.h | 22 +-
src/libedataserver/e-soup-session.c | 6 +
.../e-source-credentials-provider-impl-google.c | 8 +-
src/libedataserver/e-webdav-session.c | 277 +-
src/libedataserver/e-webdav-session.h | 26 +-
src/libedataserver/e-xml-document.c | 126 +
src/libedataserver/e-xml-document.h | 19 +
tests/libedata-cal/test-cal-meta-backend.c | 7 +-
13 files changed, 1841 insertions(+), 5370 deletions(-)
---
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.c
b/src/calendar/backends/caldav/e-cal-backend-caldav.c
index 4410112..71b3842 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -1,7 +1,6 @@
/*
- * Evolution calendar - caldav backend
- *
* Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
*
* This library is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
@@ -21,89 +20,30 @@
#include "evolution-data-server-config.h"
#include <string.h>
-#include <unistd.h>
-#include <glib/gstdio.h>
#include <glib/gi18n-lib.h>
-/* LibXML2 includes */
-#include <libxml/parser.h>
-#include <libxml/tree.h>
-#include <libxml/xpath.h>
-#include <libxml/xpathInternals.h>
-
-/* LibSoup includes */
-#include <libsoup/soup.h>
-
#include <libedataserver/libedataserver.h>
#include "e-cal-backend-caldav.h"
-#define d(x)
-
#define E_CAL_BACKEND_CALDAV_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_CAL_BACKEND_CALDAV, ECalBackendCalDAVPrivate))
-#define CALDAV_CTAG_KEY "CALDAV_CTAG"
-#define CALDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget
request */
-#define LOCAL_PREFIX "file://"
+#define E_CALDAV_MAX_MULTIGET_AMOUNT 100 /* what's the maximum count of items to fetch within a multiget
request */
-/* in seconds */
-#define DEFAULT_REFRESH_TIME 60
+#define E_CALDAV_X_ETAG "X-EVOLUTION-CALDAV-ETAG"
#define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
#define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
-typedef enum {
-
- SLAVE_SHOULD_SLEEP,
- SLAVE_SHOULD_WORK,
- SLAVE_SHOULD_WORK_NO_CTAG_CHECK,
- SLAVE_SHOULD_DIE
-
-} SlaveCommand;
-
/* Private part of the ECalBackendHttp structure */
struct _ECalBackendCalDAVPrivate {
-
- /* The local disk cache */
- ECalBackendStore *store;
-
- /* should we sync for offline mode? */
- gboolean do_offline;
-
- /* TRUE after caldav_open */
- gboolean loaded;
- /* TRUE when server reachable */
- gboolean opened;
-
- /* lock to indicate a busy state */
- GMutex busy_lock;
-
- /* cond to synch threads */
- GCond cond;
-
- /* cond to know the slave gone */
- GCond slave_gone_cond;
-
- /* BG synch thread */
- const GThread *synch_slave; /* just for a reference, whether thread exists */
- SlaveCommand slave_cmd;
- gboolean slave_busy; /* whether is slave working */
-
/* The main soup session */
- SoupSession *session;
-
- /* clandar uri */
- gchar *uri;
-
- /* Authentication info */
- ENamedParameters *credentials;
- gboolean auth_required;
+ EWebDAVSession *webdav;
/* support for 'getctag' extension */
gboolean ctag_supported;
- gchar *ctag_to_store;
/* TRUE when 'calendar-schedule' supported on the server */
gboolean calendar_schedule;
@@ -119,4513 +59,1191 @@ struct _ECalBackendCalDAVPrivate {
/* The iCloud.com requires timezone IDs as locations */
gboolean is_icloud;
-
- /* set to true if thread for ESource::changed is invoked */
- gboolean updating_source;
-
- guint refresh_id;
-
- /* If we fail to obtain an OAuth2 access token,
- * soup_authenticate_bearer() stashes an error
- * here to be claimed in caldav_credentials_required_sync().
- * This lets us propagate a more useful error
- * message than a generic SOUP_STATUS_UNAUTHORIZED description. */
- GError *bearer_auth_error;
- GMutex bearer_auth_error_lock;
- ESoupAuthBearer *using_bearer_auth;
};
-/* Forward Declarations */
-static void e_caldav_backend_initable_init
- (GInitableIface *interface);
-
-G_DEFINE_TYPE_WITH_CODE (
- ECalBackendCalDAV,
- e_cal_backend_caldav,
- E_TYPE_CAL_BACKEND_SYNC,
- G_IMPLEMENT_INTERFACE (
- G_TYPE_INITABLE,
- e_caldav_backend_initable_init))
-
-/* ************************************************************************* */
-/* Debugging */
-
-#define DEBUG_MESSAGE "message"
-#define DEBUG_MESSAGE_HEADER "message:header"
-#define DEBUG_MESSAGE_BODY "message:body"
-#define DEBUG_SERVER_ITEMS "items"
-#define DEBUG_ATTACHMENTS "attachments"
-
-static gboolean open_calendar_wrapper (ECalBackendCalDAV *cbdav,
- GCancellable *cancellable,
- GError **error,
- gboolean first_attempt,
- gboolean *know_unreachable,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors);
-
-static void convert_to_inline_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
-static void convert_to_url_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
-static void remove_cached_attachment (ECalBackendCalDAV *cbdav, const gchar *uid);
-
-static gboolean caldav_debug_all = FALSE;
-static GHashTable *caldav_debug_table = NULL;
-
-static void
-add_debug_key (const gchar *start,
- const gchar *end)
-{
- gchar *debug_key;
- gchar *debug_value;
-
- if (start == end) {
- return;
- }
-
- debug_key = debug_value = g_strndup (start, end - start);
-
- debug_key = g_strchug (debug_key);
- debug_key = g_strchomp (debug_key);
-
- if (strlen (debug_key) == 0) {
- g_free (debug_value);
- return;
- }
-
- g_hash_table_insert (
- caldav_debug_table,
- debug_key,
- debug_value);
-
- d (g_debug ("Adding %s to enabled debugging keys", debug_key));
-}
-
-static gpointer
-caldav_debug_init_once (gpointer data)
-{
- const gchar *dbg;
-
- dbg = g_getenv ("CALDAV_DEBUG");
-
- if (dbg) {
- const gchar *ptr;
-
- d (g_debug ("Got debug env variable: [%s]", dbg));
-
- caldav_debug_table = g_hash_table_new (
- g_str_hash,
- g_str_equal);
-
- ptr = dbg;
-
- while (*ptr != '\0') {
- if (*ptr == ',' || *ptr == ':') {
-
- add_debug_key (dbg, ptr);
-
- if (*ptr == ',') {
- dbg = ptr + 1;
- }
- }
-
- ptr++;
- }
-
- if (ptr - dbg > 0) {
- add_debug_key (dbg, ptr);
- }
-
- if (g_hash_table_lookup (caldav_debug_table, "all")) {
- caldav_debug_all = TRUE;
- g_hash_table_destroy (caldav_debug_table);
- caldav_debug_table = NULL;
- }
- }
-
- return NULL;
-}
-
-static void
-caldav_debug_init (void)
-{
- static GOnce debug_once = G_ONCE_INIT;
-
- g_once (
- &debug_once,
- caldav_debug_init_once,
- NULL);
-}
-
-static gboolean
-caldav_debug_show (const gchar *component)
-{
- if (G_UNLIKELY (caldav_debug_all)) {
- return TRUE;
- } else if (G_UNLIKELY (caldav_debug_table != NULL) &&
- g_hash_table_lookup (caldav_debug_table, component)) {
- return TRUE;
- }
-
- return FALSE;
-}
-
-#define DEBUG_MAX_BODY_SIZE (100 * 1024 * 1024)
-
-static void
-caldav_debug_setup (SoupSession *session)
-{
- SoupLogger *logger;
- SoupLoggerLogLevel level;
-
- if (caldav_debug_show (DEBUG_MESSAGE_BODY))
- level = SOUP_LOGGER_LOG_BODY;
- else if (caldav_debug_show (DEBUG_MESSAGE_HEADER))
- level = SOUP_LOGGER_LOG_HEADERS;
- else
- level = SOUP_LOGGER_LOG_MINIMAL;
-
- logger = soup_logger_new (level, DEBUG_MAX_BODY_SIZE);
- soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
- g_object_unref (logger);
-}
-
-/* TODO Do not replicate this in every backend */
-static icaltimezone *
-resolve_tzid (const gchar *tzid,
- gpointer user_data)
-{
- ETimezoneCache *timezone_cache;
-
- timezone_cache = E_TIMEZONE_CACHE (user_data);
-
- return e_timezone_cache_get_timezone (timezone_cache, tzid);
-}
-
-static gboolean
-put_component_to_store (ECalBackendCalDAV *cbdav,
- ECalComponent *comp)
-{
- time_t time_start, time_end;
-
- e_cal_util_get_component_occur_times (
- comp, &time_start, &time_end,
- resolve_tzid, cbdav, icaltimezone_get_utc_timezone (),
- e_cal_backend_get_kind (E_CAL_BACKEND (cbdav)));
-
- return e_cal_backend_store_put_component_with_time_range (
- cbdav->priv->store, comp, time_start, time_end);
-}
-
-static ECalBackendSyncClass *parent_class = NULL;
-
-static void caldav_source_changed_cb (ESource *source, ECalBackendCalDAV *cbdav);
-
-static gboolean remove_comp_from_cache (ECalBackendCalDAV *cbdav, const gchar *uid, const gchar *rid);
-static gboolean put_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp, const gchar *href,
const gchar *etag);
-static void put_server_comp_to_cache (ECalBackendCalDAV *cbdav, icalcomponent *icomp, const gchar *href,
const gchar *etag, GTree *c_uid2complist);
-
-/* ************************************************************************* */
-/* Misc. utility functions */
-
-static void
-caldav_ensure_bearer_auth_usage (ECalBackendCalDAV *cbdav,
- ESoupAuthBearer *bearer)
-{
- SoupSessionFeature *feature;
- SoupURI *soup_uri;
- ESourceWebdav *extension;
- ESource *source;
-
- g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
- source = e_backend_get_source (E_BACKEND (cbdav));
-
- /* Preload the SoupAuthManager with a valid "Bearer" token
- * when using OAuth 2.0. This avoids an extra unauthorized
- * HTTP round-trip, which apparently Google doesn't like. */
-
- feature = soup_session_get_feature (cbdav->priv->session, SOUP_TYPE_AUTH_MANAGER);
-
- if (!soup_session_feature_has_feature (feature, E_TYPE_SOUP_AUTH_BEARER)) {
- /* Add the "Bearer" auth type to support OAuth 2.0. */
- soup_session_feature_add_feature (feature, E_TYPE_SOUP_AUTH_BEARER);
- }
-
- extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
- soup_uri = e_source_webdav_dup_soup_uri (extension);
-
- soup_auth_manager_use_auth (
- SOUP_AUTH_MANAGER (feature),
- soup_uri, SOUP_AUTH (bearer));
-
- soup_uri_free (soup_uri);
-}
-
-static gboolean
-caldav_setup_bearer_auth (ECalBackendCalDAV *cbdav,
- ESoupAuthBearer *bearer,
- GCancellable *cancellable,
- GError **error)
-{
- ESource *source;
- gchar *access_token = NULL;
- gint expires_in_seconds = -1;
- gboolean success = FALSE;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- g_return_val_if_fail (E_IS_SOUP_AUTH_BEARER (bearer), FALSE);
-
- source = e_backend_get_source (E_BACKEND (cbdav));
-
- success = e_util_get_source_oauth2_access_token_sync (source, cbdav->priv->credentials,
- &access_token, &expires_in_seconds, cancellable, error);
-
- if (success) {
- e_soup_auth_bearer_set_access_token (bearer, access_token, expires_in_seconds);
- caldav_ensure_bearer_auth_usage (cbdav, bearer);
- }
-
- g_free (access_token);
-
- return success;
-}
+G_DEFINE_TYPE (ECalBackendCalDAV, e_cal_backend_caldav, E_TYPE_CAL_META_BACKEND)
static gboolean
-caldav_maybe_prepare_bearer_auth (ECalBackendCalDAV *cbdav,
- GCancellable *cancellable,
- GError **error)
+ecb_caldav_connect_sync (ECalMetaBackend *meta_backend,
+ const ENamedParameters *credentials,
+ ESourceAuthenticationResult *out_auth_result,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
+ ECalBackendCalDAV *cbdav;
+ GHashTable *capabilities = NULL, *allows = NULL;
ESource *source;
- gchar *auth_method = NULL;
gboolean success;
+ GError *local_error = NULL;
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-
- source = e_backend_get_source (E_BACKEND (cbdav));
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (out_auth_result != NULL, FALSE);
- if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
- ESourceAuthentication *extension;
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
- extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
- auth_method = e_source_authentication_dup_method (extension);
- } else {
+ if (cbdav->priv->webdav)
return TRUE;
- }
-
- if (g_strcmp0 (auth_method, "OAuth2") != 0 && g_strcmp0 (auth_method, "Google") != 0) {
- g_free (auth_method);
- return TRUE;
- }
- g_free (auth_method);
+ source = e_backend_get_source (E_BACKEND (meta_backend));
- if (cbdav->priv->using_bearer_auth) {
- success = caldav_setup_bearer_auth (cbdav, cbdav->priv->using_bearer_auth, cancellable,
error);
- } else {
- ESourceWebdav *extension;
- SoupAuth *soup_auth;
- SoupURI *soup_uri;
-
- extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
- soup_uri = e_source_webdav_dup_soup_uri (extension);
+ cbdav->priv->webdav = e_webdav_session_new (source);
- soup_auth = g_object_new (
- E_TYPE_SOUP_AUTH_BEARER,
- SOUP_AUTH_HOST, soup_uri->host, NULL);
+ e_soup_session_setup_logging (E_SOUP_SESSION (cbdav->priv->webdav), g_getenv ("CALDAV_DEBUG"));
- success = caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (soup_auth), cancellable,
error);
- if (success)
- cbdav->priv->using_bearer_auth = g_object_ref (soup_auth);
+ e_binding_bind_property (
+ cbdav, "proxy-resolver",
+ cbdav->priv->webdav, "proxy-resolver",
+ G_BINDING_SYNC_CREATE);
- g_object_unref (soup_auth);
- soup_uri_free (soup_uri);
- }
+ /* Thinks the 'getctag' extension is available the first time, but unset it when realizes it isn't. */
+ cbdav->priv->ctag_supported = TRUE;
- return success;
-}
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
-static void
-update_slave_cmd (ECalBackendCalDAVPrivate *priv,
- SlaveCommand slave_cmd)
-{
- g_return_if_fail (priv != NULL);
+ e_soup_session_set_credentials (E_SOUP_SESSION (cbdav->priv->webdav), credentials);
- if (priv->slave_cmd == SLAVE_SHOULD_DIE)
- return;
+ success = e_webdav_session_options_sync (cbdav->priv->webdav, NULL,
+ &capabilities, &allows, cancellable, &local_error);
- priv->slave_cmd = slave_cmd;
-}
+ if (success) {
+ ESourceWebdav *webdav_extension;
+ ECalCache *cal_cache;
+ SoupURI *soup_uri;
+ gboolean is_writable;
+ gboolean calendar_access;
-#define X_E_CALDAV "X-EVOLUTION-CALDAV-"
-#define X_E_CALDAV_ATTACHMENT_NAME X_E_CALDAV "ATTACHMENT-NAME"
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
-static void
-icomp_x_prop_set (icalcomponent *comp,
- const gchar *key,
- const gchar *value)
-{
- icalproperty *xprop;
+ /* The POST added for FastMail servers, which doesn't advertise PUT on collections. */
+ is_writable = allows && (
+ g_hash_table_contains (allows, SOUP_METHOD_PUT) ||
+ g_hash_table_contains (allows, SOUP_METHOD_POST) ||
+ g_hash_table_contains (allows, SOUP_METHOD_DELETE));
- /* Find the old one first */
- xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
+ cbdav->priv->calendar_schedule = capabilities && g_hash_table_contains (capabilities,
E_WEBDAV_CAPABILITY_CALENDAR_SCHEDULE);
+ calendar_access = capabilities && g_hash_table_contains (capabilities,
E_WEBDAV_CAPABILITY_CALENDAR_ACCESS);
- while (xprop) {
- const gchar *str = icalproperty_get_x_name (xprop);
+ if (calendar_access) {
+ e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), is_writable);
- if (!strcmp (str, key)) {
- if (value) {
- icalproperty_set_value_from_string (xprop, value, "NO");
- } else {
- icalcomponent_remove_property (comp, xprop);
- icalproperty_free (xprop);
- }
- break;
- }
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
- xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
- }
+ cbdav->priv->is_google = soup_uri && soup_uri->host && (
+ g_ascii_strcasecmp (soup_uri->host, "www.google.com") == 0 ||
+ g_ascii_strcasecmp (soup_uri->host, "apidata.googleusercontent.com") == 0);
- if (!xprop && value) {
- xprop = icalproperty_new_x (value);
- icalproperty_set_x_name (xprop, key);
- icalcomponent_add_property (comp, xprop);
- }
-}
+ cbdav->priv->is_icloud = soup_uri && soup_uri->host &&
+ e_util_utf8_strstrcase (soup_uri->host, ".icloud.com");
+ } else {
+ gchar *uri;
-static gchar *
-icomp_x_prop_get (icalcomponent *comp,
- const gchar *key)
-{
- icalproperty *xprop;
+ uri = soup_uri_to_string (soup_uri, FALSE);
- /* Find the old one first */
- xprop = icalcomponent_get_first_property (comp, ICAL_X_PROPERTY);
+ success = FALSE;
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ _("Given URL ā%sā doesn't reference CalDAV calendar"), uri);
- while (xprop) {
- const gchar *str = icalproperty_get_x_name (xprop);
+ g_free (uri);
- if (!strcmp (str, key)) {
- break;
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
}
- xprop = icalcomponent_get_next_property (comp, ICAL_X_PROPERTY);
- }
-
- if (xprop) {
- return icalproperty_get_value_as_string_r (xprop);
+ g_clear_object (&cal_cache);
+ soup_uri_free (soup_uri);
}
+ if (success) {
+ gchar *ctag = NULL;
- return NULL;
-}
-
-/* passing NULL as 'href' removes the property */
-static void
-ecalcomp_set_href (ECalComponent *comp,
- const gchar *href)
-{
- icalcomponent *icomp;
-
- icomp = e_cal_component_get_icalcomponent (comp);
- g_return_if_fail (icomp != NULL);
-
- icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
-}
-
-static gchar *
-ecalcomp_get_href (ECalComponent *comp)
-{
- icalcomponent *icomp;
- gchar *str;
-
- str = NULL;
- icomp = e_cal_component_get_icalcomponent (comp);
- g_return_val_if_fail (icomp != NULL, NULL);
-
- str = icomp_x_prop_get (icomp, X_E_CALDAV "HREF");
-
- return str;
-}
-
-/* passing NULL as 'etag' removes the property */
-static void
-ecalcomp_set_etag (ECalComponent *comp,
- const gchar *etag)
-{
- icalcomponent *icomp;
-
- icomp = e_cal_component_get_icalcomponent (comp);
- g_return_if_fail (icomp != NULL);
-
- icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", etag);
-}
-
-static gchar *
-ecalcomp_get_etag (ECalComponent *comp)
-{
- icalcomponent *icomp;
- gchar *str;
-
- str = NULL;
- icomp = e_cal_component_get_icalcomponent (comp);
- g_return_val_if_fail (icomp != NULL, NULL);
-
- str = icomp_x_prop_get (icomp, X_E_CALDAV "ETAG");
-
- /* libical 0.48 escapes quotes, thus unescape them */
- if (str && strchr (str, '\\')) {
- gint ii, jj;
-
- for (ii = 0, jj = 0; str[ii]; ii++) {
- if (str[ii] == '\\') {
- ii++;
- if (!str[ii])
- break;
- }
+ /* Some servers, notably Google, allow OPTIONS when not
+ authorized (aka without credentials), thus try something
+ more aggressive, just in case.
- str[jj] = str[ii];
- jj++;
+ The 'getctag' extension is not required, thuch check
+ for unauthorized error only. */
+ if (!e_webdav_session_getctag_sync (cbdav->priv->webdav, NULL, &ctag, cancellable,
&local_error) &&
+ g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ success = FALSE;
+ } else {
+ g_clear_error (&local_error);
}
- str[jj] = 0;
+ g_free (ctag);
}
- return str;
-}
-
-/*typedef enum {
- *
- / * object is in synch,
- * now isnt that ironic? :) * /
- ECALCOMP_IN_SYNCH = 0,
- *
- / * local changes * /
- ECALCOMP_LOCALLY_CREATED,
- ECALCOMP_LOCALLY_DELETED,
- ECALCOMP_LOCALLY_MODIFIED
- *
-} ECalCompSyncState;
- *
-/ * oos = out of synch * /
-static void
-ecalcomp_set_synch_state (ECalComponent *comp,
- * ECalCompSyncState state)
-{
- icalcomponent *icomp;
- gchar *state_string;
- *
- icomp = e_cal_component_get_icalcomponent (comp);
- *
- state_string = g_strdup_printf ("%d", state);
- *
- icomp_x_prop_set (icomp, X_E_CALDAV "ETAG", state_string);
- *
- g_free (state_string);
-}*/
-
-static gchar *
-ecalcomp_gen_href (ECalComponent *comp)
-{
- gchar *href, *uid, *tmp;
- icalcomponent *icomp;
-
- icomp = e_cal_component_get_icalcomponent (comp);
- g_return_val_if_fail (icomp != NULL, NULL);
+ if (!success) {
+ gboolean credentials_empty;
+ gboolean is_ssl_error;
- uid = g_strdup (icalcomponent_get_uid (icomp));
- if (!uid || !*uid) {
- g_free (uid);
- uid = e_cal_component_gen_uid ();
+ credentials_empty = !credentials || !e_named_parameters_count (credentials);
+ is_ssl_error = g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED);
- tmp = uid ? strchr (uid, '@') : NULL;
- if (tmp)
- *tmp = '\0';
+ *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR;
- tmp = NULL;
- } else
- tmp = isodate_from_time_t (time (NULL));
-
- /* quite long, but ensures uniqueness quite well, without using UUIDs */
- href = g_strconcat (uid ? uid : "no-uid", tmp ? "-" : "", tmp ? tmp : "", ".ics", NULL);
-
- g_free (tmp);
- g_free (uid);
-
- icomp_x_prop_set (icomp, X_E_CALDAV "HREF", href);
-
- return g_strdelimit (href, " /'\"`&();|<>$%{}!\\:*?#@", '_');
-}
-
-/* ensure etag is quoted (to workaround potential server bugs) */
-static gchar *
-quote_etag (const gchar *etag)
-{
- gchar *ret;
-
- if (etag && (strlen (etag) < 2 || etag[strlen (etag) - 1] != '\"')) {
- ret = g_strdup_printf ("\"%s\"", etag);
- } else {
- ret = g_strdup (etag);
- }
-
- return ret;
-}
-
-/* ************************************************************************* */
-
-static gboolean
-status_code_to_result (SoupMessage *message,
- ECalBackendCalDAV *cbdav,
- gboolean is_opening,
- GError **perror)
-{
- ECalBackendCalDAVPrivate *priv;
- gchar *uri;
-
- g_return_val_if_fail (cbdav != NULL, FALSE);
- g_return_val_if_fail (message != NULL, FALSE);
-
- priv = cbdav->priv;
-
- if (SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
- return TRUE;
- }
-
- if (perror && *perror)
- return FALSE;
-
- switch (message->status_code) {
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_RESOLVE_PROXY:
- case SOUP_STATUS_CANT_CONNECT:
- case SOUP_STATUS_CANT_CONNECT_PROXY:
- g_propagate_error (
- perror,
- e_data_cal_create_error_fmt (
- OtherError,
- _("Server is unreachable (%s)"),
- message->reason_phrase && *message->reason_phrase ?
message->reason_phrase :
- (soup_status_get_phrase (message->status_code) ?
soup_status_get_phrase (message->status_code) : _("Unknown error"))));
- if (priv) {
- priv->opened = FALSE;
- e_cal_backend_set_writable (
- E_CAL_BACKEND (cbdav), FALSE);
- }
- break;
- case SOUP_STATUS_NOT_FOUND:
- if (is_opening)
- g_propagate_error (perror, EDC_ERROR (NoSuchCal));
- else
- g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
- break;
-
- case SOUP_STATUS_FORBIDDEN:
- if (cbdav->priv->using_bearer_auth && message->response_body &&
- message->response_body->data && message->response_body->length) {
- gchar *body = g_strndup (message->response_body->data,
message->response_body->length);
-
- /* Do not localize this string, it is returned by the server. */
- if (body && (e_util_strstrcase (body, "Daily Limit") ||
- e_util_strstrcase (body, "https://console.developers.google.com/"))) {
- /* Special-case this condition and provide this error up to the UI. */
- g_propagate_error (perror,
- e_data_cal_create_error_fmt (OtherError, _("Failed to login to the
server: %s"), body));
- } else {
- g_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
- }
-
- g_free (body);
+ /* because evolution knows only G_IO_ERROR_CANCELLED */
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_CANCELLED)) {
+ local_error->domain = G_IO_ERROR;
+ local_error->code = G_IO_ERROR_CANCELLED;
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN) &&
credentials_empty) {
+ *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ if (credentials_empty)
+ *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ local_error = NULL;
} else {
- g_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ _("Unknown error"));
}
- break;
-
- case SOUP_STATUS_UNAUTHORIZED:
- if (priv && priv->auth_required)
- g_propagate_error (perror, EDC_ERROR (AuthenticationFailed));
- else
- g_propagate_error (perror, EDC_ERROR (AuthenticationRequired));
- break;
- case SOUP_STATUS_SSL_FAILED:
- g_propagate_error (
- perror,
- e_data_cal_create_error_fmt ( OtherError,
- _("Failed to connect to a server using SSL/TLS: %s"),
- message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
- (soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : _("Unknown error"))));
- if (is_opening && perror && *perror) {
- (*perror)->domain = SOUP_HTTP_ERROR;
- (*perror)->code = SOUP_STATUS_SSL_FAILED;
- }
- break;
+ if (is_ssl_error) {
+ *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
- default:
- d (g_debug ("CalDAV:%s: Unhandled status code %d\n", G_STRFUNC, status_code));
- uri = soup_uri_to_string (soup_message_get_uri (message), FALSE);
- g_propagate_error (
- perror,
- e_data_cal_create_error_fmt (
- OtherError,
- _("Unexpected HTTP status code %d returned (%s) for URI: %s"),
- message->status_code,
- message->reason_phrase && *message->reason_phrase ?
message->reason_phrase :
- (soup_status_get_phrase (message->status_code) ?
soup_status_get_phrase (message->status_code) : _("Unknown error")),
- uri ? uri : "[null]"));
- g_free (uri);
- break;
- }
-
- return FALSE;
-}
-
-/* !TS, call with lock held */
-static gboolean
-check_state (ECalBackendCalDAV *cbdav,
- gboolean *online,
- GError **perror)
-{
- *online = FALSE;
-
- if (!cbdav->priv->loaded) {
- g_propagate_error (perror, EDC_ERROR_EX (OtherError, _("CalDAV backend is not loaded yet")));
- return FALSE;
- }
-
- if (!e_backend_get_online (E_BACKEND (cbdav))) {
-
- if (!cbdav->priv->do_offline) {
- g_propagate_error (perror, EDC_ERROR (RepositoryOffline));
- return FALSE;
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+ e_soup_session_get_ssl_error_details (E_SOUP_SESSION (cbdav->priv->webdav),
out_certificate_pem, out_certificate_errors);
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
}
-
- } else {
- *online = TRUE;
}
- return TRUE;
-}
+ if (capabilities)
+ g_hash_table_destroy (capabilities);
+ if (allows)
+ g_hash_table_destroy (allows);
-/* ************************************************************************* */
-/* XML Parsing code */
+ if (!success)
+ g_clear_object (&cbdav->priv->webdav);
-static xmlXPathObjectPtr
-xpath_eval (xmlXPathContextPtr ctx,
- const gchar *format,
- ...)
-{
- xmlXPathObjectPtr result;
- va_list args;
- gchar *expr;
-
- if (ctx == NULL) {
- return NULL;
- }
-
- va_start (args, format);
- expr = g_strdup_vprintf (format, args);
- va_end (args);
-
- result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
- g_free (expr);
-
- if (result == NULL) {
- return NULL;
- }
-
- if (result->type == XPATH_NODESET &&
- xmlXPathNodeSetIsEmpty (result->nodesetval)) {
- xmlXPathFreeObject (result);
- return NULL;
- }
-
- return result;
+ return success;
}
-#if 0
static gboolean
-parse_status_node (xmlNodePtr node,
- guint *status_code)
-{
- xmlChar *content;
- gboolean res;
-
- content = xmlNodeGetContent (node);
-
- res = soup_headers_parse_status_line (
- (gchar *) content,
- NULL,
- status_code,
- NULL);
- xmlFree (content);
-
- return res;
-}
-#endif
-
-static gchar *
-xp_object_get_string (xmlXPathObjectPtr result)
+ecb_caldav_disconnect_sync (ECalMetaBackend *meta_backend,
+ GCancellable *cancellable,
+ GError **error)
{
- gchar *ret = NULL;
-
- if (result == NULL)
- return ret;
+ ECalBackendCalDAV *cbdav;
+ ESource *source;
- if (result->type == XPATH_STRING) {
- ret = g_strdup ((gchar *) result->stringval);
- }
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
- xmlXPathFreeObject (result);
- return ret;
-}
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
-/* like get_string but will quote the etag if necessary */
-static gchar *
-xp_object_get_etag (xmlXPathObjectPtr result)
-{
- gchar *ret = NULL;
- gchar *str;
+ if (cbdav->priv->webdav)
+ soup_session_abort (SOUP_SESSION (cbdav->priv->webdav));
- if (result == NULL)
- return ret;
+ g_clear_object (&cbdav->priv->webdav);
- if (result->type == XPATH_STRING) {
- str = (gchar *) result->stringval;
-
- ret = quote_etag (str);
- }
+ source = e_backend_get_source (E_BACKEND (meta_backend));
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
- xmlXPathFreeObject (result);
- return ret;
+ return TRUE;
}
-static guint
-xp_object_get_status (xmlXPathObjectPtr result)
+static const gchar *
+ecb_caldav_get_vcalendar_uid (icalcomponent *vcalendar)
{
- gboolean res;
- guint ret = 0;
+ const gchar *uid = NULL;
+ icalcomponent *subcomp;
- if (result == NULL)
- return ret;
+ g_return_val_if_fail (vcalendar != NULL, NULL);
+ g_return_val_if_fail (icalcomponent_isa (vcalendar) == ICAL_VCALENDAR_COMPONENT, NULL);
- if (result->type == XPATH_STRING) {
- res = soup_headers_parse_status_line (
- (gchar *) result->stringval,
- NULL,
- &ret,
- NULL);
+ for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+ subcomp && !uid;
+ subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+ icalcomponent_kind kind = icalcomponent_isa (subcomp);
- if (!res) {
- ret = 0;
+ if (kind == ICAL_VEVENT_COMPONENT ||
+ kind == ICAL_VJOURNAL_COMPONENT ||
+ kind == ICAL_VTODO_COMPONENT) {
+ uid = icalcomponent_get_uid (subcomp);
+ if (uid && !*uid)
+ uid = NULL;
}
}
- xmlXPathFreeObject (result);
- return ret;
-}
-
-#if 0
-static gint
-xp_object_get_number (xmlXPathObjectPtr result)
-{
- gint ret = -1;
-
- if (result == NULL)
- return ret;
-
- if (result->type == XPATH_STRING) {
- ret = result->boolval;
- }
-
- xmlXPathFreeObject (result);
- return ret;
+ return uid;
}
-#endif
-
-/*** *** *** *** *** *** */
-#define XPATH_HREF "string(/D:multistatus/D:response[%d]/D:href)"
-#define XPATH_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:status)"
-#define XPATH_GETETAG_STATUS
"string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag/../../D:status)"
-#define XPATH_GETETAG "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:getetag)"
-#define XPATH_CALENDAR_DATA "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/C:calendar-data)"
-#define XPATH_GETCTAG_STATUS "string(/D:multistatus/D:response/D:propstat/D:prop/CS:getctag/../../D:status)"
-#define XPATH_GETCTAG "string(/D:multistatus/D:response/D:propstat/D:prop/CS:getctag)"
-#define XPATH_OWNER_STATUS
"string(/D:multistatus/D:response/D:propstat/D:prop/D:owner/D:href/../../../D:status)"
-#define XPATH_OWNER "string(/D:multistatus/D:response/D:propstat/D:prop/D:owner/D:href)"
-#define XPATH_SCHEDULE_OUTBOX_URL_STATUS
"string(/D:multistatus/D:response/D:propstat/D:prop/C:schedule-outbox-URL/D:href/../../../D:status)"
-#define XPATH_SCHEDULE_OUTBOX_URL
"string(/D:multistatus/D:response/D:propstat/D:prop/C:schedule-outbox-URL/D:href)"
-
-typedef struct _CalDAVObject CalDAVObject;
-
-struct _CalDAVObject {
-
- gchar *href;
- gchar *etag;
-
- guint status;
-
- gchar *cdata;
-};
static void
-caldav_object_free (CalDAVObject *object,
- gboolean free_object_itself)
+ecb_caldav_update_nfo_with_vcalendar (ECalMetaBackendInfo *nfo,
+ icalcomponent *vcalendar,
+ const gchar *etag)
{
- g_free (object->href);
- g_free (object->etag);
- g_free (object->cdata);
+ icalcomponent *subcomp;
+ const gchar *uid;
- if (free_object_itself) {
- g_free (object);
- }
-}
-
-static gboolean
-parse_report_response (SoupMessage *soup_message,
- CalDAVObject **objs,
- gint *len)
-{
- xmlXPathContextPtr xpctx;
- xmlXPathObjectPtr result;
- xmlDocPtr doc;
- gint i, n;
- gboolean res;
-
- g_return_val_if_fail (soup_message != NULL, FALSE);
- g_return_val_if_fail (objs != NULL || len != NULL, FALSE);
-
- res = TRUE;
- doc = xmlReadMemory (
- soup_message->response_body->data,
- soup_message->response_body->length,
- "response.xml",
- NULL,
- 0);
-
- if (doc == NULL) {
- return FALSE;
- }
+ g_return_if_fail (nfo != NULL);
+ g_return_if_fail (vcalendar != NULL);
- xpctx = xmlXPathNewContext (doc);
+ uid = ecb_caldav_get_vcalendar_uid (vcalendar);
- xmlXPathRegisterNs (
- xpctx, (xmlChar *) "D",
- (xmlChar *) "DAV:");
+ if (!etag || !*etag)
+ etag = nfo->revision;
- xmlXPathRegisterNs (
- xpctx, (xmlChar *) "C",
- (xmlChar *) "urn:ietf:params:xml:ns:caldav");
+ for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+ subcomp;
+ subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+ icalcomponent_kind kind = icalcomponent_isa (subcomp);
- result = xpath_eval (xpctx, "/D:multistatus/D:response");
-
- if (result == NULL || result->type != XPATH_NODESET) {
- *len = 0;
- res = FALSE;
- goto out;
- }
-
- n = xmlXPathNodeSetGetLength (result->nodesetval);
- *len = n;
-
- *objs = g_new0 (CalDAVObject, n);
-
- for (i = 0; i < n; i++) {
- CalDAVObject *object;
- xmlXPathObjectPtr xpres;
-
- object = *objs + i;
- /* see if we got a status child in the response element */
-
- xpres = xpath_eval (xpctx, XPATH_HREF, i + 1);
- /* use full path from a href, to let calendar-multiget work properly */
- object->href = xp_object_get_string (xpres);
-
- xpres = xpath_eval (xpctx,XPATH_STATUS , i + 1);
- object->status = xp_object_get_status (xpres);
-
- if (object->status && object->status != 200) {
- continue;
+ if (kind == ICAL_VEVENT_COMPONENT ||
+ kind == ICAL_VJOURNAL_COMPONENT ||
+ kind == ICAL_VTODO_COMPONENT) {
+ e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
}
-
- xpres = xpath_eval (xpctx, XPATH_GETETAG_STATUS, i + 1);
- object->status = xp_object_get_status (xpres);
-
- if (object->status != 200) {
- continue;
- }
-
- xpres = xpath_eval (xpctx, XPATH_GETETAG, i + 1);
- object->etag = xp_object_get_etag (xpres);
-
- xpres = xpath_eval (xpctx, XPATH_CALENDAR_DATA, i + 1);
- object->cdata = xp_object_get_string (xpres);
- }
-
-out:
- if (result != NULL)
- xmlXPathFreeObject (result);
- xmlXPathFreeContext (xpctx);
- xmlFreeDoc (doc);
- return res;
-}
-
-/* returns whether was able to read the xpath_value from the server's response; *value contains the result */
-static gboolean
-parse_propfind_response (SoupMessage *message,
- const gchar *xpath_status,
- const gchar *xpath_value,
- gchar **value)
-{
- xmlXPathContextPtr xpctx;
- xmlDocPtr doc;
- gboolean res = FALSE;
-
- g_return_val_if_fail (message != NULL, FALSE);
- g_return_val_if_fail (value != NULL, FALSE);
-
- doc = xmlReadMemory (
- message->response_body->data,
- message->response_body->length,
- "response.xml",
- NULL,
- 0);
-
- if (doc == NULL) {
- return FALSE;
- }
-
- xpctx = xmlXPathNewContext (doc);
- xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
- xmlXPathRegisterNs (xpctx, (xmlChar *) "C", (xmlChar *) "urn:ietf:params:xml:ns:caldav");
- xmlXPathRegisterNs (xpctx, (xmlChar *) "CS", (xmlChar *) "http://calendarserver.org/ns/");
-
- if (xpath_status == NULL || xp_object_get_status (xpath_eval (xpctx, xpath_status)) == 200) {
- gchar *txt = xp_object_get_string (xpath_eval (xpctx, xpath_value));
-
- if (txt && *txt) {
- gint len = strlen (txt);
-
- if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
- /* dequote */
- *value = g_strndup (txt + 1, len - 2);
- } else {
- *value = txt;
- txt = NULL;
- }
-
- res = (*value) != NULL;
- }
-
- g_free (txt);
- }
-
- xmlXPathFreeContext (xpctx);
- xmlFreeDoc (doc);
-
- return res;
-}
-
-/* ************************************************************************* */
-/* Authentication helpers for libsoup */
-
-static void
-soup_authenticate_bearer (SoupSession *session,
- SoupMessage *message,
- SoupAuth *auth,
- ECalBackendCalDAV *cbdav)
-{
- GError *local_error = NULL;
-
- caldav_setup_bearer_auth (cbdav, E_SOUP_AUTH_BEARER (auth), NULL, &local_error);
-
- /* Stash the error to be picked up by caldav_credentials_required_sync().
- * There's no way to explicitly propagate a GError directly
- * through libsoup, so we have to work around it. */
- if (local_error != NULL) {
- g_mutex_lock (&cbdav->priv->bearer_auth_error_lock);
-
- /* Warn about an unclaimed error before we clear it.
- * This is just to verify the errors we set here are
- * actually making it back to the user. */
- g_warn_if_fail (cbdav->priv->bearer_auth_error == NULL);
- g_clear_error (&cbdav->priv->bearer_auth_error);
-
- g_propagate_error (
- &cbdav->priv->bearer_auth_error, local_error);
-
- g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
- }
-}
-
-static void
-soup_authenticate (SoupSession *session,
- SoupMessage *msg,
- SoupAuth *auth,
- gboolean retrying,
- gpointer data)
-{
- ECalBackendCalDAV *cbdav;
- ESourceAuthentication *auth_extension;
- ESource *source;
- const gchar *extension_name;
-
- cbdav = E_CAL_BACKEND_CALDAV (data);
-
- source = e_backend_get_source (E_BACKEND (data));
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
-
- if (E_IS_SOUP_AUTH_BEARER (auth)) {
- g_object_ref (auth);
- g_warn_if_fail ((gpointer) cbdav->priv->using_bearer_auth == (gpointer) auth);
- g_clear_object (&cbdav->priv->using_bearer_auth);
- cbdav->priv->using_bearer_auth = E_SOUP_AUTH_BEARER (auth);
}
- if (retrying)
- return;
-
- if (cbdav->priv->using_bearer_auth) {
- soup_authenticate_bearer (session, msg, auth, cbdav);
-
- /* do not send same password twice, but keep it for later use */
- } else {
- gchar *auth_user;
- const gchar *username;
-
- auth_user = e_source_authentication_dup_user (auth_extension);
-
- username = cbdav->priv->credentials ? e_named_parameters_get (cbdav->priv->credentials,
E_SOURCE_CREDENTIAL_USERNAME) : NULL;
- if (!username || !*username)
- username = auth_user;
+ g_warn_if_fail (nfo->object == NULL);
+ nfo->object = icalcomponent_as_ical_string_r (vcalendar);
- if (!username || !*username || !cbdav->priv->credentials ||
- !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
- soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
- else
- soup_auth_authenticate (auth, username, e_named_parameters_get
(cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
-
- g_free (auth_user);
+ if (!nfo->uid || !*(nfo->uid)) {
+ g_free (nfo->uid);
+ nfo->uid = g_strdup (uid);
}
-}
-
-/* ************************************************************************* */
-/* direct CalDAV server access functions */
-
-static void
-redirect_handler (SoupMessage *msg,
- gpointer user_data)
-{
- if (SOUP_STATUS_IS_REDIRECTION (msg->status_code)) {
- SoupSession *soup_session = user_data;
- SoupURI *new_uri;
- const gchar *new_loc;
-
- new_loc = soup_message_headers_get_list (msg->response_headers, "Location");
- if (!new_loc)
- return;
-
- new_uri = soup_uri_new_with_base (soup_message_get_uri (msg), new_loc);
- if (!new_uri) {
- soup_message_set_status_full (
- msg,
- SOUP_STATUS_MALFORMED,
- _("Invalid Redirect URL"));
- return;
- }
-
- if (new_uri->host && g_str_has_suffix (new_uri->host, "yahoo.com")) {
- /* yahoo! returns port 7070, which is unreachable;
- * it also requires https being used (below call resets port as well) */
- soup_uri_set_scheme (new_uri, SOUP_URI_SCHEME_HTTPS);
- }
- soup_message_set_uri (msg, new_uri);
- soup_session_requeue_message (soup_session, msg);
+ if (g_strcmp0 (etag, nfo->revision) != 0) {
+ gchar *copy = g_strdup (etag);
- soup_uri_free (new_uri);
+ g_free (nfo->revision);
+ nfo->revision = copy;
}
}
-static void
-send_and_handle_redirection (ECalBackendCalDAV *cbdav,
- SoupMessage *msg,
- gchar **new_location,
- GCancellable *cancellable,
- GError **error)
+static gboolean
+ecb_caldav_multiget_response_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
{
- gchar *old_uri = NULL;
-
- g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
- g_return_if_fail (msg != NULL);
-
- if (new_location)
- old_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
-
- e_soup_ssl_trust_connect (msg, e_backend_get_source (E_BACKEND (cbdav)));
-
- if (cbdav->priv->using_bearer_auth &&
- e_soup_auth_bearer_is_expired (cbdav->priv->using_bearer_auth)) {
- GError *local_error = NULL;
-
- if (!caldav_setup_bearer_auth (cbdav, cbdav->priv->using_bearer_auth, cancellable,
&local_error)) {
- if (local_error) {
- soup_message_set_status_full (msg, SOUP_STATUS_BAD_REQUEST,
local_error->message);
- g_propagate_error (error, local_error);
- } else {
- soup_message_set_status (msg, SOUP_STATUS_BAD_REQUEST);
- }
- return;
- }
- }
-
- soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
- soup_message_add_header_handler (msg, "got_body", "Location", G_CALLBACK (redirect_handler),
cbdav->priv->session);
- soup_message_headers_append (msg->request_headers, "Connection", "close");
- soup_session_send_message (cbdav->priv->session, msg);
+ GSList **from_link = user_data;
- if (new_location) {
- gchar *new_loc = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
+ g_return_val_if_fail (from_link != NULL, FALSE);
- if (new_loc && old_uri && !g_str_equal (new_loc, old_uri))
- *new_location = new_loc;
- else
- g_free (new_loc);
- }
+ if (!xpath_prop_prefix) {
+ e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+ } else if (status_code == SOUP_STATUS_OK) {
+ gchar *calendar_data, *etag;
- g_free (old_uri);
-
- if (SOUP_STATUS_IS_SUCCESSFUL (msg->status_code))
- e_backend_ensure_source_status_connected (E_BACKEND (cbdav));
-}
-
-static gchar *
-caldav_generate_uri (ECalBackendCalDAV *cbdav,
- const gchar *target)
-{
- gchar *uri;
- const gchar *slash;
+ g_return_val_if_fail (href != NULL, FALSE);
- slash = strrchr (target, '/');
- if (slash)
- target = slash + 1;
+ calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-data",
xpath_prop_prefix);
+ etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx,
"%s/D:getetag", xpath_prop_prefix));
- /* uri *have* trailing slash already */
- uri = g_strconcat (cbdav->priv->uri, target, NULL);
+ if (calendar_data) {
+ icalcomponent *vcalendar;
- return uri;
-}
+ vcalendar = icalcomponent_new_from_string (calendar_data);
+ if (vcalendar) {
+ const gchar *uid;
-static gboolean
-caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
- gboolean *server_unreachable,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors,
- GCancellable *cancellable,
- GError **perror)
-{
- SoupMessage *message;
- const gchar *header;
- gboolean calendar_access;
- gboolean put_allowed;
- gboolean delete_allowed;
- ESource *source;
+ uid = ecb_caldav_get_vcalendar_uid (vcalendar);
+ if (uid) {
+ GSList *link;
- g_return_val_if_fail (cbdav != NULL, FALSE);
- g_return_val_if_fail (server_unreachable != NULL, FALSE);
+ for (link = *from_link; link; link = g_slist_next (link)) {
+ ECalMetaBackendInfo *nfo = link->data;
- message = soup_message_new (SOUP_METHOD_OPTIONS, cbdav->priv->uri);
- if (message == NULL) {
- g_propagate_error (perror, EDC_ERROR (NoSuchCal));
- return FALSE;
- }
+ if (!nfo)
+ continue;
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
+ if (g_strcmp0 (nfo->extra, href) == 0) {
+ /* If the server returns data in the same order as it
had been requested,
+ then this speeds up lookup for the matching
object. */
+ if (link == *from_link)
+ *from_link = g_slist_next (*from_link);
- source = e_backend_get_source (E_BACKEND (cbdav));
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+ ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar,
etag);
- send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
-
- switch (message->status_code) {
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_RESOLVE_PROXY:
- case SOUP_STATUS_CANT_CONNECT:
- case SOUP_STATUS_CANT_CONNECT_PROXY:
- *server_unreachable = TRUE;
- break;
- case SOUP_STATUS_SSL_FAILED:
- if (out_certificate_pem && out_certificate_errors) {
- GTlsCertificate *certificate = NULL;
-
- g_object_get (G_OBJECT (message),
- "tls-certificate", &certificate,
- "tls-errors", out_certificate_errors,
- NULL);
-
- if (certificate) {
- g_object_get (certificate, "certificate-pem", out_certificate_pem,
NULL);
- g_object_unref (certificate);
+ break;
+ }
+ }
}
+
+ icalcomponent_free (vcalendar);
}
- break;
}
- status_code_to_result (message, cbdav, TRUE, perror);
-
- g_object_unref (message);
- return FALSE;
- }
-
- /* parse the dav header, we are intreseted in the
- * calendar-access bit only at the moment */
- header = soup_message_headers_get_list (message->response_headers, "DAV");
- if (header) {
- calendar_access = soup_header_contains (header, "calendar-access");
- cbdav->priv->calendar_schedule = soup_header_contains (header, "calendar-schedule");
- } else {
- calendar_access = FALSE;
- cbdav->priv->calendar_schedule = FALSE;
- }
-
- /* parse the Allow header and look for PUT, DELETE at the
- * moment (maybe we should check more here, for REPORT eg) */
- header = soup_message_headers_get_list (message->response_headers, "Allow");
- if (header) {
- /* The POST added for FastMail servers, which doesn't advertise PUT on collections. */
- put_allowed = soup_header_contains (header, "PUT") || soup_header_contains (header, "POST");
- delete_allowed = soup_header_contains (header, "DELETE");
- } else
- put_allowed = delete_allowed = FALSE;
-
- g_object_unref (message);
-
- if (calendar_access) {
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
- e_cal_backend_set_writable (
- E_CAL_BACKEND (cbdav),
- put_allowed && delete_allowed);
- return TRUE;
+ g_free (calendar_data);
+ g_free (etag);
}
- e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
- g_propagate_error (perror, EDC_ERROR (PermissionDenied));
- return FALSE;
-}
-
-static gpointer
-caldav_unref_thread (gpointer cbdav)
-{
- g_object_unref (cbdav);
-
- return NULL;
-}
-
-static void
-caldav_unref_in_thread (ECalBackendCalDAV *cbdav)
-{
- GThread *thread;
-
- g_return_if_fail (cbdav != NULL);
-
- thread = g_thread_new (NULL, caldav_unref_thread, cbdav);
- g_thread_unref (thread);
+ return TRUE;
}
static gboolean
-caldav_credentials_required_sync (ECalBackendCalDAV *cbdav,
- gboolean ref_cbdav,
- gboolean first_attempt,
- GCancellable *cancellable,
- GError **error)
-{
+ecb_caldav_multiget_from_sets_sync (ECalBackendCalDAV *cbdav,
+ GSList **in_link,
+ GSList **set2,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EXmlDocument *xml;
+ gint left_to_go = E_CALDAV_MAX_MULTIGET_AMOUNT;
+ GSList *link;
gboolean success = TRUE;
- if (ref_cbdav)
- g_object_ref (cbdav);
-
- /* This function is called when we receive a 4xx response code for
- * authentication failures. If we're using Bearer authentication,
- * there should be a GError available. Return the GError to avoid
- * inappropriately prompting for a password. */
- g_mutex_lock (&cbdav->priv->bearer_auth_error_lock);
- if (cbdav->priv->bearer_auth_error != NULL) {
- g_propagate_error (error, cbdav->priv->bearer_auth_error);
- cbdav->priv->bearer_auth_error = NULL;
- success = FALSE;
- }
- g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
+ g_return_val_if_fail (in_link != NULL, FALSE);
+ g_return_val_if_fail (*in_link != NULL, FALSE);
+ g_return_val_if_fail (set2 != NULL, FALSE);
- if (success) {
- success = e_backend_credentials_required_sync (E_BACKEND (cbdav),
- (first_attempt || !cbdav->priv->credentials ||
- !e_named_parameters_exists (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
- ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
- E_SOURCE_CREDENTIALS_REASON_REJECTED,
- NULL, 0, NULL, cancellable, error);
- }
+ xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-multiget");
+ g_return_val_if_fail (xml != NULL, FALSE);
- if (ref_cbdav)
- caldav_unref_in_thread (cbdav);
+ e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
- return success;
-}
+ e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "getetag");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-data");
+ e_xml_document_end_element (xml); /* prop */
-static gconstpointer
-compat_libxml_output_buffer_get_content (xmlOutputBufferPtr buf,
- gsize *out_len)
-{
-#ifdef LIBXML2_NEW_BUFFER
- *out_len = xmlOutputBufferGetSize (buf);
- return xmlOutputBufferGetContent (buf);
-#else
- *out_len = buf->buffer->use;
- return buf->buffer->content;
-#endif
-}
+ link = *in_link;
-/* Returns whether calendar changed on the server. This works only when server
- * supports 'getctag' extension. */
-static gboolean
-check_calendar_changed_on_server (ECalBackendCalDAV *cbdav,
- gboolean save_ctag,
- GCancellable *cancellable)
-{
- xmlOutputBufferPtr buf;
- SoupMessage *message;
- xmlDocPtr doc;
- xmlNodePtr root, node;
- xmlNsPtr ns, nsdav;
- gconstpointer buf_content;
- gsize buf_size;
- gboolean result = TRUE;
-
- g_return_val_if_fail (cbdav != NULL, TRUE);
-
- /* no support for 'getctag', thus update cache */
- if (!cbdav->priv->ctag_supported)
- return TRUE;
-
- /* Prepare the soup message */
- message = soup_message_new ("PROPFIND", cbdav->priv->uri);
- if (message == NULL)
- return FALSE;
+ while (link && left_to_go > 0) {
+ ECalMetaBackendInfo *nfo = link->data;
- doc = xmlNewDoc ((xmlChar *) "1.0");
- root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
- xmlDocSetRootElement (doc, root);
- nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
- ns = xmlNewNs (root, (xmlChar *) "http://calendarserver.org/ns/", (xmlChar *) "CS");
-
- node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
- node = xmlNewTextChild (node, nsdav, (xmlChar *) "getctag", NULL);
- xmlSetNs (node, ns);
-
- buf = xmlAllocOutputBuffer (NULL);
- xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
- xmlOutputBufferFlush (buf);
-
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
- soup_message_headers_append (
- message->request_headers,
- "Depth", "0");
-
- buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
- soup_message_set_request (
- message,
- "application/xml",
- SOUP_MEMORY_COPY,
- buf_content, buf_size);
-
- /* Send the request now */
- send_and_handle_redirection (cbdav, message, NULL, cancellable, NULL);
-
- /* Clean up the memory */
- xmlOutputBufferClose (buf);
- xmlFreeDoc (doc);
-
- /* Check the result */
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN) {
- caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
- } else if (message->status_code != SOUP_STATUS_MULTI_STATUS) {
- /* does not support it, but report calendar changed to update cache */
- cbdav->priv->ctag_supported = FALSE;
- } else {
- gchar *ctag = NULL;
-
- if (parse_propfind_response (message, XPATH_GETCTAG_STATUS, XPATH_GETCTAG, &ctag)) {
- const gchar *my_ctag;
-
- my_ctag = e_cal_backend_store_get_key_value (
- cbdav->priv->store, CALDAV_CTAG_KEY);
-
- if (ctag && my_ctag && g_str_equal (ctag, my_ctag)) {
- /* ctag is same, no change in the calendar */
- result = FALSE;
- } else if (save_ctag) {
- /* do not store ctag now, do it rather after complete sync */
- g_free (cbdav->priv->ctag_to_store);
- cbdav->priv->ctag_to_store = ctag;
- ctag = NULL;
- }
-
- g_free (ctag);
- } else {
- cbdav->priv->ctag_supported = FALSE;
+ link = g_slist_next (link);
+ if (!link) {
+ link = *set2;
+ *set2 = NULL;
}
- }
- g_object_unref (message);
+ if (!nfo)
+ continue;
- return result;
-}
+ left_to_go--;
-/* only_hrefs is a list of requested objects to fetch; it has precedence from
- * start_time/end_time, which are used only when both positive.
- * Times are supposed to be in UTC, if set.
- */
-static gboolean
-caldav_server_list_objects (ECalBackendCalDAV *cbdav,
- CalDAVObject **objs,
- gint *len,
- GSList *only_hrefs,
- time_t start_time,
- time_t end_time,
- GCancellable *cancellable)
-{
- xmlOutputBufferPtr buf;
- SoupMessage *message;
- xmlNodePtr node;
- xmlNodePtr sn;
- xmlNodePtr root;
- xmlDocPtr doc;
- xmlNsPtr nsdav;
- xmlNsPtr nscd;
- gconstpointer buf_content;
- gsize buf_size;
- gboolean result;
-
- /* Allocate the soup message */
- message = soup_message_new ("REPORT", cbdav->priv->uri);
- if (message == NULL)
- return FALSE;
+ /* iCloud returns broken calendar-multiget responses, with
+ empty <DAV:href> elements, thus read one-by-one for it.
+ This is confirmed as of 2017-04-11. */
+ if (cbdav->priv->is_icloud) {
+ gchar *calendar_data = NULL, *etag = NULL;
- /* Maybe we should just do a g_strdup_printf here? */
- /* Prepare request body */
- doc = xmlNewDoc ((xmlChar *) "1.0");
- if (!only_hrefs)
- root = xmlNewDocNode (doc, NULL, (xmlChar *) "calendar-query", NULL);
- else
- root = xmlNewDocNode (doc, NULL, (xmlChar *) "calendar-multiget", NULL);
- nscd = xmlNewNs (root, (xmlChar *) "urn:ietf:params:xml:ns:caldav", (xmlChar *) "C");
- xmlSetNs (root, nscd);
- xmlDocSetRootElement (doc, root);
-
- /* Add webdav tags */
- nsdav = xmlNewNs (root, (xmlChar *) "DAV:", (xmlChar *) "D");
- node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
- xmlNewTextChild (node, nsdav, (xmlChar *) "getetag", NULL);
- if (only_hrefs) {
- GSList *l;
-
- xmlNewTextChild (node, nscd, (xmlChar *) "calendar-data", NULL);
- for (l = only_hrefs; l; l = l->next) {
- if (l->data) {
- xmlNewTextChild (root, nsdav, (xmlChar *) "href", (xmlChar *) l->data);
- }
- }
- } else {
- node = xmlNewTextChild (root, nscd, (xmlChar *) "filter", NULL);
- node = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
- xmlSetProp (node, (xmlChar *) "name", (xmlChar *) "VCALENDAR");
-
- sn = xmlNewTextChild (node, nscd, (xmlChar *) "comp-filter", NULL);
- switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbdav))) {
- default:
- case ICAL_VEVENT_COMPONENT:
- xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VEVENT");
- break;
- case ICAL_VJOURNAL_COMPONENT:
- xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VJOURNAL");
- break;
- case ICAL_VTODO_COMPONENT:
- xmlSetProp (sn, (xmlChar *) "name", (xmlChar *) "VTODO");
- break;
- }
-
- if (start_time > 0 || end_time > 0) {
- gchar *tmp;
+ success = e_webdav_session_get_data_sync (cbdav->priv->webdav,
+ nfo->extra, NULL, &etag, &calendar_data, NULL, cancellable, error);
- sn = xmlNewTextChild (sn, nscd, (xmlChar *) "time-range", NULL);
+ if (success && calendar_data) {
+ icalcomponent *vcalendar;
- if (start_time > 0) {
- tmp = isodate_from_time_t (start_time);
- xmlSetProp (sn, (xmlChar *) "start", (xmlChar *) tmp);
- g_free (tmp);
+ vcalendar = icalcomponent_new_from_string (calendar_data);
+ if (vcalendar) {
+ ecb_caldav_update_nfo_with_vcalendar (nfo, vcalendar, etag);
+ icalcomponent_free (vcalendar);
+ }
}
- if (end_time > 0) {
- tmp = isodate_from_time_t (end_time);
- xmlSetProp (sn, (xmlChar *) "end", (xmlChar *) tmp);
- g_free (tmp);
- }
- }
- }
+ g_free (calendar_data);
+ g_free (etag);
- buf = xmlAllocOutputBuffer (NULL);
- xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
- xmlOutputBufferFlush (buf);
-
- /* Prepare the soup message */
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
- soup_message_headers_append (
- message->request_headers,
- "Depth", "1");
-
- buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
- soup_message_set_request (
- message,
- "application/xml",
- SOUP_MEMORY_COPY,
- buf_content, buf_size);
-
- /* Send the request now */
- send_and_handle_redirection (cbdav, message, NULL, cancellable, NULL);
-
- /* Clean up the memory */
- xmlOutputBufferClose (buf);
- xmlFreeDoc (doc);
-
- /* Check the result */
- if (message->status_code != SOUP_STATUS_MULTI_STATUS) {
- switch (message->status_code) {
- case SOUP_STATUS_CANT_RESOLVE:
- case SOUP_STATUS_CANT_RESOLVE_PROXY:
- case SOUP_STATUS_CANT_CONNECT:
- case SOUP_STATUS_CANT_CONNECT_PROXY:
- cbdav->priv->opened = FALSE;
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
- e_cal_backend_set_writable (
- E_CAL_BACKEND (cbdav), FALSE);
- break;
- case SOUP_STATUS_UNAUTHORIZED:
- case SOUP_STATUS_FORBIDDEN:
- caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
- break;
- default:
- g_warning ("Server did not response with SOUP_STATUS_MULTI_STATUS, but with code %d
(%s)", message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
- break;
+ if (!success)
+ break;
+ } else {
+ e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "href");
+ e_xml_document_write_string (xml, nfo->extra);
+ e_xml_document_end_element (xml); /* href */
}
-
- g_object_unref (message);
- return FALSE;
}
- /* Parse the response body */
- result = parse_report_response (message, objs, len);
-
- g_object_unref (message);
- return result;
-}
-
-static gboolean
-caldav_server_download_attachment (ECalBackendCalDAV *cbdav,
- const gchar *attachment_uri,
- gchar **content,
- gsize *len,
- GError **error)
-{
- SoupMessage *message;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- g_return_val_if_fail (attachment_uri != NULL, FALSE);
- g_return_val_if_fail (content != NULL, FALSE);
- g_return_val_if_fail (len != NULL, FALSE);
+ if (left_to_go != E_CALDAV_MAX_MULTIGET_AMOUNT &&
+ !cbdav->priv->is_icloud && success) {
+ GSList *from_link = *in_link;
- message = soup_message_new (SOUP_METHOD_GET, attachment_uri);
- if (message == NULL) {
- g_propagate_error (error, EDC_ERROR (InvalidObject));
- return FALSE;
+ success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, NULL, xml,
+ ecb_caldav_multiget_response_cb, &from_link, NULL, NULL, cancellable, error);
}
- soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
- send_and_handle_redirection (cbdav, message, NULL, NULL, NULL);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
- status_code_to_result (message, cbdav, FALSE, error);
+ g_object_unref (xml);
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN)
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
+ *in_link = link;
- g_object_unref (message);
- return FALSE;
- }
-
- *len = message->response_body->length;
- *content = g_memdup (message->response_body->data, *len);
-
- g_object_unref (message);
-
- return TRUE;
+ return success;
}
static gboolean
-caldav_server_get_object (ECalBackendCalDAV *cbdav,
- CalDAVObject *object,
- GCancellable *cancellable,
- GError **perror)
+ecb_caldav_get_calendar_items_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
{
- SoupMessage *message;
- const gchar *hdr;
- gchar *uri;
-
- g_return_val_if_fail (object != NULL && object->href != NULL, FALSE);
+ GHashTable *known_items = user_data; /* gchar *href ~> ECalMetaBackendInfo * */
- uri = caldav_generate_uri (cbdav, object->href);
- message = soup_message_new (SOUP_METHOD_GET, uri);
- if (message == NULL) {
- g_free (uri);
- g_propagate_error (perror, EDC_ERROR (NoSuchCal));
- return FALSE;
- }
+ g_return_val_if_fail (xpath_ctx != NULL, FALSE);
+ g_return_val_if_fail (known_items != NULL, FALSE);
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
+ if (!xpath_prop_prefix) {
+ e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+ } else if (status_code == SOUP_STATUS_OK) {
+ ECalMetaBackendInfo *nfo;
+ gchar *etag;
- send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
+ g_return_val_if_fail (href != NULL, FALSE);
- if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
- status_code_to_result (message, cbdav, FALSE, perror);
+ /* Skip collection resource, if returned by the server (like iCloud.com does) */
+ if (request_uri && request_uri->path && g_str_has_suffix (href, request_uri->path))
+ return TRUE;
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN)
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
- else if (message->status_code != SOUP_STATUS_NOT_FOUND)
- g_warning ("Could not fetch object '%s' from server, status:%d (%s)", uri,
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
- g_object_unref (message);
- g_free (uri);
- return FALSE;
- }
+ etag = e_webdav_session_util_maybe_dequote (e_xml_xpath_eval_as_string (xpath_ctx,
"%s/D:getetag", xpath_prop_prefix));
+ g_return_val_if_fail (etag != NULL, FALSE);
- hdr = soup_message_headers_get_list (message->response_headers, "Content-Type");
-
- if (hdr == NULL || g_ascii_strncasecmp (hdr, "text/calendar", 13)) {
- g_propagate_error (perror, EDC_ERROR (InvalidObject));
- g_object_unref (message);
- g_warning ("Object to fetch '%s' not of type text/calendar", uri);
- g_free (uri);
- return FALSE;
- }
+ /* UID is unknown at this moment */
+ nfo = e_cal_meta_backend_info_new ("", etag, NULL, href);
- hdr = soup_message_headers_get_list (message->response_headers, "ETag");
+ g_free (etag);
+ g_return_val_if_fail (nfo != NULL, FALSE);
- if (hdr != NULL) {
- g_free (object->etag);
- object->etag = quote_etag (hdr);
- } else if (!object->etag) {
- g_warning ("UUHH no ETag, now that's bad! (at '%s')", uri);
+ g_hash_table_insert (known_items, g_strdup (href), nfo);
}
- g_free (uri);
-
- g_free (object->cdata);
- object->cdata = g_strdup (message->response_body->data);
-
- g_object_unref (message);
return TRUE;
}
-static void
-caldav_post_freebusy (ECalBackendCalDAV *cbdav,
- const gchar *url,
- gchar **post_fb,
- GCancellable *cancellable,
- GError **error)
-{
- SoupMessage *message;
-
- message = soup_message_new (SOUP_METHOD_POST, url);
- if (message == NULL) {
- g_propagate_error (error, EDC_ERROR (NoSuchCal));
- return;
- }
-
- soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
- soup_message_set_request (
- message,
- "text/calendar; charset=utf-8",
- SOUP_MEMORY_COPY,
- *post_fb, strlen (*post_fb));
-
- send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
- if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
- status_code_to_result (message, cbdav, FALSE, error);
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN)
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
- else
- g_warning ("Could not post free/busy request to '%s', status:%d (%s)", url,
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
-
- g_object_unref (message);
-
- return;
- }
-
- g_free (*post_fb);
- *post_fb = g_strdup (message->response_body->data);
-
- g_object_unref (message);
-}
-
-static gchar *
-caldav_gen_file_from_uid (ECalBackendCalDAV *cbdav,
- const gchar *uid)
-{
- gchar *filename, *res;
-
- if (!uid)
- return NULL;
-
- filename = g_strconcat (uid, ".ics", NULL);
- res = soup_uri_encode (filename, NULL);
- g_free (filename);
-
- return res;
-}
-
-static gchar *
-caldav_gen_file_from_uid_cal (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp)
-{
- icalcomponent_kind my_kind;
- const gchar *uid = NULL;
-
- g_return_val_if_fail (cbdav != NULL, NULL);
- g_return_val_if_fail (icalcomp != NULL, NULL);
-
- my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
- if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *subcomp;
-
- for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
- uid = icalcomponent_get_uid (subcomp);
- if (uid && *uid)
- break;
- }
- } else if (icalcomponent_isa (icalcomp) == my_kind) {
- uid = icalcomponent_get_uid (icalcomp);
- }
-
- return caldav_gen_file_from_uid (cbdav, uid);
-}
+typedef struct _CalDAVChangesData {
+ gboolean is_repeat;
+ GSList **out_modified_objects;
+ GSList **out_removed_objects;
+ GHashTable *known_items; /* gchar *href ~> ECalMetaBackendInfo * */
+} CalDAVChangesData;
static gboolean
-caldav_server_put_object (ECalBackendCalDAV *cbdav,
- CalDAVObject *object,
- icalcomponent *icalcomp,
- GCancellable *cancellable,
- GError **perror)
-{
- SoupMessage *message;
- const gchar *hdr;
- gchar *uri;
-
- hdr = NULL;
-
- g_return_val_if_fail (object != NULL && object->cdata != NULL, FALSE);
-
- uri = caldav_generate_uri (cbdav, object->href);
- message = soup_message_new (SOUP_METHOD_PUT, uri);
- g_free (uri);
- if (message == NULL) {
- g_propagate_error (perror, EDC_ERROR (NoSuchCal));
- return FALSE;
- }
-
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
-
- /* For new items we use the If-None-Match so we don't
- * acidently override resources, for item updates we
- * use the If-Match header to avoid the Lost-update
- * problem */
- if (object->etag == NULL) {
- soup_message_headers_append (message->request_headers, "If-None-Match", "*");
- } else {
- soup_message_headers_append (
- message->request_headers,
- "If-Match", object->etag);
- }
-
- soup_message_set_request (
- message,
- "text/calendar; charset=utf-8",
- SOUP_MEMORY_COPY,
- object->cdata,
- strlen (object->cdata));
-
- uri = NULL;
- send_and_handle_redirection (cbdav, message, &uri, cancellable, perror);
-
- if (uri) {
- gchar *file = strrchr (uri, '/');
-
- /* there was a redirect, update href properly */
- if (file) {
- gchar *decoded;
-
- g_free (object->href);
-
- decoded = soup_uri_decode (file + 1);
- object->href = soup_uri_encode (decoded ? decoded : (file + 1), NULL);
-
- g_free (decoded);
- }
-
- g_free (uri);
- }
-
- if (status_code_to_result (message, cbdav, FALSE, perror)) {
- GError *local_error = NULL;
-
- hdr = soup_message_headers_get_list (message->response_headers, "ETag");
- if (hdr != NULL) {
- g_free (object->etag);
- object->etag = quote_etag (hdr);
- }
-
- /* "201 Created" can contain a Location with a link where the component was saved */
- hdr = soup_message_headers_get_list (message->response_headers, "Location");
- if (hdr) {
- /* reflect possible href change */
- gchar *file = strrchr (hdr, '/');
-
- if (file) {
- gchar *decoded;
-
- g_free (object->href);
-
- decoded = soup_uri_decode (file + 1);
- object->href = soup_uri_encode (decoded ? decoded : (file + 1), NULL);
-
- g_free (decoded);
- }
- }
-
- if (!caldav_server_get_object (cbdav, object, cancellable, &local_error)) {
- if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
- gchar *file;
-
- /* OK, the event was properly created, but cannot be found on the place
- * where it was PUT - why didn't server tell us where it saved it? */
- g_clear_error (&local_error);
-
- /* try whether it's saved as its UID.ics file */
- file = caldav_gen_file_from_uid_cal (cbdav, icalcomp);
- if (file) {
- g_free (object->href);
- object->href = file;
+ecb_caldav_search_changes_cb (ECalCache *cal_cache,
+ const gchar *uid,
+ const gchar *rid,
+ const gchar *revision,
+ const gchar *object,
+ const gchar *extra,
+ EOfflineState offline_state,
+ gpointer user_data)
+{
+ CalDAVChangesData *ccd = user_data;
+
+ g_return_val_if_fail (ccd != NULL, FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
- if (!caldav_server_get_object (cbdav, object, cancellable,
&local_error)) {
- if (g_error_matches (local_error, E_DATA_CAL_ERROR,
ObjectNotFound)) {
- g_clear_error (&local_error);
+ /* Can be NULL for added components in offline mode */
+ if (extra && *extra && (!rid || !*rid)) {
+ ECalMetaBackendInfo *nfo;
- /* not sure what can happen, but do not need to guess
for ever,
- * thus report success and update the calendar to get
fresh info */
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
- }
- }
+ nfo = g_hash_table_lookup (ccd->known_items, extra);
+ if (nfo) {
+ if (g_strcmp0 (revision, nfo->revision) == 0) {
+ g_hash_table_remove (ccd->known_items, extra);
+ } else {
+ if (!nfo->uid || !*(nfo->uid)) {
+ g_free (nfo->uid);
+ nfo->uid = g_strdup (uid);
}
- }
- }
- if (!local_error) {
- icalcomponent *use_comp = NULL;
+ *(ccd->out_modified_objects) = g_slist_prepend (*(ccd->out_modified_objects),
+ e_cal_meta_backend_info_copy (nfo));
- if (object->cdata) {
- /* maybe server also modified component, thus rather store the server's */
- use_comp = icalparser_parse_string (object->cdata);
+ g_hash_table_remove (ccd->known_items, extra);
}
-
- if (!use_comp)
- use_comp = icalcomp;
-
- put_comp_to_cache (cbdav, use_comp, object->href, object->etag);
-
- if (use_comp != icalcomp)
- icalcomponent_free (use_comp);
- } else {
- g_propagate_error (perror, local_error);
+ } else if (ccd->is_repeat) {
+ *(ccd->out_removed_objects) = g_slist_prepend (*(ccd->out_removed_objects),
+ e_cal_meta_backend_info_new (uid, revision, object, extra));
}
}
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN) {
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
- }
-
- g_object_unref (message);
-
return TRUE;
}
-static void
-caldav_server_delete_object (ECalBackendCalDAV *cbdav,
- CalDAVObject *object,
- GCancellable *cancellable,
- GError **perror)
-{
- SoupMessage *message;
- gchar *uri;
-
- g_return_if_fail (object != NULL && object->href != NULL);
-
- uri = caldav_generate_uri (cbdav, object->href);
- message = soup_message_new (SOUP_METHOD_DELETE, uri);
- g_free (uri);
- if (message == NULL) {
- g_propagate_error (perror, EDC_ERROR (NoSuchCal));
- return;
- }
-
- soup_message_headers_append (
- message->request_headers,
- "User-Agent", "Evolution/" VERSION);
-
- if (object->etag != NULL) {
- soup_message_headers_append (
- message->request_headers,
- "If-Match", object->etag);
- }
-
- send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
-
- status_code_to_result (message, cbdav, FALSE, perror);
-
- if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code == SOUP_STATUS_FORBIDDEN)
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
-
- g_object_unref (message);
-}
-
static gboolean
-caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav,
- GCancellable *cancellable,
- GError **error)
+ecb_caldav_get_changes_sync (ECalMetaBackend *meta_backend,
+ const gchar *last_sync_tag,
+ gboolean is_repeat,
+ gchar **out_new_sync_tag,
+ gboolean *out_repeat,
+ GSList **out_created_objects,
+ GSList **out_modified_objects,
+ GSList **out_removed_objects,
+ GCancellable *cancellable,
+ GError **error)
{
- SoupMessage *message;
- xmlOutputBufferPtr buf;
- xmlDocPtr doc;
- xmlNodePtr root, node;
- xmlNsPtr nsdav;
- gconstpointer buf_content;
- gsize buf_size;
- gchar *owner = NULL;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- g_return_val_if_fail (cbdav->priv->schedule_outbox_url == NULL, TRUE);
-
- /* Prepare the soup message */
- message = soup_message_new ("PROPFIND", cbdav->priv->uri);
- if (message == NULL)
- return FALSE;
-
- doc = xmlNewDoc ((xmlChar *) "1.0");
- root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
- xmlDocSetRootElement (doc, root);
- nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
-
- node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
- xmlNewTextChild (node, nsdav, (xmlChar *) "owner", NULL);
-
- buf = xmlAllocOutputBuffer (NULL);
- xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
- xmlOutputBufferFlush (buf);
-
- soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
- soup_message_headers_append (message->request_headers, "Depth", "0");
-
- buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
- soup_message_set_request (
- message,
- "application/xml",
- SOUP_MEMORY_COPY,
- buf_content, buf_size);
-
- /* Send the request now */
- send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
- /* Clean up the memory */
- xmlOutputBufferClose (buf);
- xmlFreeDoc (doc);
-
- /* Check the result */
- if (message->status_code == SOUP_STATUS_MULTI_STATUS && parse_propfind_response (message,
XPATH_OWNER_STATUS, XPATH_OWNER, &owner) && owner && *owner) {
- xmlNsPtr nscd;
- SoupURI *suri;
-
- g_object_unref (message);
-
- /* owner is a full path to the user's URL, thus change it in
- * calendar's uri when asking for schedule-outbox-URL */
- suri = soup_uri_new (cbdav->priv->uri);
- soup_uri_set_path (suri, owner);
- g_free (owner);
- owner = soup_uri_to_string (suri, FALSE);
- soup_uri_free (suri);
-
- message = soup_message_new ("PROPFIND", owner);
- if (message == NULL) {
- g_free (owner);
- return FALSE;
- }
-
- doc = xmlNewDoc ((xmlChar *) "1.0");
- root = xmlNewDocNode (doc, NULL, (xmlChar *) "propfind", NULL);
- xmlDocSetRootElement (doc, root);
- nsdav = xmlNewNs (root, (xmlChar *) "DAV:", NULL);
- nscd = xmlNewNs (root, (xmlChar *) "urn:ietf:params:xml:ns:caldav", (xmlChar *) "C");
-
- node = xmlNewTextChild (root, nsdav, (xmlChar *) "prop", NULL);
- xmlNewTextChild (node, nscd, (xmlChar *) "schedule-outbox-URL", NULL);
-
- buf = xmlAllocOutputBuffer (NULL);
- xmlNodeDumpOutput (buf, doc, root, 0, 1, NULL);
- xmlOutputBufferFlush (buf);
-
- soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
- soup_message_headers_append (message->request_headers, "Depth", "0");
-
- buf_content = compat_libxml_output_buffer_get_content (buf, &buf_size);
- soup_message_set_request (
- message,
- "application/xml",
- SOUP_MEMORY_COPY,
- buf_content, buf_size);
-
- /* Send the request now */
- send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
-
- if (message->status_code == SOUP_STATUS_MULTI_STATUS && parse_propfind_response (message,
XPATH_SCHEDULE_OUTBOX_URL_STATUS, XPATH_SCHEDULE_OUTBOX_URL, &cbdav->priv->schedule_outbox_url)) {
- if (!*cbdav->priv->schedule_outbox_url) {
- g_free (cbdav->priv->schedule_outbox_url);
- cbdav->priv->schedule_outbox_url = NULL;
- } else {
- /* make it a full URI */
- suri = soup_uri_new (cbdav->priv->uri);
- soup_uri_set_path (suri, cbdav->priv->schedule_outbox_url);
- g_free (cbdav->priv->schedule_outbox_url);
- cbdav->priv->schedule_outbox_url = soup_uri_to_string (suri, FALSE);
- soup_uri_free (suri);
- }
- }
+ ECalBackendCalDAV *cbdav;
+ EXmlDocument *xml;
+ GHashTable *known_items; /* gchar *href ~> ECalMetaBackendInfo * */
+ GHashTableIter iter;
+ gpointer key = NULL, value = NULL;
+ gboolean success;
- /* Clean up the memory */
- xmlOutputBufferClose (buf);
- xmlFreeDoc (doc);
- } else if (message->status_code == SOUP_STATUS_UNAUTHORIZED || message->status_code ==
SOUP_STATUS_FORBIDDEN) {
- caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (out_repeat, FALSE);
+ g_return_val_if_fail (out_new_sync_tag, FALSE);
+ g_return_val_if_fail (out_created_objects, FALSE);
+ g_return_val_if_fail (out_modified_objects, FALSE);
+ g_return_val_if_fail (out_removed_objects, FALSE);
+
+ *out_new_sync_tag = NULL;
+ *out_created_objects = NULL;
+ *out_modified_objects = NULL;
+ *out_removed_objects = NULL;
+
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
+
+ if (cbdav->priv->ctag_supported) {
+ gchar *new_sync_tag = NULL;
+
+ success = e_webdav_session_getctag_sync (cbdav->priv->webdav, NULL, &new_sync_tag,
cancellable, NULL);
+ if (!success) {
+ cbdav->priv->ctag_supported = g_cancellable_set_error_if_cancelled (cancellable,
error);
+ if (cbdav->priv->ctag_supported)
+ return FALSE;
+ } else if (!is_repeat && new_sync_tag && last_sync_tag && g_strcmp0 (last_sync_tag,
new_sync_tag) == 0) {
+ *out_new_sync_tag = new_sync_tag;
+ return TRUE;
+ }
+
+ /* Do not advertise the new ctag in the first go, otherwise a failure
+ in the second go might hide some events. */
+ if (is_repeat)
+ *out_new_sync_tag = new_sync_tag;
+ else
+ g_free (new_sync_tag);
}
- if (message)
- g_object_unref (message);
-
- g_free (owner);
-
- return cbdav->priv->schedule_outbox_url != NULL;
-}
-
-/* ************************************************************************* */
-/* Synchronization foo */
+ xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-query");
+ g_return_val_if_fail (xml != NULL, FALSE);
-static gboolean extract_timezones (ECalBackendCalDAV *cbdav, icalcomponent *icomp);
+ e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
-struct cache_comp_list
-{
- GSList *slist;
-};
+ e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "getetag");
+ e_xml_document_end_element (xml); /* prop */
-static gboolean
-remove_complist_from_cache_and_notify_cb (gpointer key,
- gpointer value,
- gpointer data)
-{
- GSList *l;
- struct cache_comp_list *ccl = value;
- ECalBackendCalDAV *cbdav = data;
-
- for (l = ccl->slist; l; l = l->next) {
- ECalComponent *old_comp = l->data;
- ECalComponentId *id;
-
- id = e_cal_component_get_id (old_comp);
- if (!id) {
- continue;
- }
-
- if (e_cal_backend_store_remove_component (cbdav->priv->store, id->uid, id->rid)) {
- e_cal_backend_notify_component_removed ((ECalBackend *) cbdav, id, old_comp, NULL);
- }
+ e_xml_document_start_element (xml, NULL, "filter");
+ e_xml_document_start_element (xml, NULL, "comp-filter");
+ e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+ e_xml_document_start_element (xml, NULL, "comp-filter");
- e_cal_component_free_id (id);
+ switch (e_cal_backend_get_kind (E_CAL_BACKEND (cbdav))) {
+ default:
+ case ICAL_VEVENT_COMPONENT:
+ e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+ break;
+ case ICAL_VJOURNAL_COMPONENT:
+ e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+ break;
+ case ICAL_VTODO_COMPONENT:
+ e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+ break;
}
- remove_cached_attachment (cbdav, (const gchar *) key);
-
- return FALSE;
-}
-
-static void
-free_comp_list (gpointer cclist)
-{
- struct cache_comp_list *ccl = cclist;
- g_return_if_fail (ccl != NULL);
-
- g_slist_foreach (ccl->slist, (GFunc) g_object_unref, NULL);
- g_slist_free (ccl->slist);
- g_free (ccl);
-}
+ if (!is_repeat) {
+ icaltimezone *utc = icaltimezone_get_utc_timezone ();
+ time_t now;
+ gchar *tmp;
-#define etags_match(_tag1, _tag2) ((_tag1 == _tag2) ? TRUE : \
- g_str_equal (_tag1 != NULL ? _tag1 : "", \
- _tag2 != NULL ? _tag2 : ""))
+ time (&now);
-/* start_time/end_time is an interval for checking changes. If both greater than zero,
- * only the interval is checked and the removed items are not notified, as they can
- * be still there.
-*/
-static void
-caldav_synchronize_cache (ECalBackendCalDAV *cbdav,
- time_t start_time,
- time_t end_time,
- gboolean can_check_ctag,
- GCancellable *cancellable)
-{
- CalDAVObject *sobjs, *object;
- GSList *c_objs, *c_iter; /* list of all items known from our cache */
- GTree *c_uid2complist; /* cache components list (with detached instances) sorted by (master's) uid */
- GHashTable *c_href2uid; /* connection between href and a (master's) uid */
- GSList *hrefs_to_update, *htu; /* list of href-s to update */
- gint i, len;
-
- /* intentionally do server-side checking first, and then the bool test,
- to store actual ctag value first, and then update the content, to not
- do it again the next time this function is called */
- if (!check_calendar_changed_on_server (cbdav, start_time == (time_t) 0, cancellable) &&
can_check_ctag) {
- /* no changes on the server, no update required */
- return;
- }
+ *out_repeat = TRUE;
- len = 0;
- sobjs = NULL;
+ /* Check for events in the month before/after today first,
+ to show user actual data as soon as possible. */
+ e_xml_document_start_element (xml, NULL, "time-range");
- /* get list of server objects */
- if (!caldav_server_list_objects (cbdav, &sobjs, &len, NULL, start_time, end_time, cancellable))
- return;
+ tmp = isodate_from_time_t (time_add_week_with_zone (now, -5, utc));
+ e_xml_document_add_attribute (xml, NULL, "start", tmp);
+ g_free (tmp);
- c_objs = e_cal_backend_store_get_components (cbdav->priv->store);
+ tmp = isodate_from_time_t (time_add_week_with_zone (now, +5, utc));
+ e_xml_document_add_attribute (xml, NULL, "end", tmp);
+ g_free (tmp);
- if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
- printf ("CalDAV - found %d objects on the server, locally stored %d objects\n", len,
g_slist_length (c_objs)); fflush (stdout);
+ e_xml_document_end_element (xml); /* time-range */
}
- /* do not store changes in cache immediately - makes things significantly quicker */
- e_cal_backend_store_freeze_changes (cbdav->priv->store);
+ e_xml_document_end_element (xml); /* comp-filter / VEVENT|VJOURNAL|VTODO */
+ e_xml_document_end_element (xml); /* comp-filter / VCALENDAR*/
+ e_xml_document_end_element (xml); /* filter */
- c_uid2complist = g_tree_new_full ((GCompareDataFunc) g_strcmp0, NULL, g_free, free_comp_list);
- c_href2uid = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+ known_items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, e_cal_meta_backend_info_free);
- /* fill indexed hash and tree with cached components */
- for (c_iter = c_objs; c_iter; c_iter = g_slist_next (c_iter)) {
- ECalComponent *ccomp = E_CAL_COMPONENT (c_iter->data);
- const gchar *uid = NULL;
- struct cache_comp_list *ccl;
- gchar *href;
+ success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS_AND_CHILDREN,
xml,
+ ecb_caldav_get_calendar_items_cb, known_items, NULL, NULL, cancellable, error);
- e_cal_component_get_uid (ccomp, &uid);
- if (!uid) {
- g_warning ("broken component with NULL Id");
- continue;
- }
+ g_object_unref (xml);
- href = ecalcomp_get_href (ccomp);
-
- if (href == NULL) {
- g_warning ("href of object NULL :(");
- continue;
- }
+ if (success) {
+ ECalCache *cal_cache;
+ CalDAVChangesData ccd;
- ccl = g_tree_lookup (c_uid2complist, uid);
- if (ccl) {
- ccl->slist = g_slist_prepend (ccl->slist, g_object_ref (ccomp));
- } else {
- ccl = g_new0 (struct cache_comp_list, 1);
- ccl->slist = g_slist_append (NULL, g_object_ref (ccomp));
+ ccd.is_repeat = is_repeat;
+ ccd.out_modified_objects = out_modified_objects;
+ ccd.out_removed_objects = out_removed_objects;
+ ccd.known_items = known_items;
- /* make a copy, which will be used in the c_href2uid too */
- uid = g_strdup (uid);
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
- g_tree_insert (c_uid2complist, (gpointer) uid, ccl);
- }
+ success = e_cal_cache_search_with_callback (cal_cache, NULL, ecb_caldav_search_changes_cb,
&ccd, cancellable, error);
- if (g_hash_table_lookup (c_href2uid, href) == NULL) {
- /* uid is from a component or c_uid2complist key, thus will not be
- * freed before a removal from c_uid2complist, thus do not duplicate it,
- * rather save memory */
- g_hash_table_insert (c_href2uid, href, (gpointer) uid);
- } else {
- g_free (href);
- }
+ g_clear_object (&cal_cache);
}
- /* clear it now, we do not need it later */
- g_slist_foreach (c_objs, (GFunc) g_object_unref, NULL);
- g_slist_free (c_objs);
- c_objs = NULL;
-
- hrefs_to_update = NULL;
-
- /* see if we have to update or add some objects */
- for (i = 0, object = sobjs; i < len && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK; i++, object++) {
- ECalComponent *ccomp = NULL;
- gchar *etag = NULL;
- const gchar *uid;
- struct cache_comp_list *ccl;
-
- if (object->status != 200) {
- /* just continue here, so that the object
- * doesnt get removed from the cobjs list
- * - therefore it will be removed */
- continue;
- }
-
- uid = g_hash_table_lookup (c_href2uid, object->href);
- if (uid) {
- ccl = g_tree_lookup (c_uid2complist, uid);
- if (ccl) {
- GSList *sl;
- for (sl = ccl->slist; sl && !etag; sl = sl->next) {
- ccomp = sl->data;
- if (ccomp)
- etag = ecalcomp_get_etag (ccomp);
- }
-
- if (!etag)
- ccomp = NULL;
- }
- }
-
- if (!etag || !etags_match (etag, object->etag)) {
- hrefs_to_update = g_slist_prepend (hrefs_to_update, object->href);
- } else if (uid && ccl) {
- /* all components cover by this uid are up-to-date */
- GSList *p;
-
- for (p = ccl->slist; p; p = p->next) {
- g_object_unref (p->data);
- }
-
- g_slist_free (ccl->slist);
- ccl->slist = NULL;
- }
-
- g_free (etag);
+ if (!success) {
+ g_hash_table_destroy (known_items);
+ return FALSE;
}
- /* free hash table, as it is not used anymore */
- g_hash_table_destroy (c_href2uid);
- c_href2uid = NULL;
-
- if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
- printf ("CalDAV - recognized %d items to update\n", g_slist_length (hrefs_to_update)); fflush
(stdout);
+ g_hash_table_iter_init (&iter, known_items);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ *out_created_objects = g_slist_prepend (*out_created_objects, e_cal_meta_backend_info_copy
(value));
}
- htu = hrefs_to_update;
- while (htu && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK) {
- gint count = 0;
- GSList *to_fetch = NULL;
+ g_hash_table_destroy (known_items);
- while (count < CALDAV_MAX_MULTIGET_AMOUNT && htu) {
- to_fetch = g_slist_prepend (to_fetch, htu->data);
- htu = htu->next;
- count++;
- }
-
- if (to_fetch && cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK) {
- CalDAVObject *up_sobjs = NULL;
-
- if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
- printf ("CalDAV - going to fetch %d items\n", g_slist_length (to_fetch));
fflush (stdout);
- }
+ if (*out_created_objects || *out_modified_objects) {
+ GSList *link, *set2 = *out_modified_objects;
- count = 0;
- if (!caldav_server_list_objects (cbdav, &up_sobjs, &count, to_fetch, 0, 0,
cancellable)) {
- fprintf (stderr, "CalDAV - failed to retrieve bunch of items\n"); fflush
(stderr);
- break;
- }
-
- if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
- printf ("CalDAV - fetched bunch of %d items\n", count); fflush (stdout);
- }
-
- /* we are going to update cache */
- /* they are downloaded, so process them */
- for (i = 0, object = up_sobjs; i < count /*&& cbdav->priv->slave_cmd ==
SLAVE_SHOULD_WORK */; i++, object++) {
- if (object->status == 200 && object->href && object->etag && object->cdata &&
*object->cdata) {
- icalcomponent *icomp = icalparser_parse_string (object->cdata);
-
- if (icomp) {
- put_server_comp_to_cache (cbdav, icomp, object->href,
object->etag, c_uid2complist);
- icalcomponent_free (icomp);
- }
- }
-
- /* these free immediately */
- caldav_object_free (object, FALSE);
- }
-
- /* cache update done for fetched items */
- g_free (up_sobjs);
- }
-
- /* do not free 'data' itself, it's part of 'sobjs' */
- g_slist_free (to_fetch);
- }
-
- /* if not interrupted and not using the time range... */
- if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK && (!start_time || !end_time)) {
- /* ...remove old (not on server anymore) items from our cache and notify of a removal */
- g_tree_foreach (c_uid2complist, remove_complist_from_cache_and_notify_cb, cbdav);
- }
-
- if (cbdav->priv->ctag_to_store) {
- /* store only when wasn't interrupted */
- if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK && start_time == 0 && end_time == 0) {
- e_cal_backend_store_put_key_value (cbdav->priv->store, CALDAV_CTAG_KEY,
cbdav->priv->ctag_to_store);
+ if (*out_created_objects) {
+ link = *out_created_objects;
+ } else {
+ link = set2;
+ set2 = NULL;
}
- g_free (cbdav->priv->ctag_to_store);
- cbdav->priv->ctag_to_store = NULL;
- }
-
- /* save cache changes to disk finally */
- e_cal_backend_store_thaw_changes (cbdav->priv->store);
-
- for (i = 0, object = sobjs; i < len; i++, object++) {
- caldav_object_free (object, FALSE);
+ do {
+ success = ecb_caldav_multiget_from_sets_sync (cbdav, &link, &set2, cancellable,
error);
+ } while (success && link);
}
- g_tree_destroy (c_uid2complist);
- g_slist_free (hrefs_to_update);
- g_free (sobjs);
-}
-
-static void
-check_server_tweaks (ECalBackendCalDAV *cbdav)
-{
- SoupURI *suri;
-
- g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
- cbdav->priv->is_google = FALSE;
- cbdav->priv->is_icloud = FALSE;
-
- g_return_if_fail (cbdav->priv->uri);
-
- suri = soup_uri_new (cbdav->priv->uri);
- g_return_if_fail (suri != NULL);
-
- cbdav->priv->is_google = suri->host && (
- g_ascii_strcasecmp (suri->host, "www.google.com") == 0 ||
- g_ascii_strcasecmp (suri->host, "apidata.googleusercontent.com") == 0);
-
- cbdav->priv->is_icloud = suri->host && e_util_utf8_strstrcase (suri->host, ".icloud.com");
-
- soup_uri_free (suri);
-}
-
-static void
-time_to_refresh_caldav_calendar_cb (ESource *source,
- gpointer user_data)
-{
- ECalBackendCalDAV *cbdav = user_data;
-
- g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
-
- g_cond_signal (&cbdav->priv->cond);
+ return success;
}
-/* ************************************************************************* */
-
-static gpointer
-caldav_synch_slave_loop (gpointer data)
+static gboolean
+ecb_caldav_extract_existing_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
{
- ECalBackendCalDAV *cbdav;
- time_t now;
- icaltimezone *utc = icaltimezone_get_utc_timezone ();
- gboolean know_unreachable;
+ GSList **out_existing_objects = user_data;
- cbdav = E_CAL_BACKEND_CALDAV (data);
+ g_return_val_if_fail (out_existing_objects != NULL, FALSE);
- g_mutex_lock (&cbdav->priv->busy_lock);
+ if (!xpath_prop_prefix) {
+ e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+ } else if (status_code == SOUP_STATUS_OK) {
+ gchar *etag;
+ gchar *calendar_data;
- know_unreachable = !cbdav->priv->opened;
+ g_return_val_if_fail (href != NULL, FALSE);
- while (cbdav->priv->slave_cmd != SLAVE_SHOULD_DIE) {
- gboolean can_check_ctag = TRUE;
+ etag = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:getetag", xpath_prop_prefix);
+ calendar_data = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:calendar-data",
xpath_prop_prefix);
- if (cbdav->priv->slave_cmd == SLAVE_SHOULD_SLEEP) {
- /* just sleep until we get woken up again */
- g_cond_wait (&cbdav->priv->cond, &cbdav->priv->busy_lock);
+ if (calendar_data) {
+ icalcomponent *vcalendar;
- /* This means to honor SLAVE_SHOULD_SLEEP only if the backend is opened */
- if (cbdav->priv->slave_cmd == SLAVE_SHOULD_DIE ||
- cbdav->priv->opened) {
- /* check if we should die, work or sleep again */
- continue;
- }
- }
-
- /* Ok here we go, do some real work
- * Synch it baby one more time ...
- */
- cbdav->priv->slave_busy = TRUE;
- if (cbdav->priv->slave_cmd == SLAVE_SHOULD_WORK_NO_CTAG_CHECK) {
- cbdav->priv->slave_cmd = SLAVE_SHOULD_WORK;
- can_check_ctag = FALSE;
- }
+ vcalendar = icalcomponent_new_from_string (calendar_data);
+ if (vcalendar) {
+ const gchar *uid;
- if (!cbdav->priv->opened) {
- gchar *certificate_pem = NULL;
- GTlsCertificateFlags certificate_errors = 0;
- GError *local_error = NULL;
-
- if (open_calendar_wrapper (cbdav, NULL, &local_error, TRUE, &know_unreachable,
&certificate_pem, &certificate_errors)) {
- cbdav->priv->opened = TRUE;
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
-
- check_server_tweaks (cbdav);
- know_unreachable = FALSE;
- } else {
- ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
- GError *local_error2 = NULL;
+ uid = ecb_caldav_get_vcalendar_uid (vcalendar);
- if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
- reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ if (uid) {
+ etag = e_webdav_session_util_maybe_dequote (etag);
+ *out_existing_objects = g_slist_prepend (*out_existing_objects,
+ e_cal_meta_backend_info_new (uid, etag, NULL, href));
}
- if (!e_backend_credentials_required_sync (E_BACKEND (cbdav), reason,
certificate_pem, certificate_errors,
- local_error, NULL, &local_error2)) {
- g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
- }
-
- g_clear_error (&local_error2);
+ icalcomponent_free (vcalendar);
}
-
- g_clear_error (&local_error);
- g_free (certificate_pem);
}
- if (cbdav->priv->opened) {
- time (&now);
- /* check for events in the month before/after today first,
- * to show user actual data as soon as possible */
- caldav_synchronize_cache (cbdav, time_add_week_with_zone (now, -5, utc),
time_add_week_with_zone (now, +5, utc), can_check_ctag, NULL);
-
- if (cbdav->priv->slave_cmd != SLAVE_SHOULD_SLEEP) {
- /* and then check for changes in a whole calendar */
- caldav_synchronize_cache (cbdav, 0, 0, can_check_ctag, NULL);
- }
-
- if (caldav_debug_show (DEBUG_SERVER_ITEMS)) {
- GSList *c_objs;
-
- c_objs = e_cal_backend_store_get_components (cbdav->priv->store);
-
- printf ("CalDAV - finished syncing with %d items in a cache\n",
g_slist_length (c_objs)); fflush (stdout);
-
- g_slist_foreach (c_objs, (GFunc) g_object_unref, NULL);
- g_slist_free (c_objs);
- }
- }
-
- cbdav->priv->slave_busy = FALSE;
-
- /* puhh that was hard, get some rest :) */
- g_cond_wait (&cbdav->priv->cond, &cbdav->priv->busy_lock);
+ g_free (calendar_data);
+ g_free (etag);
}
- cbdav->priv->synch_slave = NULL;
-
- /* signal we are done */
- g_cond_signal (&cbdav->priv->slave_gone_cond);
-
- /* we got killed ... */
- g_mutex_unlock (&cbdav->priv->busy_lock);
- return NULL;
-}
-
-static gchar *
-maybe_append_email_domain (const gchar *username,
- const gchar *may_append)
-{
- if (!username || !*username)
- return NULL;
-
- if (strchr (username, '@'))
- return g_strdup (username);
-
- return g_strconcat (username, may_append, NULL);
+ return TRUE;
}
-static gchar *
-get_usermail (ECalBackend *backend)
+static gboolean
+ecb_caldav_list_existing_sync (ECalMetaBackend *meta_backend,
+ gchar **out_new_sync_tag,
+ GSList **out_existing_objects,
+ GCancellable *cancellable,
+ GError **error)
{
ECalBackendCalDAV *cbdav;
- ESource *source;
- ESourceAuthentication *auth_extension;
- ESourceWebdav *webdav_extension;
- const gchar *extension_name;
- gchar *usermail;
- gchar *username;
- gchar *res = NULL;
-
- g_return_val_if_fail (backend != NULL, NULL);
-
- source = e_backend_get_source (E_BACKEND (backend));
-
- extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
- webdav_extension = e_source_get_extension (source, extension_name);
+ icalcomponent_kind kind;
+ EXmlDocument *xml;
+ gboolean success;
- /* This will never return an empty string. */
- usermail = e_source_webdav_dup_email_address (webdav_extension);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (out_existing_objects != NULL, FALSE);
- if (usermail != NULL)
- return usermail;
+ *out_existing_objects = NULL;
- cbdav = E_CAL_BACKEND_CALDAV (backend);
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
+ kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
- username = e_source_authentication_dup_user (auth_extension);
+ xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "calendar-query");
+ g_return_val_if_fail (xml != NULL, FALSE);
- if (cbdav->priv && cbdav->priv->is_google)
- res = maybe_append_email_domain (username, "@gmail.com");
+ e_xml_document_add_namespaces (xml, "D", E_WEBDAV_NS_DAV, NULL);
- g_free (username);
+ e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "getetag");
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "calendar-data");
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp");
+ e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp");
+ if (kind == ICAL_VEVENT_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+ else if (kind == ICAL_VJOURNAL_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+ else if (kind == ICAL_VTODO_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+ else
+ g_warn_if_reached ();
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "prop");
+ e_xml_document_add_attribute (xml, NULL, "name", "UID");
+ e_xml_document_end_element (xml); /* prop / UID */
+ e_xml_document_end_element (xml); /* comp / VEVENT|VJOURNAL|VTODO */
+ e_xml_document_end_element (xml); /* comp / VCALENDAR */
+ e_xml_document_end_element (xml); /* calendar-data */
+ e_xml_document_end_element (xml); /* prop */
+
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "filter");
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp-filter");
+ e_xml_document_add_attribute (xml, NULL, "name", "VCALENDAR");
+ e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "comp-filter");
+ if (kind == ICAL_VEVENT_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VEVENT");
+ else if (kind == ICAL_VJOURNAL_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VJOURNAL");
+ else if (kind == ICAL_VTODO_COMPONENT)
+ e_xml_document_add_attribute (xml, NULL, "name", "VTODO");
+ e_xml_document_end_element (xml); /* comp-filter / VEVENT|VJOURNAL|VTODO */
+ e_xml_document_end_element (xml); /* comp-filter / VCALENDAR */
+ e_xml_document_end_element (xml); /* filter */
+
+ success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS, xml,
+ ecb_caldav_extract_existing_cb, out_existing_objects, NULL, NULL, cancellable, error);
+
+ g_object_unref (xml);
+
+ if (success)
+ *out_existing_objects = g_slist_reverse (*out_existing_objects);
- return res;
+ return success;
}
-/* ************************************************************************* */
-/* ********** ECalBackendSync virtual function implementation ************* */
-
static gchar *
-caldav_get_backend_property (ECalBackend *backend,
- const gchar *prop_name)
-{
- g_return_val_if_fail (prop_name != NULL, FALSE);
-
- if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
- ESourceWebdav *extension;
- ESource *source;
- GString *caps;
- gchar *usermail;
- const gchar *extension_name;
-
- caps = g_string_new (
- CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
- CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
-
- usermail = get_usermail (E_CAL_BACKEND (backend));
- if (!usermail || !*usermail)
- g_string_append (caps, "," CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
- g_free (usermail);
-
- source = e_backend_get_source (E_BACKEND (backend));
-
- extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
- extension = e_source_get_extension (source, extension_name);
-
- if (e_source_webdav_get_calendar_auto_schedule (extension)) {
- g_string_append (
- caps,
- "," CAL_STATIC_CAPABILITY_CREATE_MESSAGES
- "," CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
- }
-
- return g_string_free (caps, FALSE);
-
- } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
- g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
- return get_usermail (E_CAL_BACKEND (backend));
-
- } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_DEFAULT_OBJECT)) {
- ECalComponent *comp;
- gchar *prop_value;
-
- comp = e_cal_component_new ();
-
- switch (e_cal_backend_get_kind (E_CAL_BACKEND (backend))) {
- case ICAL_VEVENT_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_EVENT);
- break;
- case ICAL_VTODO_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_TODO);
- break;
- case ICAL_VJOURNAL_COMPONENT:
- e_cal_component_set_new_vtype (comp, E_CAL_COMPONENT_JOURNAL);
- break;
- default:
- g_object_unref (comp);
- return NULL;
- }
-
- prop_value = e_cal_component_get_as_string (comp);
-
- g_object_unref (comp);
-
- return prop_value;
- }
-
- /* Chain up to parent's get_backend_property() method. */
- return E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->
- get_backend_property (backend, prop_name);
-}
-
-static void
-caldav_shutdown (ECalBackend *backend)
+ecb_caldav_uid_to_uri (ECalBackendCalDAV *cbdav,
+ const gchar *uid,
+ const gchar *extension)
{
- ECalBackendCalDAVPrivate *priv;
- ESource *source;
-
- priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (backend);
-
- /* Chain up to parent's shutdown() method. */
- E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->shutdown (backend);
-
- /* tell the slave to stop before acquiring a lock,
- * as it can work at the moment, and lock can be locked */
- update_slave_cmd (priv, SLAVE_SHOULD_DIE);
-
- g_mutex_lock (&priv->busy_lock);
-
- /* XXX Not sure if this really needs to be part of
- * shutdown or if we can just do it in dispose(). */
- source = e_backend_get_source (E_BACKEND (backend));
- if (source) {
- g_signal_handlers_disconnect_by_func (G_OBJECT (source), caldav_source_changed_cb, backend);
-
- if (priv->refresh_id) {
- e_source_refresh_remove_timeout (source, priv->refresh_id);
- priv->refresh_id = 0;
- }
- }
-
- /* stop the slave */
- while (priv->synch_slave) {
- g_cond_signal (&priv->cond);
-
- /* wait until the slave died */
- g_cond_wait (&priv->slave_gone_cond, &priv->busy_lock);
- }
-
- g_mutex_unlock (&priv->busy_lock);
-}
-
-static gboolean
-initialize_backend (ECalBackendCalDAV *cbdav,
- GError **perror)
-{
- ESourceAuthentication *auth_extension;
- ESourceOffline *offline_extension;
- ESourceWebdav *webdav_extension;
- ECalBackend *backend;
- SoupURI *soup_uri;
- ESource *source;
- gsize len;
- const gchar *cache_dir;
- const gchar *extension_name;
-
- backend = E_CAL_BACKEND (cbdav);
- cache_dir = e_cal_backend_get_cache_dir (backend);
- source = e_backend_get_source (E_BACKEND (backend));
-
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
-
- extension_name = E_SOURCE_EXTENSION_OFFLINE;
- offline_extension = e_source_get_extension (source, extension_name);
-
- extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
- webdav_extension = e_source_get_extension (source, extension_name);
-
- if (!g_signal_handler_find (G_OBJECT (source), G_SIGNAL_MATCH_FUNC | G_SIGNAL_MATCH_DATA, 0, 0, NULL,
caldav_source_changed_cb, cbdav))
- g_signal_connect (G_OBJECT (source), "changed", G_CALLBACK (caldav_source_changed_cb), cbdav);
+ ESourceWebdav *webdav_extension;
+ SoupURI *soup_uri;
+ gchar *uri, *tmp, *filename;
- cbdav->priv->loaded = TRUE;
- cbdav->priv->do_offline = e_source_offline_get_stay_synchronized (offline_extension);
- cbdav->priv->auth_required = e_source_authentication_required (auth_extension);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+ webdav_extension = e_source_get_extension (e_backend_get_source (E_BACKEND (cbdav)),
E_SOURCE_EXTENSION_WEBDAV_BACKEND);
soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+ g_return_val_if_fail (soup_uri != NULL, NULL);
- /* properly encode uri */
- if (soup_uri != NULL && soup_uri->path != NULL) {
- gchar *tmp, *path;
-
- if (strchr (soup_uri->path, '%')) {
- /* If path contains anything already encoded, then
- * decode it first, thus it'll be managed properly.
- * For example, the '#' in a path is in URI shown as
- * %23 and not doing this decode makes it being like
- * %2523, which is not what is wanted here. */
- tmp = soup_uri_decode (soup_uri->path);
- soup_uri_set_path (soup_uri, tmp);
- g_free (tmp);
- }
-
- tmp = soup_uri_encode (soup_uri->path, NULL);
- path = soup_uri_normalize (tmp, "/");
-
- soup_uri_set_path (soup_uri, path);
-
- g_free (tmp);
- g_free (path);
- }
-
- g_free (cbdav->priv->uri);
- cbdav->priv->uri = soup_uri_to_string (soup_uri, FALSE);
-
- soup_uri_free (soup_uri);
-
- g_return_val_if_fail (cbdav->priv->uri != NULL, FALSE);
-
- /* remove trailing slashes... */
- if (cbdav->priv->uri != NULL) {
- len = strlen (cbdav->priv->uri);
- while (len--) {
- if (cbdav->priv->uri[len] == '/') {
- cbdav->priv->uri[len] = '\0';
- } else {
- break;
- }
- }
- }
-
- /* ...and append exactly one slash */
- if (cbdav->priv->uri && *cbdav->priv->uri) {
- gchar *tmp = cbdav->priv->uri;
-
- cbdav->priv->uri = g_strconcat (cbdav->priv->uri, "/", NULL);
-
+ if (extension) {
+ tmp = g_strconcat (uid, extension, NULL);
+ filename = soup_uri_encode (tmp, NULL);
g_free (tmp);
+ } else {
+ filename = soup_uri_encode (uid, NULL);
}
- if (cbdav->priv->store == NULL) {
- /* remove the old cache while migrating to ECalBackendStore */
- e_cal_backend_cache_remove (cache_dir, "cache.xml");
- cbdav->priv->store = e_cal_backend_store_new (
- cache_dir, E_TIMEZONE_CACHE (cbdav));
- e_cal_backend_store_load (cbdav->priv->store);
- }
+ if (soup_uri->path) {
+ gchar *slash = strrchr (soup_uri->path, '/');
- /* Set the local attachment store */
- if (g_mkdir_with_parents (cache_dir, 0700) < 0) {
- g_propagate_error (perror, e_data_cal_create_error_fmt (OtherError, _("Cannot create local
cache folder ā%sā"), cache_dir));
- return FALSE;
+ if (slash && !slash[1])
+ *slash = '\0';
}
- if (!cbdav->priv->synch_slave) {
- GThread *slave;
-
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
- slave = g_thread_new (NULL, caldav_synch_slave_loop, cbdav);
-
- cbdav->priv->synch_slave = slave;
- g_thread_unref (slave);
- }
-
- if (cbdav->priv->refresh_id == 0) {
- cbdav->priv->refresh_id = e_source_refresh_add_timeout (
- source, NULL, time_to_refresh_caldav_calendar_cb, cbdav, NULL);
- }
-
- return TRUE;
-}
-
-static gboolean
-caldav_was_ever_connected (ECalBackendCalDAV *cbdav)
-{
- gboolean has_components;
- GSList *uids;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
-
- if (!cbdav->priv->store)
- return FALSE;
-
- uids = e_cal_backend_store_get_component_ids (cbdav->priv->store);
+ tmp = g_strconcat (soup_uri->path && *soup_uri->path ? soup_uri->path : "", "/", filename, NULL);
+ soup_uri_set_path (soup_uri, tmp);
+ g_free (tmp);
- /* Assume the calendar was connected if it has any events stored;
- obviously, empty calendars will fail this check. */
- has_components = uids != NULL;
+ uri = soup_uri_to_string (soup_uri, FALSE);
- g_slist_free_full (uids, (GDestroyNotify) e_cal_component_free_id);
+ soup_uri_free (soup_uri);
+ g_free (filename);
- return has_components;
+ return uri;
}
static gboolean
-open_calendar_wrapper (ECalBackendCalDAV *cbdav,
- GCancellable *cancellable,
- GError **error,
- gboolean first_attempt,
- gboolean *know_unreachable,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors)
-{
- gboolean server_unreachable = FALSE;
- gboolean awaiting_credentials = FALSE;
- gboolean success;
- GError *local_error = NULL;
-
- g_return_val_if_fail (cbdav != NULL, FALSE);
-
- if (!cbdav->priv->loaded && !initialize_backend (cbdav, error))
- return FALSE;
-
- if (!caldav_maybe_prepare_bearer_auth (cbdav, cancellable, error))
- return FALSE;
-
- success = caldav_server_open_calendar (cbdav, &server_unreachable, out_certificate_pem,
out_certificate_errors, cancellable, &local_error);
-
- if (first_attempt && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
- g_clear_error (&local_error);
- awaiting_credentials = TRUE;
- success = caldav_credentials_required_sync (cbdav, FALSE, first_attempt, cancellable,
&local_error);
- }
-
- if (success) {
- check_server_tweaks (cbdav);
-
- if (!awaiting_credentials) {
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
- }
- } else if (server_unreachable) {
- cbdav->priv->opened = FALSE;
- e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
- if (local_error) {
- if (know_unreachable && !*know_unreachable) {
- gchar *msg = g_strdup_printf (_("Server is unreachable, calendar is opened in
read-only mode.\nError message: %s"), local_error->message);
- e_cal_backend_notify_error (E_CAL_BACKEND (cbdav), msg);
- g_free (msg);
- g_clear_error (&local_error);
-
- *know_unreachable = TRUE;
- } else if (caldav_was_ever_connected (cbdav)) {
- /* This allows to open the calendar in read-only mode, which can be done
- if it was ever connected to the server. */
- g_clear_error (&local_error);
- success = TRUE;
- }
- }
- }
-
- if (local_error != NULL)
- g_propagate_error (error, local_error);
-
- return success;
-}
-
-static void
-caldav_do_open (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- gboolean only_if_exists,
- GError **perror)
+ecb_caldav_load_component_sync (ECalMetaBackend *meta_backend,
+ const gchar *uid,
+ const gchar *extra,
+ icalcomponent **out_component,
+ gchar **out_extra,
+ GCancellable *cancellable,
+ GError **error)
{
ECalBackendCalDAV *cbdav;
- ESourceWebdav *webdav_extension;
- ESourceAuthentication *auth_extension;
- ESource *source;
- gboolean online;
-
- cbdav = E_CAL_BACKEND_CALDAV (backend);
+ gchar *uri = NULL, *href = NULL, *etag = NULL, *bytes = NULL;
+ gsize length = -1;
+ gboolean success = FALSE;
- g_mutex_lock (&cbdav->priv->busy_lock);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
+ g_return_val_if_fail (out_component != NULL, FALSE);
- source = e_backend_get_source (E_BACKEND (cbdav));
- webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
- auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
- e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
- /* let it decide the 'getctag' extension availability again */
- cbdav->priv->ctag_supported = TRUE;
-
- if (!cbdav->priv->loaded && !initialize_backend (cbdav, perror)) {
- g_mutex_unlock (&cbdav->priv->busy_lock);
- return;
- }
+ if (extra && *extra) {
+ uri = g_strdup (extra);
- online = e_backend_get_online (E_BACKEND (backend));
+ success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes,
&length, cancellable, error);
- if (!cbdav->priv->do_offline && !online) {
- g_mutex_unlock (&cbdav->priv->busy_lock);
- g_propagate_error (perror, EDC_ERROR (RepositoryOffline));
- return;
+ if (!success) {
+ g_free (uri);
+ uri = NULL;
+ }
}
- cbdav->priv->opened = TRUE;
- cbdav->priv->is_google = FALSE;
-
- if (online) {
- gchar *certificate_pem = NULL, *auth_method;
- GTlsCertificateFlags certificate_errors = 0;
+ if (!success) {
GError *local_error = NULL;
- auth_method = e_source_authentication_dup_method (auth_extension);
-
- if ((g_strcmp0 (auth_method, "Google") == 0 ||
- !open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem,
&certificate_errors)) &&
- !g_cancellable_is_cancelled (cancellable)) {
- ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
- GError *local_error2 = NULL;
+ uri = ecb_caldav_uid_to_uri (cbdav, uid, NULL);
+ g_return_val_if_fail (uri != NULL, FALSE);
- if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
- reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
- }
-
- if (!e_backend_credentials_required_sync (E_BACKEND (backend), reason,
certificate_pem, certificate_errors,
- local_error, cancellable, &local_error2)) {
- g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
- }
+ success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href, &etag, &bytes,
&length, cancellable, &local_error);
+ if (!success && !g_cancellable_is_cancelled (cancellable) &&
+ g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_NOT_FOUND)) {
+ g_free (uri);
+ uri = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
- if (!local_error2 && (
- g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) ||
- g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired) ||
- g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed))) {
- /* These errors are treated through the authentication */
+ if (uri) {
g_clear_error (&local_error);
- } else {
- if (local_error)
- g_propagate_error (perror, local_error);
- local_error = NULL;
- }
- g_clear_error (&local_error2);
- }
-
- g_clear_error (&local_error);
- g_free (certificate_pem);
- g_free (auth_method);
- } else {
- e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
- }
-
- g_mutex_unlock (&cbdav->priv->busy_lock);
-}
-
-static void
-caldav_refresh (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- GError **perror)
-{
- ECalBackendCalDAV *cbdav;
- gboolean online;
-
- cbdav = E_CAL_BACKEND_CALDAV (backend);
-
- g_mutex_lock (&cbdav->priv->busy_lock);
- if (!cbdav->priv->loaded
- || cbdav->priv->slave_cmd == SLAVE_SHOULD_DIE) {
- g_mutex_unlock (&cbdav->priv->busy_lock);
- return;
- }
-
- if (!e_backend_get_online (E_BACKEND (backend)) &&
- e_backend_is_destination_reachable (E_BACKEND (backend), cancellable, NULL)) {
- e_backend_set_online (E_BACKEND (backend), TRUE);
- }
-
- if (!check_state (cbdav, &online, NULL) || !online) {
- g_mutex_unlock (&cbdav->priv->busy_lock);
- return;
- }
-
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK_NO_CTAG_CHECK);
-
- /* wake it up */
- g_cond_signal (&cbdav->priv->cond);
- g_mutex_unlock (&cbdav->priv->busy_lock);
-}
-
-static void
-remove_comp_from_cache_cb (gpointer value,
- gpointer user_data)
-{
- ECalComponent *comp = value;
- ECalBackendStore *store = user_data;
- ECalComponentId *id;
-
- g_return_if_fail (comp != NULL);
- g_return_if_fail (store != NULL);
-
- id = e_cal_component_get_id (comp);
- g_return_if_fail (id != NULL);
-
- e_cal_backend_store_remove_component (store, id->uid, id->rid);
- e_cal_component_free_id (id);
-}
-
-static gboolean
-remove_comp_from_cache (ECalBackendCalDAV *cbdav,
- const gchar *uid,
- const gchar *rid)
-{
- gboolean res = FALSE;
-
- if (!rid || !*rid) {
- /* get with detached instances */
- GSList *objects = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
-
- if (objects) {
- g_slist_foreach (objects, (GFunc) remove_comp_from_cache_cb, cbdav->priv->store);
- g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
- g_slist_free (objects);
-
- res = TRUE;
- }
- } else {
- res = e_cal_backend_store_remove_component (cbdav->priv->store, uid, rid);
- }
-
- return res;
-}
-
-static void
-add_detached_recur_to_vcalendar_cb (gpointer value,
- gpointer user_data)
-{
- icalcomponent *recurrence = e_cal_component_get_icalcomponent (value);
- icalcomponent *vcalendar = user_data;
-
- icalcomponent_add_component (
- vcalendar,
- icalcomponent_new_clone (recurrence));
-}
-
-static gint
-sort_master_first (gconstpointer a,
- gconstpointer b)
-{
- icalcomponent *ca, *cb;
-
- ca = e_cal_component_get_icalcomponent ((ECalComponent *) a);
- cb = e_cal_component_get_icalcomponent ((ECalComponent *) b);
-
- if (!ca) {
- if (!cb)
- return 0;
- else
- return -1;
- } else if (!cb) {
- return 1;
- }
-
- return icaltime_compare (icalcomponent_get_recurrenceid (ca), icalcomponent_get_recurrenceid (cb));
-}
-
-/* Returns new icalcomponent, with all detached instances stored in a cache.
- * The cache lock should be locked when called this function.
-*/
-static icalcomponent *
-get_comp_from_cache (ECalBackendCalDAV *cbdav,
- const gchar *uid,
- const gchar *rid,
- gchar **href,
- gchar **etag)
-{
- icalcomponent *icalcomp = NULL;
-
- if (rid == NULL || !*rid) {
- /* get with detached instances */
- GSList *objects = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
-
- if (!objects) {
- return NULL;
- }
-
- if (g_slist_length (objects) == 1) {
- ECalComponent *comp = objects->data;
-
- /* will be unreffed a bit later */
- if (comp)
- icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
- } else {
- /* if we have detached recurrences, return a VCALENDAR */
- icalcomp = e_cal_util_new_top_level ();
-
- objects = g_slist_sort (objects, sort_master_first);
-
- /* add all detached recurrences and the master object */
- g_slist_foreach (objects, add_detached_recur_to_vcalendar_cb, icalcomp);
- }
-
- /* every component has set same href and etag, thus it doesn't matter where it will be read */
- if (href)
- *href = ecalcomp_get_href (objects->data);
- if (etag)
- *etag = ecalcomp_get_etag (objects->data);
-
- g_slist_foreach (objects, (GFunc) g_object_unref, NULL);
- g_slist_free (objects);
- } else {
- /* get the exact object */
- ECalComponent *comp = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
-
- if (comp) {
- icalcomp = icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp));
- if (href)
- *href = ecalcomp_get_href (comp);
- if (etag)
- *etag = ecalcomp_get_etag (comp);
- g_object_unref (comp);
+ success = e_webdav_session_get_data_sync (cbdav->priv->webdav, uri, &href,
&etag, &bytes, &length, cancellable, error);
+ }
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
}
}
- return icalcomp;
-}
-
-static void
-put_server_comp_to_cache (ECalBackendCalDAV *cbdav,
- icalcomponent *icomp,
- const gchar *href,
- const gchar *etag,
- GTree *c_uid2complist)
-{
- icalcomponent_kind kind;
- ECalBackend *cal_backend;
-
- g_return_if_fail (cbdav != NULL);
- g_return_if_fail (icomp != NULL);
-
- cal_backend = E_CAL_BACKEND (cbdav);
- kind = icalcomponent_isa (icomp);
- extract_timezones (cbdav, icomp);
-
- if (kind == ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *subcomp;
-
- kind = e_cal_backend_get_kind (cal_backend);
-
- for (subcomp = icalcomponent_get_first_component (icomp, kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (icomp, kind)) {
- ECalComponent *new_comp, *old_comp;
-
- convert_to_url_attachment (cbdav, subcomp);
- new_comp = e_cal_component_new ();
- if (e_cal_component_set_icalcomponent (new_comp, icalcomponent_new_clone (subcomp))) {
- const gchar *uid = NULL;
- struct cache_comp_list *ccl;
-
- e_cal_component_get_uid (new_comp, &uid);
- if (!uid) {
- g_warning ("%s: no UID on component!", G_STRFUNC);
- g_object_unref (new_comp);
- continue;
- }
-
- if (href)
- ecalcomp_set_href (new_comp, href);
- if (etag)
- ecalcomp_set_etag (new_comp, etag);
-
- old_comp = NULL;
- if (c_uid2complist) {
- ccl = g_tree_lookup (c_uid2complist, uid);
- if (ccl) {
- gchar *nc_rid = e_cal_component_get_recurid_as_string
(new_comp);
- GSList *p;
+ if (success) {
+ *out_component = NULL;
- for (p = ccl->slist; p && !old_comp; p = p->next) {
- gchar *oc_rid;
+ if (href && etag && bytes && length != ((gsize) -1)) {
+ icalcomponent *icalcomp, *subcomp;
- old_comp = p->data;
+ icalcomp = icalcomponent_new_from_string (bytes);
+ if (icalcomp) {
+ e_cal_util_set_x_property (icalcomp, E_CALDAV_X_ETAG, etag);
- oc_rid = e_cal_component_get_recurid_as_string
(old_comp);
- if (g_strcmp0 (nc_rid, oc_rid) != 0) {
- old_comp = NULL;
- }
+ for (subcomp = icalcomponent_get_first_component (icalcomp,
ICAL_ANY_COMPONENT);
+ subcomp;
+ subcomp = icalcomponent_get_next_component (icalcomp,
ICAL_ANY_COMPONENT)) {
+ icalcomponent_kind kind = icalcomponent_isa (subcomp);
- g_free (oc_rid);
- }
-
- g_free (nc_rid);
+ if (kind == ICAL_VEVENT_COMPONENT ||
+ kind == ICAL_VJOURNAL_COMPONENT ||
+ kind == ICAL_VTODO_COMPONENT) {
+ e_cal_util_set_x_property (subcomp, E_CALDAV_X_ETAG, etag);
}
}
- put_component_to_store (cbdav, new_comp);
-
- if (old_comp == NULL) {
- e_cal_backend_notify_component_created (cal_backend, new_comp);
- } else {
- e_cal_backend_notify_component_modified (cal_backend, old_comp,
new_comp);
-
- if (ccl)
- ccl->slist = g_slist_remove (ccl->slist, old_comp);
- g_object_unref (old_comp);
- }
+ *out_component = icalcomp;
}
-
- g_object_unref (new_comp);
}
- }
-}
-static gboolean
-put_comp_to_cache (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp,
- const gchar *href,
- const gchar *etag)
-{
- icalcomponent_kind my_kind;
- ECalComponent *comp;
- gboolean res = FALSE;
-
- g_return_val_if_fail (cbdav != NULL, FALSE);
- g_return_val_if_fail (icalcomp != NULL, FALSE);
-
- my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
- comp = e_cal_component_new ();
-
- if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *subcomp;
-
- /* remove all old components from the cache first */
- for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
- remove_comp_from_cache (cbdav, icalcomponent_get_uid (subcomp), NULL);
- }
-
- /* then put new. It's because some detached instances could be removed on the server. */
- for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
- /* because reusing the same comp doesn't clear recur_id member properly */
- g_object_unref (comp);
- comp = e_cal_component_new ();
-
- if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
- if (href)
- ecalcomp_set_href (comp, href);
- if (etag)
- ecalcomp_set_etag (comp, etag);
-
- if (put_component_to_store (cbdav, comp))
- res = TRUE;
- }
- }
- } else if (icalcomponent_isa (icalcomp) == my_kind) {
- remove_comp_from_cache (cbdav, icalcomponent_get_uid (icalcomp), NULL);
-
- if (e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (icalcomp))) {
- if (href)
- ecalcomp_set_href (comp, href);
- if (etag)
- ecalcomp_set_etag (comp, etag);
-
- res = put_component_to_store (cbdav, comp);
+ if (!*out_component) {
+ success = FALSE;
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
}
}
- g_object_unref (comp);
-
- return res;
-}
-
-static void
-remove_property (gpointer prop,
- gpointer icomp)
-{
- icalcomponent_remove_property (icomp, prop);
- icalproperty_free (prop);
-}
-
-static void
-strip_unneeded_x_props (icalcomponent *icomp)
-{
- icalproperty *prop;
- GSList *to_remove = NULL;
-
- g_return_if_fail (icomp != NULL);
- g_return_if_fail (icalcomponent_isa (icomp) != ICAL_VCALENDAR_COMPONENT);
-
- for (prop = icalcomponent_get_first_property (icomp, ICAL_X_PROPERTY);
- prop;
- prop = icalcomponent_get_next_property (icomp, ICAL_X_PROPERTY)) {
- if (g_str_has_prefix (icalproperty_get_x_name (prop), X_E_CALDAV)) {
- to_remove = g_slist_prepend (to_remove, prop);
- }
- }
-
- for (prop = icalcomponent_get_first_property (icomp, ICAL_XLICERROR_PROPERTY);
- prop;
- prop = icalcomponent_get_next_property (icomp, ICAL_XLICERROR_PROPERTY)) {
- to_remove = g_slist_prepend (to_remove, prop);
- }
+ g_free (uri);
+ g_free (href);
+ g_free (etag);
+ g_free (bytes);
- g_slist_foreach (to_remove, remove_property, icomp);
- g_slist_free (to_remove);
+ return success;
}
static gboolean
-is_stored_on_server (ECalBackendCalDAV *cbdav,
- const gchar *uri)
+ecb_caldav_save_component_sync (ECalMetaBackend *meta_backend,
+ gboolean overwrite_existing,
+ EConflictResolution conflict_resolution,
+ const GSList *instances,
+ const gchar *extra,
+ gchar **out_new_uid,
+ gchar **out_new_extra,
+ GCancellable *cancellable,
+ GError **error)
{
- SoupURI *my_uri, *test_uri;
- gboolean res;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- g_return_val_if_fail (cbdav->priv->uri != NULL, FALSE);
- g_return_val_if_fail (uri != NULL, FALSE);
-
- my_uri = soup_uri_new (cbdav->priv->uri);
- g_return_val_if_fail (my_uri != NULL, FALSE);
-
- test_uri = soup_uri_new (uri);
- if (!test_uri) {
- soup_uri_free (my_uri);
- return FALSE;
- }
-
- res = my_uri->host && test_uri->host && g_ascii_strcasecmp (my_uri->host, test_uri->host) == 0;
-
- soup_uri_free (my_uri);
- soup_uri_free (test_uri);
-
- return res;
-}
+ ECalBackendCalDAV *cbdav;
+ icalcomponent *vcalendar, *subcomp;
+ gchar *href = NULL, *etag = NULL, *uid = NULL;
+ gchar *ical_string = NULL;
+ gboolean success;
-static void
-convert_to_inline_attachment (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp)
-{
- icalcomponent *cclone;
- icalproperty *p;
- GSList *to_remove = NULL;
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (instances != NULL, FALSE);
+ g_return_val_if_fail (out_new_uid, FALSE);
+ g_return_val_if_fail (out_new_extra, FALSE);
- g_return_if_fail (icalcomp != NULL);
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
- cclone = icalcomponent_new_clone (icalcomp);
+ vcalendar = e_cal_meta_backend_merge_instances (meta_backend, instances, cbdav->priv->is_icloud);
+ g_return_val_if_fail (vcalendar != NULL, FALSE);
- /* Remove local url attachments first */
- for (p = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
- p;
- p = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) {
- icalattach *attach;
+ for (subcomp = icalcomponent_get_first_component (vcalendar, ICAL_ANY_COMPONENT);
+ subcomp;
+ subcomp = icalcomponent_get_next_component (vcalendar, ICAL_ANY_COMPONENT)) {
+ icalcomponent_kind kind = icalcomponent_isa (subcomp);
- attach = icalproperty_get_attach ((const icalproperty *) p);
- if (icalattach_get_is_url (attach)) {
- const gchar *url;
+ if (kind == ICAL_VEVENT_COMPONENT ||
+ kind == ICAL_VJOURNAL_COMPONENT ||
+ kind == ICAL_VTODO_COMPONENT) {
+ if (!etag)
+ etag = e_cal_util_dup_x_property (subcomp, E_CALDAV_X_ETAG);
+ if (!uid)
+ uid = g_strdup (icalcomponent_get_uid (subcomp));
- url = icalattach_get_url (attach);
- if (g_str_has_prefix (url, LOCAL_PREFIX))
- to_remove = g_slist_prepend (to_remove, p);
+ e_cal_util_remove_x_property (subcomp, E_CALDAV_X_ETAG);
}
}
- g_slist_foreach (to_remove, remove_property, icalcomp);
- g_slist_free (to_remove);
-
- /* convert local url attachments to inline attachments now */
- for (p = icalcomponent_get_first_property (cclone, ICAL_ATTACH_PROPERTY);
- p;
- p = icalcomponent_get_next_property (cclone, ICAL_ATTACH_PROPERTY)) {
- icalattach *attach;
- GFile *file;
- GError *error = NULL;
- const gchar *uri;
- gchar *basename;
- gchar *content;
- gsize len;
-
- attach = icalproperty_get_attach ((const icalproperty *) p);
- if (!icalattach_get_is_url (attach))
- continue;
- uri = icalattach_get_url (attach);
- if (!g_str_has_prefix (uri, LOCAL_PREFIX))
- continue;
-
- file = g_file_new_for_uri (uri);
- basename = g_file_get_basename (file);
- if (g_file_load_contents (file, NULL, &content, &len, NULL, &error)) {
- icalproperty *prop;
- icalparameter *param;
- gchar *encoded;
-
- /*
- * do a base64 encoding so it can
- * be embedded in a soap message
- */
- encoded = g_base64_encode ((guchar *) content, len);
- attach = icalattach_new_from_data (encoded, NULL, NULL);
- g_free (content);
- g_free (encoded);
-
- prop = icalproperty_new_attach (attach);
- icalattach_unref (attach);
+ ical_string = icalcomponent_as_ical_string_r (vcalendar);
+ icalcomponent_free (vcalendar);
- param = icalparameter_new_value (ICAL_VALUE_BINARY);
- icalproperty_add_parameter (prop, param);
+ if (uid && ical_string && (!overwrite_existing || (extra && *extra))) {
+ gboolean force_write = FALSE;
- param = icalparameter_new_encoding (ICAL_ENCODING_BASE64);
- icalproperty_add_parameter (prop, param);
-
- param = icalparameter_new_x (basename);
- icalparameter_set_xname (param, X_E_CALDAV_ATTACHMENT_NAME);
- icalproperty_add_parameter (prop, param);
-
- icalcomponent_add_property (icalcomp, prop);
- } else {
- g_warning ("%s\n", error->message);
- g_clear_error (&error);
- }
- g_free (basename);
- g_object_unref (file);
- }
- icalcomponent_free (cclone);
-}
+ if (!extra)
+ href = ecb_caldav_uid_to_uri (cbdav, uid, ".ics");
-static void
-convert_to_url_attachment (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp)
-{
- ECalBackend *backend;
- GSList *to_remove = NULL, *to_remove_after_download = NULL;
- icalcomponent *cclone;
- icalproperty *p;
- gint fileindex;
-
- g_return_if_fail (cbdav != NULL);
- g_return_if_fail (icalcomp != NULL);
-
- backend = E_CAL_BACKEND (cbdav);
- cclone = icalcomponent_new_clone (icalcomp);
-
- /* Remove all inline attachments first */
- for (p = icalcomponent_get_first_property (icalcomp, ICAL_ATTACH_PROPERTY);
- p;
- p = icalcomponent_get_next_property (icalcomp, ICAL_ATTACH_PROPERTY)) {
- icalattach *attach;
-
- attach = icalproperty_get_attach ((const icalproperty *) p);
- if (!icalattach_get_is_url (attach))
- to_remove = g_slist_prepend (to_remove, p);
- else if (is_stored_on_server (cbdav, icalattach_get_url (attach)))
- to_remove_after_download = g_slist_prepend (to_remove_after_download, p);
- }
- g_slist_foreach (to_remove, remove_property, icalcomp);
- g_slist_free (to_remove);
-
- /* convert inline attachments to url attachments now */
- for (p = icalcomponent_get_first_property (cclone, ICAL_ATTACH_PROPERTY), fileindex = 0;
- p;
- p = icalcomponent_get_next_property (cclone, ICAL_ATTACH_PROPERTY), fileindex++) {
- icalattach *attach;
- gsize len = -1;
- gchar *decoded = NULL;
- gchar *basename, *local_filename;
-
- attach = icalproperty_get_attach ((const icalproperty *) p);
- if (icalattach_get_is_url (attach)) {
- const gchar *attach_url = icalattach_get_url (attach);
- GError *error = NULL;
-
- if (!is_stored_on_server (cbdav, attach_url))
- continue;
-
- if (!caldav_server_download_attachment (cbdav, attach_url, &decoded, &len, &error)) {
- if (caldav_debug_show (DEBUG_ATTACHMENTS))
- g_print ("CalDAV::%s: Failed to download from a server: %s\n",
G_STRFUNC, error ? error->message : "Unknown error");
- continue;
+ if (overwrite_existing) {
+ switch (conflict_resolution) {
+ case E_CONFLICT_RESOLUTION_FAIL:
+ case E_CONFLICT_RESOLUTION_USE_NEWER:
+ case E_CONFLICT_RESOLUTION_KEEP_SERVER:
+ case E_CONFLICT_RESOLUTION_WRITE_COPY:
+ break;
+ case E_CONFLICT_RESOLUTION_KEEP_LOCAL:
+ force_write = TRUE;
+ break;
}
}
- basename = icalproperty_get_parameter_as_string_r (p, X_E_CALDAV_ATTACHMENT_NAME);
- local_filename = e_cal_backend_create_cache_filename (backend, icalcomponent_get_uid
(icalcomp), basename, fileindex);
- g_free (basename);
+ success = e_webdav_session_put_data_sync (cbdav->priv->webdav, (extra && *extra) ? extra :
href,
+ force_write ? "" : overwrite_existing ? etag : NULL, E_WEBDAV_CONTENT_TYPE_CALENDAR,
+ ical_string, -1, out_new_extra, NULL, cancellable, error);
- if (local_filename) {
- GError *error = NULL;
-
- if (decoded == NULL) {
- gchar *content;
-
- content = (gchar *) icalattach_get_data (attach);
- decoded = (gchar *) g_base64_decode (content, &len);
- }
-
- if (g_file_set_contents (local_filename, decoded, len, &error)) {
- icalproperty *prop;
- gchar *url;
-
- url = g_filename_to_uri (local_filename, NULL, NULL);
- attach = icalattach_new_from_url (url);
- prop = icalproperty_new_attach (attach);
- icalattach_unref (attach);
- icalcomponent_add_property (icalcomp, prop);
- g_free (url);
- } else {
- g_warning ("%s\n", error->message);
- g_clear_error (&error);
- }
-
- g_free (local_filename);
- }
+ /* To read the component back, because server can change it */
+ if (success)
+ *out_new_uid = g_strdup (uid);
+ } else {
+ success = FALSE;
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
}
- icalcomponent_free (cclone);
-
- g_slist_foreach (to_remove_after_download, remove_property, icalcomp);
- g_slist_free (to_remove_after_download);
-}
-
-static void
-remove_files (const gchar *dir,
- const gchar *fileprefix)
-{
- GDir *d;
-
- g_return_if_fail (dir != NULL);
- g_return_if_fail (fileprefix != NULL);
- g_return_if_fail (*fileprefix != '\0');
-
- d = g_dir_open (dir, 0, NULL);
- if (d) {
- const gchar *entry;
- gint len = strlen (fileprefix);
-
- while ((entry = g_dir_read_name (d)) != NULL) {
- if (entry && strncmp (entry, fileprefix, len) == 0) {
- gchar *path;
+ g_free (ical_string);
+ g_free (href);
+ g_free (etag);
+ g_free (uid);
- path = g_build_filename (dir, entry, NULL);
- if (!g_file_test (path, G_FILE_TEST_IS_DIR))
- g_unlink (path);
- g_free (path);
- }
- }
- g_dir_close (d);
- }
+ return success;
}
-static void
-remove_cached_attachment (ECalBackendCalDAV *cbdav,
- const gchar *uid)
+static gboolean
+ecb_caldav_remove_component_sync (ECalMetaBackend *meta_backend,
+ EConflictResolution conflict_resolution,
+ const gchar *uid,
+ const gchar *extra,
+ const gchar *object,
+ GCancellable *cancellable,
+ GError **error)
{
- GSList *l;
- guint len;
- gchar *dir;
- gchar *fileprefix;
-
- g_return_if_fail (cbdav != NULL);
- g_return_if_fail (uid != NULL);
-
- l = e_cal_backend_store_get_components_by_uid (cbdav->priv->store, uid);
- len = g_slist_length (l);
- g_slist_foreach (l, (GFunc) g_object_unref, NULL);
- g_slist_free (l);
- if (len > 0)
- return;
-
- dir = e_cal_backend_create_cache_filename (E_CAL_BACKEND (cbdav), uid, "a", 0);
- if (!dir)
- return;
-
- fileprefix = g_strrstr (dir, G_DIR_SEPARATOR_S);
- if (fileprefix) {
- *fileprefix = '\0';
- fileprefix++;
-
- if (*fileprefix)
- fileprefix[strlen (fileprefix) - 1] = '\0';
-
- remove_files (dir, fileprefix);
- }
-
- g_free (dir);
-}
-
-/* callback for icalcomponent_foreach_tzid */
-typedef struct {
- ECalBackendStore *store;
- icalcomponent *vcal_comp;
+ ECalBackendCalDAV *cbdav;
icalcomponent *icalcomp;
-} ForeachTzidData;
-
-static void
-add_timezone_cb (icalparameter *param,
- gpointer data)
-{
- icaltimezone *tz;
- const gchar *tzid;
- icalcomponent *vtz_comp;
- ForeachTzidData *f_data = (ForeachTzidData *) data;
- ETimezoneCache *cache;
-
- tzid = icalparameter_get_tzid (param);
- if (!tzid)
- return;
-
- tz = icalcomponent_get_timezone (f_data->vcal_comp, tzid);
- if (tz)
- return;
-
- cache = e_cal_backend_store_ref_timezone_cache (f_data->store);
-
- tz = icalcomponent_get_timezone (f_data->icalcomp, tzid);
- if (tz == NULL)
- tz = icaltimezone_get_builtin_timezone_from_tzid (tzid);
- if (tz == NULL)
- tz = e_timezone_cache_get_timezone (cache, tzid);
-
- vtz_comp = icaltimezone_get_component (tz);
-
- if (tz != NULL && vtz_comp != NULL)
- icalcomponent_add_component (
- f_data->vcal_comp,
- icalcomponent_new_clone (vtz_comp));
-
- g_object_unref (cache);
-}
-
-static void
-add_timezones_from_component (ECalBackendCalDAV *cbdav,
- icalcomponent *vcal_comp,
- icalcomponent *icalcomp)
-{
- ForeachTzidData f_data;
-
- g_return_if_fail (cbdav != NULL);
- g_return_if_fail (vcal_comp != NULL);
- g_return_if_fail (icalcomp != NULL);
-
- f_data.store = cbdav->priv->store;
- f_data.vcal_comp = vcal_comp;
- f_data.icalcomp = icalcomp;
-
- icalcomponent_foreach_tzid (icalcomp, add_timezone_cb, &f_data);
-}
-
-/* also removes X-EVOLUTION-CALDAV from all the components */
-static gchar *
-pack_cobj (ECalBackendCalDAV *cbdav,
- icalcomponent *icomp)
-{
- icalcomponent *calcomp;
- gchar *objstr;
+ gchar *etag = NULL;
+ gboolean success;
- if (icalcomponent_isa (icomp) != ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *cclone;
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (meta_backend), FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
- calcomp = e_cal_util_new_top_level ();
+ cbdav = E_CAL_BACKEND_CALDAV (meta_backend);
- cclone = icalcomponent_new_clone (icomp);
- strip_unneeded_x_props (cclone);
- convert_to_inline_attachment (cbdav, cclone);
- icalcomponent_add_component (calcomp, cclone);
- add_timezones_from_component (cbdav, calcomp, cclone);
- } else {
- icalcomponent *subcomp;
- icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
-
- calcomp = icalcomponent_new_clone (icomp);
- for (subcomp = icalcomponent_get_first_component (calcomp, my_kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (calcomp, my_kind)) {
- strip_unneeded_x_props (subcomp);
- convert_to_inline_attachment (cbdav, subcomp);
- add_timezones_from_component (cbdav, calcomp, subcomp);
- }
+ if (!extra || !*extra) {
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return FALSE;
}
- objstr = icalcomponent_as_ical_string_r (calcomp);
- icalcomponent_free (calcomp);
-
- g_return_val_if_fail (objstr != NULL, NULL);
-
- return objstr;
-
-}
-
-static void
-maybe_correct_tzid (ECalBackendCalDAV *cbdav,
- ECalComponentDateTime *dt)
-{
- icaltimezone *zone;
-
- zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cbdav), dt->tzid);
- if (!zone) {
- g_free ((gchar *) dt->tzid);
- dt->tzid = g_strdup ("UTC");
- } else if (cbdav->priv->is_icloud && !dt->value->is_date) {
- const gchar *location = icaltimezone_get_location (zone);
-
- if (location && *location) {
- g_free ((gchar *) dt->tzid);
- dt->tzid = g_strdup (location);
- } else {
- /* No location available for this timezone, convert to UTC */
- dt->value->zone = zone;
- *dt->value = icaltime_convert_to_zone (*dt->value, icaltimezone_get_utc_timezone ());
- g_free ((gchar *) dt->tzid);
- dt->tzid = g_strdup ("UTC");
- }
+ icalcomp = icalcomponent_new_from_string (object);
+ if (!icalcomp) {
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
+ return FALSE;
}
-}
-static void
-sanitize_component (ECalBackendCalDAV *cbdav,
- ECalComponent *comp)
-{
- ECalComponentDateTime dt;
-
- /* Check dtstart, dtend and due's timezone, and convert it to local
- * default timezone if the timezone is not in our builtin timezone
- * list */
- e_cal_component_get_dtstart (comp, &dt);
- if (dt.value && dt.tzid) {
- maybe_correct_tzid (cbdav, &dt);
- e_cal_component_set_dtstart (comp, &dt);
- }
- e_cal_component_free_datetime (&dt);
+ if (conflict_resolution != E_CONFLICT_RESOLUTION_FAIL)
+ etag = e_cal_util_dup_x_property (icalcomp, E_CALDAV_X_ETAG);
- e_cal_component_get_dtend (comp, &dt);
- if (dt.value && dt.tzid) {
- maybe_correct_tzid (cbdav, &dt);
- e_cal_component_set_dtend (comp, &dt);
- }
- e_cal_component_free_datetime (&dt);
+ success = e_webdav_session_delete_sync (cbdav->priv->webdav, extra,
+ E_WEBDAV_DEPTH_THIS, etag, cancellable, error);
- e_cal_component_get_due (comp, &dt);
- if (dt.value && dt.tzid) {
- maybe_correct_tzid (cbdav, &dt);
- e_cal_component_set_due (comp, &dt);
- }
- e_cal_component_free_datetime (&dt);
+ icalcomponent_free (icalcomp);
+ g_free (etag);
- e_cal_component_abort_sequence (comp);
+ return success;
}
static gboolean
-cache_contains (ECalBackendCalDAV *cbdav,
- const gchar *uid,
- const gchar *rid)
-{
- gboolean res;
- ECalComponent *comp;
-
- g_return_val_if_fail (cbdav != NULL, FALSE);
- g_return_val_if_fail (uid != NULL, FALSE);
-
- g_return_val_if_fail (cbdav->priv->store != NULL, FALSE);
-
- comp = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
- res = comp != NULL;
-
- if (comp)
- g_object_unref (comp);
-
- return res;
-}
-
-/* Returns subcomponent of icalcomp, which is a master object, or icalcomp itself, if it's not a VCALENDAR;
- * Do not free returned pointer, it'll be freed together with the icalcomp.
-*/
-static icalcomponent *
-get_master_comp (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp)
+ecb_caldav_propfind_get_owner_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
{
- icalcomponent *master = icalcomp;
+ gchar **out_owner_href = user_data;
- g_return_val_if_fail (cbdav != NULL, NULL);
- g_return_val_if_fail (icalcomp != NULL, NULL);
+ g_return_val_if_fail (out_owner_href != NULL, FALSE);
- if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *subcomp;
- icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
+ if (xpath_prop_prefix &&
+ status_code == SOUP_STATUS_OK) {
+ gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/D:owner/D:href", xpath_prop_prefix);
- master = NULL;
+ if (tmp && *tmp)
+ *out_owner_href = e_webdav_session_ensure_full_uri (webdav, request_uri, tmp);
- for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
- subcomp;
- subcomp = icalcomponent_get_next_component (icalcomp, my_kind)) {
- struct icaltimetype sub_rid = icalcomponent_get_recurrenceid (subcomp);
+ g_free (tmp);
- if (icaltime_is_null_time (sub_rid)) {
- master = subcomp;
- break;
- }
- }
+ return FALSE;
}
- return master;
+ return TRUE;
}
static gboolean
-remove_instance (ECalBackendCalDAV *cbdav,
- icalcomponent *icalcomp,
- struct icaltimetype rid,
- ECalObjModType mod,
- gboolean also_exdate)
-{
- icalcomponent *master = icalcomp;
- struct icaltimetype master_dtstart;
- gboolean res = FALSE;
-
- g_return_val_if_fail (icalcomp != NULL, res);
- g_return_val_if_fail (!icaltime_is_null_time (rid), res);
-
- master_dtstart = icalcomponent_get_dtstart (master);
- if (master_dtstart.zone && master_dtstart.zone != rid.zone)
- rid = icaltime_convert_to_zone (rid, (icaltimezone *) master_dtstart.zone);
-
- rid = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
-
- /* remove an instance only */
- if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT) {
- icalcomponent *subcomp;
- icalcomponent_kind my_kind = e_cal_backend_get_kind (E_CAL_BACKEND (cbdav));
- gint left = 0;
- gboolean start_first = FALSE;
-
- master = NULL;
-
- /* remove old instance first */
- for (subcomp = icalcomponent_get_first_component (icalcomp, my_kind);
- subcomp;
- subcomp = start_first ? icalcomponent_get_first_component (icalcomp, my_kind) :
icalcomponent_get_next_component (icalcomp, my_kind)) {
- struct icaltimetype sub_rid = icalcomponent_get_recurrenceid (subcomp);
-
- start_first = FALSE;
-
- if (icaltime_is_null_time (sub_rid)) {
- master = subcomp;
- left++;
- } else if (icaltime_compare (sub_rid, rid) == 0) {
- icalcomponent_remove_component (icalcomp, subcomp);
- icalcomponent_free (subcomp);
- if (master) {
- break;
- } else {
- /* either no master or master not as the first component, thus rescan
*/
- left = 0;
- start_first = TRUE;
- }
- } else {
- left++;
- }
- }
-
- /* whether left at least one instance or a master object */
- res = left > 0;
- } else {
- res = TRUE;
- }
-
- if (master && also_exdate) {
- e_cal_util_remove_instances (master, rid, mod);
- }
-
- return res;
-}
-
-static icalcomponent *
-replace_master (ECalBackendCalDAV *cbdav,
- icalcomponent *old_comp,
- icalcomponent *new_master)
-{
- icalcomponent *old_master;
- if (icalcomponent_isa (old_comp) != ICAL_VCALENDAR_COMPONENT) {
- icalcomponent_free (old_comp);
- return new_master;
- }
-
- old_master = get_master_comp (cbdav, old_comp);
- if (!old_master) {
- /* no master, strange */
- icalcomponent_free (new_master);
- } else {
- icalcomponent_remove_component (old_comp, old_master);
- icalcomponent_free (old_master);
-
- icalcomponent_add_component (old_comp, new_master);
- }
-
- return old_comp;
-}
-
-/* the resulting component should be unreffed when done with it;
- * the fallback_comp is cloned, if used */
-static ECalComponent *
-get_ecalcomp_master_from_cache_or_fallback (ECalBackendCalDAV *cbdav,
- const gchar *uid,
- const gchar *rid,
- ECalComponent *fallback_comp)
+ecb_caldav_propfind_get_schedule_outbox_url_cb (EWebDAVSession *webdav,
+ xmlXPathContextPtr xpath_ctx,
+ const gchar *xpath_prop_prefix,
+ const SoupURI *request_uri,
+ const gchar *href,
+ guint status_code,
+ gpointer user_data)
{
- ECalComponent *comp = NULL;
- icalcomponent *icalcomp;
-
- g_return_val_if_fail (cbdav != NULL, NULL);
- g_return_val_if_fail (uid != NULL, NULL);
+ gchar **out_schedule_outbox_url = user_data;
- icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
- if (icalcomp) {
- icalcomponent *master = get_master_comp (cbdav, icalcomp);
-
- if (master) {
- comp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master));
- }
-
- icalcomponent_free (icalcomp);
- }
+ g_return_val_if_fail (out_schedule_outbox_url != NULL, FALSE);
- if (!comp && fallback_comp)
- comp = e_cal_component_clone (fallback_comp);
+ if (!xpath_prop_prefix) {
+ e_xml_xpath_context_register_namespaces (xpath_ctx, "C", E_WEBDAV_NS_CALDAV, NULL);
+ } else if (status_code == SOUP_STATUS_OK) {
+ gchar *tmp = e_xml_xpath_eval_as_string (xpath_ctx, "%s/C:schedule-outbox-URL/D:href",
xpath_prop_prefix);
- return comp;
-}
-
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_create_objects (ECalBackendCalDAV *cbdav,
- const GSList *in_calobjs,
- GSList **uids,
- GSList **new_components,
- GCancellable *cancellable,
- GError **perror)
-{
- ECalComponent *comp;
- gboolean online, did_put = FALSE;
- struct icaltimetype current;
- icalcomponent *icalcomp;
- const gchar *in_calobj = in_calobjs->data;
- const gchar *comp_uid;
-
- if (!check_state (cbdav, &online, perror))
- return;
-
- /* We make the assumption that the in_calobjs list we're passed is always exactly one element long,
since we haven't specified "bulk-adds"
- * in our static capability list. This simplifies a lot of the logic, especially around asynchronous
results. */
- if (in_calobjs->next != NULL) {
- g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not
support bulk additions")));
- return;
- }
-
- comp = e_cal_component_new_from_string (in_calobj);
-
- if (comp == NULL) {
- g_propagate_error (perror, EDC_ERROR (InvalidObject));
- return;
- }
-
- icalcomp = e_cal_component_get_icalcomponent (comp);
- if (icalcomp == NULL) {
- g_object_unref (comp);
- g_propagate_error (perror, EDC_ERROR (InvalidObject));
- return;
- }
-
- comp_uid = icalcomponent_get_uid (icalcomp);
- if (!comp_uid) {
- gchar *new_uid;
-
- new_uid = e_cal_component_gen_uid ();
- if (!new_uid) {
- g_object_unref (comp);
- g_propagate_error (perror, EDC_ERROR (InvalidObject));
- return;
- }
+ if (tmp && *tmp)
+ *out_schedule_outbox_url = e_webdav_session_ensure_full_uri (webdav, request_uri,
tmp);
- icalcomponent_set_uid (icalcomp, new_uid);
- comp_uid = icalcomponent_get_uid (icalcomp);
-
- g_free (new_uid);
- }
-
- /* check the object is not in our cache */
- if (cache_contains (cbdav, comp_uid, NULL)) {
- g_object_unref (comp);
- g_propagate_error (perror, EDC_ERROR (ObjectIdAlreadyExists));
- return;
- }
-
- /* Set the created and last modified times on the component */
- current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_created (comp, ¤t);
- e_cal_component_set_last_modified (comp, ¤t);
-
- /* sanitize the component*/
- sanitize_component (cbdav, comp);
-
- if (online) {
- CalDAVObject object;
-
- object.href = ecalcomp_gen_href (comp);
- object.etag = NULL;
- object.cdata = pack_cobj (cbdav, icalcomp);
-
- did_put = caldav_server_put_object (cbdav, &object, icalcomp, cancellable, perror);
-
- caldav_object_free (&object, FALSE);
- } else {
- /* mark component as out of synch */
- /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_CREATED); */
- }
-
- if (did_put) {
- if (uids)
- *uids = g_slist_prepend (*uids, g_strdup (comp_uid));
+ g_free (tmp);
- if (new_components)
- *new_components = g_slist_prepend(*new_components,
get_ecalcomp_master_from_cache_or_fallback (cbdav, comp_uid, NULL, comp));
+ return FALSE;
}
- g_object_unref (comp);
+ return TRUE;
}
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_modify_objects (ECalBackendCalDAV *cbdav,
- const GSList *calobjs,
- ECalObjModType mod,
- GSList **old_components,
- GSList **new_components,
- GCancellable *cancellable,
- GError **error)
+static gboolean
+ecb_caldav_receive_schedule_outbox_url_sync (ECalBackendCalDAV *cbdav,
+ GCancellable *cancellable,
+ GError **error)
{
- ECalComponent *comp;
- icalcomponent *cache_comp, *master_comp;
- gboolean online, did_put = FALSE, success = TRUE;
- ECalComponentId *id;
- struct icaltimetype current;
- gchar *href = NULL, *etag = NULL;
- const gchar *calobj = calobjs->data;
-
- if (new_components)
- *new_components = NULL;
-
- if (!check_state (cbdav, &online, error))
- return;
-
- /* We make the assumption that the calobjs list we're passed is always exactly one element long,
since we haven't specified "bulk-modifies"
- * in our static capability list. This simplifies a lot of the logic, especially around asynchronous
results. */
- if (calobjs->next != NULL) {
- g_propagate_error (error, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not
support bulk modifications")));
- return;
- }
-
- comp = e_cal_component_new_from_string (calobj);
-
- if (comp == NULL) {
- g_propagate_error (error, EDC_ERROR (InvalidObject));
- return;
- }
+ EXmlDocument *xml;
+ gchar *owner_href = NULL, *schedule_outbox_url = NULL;
+ gboolean success;
- if (!e_cal_component_get_icalcomponent (comp) ||
- icalcomponent_isa (e_cal_component_get_icalcomponent (comp)) != e_cal_backend_get_kind
(E_CAL_BACKEND (cbdav))) {
- g_object_unref (comp);
- g_propagate_error (error, EDC_ERROR (InvalidObject));
- return;
- }
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+ g_return_val_if_fail (cbdav->priv->schedule_outbox_url == NULL, TRUE);
- /* Set the last modified time on the component */
- current = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_last_modified (comp, ¤t);
+ xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+ g_return_val_if_fail (xml != NULL, FALSE);
- /* sanitize the component */
- sanitize_component (cbdav, comp);
+ e_xml_document_start_element (xml, NULL, "prop");
+ e_xml_document_add_empty_element (xml, NULL, "owner");
+ e_xml_document_end_element (xml); /* prop */
- id = e_cal_component_get_id (comp);
- if (id == NULL) {
- g_set_error_literal (
- error, E_CAL_CLIENT_ERROR,
- E_CAL_CLIENT_ERROR_INVALID_OBJECT,
- e_cal_client_error_to_string (
- E_CAL_CLIENT_ERROR_INVALID_OBJECT));
- return;
- }
+ success = e_webdav_session_propfind_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_THIS, xml,
+ ecb_caldav_propfind_get_owner_cb, &owner_href, cancellable, error);
- /* fetch full component from cache, it will be pushed to the server */
- cache_comp = get_comp_from_cache (cbdav, id->uid, NULL, &href, &etag);
+ g_object_unref (xml);
- if (cache_comp == NULL) {
- e_cal_component_free_id (id);
- g_object_unref (comp);
- g_free (href);
- g_free (etag);
- g_propagate_error (error, EDC_ERROR (ObjectNotFound));
- return;
- }
-
- if (!online) {
- /* mark component as out of synch */
- /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_MODIFIED);*/
+ if (!success || !owner_href || !*owner_href) {
+ g_free (owner_href);
+ return FALSE;
}
- if (old_components) {
- *old_components = NULL;
-
- if (e_cal_component_is_instance (comp)) {
- /* set detached instance as the old object, if any */
- ECalComponent *old_instance = e_cal_backend_store_get_component (cbdav->priv->store,
id->uid, id->rid);
-
- /* This will give a reference to 'old_component' */
- if (old_instance) {
- *old_components = g_slist_prepend (*old_components, e_cal_component_clone
(old_instance));
- g_object_unref (old_instance);
- }
- }
-
- if (!*old_components) {
- icalcomponent *master = get_master_comp (cbdav, cache_comp);
-
- if (master) {
- /* set full component as the old object */
- *old_components = g_slist_prepend (*old_components,
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
- }
- }
+ xml = e_xml_document_new (E_WEBDAV_NS_DAV, "propfind");
+ if (!xml) {
+ g_warn_if_fail (xml != NULL);
+ g_free (owner_href);
+ return FALSE;
}
- switch (mod) {
- case E_CAL_OBJ_MOD_ONLY_THIS:
- case E_CAL_OBJ_MOD_THIS:
- if (e_cal_component_is_instance (comp)) {
- icalcomponent *new_comp = e_cal_component_get_icalcomponent (comp);
-
- /* new object is only this instance */
- if (new_components)
- *new_components = g_slist_prepend (*new_components, e_cal_component_clone
(comp));
-
- /* add the detached instance */
- if (icalcomponent_isa (cache_comp) == ICAL_VCALENDAR_COMPONENT) {
- /* do not modify the EXDATE, as the component will be put back */
- remove_instance (cbdav, cache_comp, icalcomponent_get_recurrenceid
(new_comp), mod, FALSE);
- } else {
- /* this is only a master object, thus make is a VCALENDAR component */
- icalcomponent *icomp;
-
- icomp = e_cal_util_new_top_level ();
- icalcomponent_add_component (icomp, cache_comp);
-
- /* no need to free the cache_comp, as it is inside icomp */
- cache_comp = icomp;
- }
+ e_xml_document_add_namespaces (xml, "C", E_WEBDAV_NS_CALDAV, NULL);
- if (cache_comp && cbdav->priv->is_google) {
- icalcomponent_set_sequence (cache_comp, icalcomponent_get_sequence
(cache_comp) + 1);
- icalcomponent_set_sequence (new_comp, icalcomponent_get_sequence (new_comp) +
1);
- }
+ e_xml_document_start_element (xml, NULL, "prop");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "schedule-outbox-URL");
+ e_xml_document_end_element (xml); /* prop */
- /* add the detached instance finally */
- icalcomponent_add_component (cache_comp, icalcomponent_new_clone (new_comp));
- } else {
- cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
- }
- break;
- case E_CAL_OBJ_MOD_ALL:
- e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
- cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
- break;
- case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
- case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
- master_comp = get_master_comp (cbdav, cache_comp);
- if (e_cal_component_is_instance (comp) && master_comp) {
- ECalComponent *mcomp;
- struct icaltimetype rid, master_dtstart;
- icalcomponent *icalcomp = e_cal_component_get_icalcomponent (comp);
- icalcomponent *split_icalcomp;
- icalproperty *prop;
-
- rid = icalcomponent_get_recurrenceid (icalcomp);
- mcomp = e_cal_component_new_from_icalcomponent (icalcomponent_new_clone
(master_comp));
-
- if (mod == E_CAL_OBJ_MOD_THIS_AND_FUTURE &&
- e_cal_util_is_first_instance (mcomp, icalcomponent_get_recurrenceid (icalcomp),
resolve_tzid, cbdav)) {
- icalproperty *prop = icalcomponent_get_first_property (icalcomp,
ICAL_RECURRENCEID_PROPERTY);
-
- if (prop)
- icalcomponent_remove_property (icalcomp, prop);
-
- e_cal_component_rescan (comp);
- e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
-
- /* Then do it like for "mod_all" */
- cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
- g_clear_object (&mcomp);
-
- if (new_components) {
- /* read the comp from cache again, as some servers can modify it on
put */
- *new_components = g_slist_prepend (*new_components,
get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, NULL, comp));
- }
- break;
- }
-
- prop = icalcomponent_get_first_property (icalcomp, ICAL_RECURRENCEID_PROPERTY);
- if (prop)
- icalcomponent_remove_property (icalcomp, prop);
- e_cal_component_rescan (comp);
-
- master_dtstart = icalcomponent_get_dtstart (master_comp);
- if (master_dtstart.zone && master_dtstart.zone != rid.zone)
- rid = icaltime_convert_to_zone (rid, (icaltimezone *) master_dtstart.zone);
- split_icalcomp = e_cal_util_split_at_instance (icalcomp, rid, master_dtstart);
- if (split_icalcomp) {
- ECalComponent *prev_comp;
-
- prev_comp = e_cal_component_clone (mcomp);
+ success = e_webdav_session_propfind_sync (cbdav->priv->webdav, owner_href, E_WEBDAV_DEPTH_THIS, xml,
+ ecb_caldav_propfind_get_schedule_outbox_url_cb, &schedule_outbox_url, cancellable, error);
- rid = icaltime_convert_to_zone (rid, icaltimezone_get_utc_timezone ());
- e_cal_util_remove_instances (master_comp, rid, mod);
- e_cal_component_rescan (mcomp);
- e_cal_recur_ensure_end_dates (mcomp, TRUE, resolve_tzid, cbdav);
+ g_object_unref (xml);
+ g_free (owner_href);
- if (new_components) {
- *new_components = g_slist_prepend (*new_components,
- get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid,
NULL, mcomp));
- }
-
- g_clear_object (&prev_comp);
- }
-
- cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(master_comp));
- if (split_icalcomp) {
- gchar *new_uid;
-
- new_uid = e_cal_component_gen_uid ();
- icalcomponent_set_uid (split_icalcomp, new_uid);
- g_free (new_uid);
-
- g_warn_if_fail (e_cal_component_set_icalcomponent (comp, split_icalcomp));
-
- e_cal_recur_ensure_end_dates (comp, TRUE, resolve_tzid, cbdav);
-
- /* sanitize the component */
- sanitize_component (cbdav, comp);
-
- if (online) {
- CalDAVObject object;
-
- object.href = ecalcomp_gen_href (comp);
- object.etag = NULL;
- object.cdata = pack_cobj (cbdav, split_icalcomp);
-
- success = caldav_server_put_object (cbdav, &object, split_icalcomp,
cancellable, error);
- if (success && new_components) {
- ECalComponent *new_comp;
-
- /* read the comp from cache again, as some servers can modify
it on put */
- new_comp = get_ecalcomp_master_from_cache_or_fallback (cbdav,
icalcomponent_get_uid (split_icalcomp), NULL, comp);
- if (new_comp)
- e_cal_backend_notify_component_created (E_CAL_BACKEND
(cbdav), new_comp);
-
- g_clear_object (&new_comp);
- }
-
- caldav_object_free (&object, FALSE);
- } else {
- /* mark component as out of synch */
- /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_CREATED); */
- }
- }
+ if (success && schedule_outbox_url && *schedule_outbox_url) {
+ g_free (cbdav->priv->schedule_outbox_url);
+ cbdav->priv->schedule_outbox_url = schedule_outbox_url;
- g_clear_object (&mcomp);
- } else {
- cache_comp = replace_master (cbdav, cache_comp, icalcomponent_new_clone
(e_cal_component_get_icalcomponent (comp)));
- }
- break;
- }
-
- if (online) {
- CalDAVObject object;
-
- object.href = href;
- object.etag = etag;
- object.cdata = pack_cobj (cbdav, cache_comp);
-
- did_put = success && caldav_server_put_object (cbdav, &object, cache_comp, cancellable,
error);
-
- caldav_object_free (&object, FALSE);
- href = NULL;
- etag = NULL;
+ schedule_outbox_url = NULL;
} else {
- /* mark component as out of synch */
- /*ecalcomp_set_synch_state (comp, ECALCOMP_LOCALLY_MODIFIED);*/
- }
-
- if (did_put) {
- if (new_components && !*new_components) {
- /* read the comp from cache again, as some servers can modify it on put */
- *new_components = g_slist_prepend (*new_components,
get_ecalcomp_master_from_cache_or_fallback (cbdav, id->uid, id->rid, NULL));
- }
- }
-
- e_cal_component_free_id (id);
- icalcomponent_free (cache_comp);
- g_object_unref (comp);
- g_free (href);
- g_free (etag);
-}
-
-/* a busy_lock is supposed to be locked already, when calling this function */
-static void
-do_remove_objects (ECalBackendCalDAV *cbdav,
- const GSList *ids,
- ECalObjModType mod,
- GSList **old_components,
- GSList **new_components,
- GCancellable *cancellable,
- GError **perror)
-{
- icalcomponent *cache_comp;
- gboolean online;
- gchar *href = NULL, *etag = NULL;
- const gchar *uid = ((ECalComponentId *) ids->data)->uid;
- const gchar *rid = ((ECalComponentId *) ids->data)->rid;
-
- if (new_components)
- *new_components = NULL;
-
- if (!check_state (cbdav, &online, perror))
- return;
-
- /* We make the assumption that the ids list we're passed is always exactly one element long, since we
haven't specified "bulk-removes"
- * in our static capability list. This simplifies a lot of the logic, especially around asynchronous
results. */
- if (ids->next != NULL) {
- g_propagate_error (perror, e_data_cal_create_error (UnsupportedMethod, _("CalDAV does not
support bulk removals")));
- return;
- }
-
- cache_comp = get_comp_from_cache (cbdav, uid, NULL, &href, &etag);
-
- if (cache_comp == NULL) {
- g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
- return;
- }
-
- if (old_components) {
- ECalComponent *old = e_cal_backend_store_get_component (cbdav->priv->store, uid, rid);
-
- if (old) {
- *old_components = g_slist_prepend (*old_components, e_cal_component_clone (old));
- g_object_unref (old);
- } else {
- icalcomponent *master = get_master_comp (cbdav, cache_comp);
- if (master) {
- *old_components = g_slist_prepend (*old_components,
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
- }
- }
- }
-
- switch (mod) {
- case E_CAL_OBJ_MOD_ONLY_THIS:
- case E_CAL_OBJ_MOD_THIS:
- if (rid && *rid) {
- /* remove one instance from the component */
- if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, mod !=
E_CAL_OBJ_MOD_ONLY_THIS)) {
- if (new_components) {
- icalcomponent *master = get_master_comp (cbdav, cache_comp);
- if (master) {
- *new_components = g_slist_prepend (*new_components,
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
- }
- }
- } else {
- /* this was the last instance, thus delete whole component */
- rid = NULL;
- remove_comp_from_cache (cbdav, uid, NULL);
- }
- } else {
- /* remove whole object */
- remove_comp_from_cache (cbdav, uid, NULL);
- }
- break;
- case E_CAL_OBJ_MOD_ALL:
- remove_comp_from_cache (cbdav, uid, NULL);
- break;
- case E_CAL_OBJ_MOD_THIS_AND_PRIOR:
- case E_CAL_OBJ_MOD_THIS_AND_FUTURE:
- if (remove_instance (cbdav, cache_comp, icaltime_from_string (rid), mod, TRUE)) {
- if (new_components) {
- icalcomponent *master = get_master_comp (cbdav, cache_comp);
- if (master) {
- *new_components = g_slist_prepend (*new_components,
e_cal_component_new_from_icalcomponent (icalcomponent_new_clone (master)));
- }
- }
- }
- break;
+ success = FALSE;
}
- if (online) {
- CalDAVObject caldav_object;
-
- caldav_object.href = href;
- caldav_object.etag = etag;
- caldav_object.cdata = NULL;
-
- if (mod == E_CAL_OBJ_MOD_THIS && rid && *rid) {
- caldav_object.cdata = pack_cobj (cbdav, cache_comp);
-
- caldav_server_put_object (cbdav, &caldav_object, cache_comp, cancellable, perror);
- } else
- caldav_server_delete_object (cbdav, &caldav_object, cancellable, perror);
-
- caldav_object_free (&caldav_object, FALSE);
- href = NULL;
- etag = NULL;
- } else {
- /* mark component as out of synch */
- /*if (mod == E_CAL_OBJ_MOD_THIS && rid && *rid)
- ecalcomp_set_synch_state (cache_comp_master, ECALCOMP_LOCALLY_MODIFIED);
- else
- ecalcomp_set_synch_state (cache_comp_master, ECALCOMP_LOCALLY_DELETED);*/
- }
- remove_cached_attachment (cbdav, uid);
+ g_free (schedule_outbox_url);
- icalcomponent_free (cache_comp);
- g_free (href);
- g_free (etag);
+ return success;
}
static void
-extract_objects (icalcomponent *icomp,
- icalcomponent_kind ekind,
- GSList **objects,
- GError **error)
+ecb_caldav_extract_objects (icalcomponent *icomp,
+ icalcomponent_kind ekind,
+ GSList **out_objects,
+ GError **error)
{
- icalcomponent *scomp;
- icalcomponent_kind kind;
+ icalcomponent *scomp;
+ icalcomponent_kind kind;
GSList *link;
+ g_return_if_fail (icomp != NULL);
+ g_return_if_fail (out_objects != NULL);
+
kind = icalcomponent_isa (icomp);
if (kind == ekind) {
- *objects = g_slist_prepend (NULL, icomp);
+ *out_objects = g_slist_prepend (NULL, icalcomponent_new_clone (icomp));
return;
}
@@ -4634,648 +1252,107 @@ extract_objects (icalcomponent *icomp,
return;
}
- *objects = NULL;
+ *out_objects = NULL;
scomp = icalcomponent_get_first_component (icomp, ekind);
while (scomp) {
- *objects = g_slist_prepend (*objects, scomp);
+ *out_objects = g_slist_prepend (*out_objects, scomp);
scomp = icalcomponent_get_next_component (icomp, ekind);
}
- for (link = *objects; link; link = g_slist_next (link)) {
+ for (link = *out_objects; link; link = g_slist_next (link)) {
/* Remove components from toplevel here */
icalcomponent_remove_component (icomp, link->data);
}
-}
-static gboolean
-extract_timezones (ECalBackendCalDAV *cbdav,
- icalcomponent *icomp)
-{
- ETimezoneCache *timezone_cache;
- GSList *timezones = NULL, *iter;
- icaltimezone *zone;
- GError *err = NULL;
-
- g_return_val_if_fail (cbdav != NULL, FALSE);
- g_return_val_if_fail (icomp != NULL, FALSE);
-
- timezone_cache = E_TIMEZONE_CACHE (cbdav);
-
- extract_objects (icomp, ICAL_VTIMEZONE_COMPONENT, &timezones, &err);
- if (err) {
- g_error_free (err);
- return FALSE;
- }
-
- zone = icaltimezone_new ();
- for (iter = timezones; iter; iter = iter->next) {
- if (icaltimezone_set_component (zone, iter->data)) {
- e_timezone_cache_add_timezone (timezone_cache, zone);
- } else {
- icalcomponent_free (iter->data);
- }
- }
-
- icaltimezone_free (zone, TRUE);
- g_slist_free (timezones);
-
- return TRUE;
+ *out_objects = g_slist_reverse (*out_objects);
}
-static void
-process_object (ECalBackendCalDAV *cbdav,
- ECalComponent *ecomp,
- gboolean online,
- icalproperty_method method,
- GCancellable *cancellable,
- GError **error)
+static gchar *
+ecb_caldav_maybe_append_email_domain (const gchar *username,
+ const gchar *may_append)
{
- ESourceRegistry *registry;
- ECalBackend *backend;
- struct icaltimetype now;
- gchar *new_obj_str;
- gboolean is_declined, is_in_cache;
- ECalObjModType mod;
- ECalComponentId *id = e_cal_component_get_id (ecomp);
- GError *err = NULL;
-
- backend = E_CAL_BACKEND (cbdav);
-
- if (id == NULL) {
- g_set_error_literal (
- error, E_CAL_CLIENT_ERROR,
- E_CAL_CLIENT_ERROR_INVALID_OBJECT,
- e_cal_client_error_to_string (
- E_CAL_CLIENT_ERROR_INVALID_OBJECT));
- return;
- }
-
- registry = e_cal_backend_get_registry (E_CAL_BACKEND (cbdav));
-
- /* ctime, mtime */
- now = icaltime_current_time_with_zone (icaltimezone_get_utc_timezone ());
- e_cal_component_set_created (ecomp, &now);
- e_cal_component_set_last_modified (ecomp, &now);
-
- /* just to check whether component exists in a cache */
- is_in_cache = cache_contains (cbdav, id->uid, NULL) || cache_contains (cbdav, id->uid, id->rid);
-
- new_obj_str = e_cal_component_get_as_string (ecomp);
- mod = e_cal_component_is_instance (ecomp) ? E_CAL_OBJ_MOD_THIS : E_CAL_OBJ_MOD_ALL;
-
- switch (method) {
- case ICAL_METHOD_PUBLISH:
- case ICAL_METHOD_REQUEST:
- case ICAL_METHOD_REPLY:
- is_declined = e_cal_backend_user_declined (
- registry, e_cal_component_get_icalcomponent (ecomp));
- if (is_in_cache) {
- if (!is_declined) {
- GSList *new_components = NULL, *old_components = NULL;
- GSList new_obj_strs = {0,};
-
- new_obj_strs.data = new_obj_str;
- do_modify_objects (cbdav, &new_obj_strs, mod,
- &old_components, &new_components, cancellable, &err);
- if (!err && new_components && new_components->data) {
- if (!old_components || !old_components->data) {
- e_cal_backend_notify_component_created (backend,
new_components->data);
- } else {
- e_cal_backend_notify_component_modified (backend,
old_components->data, new_components->data);
- }
- }
-
- e_util_free_nullable_object_slist (old_components);
- e_util_free_nullable_object_slist (new_components);
- } else {
- GSList *new_components = NULL, *old_components = NULL;
- GSList ids = {0,};
-
- ids.data = id;
- do_remove_objects (cbdav, &ids, mod, &old_components, &new_components,
cancellable, &err);
- if (!err && old_components && old_components->data) {
- if (new_components && new_components->data) {
- e_cal_backend_notify_component_modified (backend,
old_components->data, new_components->data);
- } else {
- e_cal_backend_notify_component_removed (backend, id,
old_components->data, NULL);
- }
- }
-
- e_util_free_nullable_object_slist (old_components);
- e_util_free_nullable_object_slist (new_components);
- }
- } else if (!is_declined) {
- GSList *new_components = NULL;
- GSList new_objs = {0,};
-
- new_objs.data = new_obj_str;
-
- do_create_objects (cbdav, &new_objs, NULL, &new_components, cancellable, &err);
-
- if (!err) {
- if (new_components && new_components->data)
- e_cal_backend_notify_component_created (backend,
new_components->data);
- }
-
- e_util_free_nullable_object_slist (new_components);
- }
- break;
- case ICAL_METHOD_CANCEL:
- if (is_in_cache) {
- GSList *new_components = NULL, *old_components = NULL;
- GSList ids = {0,};
-
- ids.data = id;
- do_remove_objects (cbdav, &ids, E_CAL_OBJ_MOD_THIS, &old_components, &new_components,
cancellable, &err);
- if (!err && old_components && old_components->data) {
- if (new_components && new_components->data) {
- e_cal_backend_notify_component_modified (backend,
old_components->data, new_components->data);
- } else {
- e_cal_backend_notify_component_removed (backend, id,
old_components->data, NULL);
- }
- }
-
- e_util_free_nullable_object_slist (old_components);
- e_util_free_nullable_object_slist (new_components);
- } else {
- err = EDC_ERROR (ObjectNotFound);
- }
- break;
-
- default:
- err = EDC_ERROR (UnsupportedMethod);
- break;
- }
+ if (!username || !*username)
+ return NULL;
- e_cal_component_free_id (id);
- g_free (new_obj_str);
+ if (strchr (username, '@'))
+ return g_strdup (username);
- if (err)
- g_propagate_error (error, err);
+ return g_strconcat (username, may_append, NULL);
}
-static void
-do_receive_objects (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *calobj,
- GError **perror)
+static gchar *
+ecb_caldav_get_usermail (ECalBackendCalDAV *cbdav)
{
- ECalBackendCalDAV *cbdav;
- icalcomponent *icomp;
- icalcomponent_kind kind;
- icalproperty_method tmethod;
- gboolean online;
- GSList *objects, *iter;
- GError *err = NULL;
-
- cbdav = E_CAL_BACKEND_CALDAV (backend);
-
- if (!check_state (cbdav, &online, perror))
- return;
-
- icomp = icalparser_parse_string (calobj);
-
- /* Try to parse cal object string */
- if (icomp == NULL) {
- g_propagate_error (perror, EDC_ERROR (InvalidObject));
- return;
- }
-
- kind = e_cal_backend_get_kind (E_CAL_BACKEND (backend));
- extract_objects (icomp, kind, &objects, &err);
-
- if (err) {
- icalcomponent_free (icomp);
- g_propagate_error (perror, err);
- return;
- }
-
- /* Extract optional timezone compnents */
- extract_timezones (cbdav, icomp);
-
- if (icalcomponent_get_first_property (icomp, ICAL_METHOD_PROPERTY))
- tmethod = icalcomponent_get_method (icomp);
- else
- tmethod = ICAL_METHOD_PUBLISH;
-
- for (iter = objects; iter && !err; iter = iter->next) {
- icalcomponent *scomp;
- ECalComponent *ecomp;
- icalproperty_method method;
-
- scomp = (icalcomponent *) iter->data;
- ecomp = e_cal_component_new ();
+ ESource *source;
+ ESourceAuthentication *auth_extension;
+ ESourceWebdav *webdav_extension;
+ const gchar *extension_name;
+ gchar *usermail;
+ gchar *username;
+ gchar *res = NULL;
- e_cal_component_set_icalcomponent (ecomp, scomp);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), NULL);
- if (icalcomponent_get_first_property (scomp, ICAL_METHOD_PROPERTY)) {
- method = icalcomponent_get_method (scomp);
- } else {
- method = tmethod;
- }
+ source = e_backend_get_source (E_BACKEND (cbdav));
- process_object (cbdav, ecomp, online, method, cancellable, &err);
- g_object_unref (ecomp);
- }
+ extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+ webdav_extension = e_source_get_extension (source, extension_name);
- g_slist_free (objects);
+ /* This will never return an empty string. */
+ usermail = e_source_webdav_dup_email_address (webdav_extension);
- icalcomponent_free (icomp);
+ if (usermail)
+ return usermail;
- if (err)
- g_propagate_error (perror, err);
-}
+ extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
+ auth_extension = e_source_get_extension (source, extension_name);
+ username = e_source_authentication_dup_user (auth_extension);
-#define caldav_busy_stub(_func_name, _params, _call_func, _call_params) \
-static void \
-_func_name _params \
-{ \
- ECalBackendCalDAV *cbdav; \
- SlaveCommand old_slave_cmd; \
- gboolean was_slave_busy; \
- \
- cbdav = E_CAL_BACKEND_CALDAV (backend); \
- \
- /* this is done before locking */ \
- old_slave_cmd = cbdav->priv->slave_cmd; \
- was_slave_busy = cbdav->priv->slave_busy; \
- if (was_slave_busy) { \
- /* let it pause its work and do our job */ \
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP); \
- } \
- \
- g_mutex_lock (&cbdav->priv->busy_lock); \
- _call_func _call_params; \
- \
- /* this is done before unlocking */ \
- if (was_slave_busy) { \
- update_slave_cmd (cbdav->priv, old_slave_cmd); \
- g_cond_signal (&cbdav->priv->cond); \
- } \
- \
- g_mutex_unlock (&cbdav->priv->busy_lock); \
-}
+ if (cbdav->priv->is_google)
+ res = ecb_caldav_maybe_append_email_domain (username, "@gmail.com");
-caldav_busy_stub (
- caldav_create_objects,
- (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const GSList *in_calobjs,
- GSList **uids,
- GSList **new_components,
- GError **perror),
- do_create_objects,
- (cbdav,
- in_calobjs,
- uids,
- new_components,
- cancellable,
- perror))
-
-caldav_busy_stub (
- caldav_modify_objects,
- (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const GSList *calobjs,
- ECalObjModType mod,
- GSList **old_components,
- GSList **new_components,
- GError **perror),
- do_modify_objects,
- (cbdav,
- calobjs,
- mod,
- old_components,
- new_components,
- cancellable,
- perror))
-
-caldav_busy_stub (
- caldav_remove_objects,
- (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const GSList *ids,
- ECalObjModType mod,
- GSList **old_components,
- GSList **new_components,
- GError **perror),
- do_remove_objects,
- (cbdav,
- ids,
- mod,
- old_components,
- new_components,
- cancellable,
- perror))
-
-caldav_busy_stub (
- caldav_receive_objects,
- (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *calobj,
- GError **perror),
- do_receive_objects,
- (backend,
- cal,
- cancellable,
- calobj,
- perror))
+ g_free (username);
-static void
-caldav_send_objects (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *calobj,
- GSList **users,
- gchar **modified_calobj,
- GError **perror)
-{
- *users = NULL;
- *modified_calobj = g_strdup (calobj);
+ return res;
}
static gboolean
-caldav_server_download_uid (ECalBackendCalDAV *cbdav,
- const gchar *uid,
- GCancellable *cancellable,
- GError **error)
-{
- CalDAVObject obj;
- GError *local_error = NULL;
-
- g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- g_return_val_if_fail (uid != NULL, FALSE);
-
- obj.href = g_strdup (uid);
- obj.etag = NULL;
- obj.status = 0;
- obj.cdata = NULL;
-
- if (!caldav_server_get_object (cbdav, &obj, cancellable, &local_error)) {
- if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
- gchar *file;
-
- /* OK, the event was properly created, but cannot be found on the place
- * where it was PUT - why didn't server tell us where it saved it? */
- g_clear_error (&local_error);
-
- /* try whether it's saved as its UID.ics file */
- file = caldav_gen_file_from_uid (cbdav, uid);
- if (file) {
- g_free (obj.href);
- obj.href = file;
-
- if (!caldav_server_get_object (cbdav, &obj, cancellable, &local_error)) {
- }
- }
- }
- }
-
- if (!local_error) {
- icalcomponent *use_comp = NULL;
-
- if (obj.cdata) {
- /* maybe server also modified component, thus rather store the server's */
- use_comp = icalparser_parse_string (obj.cdata);
- put_comp_to_cache (cbdav, use_comp, obj.href, obj.etag);
- }
-
- if (use_comp)
- icalcomponent_free (use_comp);
- else
- local_error = EDC_ERROR (ObjectNotFound);
- }
-
- if (local_error) {
- g_propagate_error (error, local_error);
-
- return FALSE;
- }
-
- g_free (obj.href);
- g_free (obj.etag);
- g_free (obj.cdata);
-
- return TRUE;
-}
-
-static void
-caldav_get_object (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *uid,
- const gchar *rid,
- gchar **object,
- GError **perror)
-{
- ECalBackendCalDAV *cbdav;
- icalcomponent *icalcomp;
-
- cbdav = E_CAL_BACKEND_CALDAV (backend);
-
- *object = NULL;
- icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
-
- if (!icalcomp && e_backend_get_online (E_BACKEND (backend))) {
- /* try to fetch from the server, maybe the event was received only recently */
- if (caldav_server_download_uid (cbdav, uid, cancellable, NULL)) {
- icalcomp = get_comp_from_cache (cbdav, uid, rid, NULL, NULL);
- }
- }
-
- if (!icalcomp) {
- g_propagate_error (perror, EDC_ERROR (ObjectNotFound));
- return;
- }
-
- *object = icalcomponent_as_ical_string_r (icalcomp);
- icalcomponent_free (icalcomp);
-}
-
-static void
-caldav_add_timezone (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *tzobj,
- GError **error)
+ecb_caldav_get_free_busy_from_schedule_outbox_sync (ECalBackendCalDAV *cbdav,
+ const GSList *users,
+ time_t start,
+ time_t end,
+ GSList **out_freebusy,
+ GCancellable *cancellable,
+ GError **error)
{
- ETimezoneCache *timezone_cache;
- icalcomponent *tz_comp;
-
- timezone_cache = E_TIMEZONE_CACHE (backend);
-
- tz_comp = icalparser_parse_string (tzobj);
- if (!tz_comp) {
- g_propagate_error (error, EDC_ERROR (InvalidObject));
- return;
- }
-
- if (icalcomponent_isa (tz_comp) == ICAL_VTIMEZONE_COMPONENT) {
- icaltimezone *zone;
-
- zone = icaltimezone_new ();
- icaltimezone_set_component (zone, tz_comp);
-
- e_timezone_cache_add_timezone (timezone_cache, zone);
-
- icaltimezone_free (zone, TRUE);
- } else {
- icalcomponent_free (tz_comp);
- }
-}
-
-static void
-caldav_get_object_list (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const gchar *sexp_string,
- GSList **objects,
- GError **perror)
-{
- ECalBackendCalDAV *cbdav;
- ECalBackendSExp *sexp;
- ETimezoneCache *cache;
- gboolean do_search;
- GSList *list, *iter;
- time_t occur_start = -1, occur_end = -1;
- gboolean prunning_by_time;
-
- cbdav = E_CAL_BACKEND_CALDAV (backend);
-
- sexp = e_cal_backend_sexp_new (sexp_string);
-
- if (sexp == NULL) {
- g_propagate_error (perror, EDC_ERROR (InvalidQuery));
- return;
- }
-
- if (g_str_equal (sexp_string, "#t")) {
- do_search = FALSE;
- } else {
- do_search = TRUE;
- }
-
- *objects = NULL;
-
- prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (sexp, &occur_start, &occur_end);
-
- cache = E_TIMEZONE_CACHE (backend);
-
- list = prunning_by_time ?
- e_cal_backend_store_get_components_occuring_in_range (cbdav->priv->store, occur_start,
occur_end)
- : e_cal_backend_store_get_components (cbdav->priv->store);
-
- for (iter = list; iter; iter = g_slist_next (iter)) {
- ECalComponent *comp = E_CAL_COMPONENT (iter->data);
-
- if (!do_search ||
- e_cal_backend_sexp_match_comp (sexp, comp, cache)) {
- *objects = g_slist_prepend (*objects, e_cal_component_get_as_string (comp));
- }
-
- g_object_unref (comp);
- }
-
- g_object_unref (sexp);
- g_slist_free (list);
-}
-
-static void
-caldav_start_view (ECalBackend *backend,
- EDataCalView *query)
-{
- ECalBackendCalDAV *cbdav;
- ECalBackendSExp *sexp;
- ETimezoneCache *cache;
- gboolean do_search;
- GSList *list, *iter;
- const gchar *sexp_string;
- time_t occur_start = -1, occur_end = -1;
- gboolean prunning_by_time;
- cbdav = E_CAL_BACKEND_CALDAV (backend);
-
- sexp = e_data_cal_view_get_sexp (query);
- sexp_string = e_cal_backend_sexp_text (sexp);
-
- if (g_str_equal (sexp_string, "#t")) {
- do_search = FALSE;
- } else {
- do_search = TRUE;
- }
- prunning_by_time = e_cal_backend_sexp_evaluate_occur_times (
- sexp,
- &occur_start,
- &occur_end);
-
- cache = E_TIMEZONE_CACHE (backend);
-
- list = prunning_by_time ?
- e_cal_backend_store_get_components_occuring_in_range (cbdav->priv->store, occur_start,
occur_end)
- : e_cal_backend_store_get_components (cbdav->priv->store);
-
- for (iter = list; iter; iter = g_slist_next (iter)) {
- ECalComponent *comp = E_CAL_COMPONENT (iter->data);
-
- if (!do_search ||
- e_cal_backend_sexp_match_comp (sexp, comp, cache)) {
- e_data_cal_view_notify_components_added_1 (query, comp);
- }
-
- g_object_unref (comp);
- }
-
- g_slist_free (list);
-
- e_data_cal_view_notify_complete (query, NULL /* Success */);
-}
-
-static void
-caldav_get_free_busy (ECalBackendSync *backend,
- EDataCal *cal,
- GCancellable *cancellable,
- const GSList *users,
- time_t start,
- time_t end,
- GSList **freebusy,
- GError **error)
-{
- ECalBackendCalDAV *cbdav;
icalcomponent *icalcomp;
ECalComponent *comp;
ECalComponentDateTime dt;
- ECalComponentOrganizer organizer = {NULL};
+ ECalComponentOrganizer organizer = { NULL };
ESourceAuthentication *auth_extension;
ESource *source;
struct icaltimetype dtvalue;
icaltimezone *utc;
gchar *str;
- const GSList *u;
+ GSList *link;
GSList *attendees = NULL, *to_free = NULL;
const gchar *extension_name;
gchar *usermail;
- GError *err = NULL;
+ GByteArray *response = NULL;
+ GError *local_error = NULL;
- cbdav = E_CAL_BACKEND_CALDAV (backend);
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
- if (!cbdav->priv->calendar_schedule) {
- g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Calendar doesnāt support Free/Busy")));
- return;
- }
+ if (!cbdav->priv->calendar_schedule)
+ return FALSE;
if (!cbdav->priv->schedule_outbox_url) {
- caldav_receive_schedule_outbox_url (cbdav, cancellable, error);
- if (!cbdav->priv->schedule_outbox_url) {
+ if (!ecb_caldav_receive_schedule_outbox_url_sync (cbdav, cancellable, error) ||
+ !cbdav->priv->schedule_outbox_url) {
cbdav->priv->calendar_schedule = FALSE;
- if (error && !*error)
- g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Schedule outbox url
not found")));
- return;
+ return FALSE;
}
}
@@ -5299,17 +1376,17 @@ caldav_get_free_busy (ECalBackendSync *backend,
dtvalue = icaltime_from_timet_with_zone (end, FALSE, utc);
e_cal_component_set_dtend (comp, &dt);
- usermail = get_usermail (E_CAL_BACKEND (backend));
+ usermail = ecb_caldav_get_usermail (cbdav);
if (usermail != NULL && *usermail == '\0') {
g_free (usermail);
usermail = NULL;
}
- source = e_backend_get_source (E_BACKEND (backend));
+ source = e_backend_get_source (E_BACKEND (cbdav));
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
auth_extension = e_source_get_extension (source, extension_name);
- if (usermail == NULL)
+ if (!usermail)
usermail = e_source_authentication_dup_user (auth_extension);
organizer.value = g_strconcat ("mailto:", usermail, NULL);
@@ -5318,9 +1395,9 @@ caldav_get_free_busy (ECalBackendSync *backend,
g_free (usermail);
- for (u = users; u; u = u->next) {
+ for (link = (GSList *) users; link; link = g_slist_next (link)) {
ECalComponentAttendee *ca;
- gchar *temp = g_strconcat ("mailto:", (const gchar *) u->data, NULL);
+ gchar *temp = g_strconcat ("mailto:", (const gchar *) link->data, NULL);
ca = g_new0 (ECalComponentAttendee, 1);
@@ -5335,11 +1412,8 @@ caldav_get_free_busy (ECalBackendSync *backend,
e_cal_component_set_attendee_list (comp, attendees);
- g_slist_foreach (attendees, (GFunc) g_free, NULL);
- g_slist_free (attendees);
-
- g_slist_foreach (to_free, (GFunc) g_free, NULL);
- g_slist_free (to_free);
+ g_slist_free_full (attendees, g_free);
+ g_slist_free_full (to_free, g_free);
e_cal_component_abort_sequence (comp);
@@ -5353,387 +1427,361 @@ caldav_get_free_busy (ECalBackendSync *backend,
icalcomponent_free (icalcomp);
g_object_unref (comp);
- caldav_post_freebusy (cbdav, cbdav->priv->schedule_outbox_url, &str, cancellable, &err);
-
- if (!err) {
+ if (e_webdav_session_post_sync (cbdav->priv->webdav, cbdav->priv->schedule_outbox_url, str, -1, NULL,
&response, cancellable, &local_error) &&
+ response) {
/* parse returned xml */
xmlDocPtr doc;
+ xmlXPathContextPtr xpath_ctx = NULL;
+
+ doc = e_xml_parse_data (response->data, response->len);
+
+ if (!doc) {
+ g_set_error_literal (&local_error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
+ _("Failed to parse response data"));
+ } else {
+ xpath_ctx = e_xml_new_xpath_context_with_namespaces (doc,
+ "D", E_WEBDAV_NS_DAV,
+ "C", E_WEBDAV_NS_CALDAV,
+ NULL);
+ }
- doc = xmlReadMemory (str, strlen (str), "response.xml", NULL, 0);
- if (doc != NULL) {
- xmlXPathContextPtr xpctx;
- xmlXPathObjectPtr result;
+ if (xpath_ctx) {
+ xmlXPathObjectPtr xpath_obj_response;
- xpctx = xmlXPathNewContext (doc);
- xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
- xmlXPathRegisterNs (xpctx, (xmlChar *) "C", (xmlChar *)
"urn:ietf:params:xml:ns:caldav");
+ xpath_obj_response = e_xml_xpath_eval (xpath_ctx, "/C:schedule-response/C:response");
- result = xpath_eval (xpctx, "/C:schedule-response/C:response");
+ if (xpath_obj_response) {
+ gint response_index, response_length;
- if (result == NULL || result->type != XPATH_NODESET) {
- err = EDC_ERROR_EX (OtherError, _("Unexpected result in schedule-response"));
- } else {
- gint i, n;
+ response_length = xmlXPathNodeSetGetLength (xpath_obj_response->nodesetval);
- n = xmlXPathNodeSetGetLength (result->nodesetval);
- for (i = 0; i < n; i++) {
+ for (response_index = 0; response_index < response_length; response_index++) {
gchar *tmp;
- tmp = xp_object_get_string (xpath_eval (xpctx,
"string(/C:schedule-response/C:response[%d]/C:calendar-data)", i + 1));
+ tmp = e_xml_xpath_eval_as_string
(xpath_ctx,"/C:schedule-response/C:response[%d]/C:calendar-data", response_index + 1);
if (tmp && *tmp) {
- GSList *objects = NULL, *o;
+ GSList *objects = NULL;
icalcomp = icalparser_parse_string (tmp);
if (icalcomp)
- extract_objects (icalcomp, ICAL_VFREEBUSY_COMPONENT,
&objects, &err);
- if (icalcomp && !err) {
- for (o = objects; o; o = o->next) {
- gchar *obj_str =
icalcomponent_as_ical_string_r (o->data);
+ ecb_caldav_extract_objects (icalcomp,
ICAL_VFREEBUSY_COMPONENT, &objects, &local_error);
+ if (icalcomp && !local_error) {
+ for (link = objects; link; link = g_slist_next
(link)) {
+ gchar *obj_str =
icalcomponent_as_ical_string_r (link->data);
if (obj_str && *obj_str)
- *freebusy = g_slist_append
(*freebusy, obj_str);
+ *out_freebusy = g_slist_prepend
(*out_freebusy, obj_str);
else
g_free (obj_str);
}
}
- g_slist_foreach (objects, (GFunc) icalcomponent_free, NULL);
- g_slist_free (objects);
+ g_slist_free_full (objects, (GDestroyNotify)
icalcomponent_free);
if (icalcomp)
icalcomponent_free (icalcomp);
- if (err)
- g_error_free (err);
- err = NULL;
+ g_clear_error (&local_error);
}
g_free (tmp);
}
+
+ xmlXPathFreeObject (xpath_obj_response);
}
- if (result != NULL)
- xmlXPathFreeObject (result);
- xmlXPathFreeContext (xpctx);
- xmlFreeDoc (doc);
+ xmlXPathFreeContext (xpath_ctx);
}
+
+ if (doc)
+ xmlFreeDoc (doc);
}
+ if (response)
+ g_byte_array_free (response, TRUE);
g_free (str);
- if (err)
- g_propagate_error (error, err);
-}
+ if (local_error)
+ g_propagate_error (error, local_error);
-static void
-caldav_notify_online_cb (ECalBackend *backend,
- GParamSpec *pspec)
-{
- ECalBackendCalDAV *cbdav;
- gboolean online;
+ return local_error != NULL;
+}
- cbdav = E_CAL_BACKEND_CALDAV (backend);
+static gboolean
+ecb_caldav_get_free_busy_from_principal_sync (ECalBackendCalDAV *cbdav,
+ const gchar *usermail,
+ time_t start,
+ time_t end,
+ GSList **out_freebusy,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EWebDAVResource *resource;
+ GSList *principals = NULL;
+ EXmlDocument *xml;
+ gchar *href;
+ gchar *content_type = NULL;
+ GByteArray *content = NULL;
+ gboolean success;
- /*g_mutex_lock (&cbdav->priv->busy_lock);*/
+ g_return_val_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav), FALSE);
+ g_return_val_if_fail (usermail != NULL, FALSE);
+ g_return_val_if_fail (out_freebusy != NULL, FALSE);
- online = e_backend_get_online (E_BACKEND (backend));
+ if (!e_webdav_session_principal_property_search_sync (cbdav->priv->webdav, NULL, TRUE,
+ E_WEBDAV_NS_CALDAV, "calendar-user-address-set", usermail, &principals, cancellable, error)) {
+ return FALSE;
+ }
- if (!cbdav->priv->loaded) {
- /*g_mutex_unlock (&cbdav->priv->busy_lock);*/
- return;
+ if (!principals || principals->next || !principals->data) {
+ g_slist_free_full (principals, e_webdav_resource_free);
+ return FALSE;
}
- if (online) {
- /* Wake up the slave thread */
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
- } else {
- soup_session_abort (cbdav->priv->session);
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
+ resource = principals->data;
+ href = g_strdup (resource->href);
+
+ g_slist_free_full (principals, e_webdav_resource_free);
+
+ if (!href || !*href) {
+ g_free (href);
+ return FALSE;
}
- /*g_mutex_unlock (&cbdav->priv->busy_lock);*/
-}
+ xml = e_xml_document_new (E_WEBDAV_NS_CALDAV, "free-busy-query");
-static gpointer
-caldav_source_changed_thread (gpointer data)
-{
- ECalBackendCalDAV *cbdav = data;
- SlaveCommand old_slave_cmd;
- gboolean old_slave_busy;
+ e_xml_document_start_element (xml, NULL, "time-range");
+ e_xml_document_add_attribute_time (xml, NULL, "start", start);
+ e_xml_document_add_attribute_time (xml, NULL, "end", end);
+ e_xml_document_end_element (xml); /* time-range */
- g_return_val_if_fail (cbdav != NULL, NULL);
+ success = e_webdav_session_report_sync (cbdav->priv->webdav, NULL, E_WEBDAV_DEPTH_INFINITY, xml,
NULL, NULL, &content_type, &content, cancellable, error);
- old_slave_cmd = cbdav->priv->slave_cmd;
- old_slave_busy = cbdav->priv->slave_busy;
- if (old_slave_busy)
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_SLEEP);
+ g_object_unref (xml);
- g_mutex_lock (&cbdav->priv->busy_lock);
+ if (success && content_type && content && content->data && content->len &&
+ g_ascii_strcasecmp (content_type, "text/calendar") == 0) {
+ icalcomponent *vcalendar;
- /* guard the call with busy_lock, thus the two threads (this 'source changed'
- * thread and the 'backend open' thread) will not clash on internal data
- * when they are called in once */
- initialize_backend (cbdav, NULL);
+ vcalendar = icalcomponent_new_from_string ((const gchar *) content->data);
+ if (vcalendar) {
+ GSList *comps = NULL, *link;
- /* always wakeup thread, even when it was sleeping */
- g_cond_signal (&cbdav->priv->cond);
+ ecb_caldav_extract_objects (vcalendar, ICAL_VFREEBUSY_COMPONENT, &comps, NULL);
- if (old_slave_busy)
- update_slave_cmd (cbdav->priv, old_slave_cmd);
+ for (link = comps; link; link = g_slist_next (link)) {
+ icalcomponent *subcomp = link->data;
+ gchar *obj_str;
- g_mutex_unlock (&cbdav->priv->busy_lock);
+ if (!icalcomponent_get_first_property (subcomp, ICAL_ATTENDEE_PROPERTY)) {
+ icalproperty *prop;
+ gchar *mailto;
- cbdav->priv->updating_source = FALSE;
+ mailto = g_strconcat ("mailto:", usermail, NULL);
+ prop = icalproperty_new_attendee (mailto);
+ g_free (mailto);
- g_object_unref (cbdav);
+ icalcomponent_add_property (subcomp, prop);
+ }
- return NULL;
-}
+ obj_str = icalcomponent_as_ical_string_r (subcomp);
-static void
-caldav_source_changed_cb (ESource *source,
- ECalBackendCalDAV *cbdav)
-{
- GThread *thread;
+ if (obj_str && *obj_str)
+ *out_freebusy = g_slist_prepend (*out_freebusy, obj_str);
+ else
+ g_free (obj_str);
+ }
- g_return_if_fail (source != NULL);
- g_return_if_fail (cbdav != NULL);
+ success = comps != NULL;
- if (cbdav->priv->updating_source ||
- !cbdav->priv->loaded ||
- !e_cal_backend_is_opened (E_CAL_BACKEND (cbdav)))
- return;
+ g_slist_free_full (comps, (GDestroyNotify) icalcomponent_free);
+ } else {
+ success = FALSE;
+ }
+ }
- cbdav->priv->updating_source = TRUE;
+ if (content)
+ g_byte_array_free (content, TRUE);
+ g_free (content_type);
+ g_free (href);
- thread = g_thread_new (NULL, caldav_source_changed_thread, g_object_ref (cbdav));
- g_thread_unref (thread);
+ return success;
}
-static ESourceAuthenticationResult
-caldav_authenticate_sync (EBackend *backend,
- const ENamedParameters *credentials,
- gchar **out_certificate_pem,
- GTlsCertificateFlags *out_certificate_errors,
- GCancellable *cancellable,
- GError **error)
+static void
+ecb_caldav_get_free_busy_sync (ECalBackendSync *sync_backend,
+ EDataCal *cal,
+ GCancellable *cancellable,
+ const GSList *users,
+ time_t start,
+ time_t end,
+ GSList **out_freebusy,
+ GError **error)
{
ECalBackendCalDAV *cbdav;
- ESourceAuthenticationResult result;
- GError *local_error = NULL;
- cbdav = E_CAL_BACKEND_CALDAV (backend);
+ g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (sync_backend));
+ g_return_if_fail (out_freebusy != NULL);
- g_mutex_lock (&cbdav->priv->busy_lock);
+ cbdav = E_CAL_BACKEND_CALDAV (sync_backend);
- e_named_parameters_free (cbdav->priv->credentials);
- cbdav->priv->credentials = e_named_parameters_new_clone (credentials);
-
- open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL, out_certificate_pem,
out_certificate_errors);
-
- if (local_error == NULL) {
- result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+ if (e_backend_get_online (E_BACKEND (cbdav)) &&
+ cbdav->priv->webdav) {
+ const GSList *link;
+ GError *local_error = NULL;
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
- } else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed) ||
- g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired)) {
- const gchar *username;
- gchar *auth_user = NULL;
+ if (ecb_caldav_get_free_busy_from_schedule_outbox_sync (cbdav, users, start, end,
out_freebusy, cancellable, &local_error))
+ return;
- username = e_named_parameters_get (cbdav->priv->credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ g_clear_error (&local_error);
- if (!username || !*username) {
- ESource *source;
- ESourceAuthentication *auth_extension;
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
- source = e_backend_get_source (backend);
- auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
- auth_user = e_source_authentication_dup_user (auth_extension);
+ *out_freebusy = NULL;
- username = auth_user;
+ for (link = users; link && !g_cancellable_is_cancelled (cancellable); link = g_slist_next
(link)) {
+ if (!ecb_caldav_get_free_busy_from_principal_sync (cbdav, link->data, start, end,
out_freebusy, cancellable, &local_error))
+ g_clear_error (&local_error);
}
- if (username && *username) {
- if (!cbdav->priv->using_bearer_auth &&
- !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
- result = E_SOURCE_AUTHENTICATION_REQUIRED;
- else
- result = E_SOURCE_AUTHENTICATION_REJECTED;
- g_clear_error (&local_error);
- } else {
- result = E_SOURCE_AUTHENTICATION_ERROR;
- g_propagate_error (error, local_error);
- }
+ g_clear_error (&local_error);
- g_free (auth_user);
- } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
- result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
- g_propagate_error (error, local_error);
- } else {
- result = E_SOURCE_AUTHENTICATION_ERROR;
- g_propagate_error (error, local_error);
+ if (*out_freebusy || g_cancellable_set_error_if_cancelled (cancellable, error))
+ return;
}
- g_mutex_unlock (&cbdav->priv->busy_lock);
-
- return result;
+ /* Chain up to parent's method. */
+ E_CAL_BACKEND_SYNC_CLASS (e_cal_backend_caldav_parent_class)->get_free_busy_sync (sync_backend, cal,
cancellable,
+ users, start, end, out_freebusy, error);
}
-/* ************************************************************************* */
-/* ***************************** GObject Foo ******************************* */
-
-static void
-e_cal_backend_caldav_dispose (GObject *object)
+static gchar *
+ecb_caldav_get_backend_property (ECalBackend *backend,
+ const gchar *prop_name)
{
- ECalBackendCalDAVPrivate *priv;
-
- priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (object);
+ g_return_val_if_fail (prop_name != NULL, NULL);
- g_clear_object (&priv->store);
- g_clear_object (&priv->session);
- g_clear_object (&priv->using_bearer_auth);
+ if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+ ESourceWebdav *extension;
+ ESource *source;
+ GString *caps;
+ gchar *usermail;
+ const gchar *extension_name;
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (parent_class)->dispose (object);
-}
+ caps = g_string_new (
+ CAL_STATIC_CAPABILITY_NO_THISANDPRIOR ","
+ CAL_STATIC_CAPABILITY_REFRESH_SUPPORTED);
+ g_string_append (caps, ",");
+ g_string_append (caps, e_cal_meta_backend_get_capabilities (E_CAL_META_BACKEND (backend)));
-static void
-e_cal_backend_caldav_finalize (GObject *object)
-{
- ECalBackendCalDAVPrivate *priv;
+ usermail = ecb_caldav_get_usermail (E_CAL_BACKEND_CALDAV (backend));
+ if (!usermail || !*usermail)
+ g_string_append (caps, "," CAL_STATIC_CAPABILITY_NO_EMAIL_ALARMS);
+ g_free (usermail);
- priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (object);
+ source = e_backend_get_source (E_BACKEND (backend));
- g_mutex_clear (&priv->busy_lock);
- g_cond_clear (&priv->cond);
- g_cond_clear (&priv->slave_gone_cond);
+ extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+ extension = e_source_get_extension (source, extension_name);
- g_free (priv->uri);
- e_named_parameters_free (priv->credentials);
- priv->credentials = NULL;
- g_free (priv->schedule_outbox_url);
+ if (e_source_webdav_get_calendar_auto_schedule (extension)) {
+ g_string_append (
+ caps,
+ "," CAL_STATIC_CAPABILITY_CREATE_MESSAGES
+ "," CAL_STATIC_CAPABILITY_SAVE_SCHEDULES);
+ }
- if (priv->ctag_to_store) {
- g_free (priv->ctag_to_store);
- priv->ctag_to_store = NULL;
+ return g_string_free (caps, FALSE);
+ } else if (g_str_equal (prop_name, CAL_BACKEND_PROPERTY_CAL_EMAIL_ADDRESS) ||
+ g_str_equal (prop_name, CAL_BACKEND_PROPERTY_ALARM_EMAIL_ADDRESS)) {
+ return ecb_caldav_get_usermail (E_CAL_BACKEND_CALDAV (backend));
}
- g_clear_error (&priv->bearer_auth_error);
- g_mutex_clear (&priv->bearer_auth_error_lock);
-
- /* Chain up to parent's finalize() method. */
- G_OBJECT_CLASS (parent_class)->finalize (object);
+ /* Chain up to parent's method. */
+ return E_CAL_BACKEND_CLASS (e_cal_backend_caldav_parent_class)->get_backend_property (backend,
prop_name);
}
-static gboolean
-caldav_backend_initable_init (GInitable *initable,
- GCancellable *cancellable,
- GError **error)
+static gchar *
+ecb_caldav_dup_component_revision_cb (ECalCache *cal_cache,
+ icalcomponent *icalcomp)
{
- ECalBackendCalDAVPrivate *priv;
-
- priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (initable);
-
- g_mutex_init (&priv->bearer_auth_error_lock);
+ g_return_val_if_fail (icalcomp != NULL, NULL);
- return TRUE;
+ return e_cal_util_dup_x_property (icalcomp, E_CALDAV_X_ETAG);
}
static void
-e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
+e_cal_backend_caldav_constructed (GObject *object)
{
- cbdav->priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
- cbdav->priv->session = soup_session_sync_new ();
- g_object_set (
- cbdav->priv->session,
- SOUP_SESSION_TIMEOUT, 90,
- SOUP_SESSION_SSL_STRICT, TRUE,
- SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
- SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
- NULL);
+ ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
+ ECalCache *cal_cache;
- e_binding_bind_property (
- cbdav, "proxy-resolver",
- cbdav->priv->session, "proxy-resolver",
- G_BINDING_SYNC_CREATE);
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->constructed (object);
- if (G_UNLIKELY (caldav_debug_show (DEBUG_MESSAGE)))
- caldav_debug_setup (cbdav->priv->session);
+ cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cbdav));
- cbdav->priv->loaded = FALSE;
- cbdav->priv->opened = FALSE;
+ g_signal_connect (cal_cache, "dup-component-revision",
+ G_CALLBACK (ecb_caldav_dup_component_revision_cb), NULL);
- /* Thinks the 'getctag' extension is available the first time, but unset it when realizes it isn't. */
- cbdav->priv->ctag_supported = TRUE;
- cbdav->priv->ctag_to_store = NULL;
+ g_clear_object (&cal_cache);
+}
+
+static void
+e_cal_backend_caldav_dispose (GObject *object)
+{
+ ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
- cbdav->priv->schedule_outbox_url = NULL;
+ g_clear_object (&cbdav->priv->webdav);
- cbdav->priv->is_google = FALSE;
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->dispose (object);
+}
- g_mutex_init (&cbdav->priv->busy_lock);
- g_cond_init (&cbdav->priv->cond);
- g_cond_init (&cbdav->priv->slave_gone_cond);
+static void
+e_cal_backend_caldav_finalize (GObject *object)
+{
+ ECalBackendCalDAV *cbdav = E_CAL_BACKEND_CALDAV (object);
- /* Slave control ... */
- cbdav->priv->slave_cmd = SLAVE_SHOULD_SLEEP;
- cbdav->priv->slave_busy = FALSE;
+ g_clear_pointer (&cbdav->priv->schedule_outbox_url, g_free);
- g_signal_connect (
- cbdav->priv->session, "authenticate",
- G_CALLBACK (soup_authenticate), cbdav);
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->finalize (object);
+}
- g_signal_connect (
- cbdav, "notify::online",
- G_CALLBACK (caldav_notify_online_cb), NULL);
+static void
+e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
+{
+ cbdav->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbdav, E_TYPE_CAL_BACKEND_CALDAV,
ECalBackendCalDAVPrivate);
}
static void
-e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
+e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *klass)
{
GObjectClass *object_class;
- EBackendClass *backend_class;
ECalBackendClass *cal_backend_class;
- ECalBackendSyncClass *sync_class;
+ ECalBackendSyncClass *cal_backend_sync_class;
+ ECalMetaBackendClass *cal_meta_backend_class;
+
+ g_type_class_add_private (klass, sizeof (ECalBackendCalDAVPrivate));
- object_class = G_OBJECT_CLASS (class);
- backend_class = E_BACKEND_CLASS (class);
- cal_backend_class = E_CAL_BACKEND_CLASS (class);
- sync_class = E_CAL_BACKEND_SYNC_CLASS (class);
+ cal_meta_backend_class = E_CAL_META_BACKEND_CLASS (klass);
+ cal_meta_backend_class->connect_sync = ecb_caldav_connect_sync;
+ cal_meta_backend_class->disconnect_sync = ecb_caldav_disconnect_sync;
+ cal_meta_backend_class->get_changes_sync = ecb_caldav_get_changes_sync;
+ cal_meta_backend_class->list_existing_sync = ecb_caldav_list_existing_sync;
+ cal_meta_backend_class->load_component_sync = ecb_caldav_load_component_sync;
+ cal_meta_backend_class->save_component_sync = ecb_caldav_save_component_sync;
+ cal_meta_backend_class->remove_component_sync = ecb_caldav_remove_component_sync;
- caldav_debug_init ();
+ cal_backend_sync_class = E_CAL_BACKEND_SYNC_CLASS (klass);
+ cal_backend_sync_class->get_free_busy_sync = ecb_caldav_get_free_busy_sync;
- parent_class = (ECalBackendSyncClass *) g_type_class_peek_parent (class);
- g_type_class_add_private (class, sizeof (ECalBackendCalDAVPrivate));
+ cal_backend_class = E_CAL_BACKEND_CLASS (klass);
+ cal_backend_class->get_backend_property = ecb_caldav_get_backend_property;
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->constructed = e_cal_backend_caldav_constructed;
object_class->dispose = e_cal_backend_caldav_dispose;
object_class->finalize = e_cal_backend_caldav_finalize;
-
- backend_class->authenticate_sync = caldav_authenticate_sync;
-
- cal_backend_class->get_backend_property = caldav_get_backend_property;
- cal_backend_class->shutdown = caldav_shutdown;
- cal_backend_class->start_view = caldav_start_view;
-
- sync_class->open_sync = caldav_do_open;
- sync_class->refresh_sync = caldav_refresh;
-
- sync_class->create_objects_sync = caldav_create_objects;
- sync_class->modify_objects_sync = caldav_modify_objects;
- sync_class->remove_objects_sync = caldav_remove_objects;
-
- sync_class->receive_objects_sync = caldav_receive_objects;
- sync_class->send_objects_sync = caldav_send_objects;
- sync_class->get_object_sync = caldav_get_object;
- sync_class->get_object_list_sync = caldav_get_object_list;
- sync_class->add_timezone_sync = caldav_add_timezone;
- sync_class->get_free_busy_sync = caldav_get_free_busy;
}
-
-static void
-e_caldav_backend_initable_init (GInitableIface *interface)
-{
- interface->init = caldav_backend_initable_init;
-}
-
diff --git a/src/calendar/backends/caldav/e-cal-backend-caldav.h
b/src/calendar/backends/caldav/e-cal-backend-caldav.h
index 3ea3273..3a8b639 100644
--- a/src/calendar/backends/caldav/e-cal-backend-caldav.h
+++ b/src/calendar/backends/caldav/e-cal-backend-caldav.h
@@ -47,12 +47,12 @@ typedef struct _ECalBackendCalDAVClass ECalBackendCalDAVClass;
typedef struct _ECalBackendCalDAVPrivate ECalBackendCalDAVPrivate;
struct _ECalBackendCalDAV {
- ECalBackendSync parent;
+ ECalMetaBackend parent;
ECalBackendCalDAVPrivate *priv;
};
struct _ECalBackendCalDAVClass {
- ECalBackendSyncClass parent_class;
+ ECalMetaBackendClass parent_class;
};
GType e_cal_backend_caldav_get_type (void);
diff --git a/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
b/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
index e74f3ba..ddbc6cf 100644
--- a/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
+++ b/src/calendar/backends/gtasks/e-cal-backend-gtasks.c
@@ -537,6 +537,7 @@ ecb_gtasks_check_tasklist_changed_sync (ECalBackendGTasks *cbgtasks,
static gboolean
ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -644,7 +645,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
e_cal_component_set_created (new_comp,
cached_tt);
*out_modified_objects = g_slist_prepend
(*out_modified_objects,
- e_cal_meta_backend_info_new (uid, NULL,
revision, object));
+ e_cal_meta_backend_info_new (uid, revision,
object, NULL));
}
if (cached_tt)
@@ -653,7 +654,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
e_cal_component_free_icaltimetype (new_tt);
} else {
*out_created_objects = g_slist_prepend (*out_created_objects,
- e_cal_meta_backend_info_new (uid, NULL, revision,
object));
+ e_cal_meta_backend_info_new (uid, revision, object,
NULL));
}
g_free (revision);
@@ -705,6 +706,7 @@ ecb_gtasks_get_changes_sync (ECalMetaBackend *meta_backend,
static gboolean
ecb_gtasks_load_component_sync (ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_instances,
gchar **out_extra,
GCancellable *cancellable,
@@ -751,6 +753,7 @@ ecb_gtasks_save_component_sync (ECalMetaBackend *meta_backend,
const GSList *instances, /* ECalComponent * */
const gchar *extra,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error)
{
@@ -846,39 +849,20 @@ ecb_gtasks_remove_component_sync (ECalMetaBackend *meta_backend,
GError **error)
{
ECalBackendGTasks *cbgtasks;
- ECalCache *cal_cache;
GDataTasksTask *task;
ECalComponent *cached_comp = NULL;
GError *local_error = NULL;
g_return_val_if_fail (E_IS_CAL_BACKEND_GTASKS (meta_backend), FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
- cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
- g_return_val_if_fail (cal_cache != NULL, FALSE);
-
- if (object) {
- cached_comp = e_cal_component_new_from_string (object);
- if (!cached_comp) {
- g_propagate_error (error, EDC_ERROR (InvalidObject));
- g_object_unref (cal_cache);
-
- return FALSE;
- }
- } else if (!e_cal_cache_get_component (cal_cache, uid, NULL, &cached_comp, cancellable,
&local_error)) {
- if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
- g_clear_error (&local_error);
- g_propagate_error (error, EDC_ERROR (ObjectNotFound));
- } else if (local_error) {
- g_propagate_error (error, local_error);
- }
-
- g_object_unref (cal_cache);
-
+ cached_comp = e_cal_component_new_from_string (object);
+ if (!cached_comp) {
+ g_propagate_error (error, EDC_ERROR (InvalidObject));
return FALSE;
}
- g_object_unref (cal_cache);
-
cbgtasks = E_CAL_BACKEND_GTASKS (meta_backend);
if (!cached_comp) {
diff --git a/src/calendar/backends/http/e-cal-backend-http.c b/src/calendar/backends/http/e-cal-backend-http.c
index 137edfe..51a4246 100644
--- a/src/calendar/backends/http/e-cal-backend-http.c
+++ b/src/calendar/backends/http/e-cal-backend-http.c
@@ -26,6 +26,7 @@
#include <libsoup/soup.h>
#include <libedata-cal/libedata-cal.h>
+
#include "e-cal-backend-http.h"
#define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
@@ -167,15 +168,17 @@ ecb_http_connect_sync (ECalMetaBackend *meta_backend,
"%s", message->reason_phrase);
} else if (status_code == SOUP_STATUS_FORBIDDEN && credentials_empty) {
*out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
- } else if (status_code == SOUP_STATUS_UNAUTHORIZED ||
- status_code == SOUP_STATUS_FORBIDDEN) {
- *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
+ } else if (status_code == SOUP_STATUS_UNAUTHORIZED) {
+ if (credentials_empty)
+ *out_auth_result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ *out_auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
} else if (local_error) {
g_propagate_error (error, local_error);
local_error = NULL;
} else {
- g_set_error (error, SOUP_HTTP_ERROR, status_code,
- "%s", message ? message->reason_phrase : soup_status_get_phrase
(status_code));
+ g_set_error_literal (error, SOUP_HTTP_ERROR, status_code,
+ message ? message->reason_phrase : soup_status_get_phrase
(status_code));
}
if (status_code == SOUP_STATUS_SSL_FAILED) {
@@ -268,34 +271,10 @@ ecb_http_read_stream_sync (GInputStream *input_stream,
return g_string_free (icalstr, !success);
}
-static void
-ecb_http_gather_uids_cb (gpointer uid,
- gpointer icalcomp,
- gpointer user_data)
-{
- GHashTable *hash_table = user_data;
-
- g_return_if_fail (hash_table != NULL);
-
- if (uid)
- g_hash_table_insert (hash_table, g_strdup (uid), NULL);
-}
-
-static gboolean
-ecb_http_remove_unchanged_cb (gpointer uid,
- gpointer icalcomp,
- gpointer user_data)
-{
- GHashTable *unchanged = user_data;
-
- g_return_val_if_fail (unchanged != NULL, FALSE);
-
- return !uid || g_hash_table_contains (unchanged, uid);
-}
-
static gboolean
ecb_http_get_changes_sync (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects,
@@ -424,40 +403,14 @@ ecb_http_get_changes_sync (ECalMetaBackend *meta_backend,
}
}
+ g_warn_if_fail (cbhttp->priv->components == NULL);
cbhttp->priv->components = components;
icalcomponent_free (vcalendar);
success = E_CAL_META_BACKEND_CLASS (e_cal_backend_http_parent_class)->get_changes_sync
(meta_backend,
- last_sync_tag, out_new_sync_tag, out_repeat, out_created_objects,
+ last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
out_modified_objects, out_removed_objects, cancellable, error);
-
- if (success) {
- GHashTable *unchanged;
- GSList *link;
-
- unchanged = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
- g_hash_table_foreach (cbhttp->priv->components, ecb_http_gather_uids_cb, unchanged);
-
- for (link = *out_created_objects; link; link = g_slist_next (link)) {
- ECalMetaBackendInfo *nfo = link->data;
-
- if (nfo && nfo->uid)
- g_hash_table_remove (unchanged, nfo->uid);
- }
-
- for (link = *out_modified_objects; link; link = g_slist_next (link)) {
- ECalMetaBackendInfo *nfo = link->data;
-
- if (nfo && nfo->uid)
- g_hash_table_remove (unchanged, nfo->uid);
- }
-
- if (g_hash_table_size (unchanged))
- g_hash_table_foreach_remove (cbhttp->priv->components,
ecb_http_remove_unchanged_cb, unchanged);
-
- g_hash_table_destroy (unchanged);
- }
} else {
icalcomponent_free (vcalendar);
}
@@ -512,7 +465,7 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
revision = e_cal_cache_dup_component_revision (cal_cache, icalcomp);
object = icalcomponent_as_ical_string_r (value);
- nfo = e_cal_meta_backend_info_new (uid, NULL, revision, object);
+ nfo = e_cal_meta_backend_info_new (uid, revision, object, NULL);
*out_existing_objects = g_slist_prepend (*out_existing_objects, nfo);
@@ -522,9 +475,6 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
g_object_unref (cal_cache);
- g_hash_table_destroy (cbhttp->priv->components);
- cbhttp->priv->components = NULL;
-
ecb_http_disconnect_sync (meta_backend, cancellable, NULL);
return TRUE;
@@ -533,6 +483,7 @@ ecb_http_list_existing_sync (ECalMetaBackend *meta_backend,
static gboolean
ecb_http_load_component_sync (ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_component,
gchar **out_extra,
GCancellable *cancellable,
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index a0af4f9..aab860c 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -43,8 +43,9 @@
#include "e-cal-backend-util.h"
#include "e-cal-meta-backend.h"
-#define ECMB_KEY_SYNC_TAG "sync-tag"
-#define ECMB_KEY_DID_CONNECT "did-connect"
+#define ECMB_KEY_SYNC_TAG "ecmb::sync-tag"
+#define ECMB_KEY_EVER_CONNECTED "ecmb::ever-connected"
+#define ECMB_KEY_CONNECTED_WRITABLE "ecmb::connected-writable"
#define LOCAL_PREFIX "file://"
@@ -64,6 +65,8 @@ struct _ECalMetaBackendPrivate {
guint refresh_timeout_id;
gboolean refresh_after_authenticate;
+ gint ever_connected;
+ gint connected_writable;
/* Last successful connect data, for some extensions */
guint16 authentication_port;
@@ -99,6 +102,7 @@ static gboolean ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
ECalCache *cal_cache,
const gchar *uid,
const gchar *preloaded_object,
+ const gchar *preloaded_extra,
GCancellable *cancellable,
GError **error);
static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
@@ -110,28 +114,29 @@ static gboolean ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
const gchar *orig_uid,
gboolean *out_requires_put,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error);
/**
* e_cal_cache_search_data_new:
* @uid: a component UID; cannot be %NULL
- * @rid: (nullable): a component Recurrence-ID; can be %NULL
* @revision: (nullable): the component revision; can be %NULL
* @object: (nullable): the component object as an iCalendar string; can be %NULL
+ * @extra: (nullable): extra backend-specific data; can be %NULL
*
* Creates a new #ECalMetaBackendInfo prefilled with the given values.
*
* Returns: (transfer full): A new #ECalMetaBackendInfo. Free it with
- * e_cal_meta_backend_info_free() when no longer needed.
+ * e_cal_meta_backend_info_free(), when no longer needed.
*
* Since: 3.26
**/
ECalMetaBackendInfo *
e_cal_meta_backend_info_new (const gchar *uid,
- const gchar *rid,
const gchar *revision,
- const gchar *object)
+ const gchar *object,
+ const gchar *extra)
{
ECalMetaBackendInfo *info;
@@ -139,9 +144,9 @@ e_cal_meta_backend_info_new (const gchar *uid,
info = g_new0 (ECalMetaBackendInfo, 1);
info->uid = g_strdup (uid);
- info->rid = g_strdup (rid && *rid ? rid : NULL);
info->revision = g_strdup (revision);
info->object = g_strdup (object);
+ info->extra = g_strdup (extra);
return info;
}
@@ -159,10 +164,10 @@ e_cal_meta_backend_info_new (const gchar *uid,
ECalMetaBackendInfo *
e_cal_meta_backend_info_copy (const ECalMetaBackendInfo *src)
{
- if (src)
+ if (!src)
return NULL;
- return e_cal_meta_backend_info_new (src->uid, src->rid, src->revision, src->object);
+ return e_cal_meta_backend_info_new (src->uid, src->revision, src->object, src->extra);
}
/**
@@ -181,9 +186,9 @@ e_cal_meta_backend_info_free (gpointer ptr)
if (info) {
g_free (info->uid);
- g_free (info->rid);
g_free (info->revision);
g_free (info->object);
+ g_free (info->extra);
g_free (info);
}
}
@@ -271,6 +276,9 @@ ecmb_update_connection_values (ECalMetaBackend *meta_backend)
}
g_mutex_unlock (&meta_backend->priv->property_lock);
+
+ e_cal_meta_backend_set_ever_connected (meta_backend, TRUE);
+ e_cal_meta_backend_set_connected_writable (meta_backend, e_cal_backend_get_writable (E_CAL_BACKEND
(meta_backend)));
}
static gboolean
@@ -369,6 +377,7 @@ ecmb_gather_locally_cached_objects_cb (ECalCache *cal_cache,
static gboolean
ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects,
@@ -421,7 +430,7 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
continue;
id.uid = nfo->uid;
- id.rid = nfo->rid;
+ id.rid = NULL;
if (!g_hash_table_contains (locally_cached, &id)) {
link->data = NULL;
@@ -463,7 +472,7 @@ ecmb_get_changes_sync (ECalMetaBackend *meta_backend,
continue;
}
- nfo = e_cal_meta_backend_info_new (id->uid, id->rid, revision, NULL);
+ nfo = e_cal_meta_backend_info_new (id->uid, revision, NULL, NULL);
*out_removed_objects = g_slist_prepend (*out_removed_objects, nfo);
}
@@ -611,7 +620,7 @@ ecmb_upload_local_changes_sync (ECalMetaBackend *meta_backend,
if (success) {
success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache,
change->state == E_OFFLINE_STATE_LOCALLY_MODIFIED,
- conflict_resolution, instances, extra, change->uid, NULL, NULL,
cancellable, error);
+ conflict_resolution, instances, extra, change->uid, NULL, NULL, NULL,
cancellable, error);
}
g_slist_free_full (instances, g_object_unref);
@@ -645,6 +654,60 @@ ecmb_upload_local_changes_sync (ECalMetaBackend *meta_backend,
return success;
}
+static gboolean
+ecmb_maybe_remove_from_cache (ECalMetaBackend *meta_backend,
+ ECalCache *cal_cache,
+ ECacheOfflineFlag offline_flag,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECalBackend *cal_backend;
+ GSList *comps = NULL, *link;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (uid != NULL, FALSE);
+
+ if (!e_cal_cache_get_components_by_uid (cal_cache, uid, &comps, cancellable, &local_error)) {
+ if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
+ g_clear_error (&local_error);
+ return TRUE;
+ }
+
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ cal_backend = E_CAL_BACKEND (meta_backend);
+
+ for (link = comps; link; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
+ ECalComponentId *id;
+
+ g_warn_if_fail (E_IS_CAL_COMPONENT (comp));
+
+ if (!E_IS_CAL_COMPONENT (comp))
+ continue;
+
+ id = e_cal_component_get_id (comp);
+ if (id) {
+ if (!e_cal_cache_remove_component (cal_cache, id->uid, id->rid, offline_flag,
cancellable, error)) {
+ e_cal_component_free_id (id);
+ g_slist_free_full (comps, g_object_unref);
+
+ return FALSE;
+ }
+
+ e_cal_backend_notify_component_removed (cal_backend, id, comp, NULL);
+ e_cal_component_free_id (id);
+ }
+ }
+
+ g_slist_free_full (comps, g_object_unref);
+
+ return TRUE;
+}
+
static void
ecmb_refresh_thread_func (ECalBackend *cal_backend,
gpointer user_data,
@@ -653,8 +716,8 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
{
ECalMetaBackend *meta_backend;
ECalCache *cal_cache;
- ECacheOfflineFlag offline_flag = E_CACHE_IS_ONLINE;
- gboolean success, repeat = TRUE;
+ gboolean success, repeat = TRUE, is_repeat = FALSE;
+ GString *invalid_objects = NULL;
g_return_if_fail (E_IS_CAL_META_BACKEND (cal_backend));
@@ -697,7 +760,7 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
last_sync_tag = NULL;
}
- success = e_cal_meta_backend_get_changes_sync (meta_backend, last_sync_tag, &new_sync_tag,
&repeat,
+ success = e_cal_meta_backend_get_changes_sync (meta_backend, last_sync_tag, is_repeat,
&new_sync_tag, &repeat,
&created_objects, &modified_objects, &removed_objects, cancellable, error);
if (success) {
@@ -708,24 +771,19 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
/* Removed objects first */
for (link = removed_objects; link && success; link = g_slist_next (link)) {
ECalMetaBackendInfo *nfo = link->data;
- ECalComponentId id;
if (!nfo) {
g_warn_if_reached ();
continue;
}
- id.uid = nfo->uid;
- id.rid = nfo->rid;
-
- success = e_cal_cache_remove_component (cal_cache, id.uid, id.rid,
offline_flag, cancellable, error);
- if (success)
- e_cal_backend_notify_component_removed (cal_backend, &id, NULL, NULL);
+ success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache,
E_CACHE_IS_ONLINE, nfo->uid, cancellable, error);
}
/* Then modified objects */
for (link = modified_objects; link && success; link = g_slist_next (link)) {
ECalMetaBackendInfo *nfo = link->data;
+ GError *local_error = NULL;
if (!nfo || !nfo->uid) {
g_warn_if_reached ();
@@ -737,7 +795,21 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
g_hash_table_insert (covered_uids, nfo->uid, NULL);
- success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, nfo->object, cancellable, error);
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, nfo->object, nfo->extra, cancellable, &local_error);
+
+ /* Do not stop on invalid objects, just notify about them later, and load as
many as possible */
+ if (!success && g_error_matches (local_error, E_DATA_CAL_ERROR,
InvalidObject)) {
+ if (!invalid_objects) {
+ invalid_objects = g_string_new (local_error->message);
+ } else {
+ g_string_append_c (invalid_objects, '\n');
+ g_string_append (invalid_objects, local_error->message);
+ }
+ g_clear_error (&local_error);
+ success = TRUE;
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ }
}
g_hash_table_remove_all (covered_uids);
@@ -745,13 +817,28 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
/* Finally created objects */
for (link = created_objects; link && success; link = g_slist_next (link)) {
ECalMetaBackendInfo *nfo = link->data;
+ GError *local_error = NULL;
if (!nfo || !nfo->uid) {
g_warn_if_reached ();
continue;
}
- success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, nfo->object, cancellable, error);
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache,
nfo->uid, nfo->object, nfo->extra, cancellable, &local_error);
+
+ /* Do not stop on invalid objects, just notify about them later, and load as
many as possible */
+ if (!success && g_error_matches (local_error, E_DATA_CAL_ERROR,
InvalidObject)) {
+ if (!invalid_objects) {
+ invalid_objects = g_string_new (local_error->message);
+ } else {
+ g_string_append_c (invalid_objects, '\n');
+ g_string_append (invalid_objects, local_error->message);
+ }
+ g_clear_error (&local_error);
+ success = TRUE;
+ } else if (local_error) {
+ g_propagate_error (error, local_error);
+ }
}
g_hash_table_destroy (covered_uids);
@@ -765,6 +852,8 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
g_slist_free_full (removed_objects, e_cal_meta_backend_info_free);
g_free (last_sync_tag);
g_free (new_sync_tag);
+
+ is_repeat = TRUE;
}
g_object_unref (cal_cache);
@@ -777,6 +866,12 @@ ecmb_refresh_thread_func (ECalBackend *cal_backend,
g_mutex_unlock (&meta_backend->priv->property_lock);
+ if (invalid_objects) {
+ e_cal_backend_notify_error (E_CAL_BACKEND (meta_backend), invalid_objects->str);
+
+ g_string_free (invalid_objects, TRUE);
+ }
+
g_signal_emit (meta_backend, signals[REFRESH_COMPLETED], 0, NULL);
}
@@ -894,60 +989,6 @@ ecmb_find_in_instances (const GSList *instances, /* ECalComponent * */
}
static gboolean
-ecmb_maybe_remove_from_cache (ECalMetaBackend *meta_backend,
- ECalCache *cal_cache,
- ECacheOfflineFlag offline_flag,
- const gchar *uid,
- GCancellable *cancellable,
- GError **error)
-{
- ECalBackend *cal_backend;
- GSList *comps = NULL, *link;
- GError *local_error = NULL;
-
- g_return_val_if_fail (uid != NULL, FALSE);
-
- if (!e_cal_cache_get_components_by_uid (cal_cache, uid, &comps, cancellable, &local_error)) {
- if (g_error_matches (local_error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND)) {
- g_clear_error (&local_error);
- return TRUE;
- }
-
- g_propagate_error (error, local_error);
- return FALSE;
- }
-
- cal_backend = E_CAL_BACKEND (meta_backend);
-
- for (link = comps; link; link = g_slist_next (link)) {
- ECalComponent *comp = link->data;
- ECalComponentId *id;
-
- g_warn_if_fail (E_IS_CAL_COMPONENT (comp));
-
- if (!E_IS_CAL_COMPONENT (comp))
- continue;
-
- id = e_cal_component_get_id (comp);
- if (id) {
- if (!e_cal_cache_remove_component (cal_cache, id->uid, id->rid, offline_flag,
cancellable, error)) {
- e_cal_component_free_id (id);
- g_slist_free_full (comps, g_object_unref);
-
- return FALSE;
- }
-
- e_cal_backend_notify_component_removed (cal_backend, id, comp, NULL);
- e_cal_component_free_id (id);
- }
- }
-
- g_slist_free_full (comps, g_object_unref);
-
- return TRUE;
-}
-
-static gboolean
ecmb_put_one_component (ECalMetaBackend *meta_backend,
ECalCache *cal_cache,
ECacheOfflineFlag offline_flag,
@@ -1086,6 +1127,7 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
ECalCache *cal_cache,
const gchar *uid,
const gchar *preloaded_object,
+ const gchar *preloaded_extra,
GCancellable *cancellable,
GError **error)
{
@@ -1098,11 +1140,14 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
if (preloaded_object && *preloaded_object) {
icalcomp = icalcomponent_new_from_string (preloaded_object);
if (!icalcomp) {
- g_propagate_error (error, e_data_cal_create_error (InvalidObject, _("Preloaded object
is invalid")));
+ g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Preloaded
object for UID ā%sā is invalid"), uid));
return FALSE;
}
- } else if (!e_cal_meta_backend_load_component_sync (meta_backend, uid, &icalcomp, &extra,
cancellable, error) ||
- !icalcomp) {
+ } else if (!e_cal_meta_backend_load_component_sync (meta_backend, uid, preloaded_extra, &icalcomp,
&extra, cancellable, error)) {
+ g_free (extra);
+ return FALSE;
+ } else if (!icalcomp) {
+ g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Received object for
UID ā%sā is invalid"), uid));
g_free (extra);
return FALSE;
}
@@ -1136,9 +1181,9 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
new_instances = g_slist_reverse (new_instances);
success = ecmb_put_instances (meta_backend, cal_cache, uid, offline_flag,
- new_instances, extra, cancellable, error);
+ new_instances, extra ? extra : preloaded_extra, cancellable, error);
} else {
- g_propagate_error (error, e_data_cal_create_error (InvalidObject, _("Received object is
invalid")));
+ g_propagate_error (error, e_data_cal_create_error_fmt (InvalidObject, _("Received object for
UID ā%sā doesn't contain any expected component"), uid));
success = FALSE;
}
@@ -1160,11 +1205,12 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
const gchar *orig_uid,
gboolean *out_requires_put,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error)
{
GSList *link, *instances = NULL;
- gchar *new_uid = NULL;
+ gchar *new_uid = NULL, *new_extra = NULL;
gboolean has_attachments = FALSE, success = TRUE;
if (out_requires_put)
@@ -1197,10 +1243,11 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
}
success = success && e_cal_meta_backend_save_component_sync (meta_backend, overwrite_existing,
conflict_resolution,
- instances ? instances : in_instances, extra, &new_uid, cancellable, error);
+ instances ? instances : in_instances, extra, &new_uid, &new_extra, cancellable, error);
if (success && new_uid) {
- success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, NULL,
cancellable, error);
+ success = ecmb_load_component_wrapper_sync (meta_backend, cal_cache, new_uid, NULL,
+ new_extra ? new_extra : extra, cancellable, error);
if (success && g_strcmp0 (new_uid, orig_uid) != 0)
success = ecmb_maybe_remove_from_cache (meta_backend, cal_cache, E_CACHE_IS_ONLINE,
orig_uid, cancellable, error);
@@ -1214,6 +1261,11 @@ ecmb_save_component_wrapper_sync (ECalMetaBackend *meta_backend,
*out_requires_put = FALSE;
}
+ if (success && out_new_extra)
+ *out_new_extra = new_extra;
+ else
+ g_free (new_extra);
+
g_slist_free_full (instances, g_object_unref);
return success;
@@ -1255,6 +1307,14 @@ ecmb_open_sync (ECalBackendSync *sync_backend,
e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
}
+ if (e_cal_meta_backend_get_ever_connected (meta_backend)) {
+ e_cal_backend_set_writable (E_CAL_BACKEND (meta_backend),
+ e_cal_meta_backend_get_connected_writable (meta_backend));
+ } else {
+ if (!ecmb_connect_wrapper_sync (meta_backend, cancellable, error))
+ return;
+ }
+
ecmb_schedule_refresh (E_CAL_META_BACKEND (sync_backend));
}
@@ -1309,7 +1369,7 @@ ecmb_get_object_sync (ECalBackendSync *sync_backend,
/* Ignore errors here, just try whether it's on the remote side, but not in the local cache */
if (e_backend_get_online (E_BACKEND (meta_backend)) &&
ecmb_connect_wrapper_sync (meta_backend, cancellable, NULL) &&
- ecmb_load_component_wrapper_sync (meta_backend, cal_cache, uid, NULL, cancellable, NULL))
{
+ ecmb_load_component_wrapper_sync (meta_backend, cal_cache, uid, NULL, NULL, cancellable,
NULL)) {
found = e_cal_cache_get_component_as_string (cal_cache, uid, rid, calobj,
cancellable, NULL);
}
@@ -1531,7 +1591,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
icalcomponent *icalcomp;
struct icaltimetype itt;
const gchar *uid;
- gchar *new_uid = NULL;
+ gchar *new_uid = NULL, *new_extra = NULL;
gboolean success, requires_put = TRUE;
g_return_val_if_fail (comp != NULL, FALSE);
@@ -1582,7 +1642,8 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
instances = g_slist_prepend (NULL, comp);
- if (!ecmb_save_component_wrapper_sync (meta_backend, cal_cache, FALSE, conflict_resolution,
instances, NULL, uid, &requires_put, &new_uid, cancellable, error)) {
+ if (!ecmb_save_component_wrapper_sync (meta_backend, cal_cache, FALSE, conflict_resolution,
instances, NULL, uid,
+ &requires_put, &new_uid, &new_extra, cancellable, error)) {
g_slist_free (instances);
return FALSE;
}
@@ -1591,7 +1652,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
}
if (requires_put) {
- success = e_cal_cache_put_component (cal_cache, comp, NULL, *offline_flag, cancellable,
error);
+ success = e_cal_cache_put_component (cal_cache, comp, new_extra, *offline_flag, cancellable,
error);
if (success && !out_new_comp) {
e_cal_backend_notify_component_created (E_CAL_BACKEND (meta_backend), comp);
}
@@ -1613,6 +1674,7 @@ ecmb_create_object_sync (ECalMetaBackend *meta_backend,
}
g_free (new_uid);
+ g_free (new_extra);
return success;
}
@@ -1700,7 +1762,7 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
ECalComponentId *id;
ECalComponent *old_comp = NULL, *new_comp = NULL, *master_comp, *existing_comp = NULL;
GSList *instances = NULL;
- gchar *extra = NULL, *new_uid = NULL;
+ gchar *extra = NULL, *new_uid = NULL, *new_extra = NULL;
gboolean success = TRUE, requires_put = TRUE;
GError *local_error = NULL;
@@ -1876,11 +1938,11 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
if (success && *offline_flag == E_CACHE_IS_ONLINE) {
success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE,
conflict_resolution,
- instances, extra, id->uid, &requires_put, &new_uid, cancellable, error);
+ instances, extra, id->uid, &requires_put, &new_uid, &new_extra, cancellable, error);
}
if (success && requires_put)
- success = ecmb_put_instances (meta_backend, cal_cache, id->uid, *offline_flag, instances,
extra, cancellable, error);
+ success = ecmb_put_instances (meta_backend, cal_cache, id->uid, *offline_flag, instances,
new_extra ? new_extra : extra, cancellable, error);
if (!success) {
g_clear_object (&old_comp);
@@ -1901,6 +1963,7 @@ ecmb_modify_object_sync (ECalMetaBackend *meta_backend,
g_slist_free_full (instances, g_object_unref);
e_cal_component_free_id (id);
g_clear_object (&new_comp);
+ g_free (new_extra);
g_free (new_uid);
g_free (extra);
@@ -2151,13 +2214,19 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
if (mod == E_CAL_OBJ_MOD_ALL) {
if (*offline_flag == E_CACHE_IS_ONLINE) {
- success = e_cal_meta_backend_remove_component_sync (meta_backend,
conflict_resolution, uid, extra, NULL, cancellable, error);
+ gchar *ical_string = NULL;
+
+ g_warn_if_fail (e_cal_cache_get_component_as_string (cal_cache, uid, NULL,
&ical_string, cancellable, NULL));
+
+ success = e_cal_meta_backend_remove_component_sync (meta_backend,
conflict_resolution, uid, extra, ical_string, cancellable, error);
+
+ g_free (ical_string);
}
success = success && ecmb_maybe_remove_from_cache (meta_backend, cal_cache,
*offline_flag, uid, cancellable, error);
} else {
gboolean requires_put = TRUE;
- gchar *new_uid = NULL;
+ gchar *new_uid = NULL, *new_extra = NULL;
if (master_comp) {
icalcomponent *icalcomp = e_cal_component_get_icalcomponent (master_comp);
@@ -2173,11 +2242,11 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
if (*offline_flag == E_CACHE_IS_ONLINE) {
success = ecmb_save_component_wrapper_sync (meta_backend, cal_cache, TRUE,
conflict_resolution,
- instances, extra, uid, &requires_put, &new_uid, cancellable, error);
+ instances, extra, uid, &requires_put, &new_uid, &new_extra,
cancellable, error);
}
if (success && requires_put)
- success = ecmb_put_instances (meta_backend, cal_cache, uid, *offline_flag,
instances, extra, cancellable, error);
+ success = ecmb_put_instances (meta_backend, cal_cache, uid, *offline_flag,
instances, new_extra ? new_extra : extra, cancellable, error);
if (success && new_uid && !requires_put) {
g_clear_object (&new_comp);
@@ -2187,6 +2256,7 @@ ecmb_remove_object_sync (ECalMetaBackend *meta_backend,
}
g_free (new_uid);
+ g_free (new_extra);
}
g_free (extra);
@@ -3166,6 +3236,8 @@ e_cal_meta_backend_init (ECalMetaBackend *meta_backend)
meta_backend->priv->view_cancellables = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
g_object_unref);
meta_backend->priv->current_online_state = FALSE;
meta_backend->priv->refresh_after_authenticate = FALSE;
+ meta_backend->priv->ever_connected = -1;
+ meta_backend->priv->connected_writable = -1;
}
/**
@@ -3191,6 +3263,132 @@ e_cal_meta_backend_get_capabilities (ECalMetaBackend *meta_backend)
}
/**
+ * e_cal_meta_backend_set_ever_connected:
+ * @meta_backend: an #ECalMetaBackend
+ * @value: value to set
+ *
+ * Sets whether the @meta_backend ever made a successful connection
+ * to its destination.
+ *
+ * This is used by the @meta_backend itself, during the opening phase,
+ * when it had not been connected yet, then it does so immediately, to
+ * eventually report settings error easily.
+ *
+ * Since: 3.26
+ **/
+void
+e_cal_meta_backend_set_ever_connected (ECalMetaBackend *meta_backend,
+ gboolean value)
+{
+ ECalCache *cal_cache;
+
+ g_return_if_fail (E_IS_CAL_META_BACKEND (meta_backend));
+
+ if ((value ? 1 : 0) == meta_backend->priv->ever_connected)
+ return;
+
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ meta_backend->priv->ever_connected = value ? 1 : 0;
+ e_cache_set_key_int (E_CACHE (cal_cache), ECMB_KEY_EVER_CONNECTED,
meta_backend->priv->ever_connected, NULL);
+ g_clear_object (&cal_cache);
+}
+
+/**
+ * e_cal_meta_backend_get_ever_connected:
+ * @meta_backend: an #ECalMetaBackend
+ *
+ * Returns: Whether the @meta_backend ever made a successful connection
+ * to its destination.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cal_meta_backend_get_ever_connected (ECalMetaBackend *meta_backend)
+{
+ gboolean result;
+
+ g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
+
+ if (meta_backend->priv->ever_connected == -1) {
+ ECalCache *cal_cache;
+
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ result = e_cache_get_key_int (E_CACHE (cal_cache), ECMB_KEY_EVER_CONNECTED, NULL) == 1;
+ g_clear_object (&cal_cache);
+
+ meta_backend->priv->ever_connected = result ? 1 : 0;
+ } else {
+ result = meta_backend->priv->ever_connected == 1;
+ }
+
+ return result;
+}
+
+/**
+ * e_cal_meta_backend_set_connected_writable:
+ * @meta_backend: an #ECalMetaBackend
+ * @value: value to set
+ *
+ * Sets whether the @meta_backend connected to a writable destination.
+ * This value has meaning only if e_cal_meta_backend_get_ever_connected()
+ * is %TRUE.
+ *
+ * This is used by the @meta_backend itself, during the opening phase,
+ * to set the backend writable or not also in the offline mode.
+ *
+ * Since: 3.26
+ **/
+void
+e_cal_meta_backend_set_connected_writable (ECalMetaBackend *meta_backend,
+ gboolean value)
+{
+ ECalCache *cal_cache;
+
+ g_return_if_fail (E_IS_CAL_META_BACKEND (meta_backend));
+
+ if ((value ? 1 : 0) == meta_backend->priv->connected_writable)
+ return;
+
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ meta_backend->priv->connected_writable = value ? 1 : 0;
+ e_cache_set_key_int (E_CACHE (cal_cache), ECMB_KEY_CONNECTED_WRITABLE,
meta_backend->priv->connected_writable, NULL);
+ g_clear_object (&cal_cache);
+}
+
+/**
+ * e_cal_meta_backend_get_connected_writable:
+ * @meta_backend: an #ECalMetaBackend
+ *
+ * This value has meaning only if e_cal_meta_backend_get_ever_connected()
+ * is %TRUE.
+ *
+ * Returns: Whether the @meta_backend connected to a writable destination.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_cal_meta_backend_get_connected_writable (ECalMetaBackend *meta_backend)
+{
+ gboolean result;
+
+ g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
+
+ if (meta_backend->priv->connected_writable == -1) {
+ ECalCache *cal_cache;
+
+ cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
+ result = e_cache_get_key_int (E_CACHE (cal_cache), ECMB_KEY_CONNECTED_WRITABLE, NULL) == 1;
+ g_clear_object (&cal_cache);
+
+ meta_backend->priv->connected_writable = result ? 1 : 0;
+ } else {
+ result = meta_backend->priv->connected_writable == 1;
+ }
+
+ return result;
+}
+
+/**
* e_cal_meta_backend_set_cache:
* @meta_backend: an #ECalMetaBackend
* @cache: an #ECalCache to use
@@ -3719,12 +3917,15 @@ e_cal_meta_backend_empty_cache_sync (ECalMetaBackend *meta_backend,
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
- * This is called always before any operations which requires
- * a connection to the remote side. It can fail with
- * an #E_CLIENT_ERROR_REPOSITORY_OFFLINE error to indicate
- * that the remote side cannot be currently reached. Other
- * errors are propagated to the caller/client side.
- * This method is not called when the backend is not online.
+ * This is called always before any operations which requires a connection
+ * to the remote side. It can fail with an #E_CLIENT_ERROR_REPOSITORY_OFFLINE
+ * error to indicate that the remote side cannot be currently reached. Other
+ * errors are propagated to the caller/client side. This method is not called
+ * when the backend is not online.
+ *
+ * The descendant should also call e_cal_backend_set_writable() after successful
+ * connect to the remote side. This value is stored for later use, when being
+ * opened offline.
*
* The @credentials parameter consists of the previously used credentials.
* It's always %NULL with the first connection attempt. To get the credentials,
@@ -3804,6 +4005,7 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
* e_cal_meta_backend_get_changes_sync:
* @meta_backend: an #ECalMetaBackend
* @last_sync_tag: (nullable): optional sync tag from the last check
+ * @is_repeat: set to %TRUE when this is the repeated call
* @out_new_sync_tag: (out) (transfer full): new sync tag to store on success
* @out_repeat: (out): whether to repeat this call again; default is %FALSE
* @out_created_objects: (out) (element-type ECalMetaBackendInfo) (transfer full):
@@ -3829,7 +4031,8 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
* The @out_repeat can be set to %TRUE when the descendant didn't finish
* read of all the changes. In that case the @meta_backend calls this
* function again with the @out_new_sync_tag as the @last_sync_tag, but also
- * notifies about the found changes immediately.
+ * notifies about the found changes immediately. The @is_repeat is set
+ * to %TRUE as well in this case, otherwise it's %FALSE.
*
* The descendant can populate also ECalMetaBackendInfo::object of
* the @out_created_objects and @out_modified_objects, if known, in which
@@ -3851,6 +4054,7 @@ e_cal_meta_backend_disconnect_sync (ECalMetaBackend *meta_backend,
gboolean
e_cal_meta_backend_get_changes_sync (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects,
@@ -3875,6 +4079,7 @@ e_cal_meta_backend_get_changes_sync (ECalMetaBackend *meta_backend,
return klass->get_changes_sync (meta_backend,
last_sync_tag,
+ is_repeat,
out_new_sync_tag,
out_repeat,
out_created_objects,
@@ -3933,6 +4138,7 @@ e_cal_meta_backend_list_existing_sync (ECalMetaBackend *meta_backend,
* e_cal_meta_backend_load_component_sync:
* @meta_backend: an #ECalMetaBackend
* @uid: a component UID
+ * @extra: (nullable): optional extra data stored with the component, or %NULL
* @out_component: (out) (transfer full): a loaded component, as icalcomponent
* @out_extra: (out) (transfer full): an extra data to store to #ECalCache with this component
* @cancellable: optional #GCancellable object, or %NULL
@@ -3959,6 +4165,7 @@ e_cal_meta_backend_list_existing_sync (ECalMetaBackend *meta_backend,
gboolean
e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_component,
gchar **out_extra,
GCancellable *cancellable,
@@ -3975,7 +4182,7 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
g_return_val_if_fail (klass != NULL, FALSE);
g_return_val_if_fail (klass->load_component_sync != NULL, FALSE);
- return klass->load_component_sync (meta_backend, uid, out_component, out_extra, cancellable, error);
+ return klass->load_component_sync (meta_backend, uid, extra, out_component, out_extra, cancellable,
error);
}
/**
@@ -3986,6 +4193,7 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
* @instances: (element-type ECalComponent): instances of the component to save
* @extra: (nullable): extra data saved with the components in an #ECalCache
* @out_new_uid: (out) (transfer full): return location for the UID of the saved component
+ * @out_new_extra: (out) (transfer full): return location for the extra data to store with the component
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
@@ -4008,6 +4216,9 @@ e_cal_meta_backend_load_component_sync (ECalMetaBackend *meta_backend,
* assigned it to it. This UID, if set, is loaded from the remote side afterwards,
* also to see whether any changes had been made to the component by the remote side.
*
+ * The @out_new_extra can be populated with a new extra data to save with the component.
+ * Left it %NULL, to keep the same value as the @extra.
+ *
* The descendant can use an #E_CLIENT_ERROR_OUT_OF_SYNC error to indicate that
* the save failed due to made changes on the remote side, and let the @meta_backend
* to resolve this conflict based on the @conflict_resolution on its own.
@@ -4027,6 +4238,7 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
const GSList *instances,
const gchar *extra,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error)
{
@@ -4035,6 +4247,7 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), FALSE);
g_return_val_if_fail (instances != NULL, FALSE);
g_return_val_if_fail (out_new_uid != NULL, FALSE);
+ g_return_val_if_fail (out_new_extra != NULL, FALSE);
klass = E_CAL_META_BACKEND_GET_CLASS (meta_backend);
g_return_val_if_fail (klass != NULL, FALSE);
@@ -4044,7 +4257,15 @@ e_cal_meta_backend_save_component_sync (ECalMetaBackend *meta_backend,
return FALSE;
}
- return klass->save_component_sync (meta_backend, overwrite_existing, conflict_resolution, instances,
extra, out_new_uid, cancellable, error);
+ return klass->save_component_sync (meta_backend,
+ overwrite_existing,
+ conflict_resolution,
+ instances,
+ extra,
+ out_new_uid,
+ out_new_extra,
+ cancellable,
+ error);
}
/**
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.h b/src/calendar/libedata-cal/e-cal-meta-backend.h
index d4a7bf5..ba1a4e1 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.h
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.h
@@ -50,9 +50,9 @@ G_BEGIN_DECLS
typedef struct _ECalMetaBackendInfo {
gchar *uid;
- gchar *rid;
gchar *revision;
gchar *object;
+ gchar *extra;
} ECalMetaBackendInfo;
#define E_TYPE_CAL_META_BACKEND_INFO (e_cal_meta_backend_info_get_type ())
@@ -61,9 +61,9 @@ GType e_cal_meta_backend_info_get_type
(void) G_GNUC_CONST;
ECalMetaBackendInfo *
e_cal_meta_backend_info_new (const gchar *uid,
- const gchar *rid,
const gchar *revision,
- const gchar *object);
+ const gchar *object,
+ const gchar *extra);
ECalMetaBackendInfo *
e_cal_meta_backend_info_copy (const ECalMetaBackendInfo *src);
void e_cal_meta_backend_info_free (gpointer ptr /* ECalMetaBackendInfo * */);
@@ -111,6 +111,7 @@ struct _ECalMetaBackendClass {
gboolean (* get_changes_sync) (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -125,6 +126,7 @@ struct _ECalMetaBackendClass {
GError **error);
gboolean (* load_component_sync) (ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_component,
gchar **out_extra,
GCancellable *cancellable,
@@ -135,6 +137,7 @@ struct _ECalMetaBackendClass {
const GSList *instances, /* ECalComponent * */
const gchar *extra,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error);
gboolean (* remove_component_sync)
@@ -158,6 +161,16 @@ GType e_cal_meta_backend_get_type (void) G_GNUC_CONST;
const gchar * e_cal_meta_backend_get_capabilities
(ECalMetaBackend *meta_backend);
+void e_cal_meta_backend_set_ever_connected
+ (ECalMetaBackend *meta_backend,
+ gboolean value);
+gboolean e_cal_meta_backend_get_ever_connected
+ (ECalMetaBackend *meta_backend);
+void e_cal_meta_backend_set_connected_writable
+ (ECalMetaBackend *meta_backend,
+ gboolean value);
+gboolean e_cal_meta_backend_get_connected_writable
+ (ECalMetaBackend *meta_backend);
void e_cal_meta_backend_set_cache (ECalMetaBackend *meta_backend,
ECalCache *cache);
ECalCache * e_cal_meta_backend_ref_cache (ECalMetaBackend *meta_backend);
@@ -199,6 +212,7 @@ gboolean e_cal_meta_backend_disconnect_sync
gboolean e_cal_meta_backend_get_changes_sync
(ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects, /* ECalMetaBackendInfo * */
@@ -215,6 +229,7 @@ gboolean e_cal_meta_backend_list_existing_sync
gboolean e_cal_meta_backend_load_component_sync
(ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_component,
gchar **out_extra,
GCancellable *cancellable,
@@ -226,6 +241,7 @@ gboolean e_cal_meta_backend_save_component_sync
const GSList *instances, /* ECalComponent * */
const gchar *extra,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error);
gboolean e_cal_meta_backend_remove_component_sync
diff --git a/src/libedataserver/e-soup-session.c b/src/libedataserver/e-soup-session.c
index bf8bb36..4a175d1 100644
--- a/src/libedataserver/e-soup-session.c
+++ b/src/libedataserver/e-soup-session.c
@@ -114,6 +114,12 @@ e_soup_session_setup_bearer_auth (ESoupSession *session,
source = e_soup_session_get_source (session);
credentials = e_soup_session_dup_credentials (session);
+ if (!credentials || !e_named_parameters_count (credentials)) {
+ e_named_parameters_free (credentials);
+ g_set_error_literal (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED, _("Credentials
required"));
+ return FALSE;
+ }
+
success = e_util_get_source_oauth2_access_token_sync (source, credentials,
&access_token, &expires_in_seconds, cancellable, error);
diff --git a/src/libedataserver/e-source-credentials-provider-impl-google.c
b/src/libedataserver/e-source-credentials-provider-impl-google.c
index 80146f2..9867df4 100644
--- a/src/libedataserver/e-source-credentials-provider-impl-google.c
+++ b/src/libedataserver/e-source-credentials-provider-impl-google.c
@@ -522,6 +522,7 @@ e_source_credentials_google_get_access_token_sync (ESource *source,
GError **error)
{
ENamedParameters *tmp_credentials = NULL;
+ gboolean success;
g_return_val_if_fail (credentials != NULL, FALSE);
@@ -546,10 +547,13 @@ e_source_credentials_google_get_access_token_sync (ESource *source,
return TRUE;
}
+ /* Try to refresh the token */
+ success = e_source_credentials_google_refresh_token_sync (source, tmp_credentials ? tmp_credentials :
credentials,
+ out_access_token, out_expires_in_seconds, cancellable, error);
+
e_named_parameters_free (tmp_credentials);
- /* Try to refresh the token */
- return e_source_credentials_google_refresh_token_sync (source, credentials, out_access_token,
out_expires_in_seconds, cancellable, error);
+ return success;
}
gboolean
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
index 6202396..5752d8c 100644
--- a/src/libedataserver/e-webdav-session.c
+++ b/src/libedataserver/e-webdav-session.c
@@ -628,7 +628,7 @@ e_webdav_session_new (ESource *source)
}
/**
- * e_webdav_session_options_sync:
+ * e_webdav_session_new_request:
* @webdav: an #EWebDAVSession
* @method: an HTTP method
* @uri: (nullable): URI to create the request for, or %NULL to read from #ESource
@@ -998,12 +998,14 @@ e_webdav_session_replace_with_detailed_error (EWebDAVSession *webdav,
/**
* e_webdav_session_ensure_full_uri:
* @webdav: an #EWebDAVSession
- * @request_uri: a #SoupURI to which the @href belongs
+ * @request_uri: (nullable): a #SoupURI to which the @href belongs, or %NULL
* @href: a possibly path-only href
*
- * Converts possbily path-only @href into a full URI under
- * the @request_uri. Free the returned pointer with g_free(),
- * when no longer needed.
+ * Converts possibly path-only @href into a full URI under the @request_uri.
+ * When the @request_uri is %NULL, the URI defined in associated #ESource is
+ * used instead.
+ *
+ * Free the returned pointer with g_free(), when no longer needed.
*
* Returns: (transfer full): The @href as a full URI
*
@@ -1015,14 +1017,27 @@ e_webdav_session_ensure_full_uri (EWebDAVSession *webdav,
const gchar *href)
{
g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), NULL);
- g_return_val_if_fail (request_uri != NULL, NULL);
g_return_val_if_fail (href != NULL, NULL);
if (*href == '/' || !strstr (href, "://")) {
SoupURI *soup_uri;
gchar *full_uri;
- soup_uri = soup_uri_copy ((SoupURI *) request_uri);
+ if (request_uri) {
+ soup_uri = soup_uri_copy ((SoupURI *) request_uri);
+ } else {
+ ESource *source;
+ ESourceWebdav *webdav_extension;
+
+ source = e_soup_session_get_source (E_SOUP_SESSION (webdav));
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ soup_uri = e_source_webdav_dup_soup_uri (webdav_extension);
+ }
+
+ g_return_val_if_fail (soup_uri != NULL, NULL);
+
soup_uri_set_path (soup_uri, href);
soup_uri_set_user (soup_uri, NULL);
soup_uri_set_password (soup_uri, NULL);
@@ -1093,7 +1108,7 @@ e_webdav_session_comma_header_to_hashtable (SoupMessageHeaders *headers,
* value can be %NULL on success, it's when the server doesn't provide the information.
*
* The @out_allows contains a set of allowed methods returned by the server. Some known
- * are defined as E_WEBDAV_ALLOW_OPTIONS, and so on. The 'value' of the #GHashTable
+ * are defined as %SOUP_METHOD_OPTIONS, and so on. The 'value' of the #GHashTable
* doesn't have any particular meaning and the strings are compared case insensitively.
* Free the hash table with g_hash_table_destroy(), when no longer needed. The returned
* value can be %NULL on success, it's when the server doesn't provide the information.
@@ -1148,6 +1163,96 @@ e_webdav_session_options_sync (EWebDAVSession *webdav,
}
/**
+ * e_webdav_session_post_sync:
+ * @webdav: an #EWebDAVSession
+ * @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
+ * @data: data to post to the server
+ * @data_length: length of @data, or -1, when @data is NUL-terminated
+ * @out_content_type: (nullable) (transfer full): return location for response Content-Type, or %NULL
+ * @out_content: (nullable) (transfer full): return location for response content, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Issues POST request on the provided @uri, or, in case it's %NULL, on the URI
+ * defined in associated #ESource.
+ *
+ * The optional @out_content_type can be used to get content type of the response.
+ * Free it with g_free(), when no longer needed.
+ *
+ * The optional @out_content can be used to get actual result content. Free it
+ * with g_byte_array_free(), when no longer needed.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_webdav_session_post_sync (EWebDAVSession *webdav,
+ const gchar *uri,
+ const gchar *data,
+ gsize data_length,
+ gchar **out_content_type,
+ GByteArray **out_content,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupRequestHTTP *request;
+ SoupMessage *message;
+ GByteArray *bytes;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ if (out_content_type)
+ *out_content_type = NULL;
+
+ if (out_content)
+ *out_content = NULL;
+
+ if (data_length == (gsize) -1)
+ data_length = strlen (data);
+
+ request = e_webdav_session_new_request (webdav, SOUP_METHOD_POST, uri, error);
+ if (!request)
+ return FALSE;
+
+ message = soup_request_http_get_message (request);
+ if (!message) {
+ g_warn_if_fail (message != NULL);
+ g_object_unref (request);
+
+ return FALSE;
+ }
+
+ soup_message_set_request (message, E_WEBDAV_CONTENT_TYPE_XML,
+ SOUP_MEMORY_COPY, data, data_length);
+
+ bytes = e_soup_session_send_request_simple_sync (E_SOUP_SESSION (webdav), request, cancellable,
error);
+
+ success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to
post data"), error) &&
+ bytes != NULL;
+
+ if (success) {
+ if (out_content_type) {
+ *out_content_type = g_strdup (soup_message_headers_get_content_type
(message->response_headers, NULL));
+ }
+
+ if (out_content) {
+ *out_content = bytes;
+ bytes = NULL;
+ }
+ }
+
+ if (bytes)
+ g_byte_array_free (bytes, TRUE);
+ g_object_unref (message);
+ g_object_unref (request);
+
+ return success;
+}
+
+/**
* e_webdav_session_propfind_sync:
* @webdav: an #EWebDAVSession
* @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
@@ -1318,7 +1423,7 @@ e_webdav_session_proppatch_sync (EWebDAVSession *webdav,
* e_webdav_session_report_sync:
* @webdav: an #EWebDAVSession
* @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
- * @depth: (nullable): requested depth, can be %NULL, then %E_WEBDAV_DEPTH_THIS is used
+ * @depth: (nullable): requested depth, can be %NULL, then no Depth header is sent
* @xml: the request itself, as an #EXmlDocument
* @func: (nullable): an #EWebDAVPropstatTraverseFunc function to call for each DAV:propstat in the
multistatus response, or %NULL
* @func_user_data: user data passed to @func
@@ -1389,10 +1494,8 @@ e_webdav_session_report_sync (EWebDAVSession *webdav,
return FALSE;
}
- if (!depth)
- depth = E_WEBDAV_DEPTH_THIS;
-
- soup_message_headers_replace (message->request_headers, "Depth", depth);
+ if (depth)
+ soup_message_headers_replace (message->request_headers, "Depth", depth);
content = e_xml_document_get_content (xml, &content_length);
if (!content) {
@@ -1534,10 +1637,8 @@ e_webdav_session_mkcol_addressbook_sync (EWebDAVSession *webdav,
e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "set");
e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "prop");
e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "resourcetype");
- e_xml_document_start_element (xml, E_WEBDAV_NS_DAV, "collection");
- e_xml_document_end_element (xml);
- e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_DAV, "collection");
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook");
e_xml_document_end_element (xml); /* resourcetype */
if (display_name && *display_name) {
@@ -1841,6 +1942,7 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
SoupLoggerLogLevel log_level = e_soup_session_get_log_level (E_SOUP_SESSION (webdav));
gpointer buffer;
gsize nread = 0, nwritten;
+ gboolean first_chunk = TRUE;
buffer = g_malloc (BUFFER_SIZE);
@@ -1851,11 +1953,27 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
fflush (stdout);
}
+ if (first_chunk) {
+ GByteArray tmp_bytes;
+
+ first_chunk = FALSE;
+
+ tmp_bytes.data = buffer;
+ tmp_bytes.len = nread;
+
+ success = !e_webdav_session_replace_with_detailed_error (webdav, request,
&tmp_bytes, FALSE, _("Failed to read resource"), error);
+ if (!success)
+ break;
+ }
+
success = g_output_stream_write_all (out_stream, buffer, nread, &nwritten,
cancellable, error);
if (!success)
break;
}
+ if (success && first_chunk)
+ success = !e_webdav_session_replace_with_detailed_error (webdav, request, NULL,
FALSE, _("Failed to read resource"), error);
+
g_free (buffer);
}
@@ -1884,8 +2002,9 @@ e_webdav_session_get_sync (EWebDAVSession *webdav,
* reference a collection.
*
* The @out_bytes is filled by actual data being read. If not %NULL, @out_length
- * is populated with how many bytes had been read. Free the @out_bytes with g_free(),
- * when no longer needed.
+ * is populated with how many bytes had been read. The @out_bytes is always
+ * NUL-terminated, while this termination byte is not part of @out_length.
+ * Free the @out_bytes with g_free(), when no longer needed.
*
* Free returned pointer of @out_href and @out_etag, if not %NULL, with g_free(),
* when no longer needed.
@@ -1907,6 +2026,7 @@ e_webdav_session_get_data_sync (EWebDAVSession *webdav,
GError **error)
{
GOutputStream *output_stream;
+ gsize bytes_written = 0;
gboolean success;
g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
@@ -1920,11 +2040,13 @@ e_webdav_session_get_data_sync (EWebDAVSession *webdav,
output_stream = g_memory_output_stream_new_resizable ();
success = e_webdav_session_get_sync (webdav, uri, out_href, out_etag, output_stream, cancellable,
error) &&
+ g_output_stream_write_all (output_stream, "", 1, &bytes_written, cancellable, error) &&
g_output_stream_close (output_stream, cancellable, error);
if (success) {
if (out_length)
- *out_length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM
(output_stream));
+ *out_length = g_memory_output_stream_get_data_size (G_MEMORY_OUTPUT_STREAM
(output_stream)) - 1;
+
*out_bytes = g_memory_output_stream_steal_data (G_MEMORY_OUTPUT_STREAM (output_stream));
}
@@ -3075,8 +3197,7 @@ e_webdav_session_getctag_sync (EWebDAVSession *webdav,
e_xml_document_add_namespaces (xml, "CS", E_WEBDAV_NS_CALENDARSERVER, NULL);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
- e_xml_document_end_element (xml); /* getctag */
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -3363,8 +3484,7 @@ e_webdav_session_list_sync (EWebDAVSession *webdav,
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "resourcetype");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "resourcetype");
if ((flags & E_WEBDAV_LIST_SUPPORTS) != 0 ||
(flags & E_WEBDAV_LIST_DESCRIPTION) != 0 ||
@@ -3373,60 +3493,49 @@ e_webdav_session_list_sync (EWebDAVSession *webdav,
}
if ((flags & E_WEBDAV_LIST_SUPPORTS) != 0) {
- e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "supported-calendar-component-set");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV,
"supported-calendar-component-set");
}
if ((flags & E_WEBDAV_LIST_DISPLAY_NAME) != 0) {
- e_xml_document_start_element (xml, NULL, "displayname");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "displayname");
}
if ((flags & E_WEBDAV_LIST_ETAG) != 0) {
- e_xml_document_start_element (xml, NULL, "getetag");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "getetag");
e_xml_document_add_namespaces (xml, "CS", E_WEBDAV_NS_CALENDARSERVER, NULL);
- e_xml_document_start_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALENDARSERVER, "getctag");
}
if ((flags & E_WEBDAV_LIST_CONTENT_TYPE) != 0) {
- e_xml_document_start_element (xml, NULL, "getcontenttype");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "getcontenttype");
}
if ((flags & E_WEBDAV_LIST_CONTENT_LENGTH) != 0) {
- e_xml_document_start_element (xml, NULL, "getcontentlength");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "getcontentlength");
}
if ((flags & E_WEBDAV_LIST_CREATION_DATE) != 0) {
- e_xml_document_start_element (xml, NULL, "creationdate");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "creationdate");
}
if ((flags & E_WEBDAV_LIST_LAST_MODIFIED) != 0) {
- e_xml_document_start_element (xml, NULL, "getlastmodified");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "getlastmodified");
}
if ((flags & E_WEBDAV_LIST_DESCRIPTION) != 0) {
- e_xml_document_start_element (xml, E_WEBDAV_NS_CALDAV, "calendar-description");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CALDAV, "calendar-description");
e_xml_document_add_namespaces (xml, "A", E_WEBDAV_NS_CARDDAV, NULL);
- e_xml_document_start_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-description");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_CARDDAV, "addressbook-description");
}
if ((flags & E_WEBDAV_LIST_COLOR) != 0) {
e_xml_document_add_namespaces (xml, "IC", E_WEBDAV_NS_ICAL, NULL);
- e_xml_document_start_element (xml, E_WEBDAV_NS_ICAL, "calendar-color");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, E_WEBDAV_NS_ICAL, "calendar-color");
}
e_xml_document_end_element (xml); /* prop */
@@ -3531,10 +3640,9 @@ e_webdav_session_update_properties_sync (EWebDAVSession *webdav,
case E_WEBDAV_PROPERTY_REMOVE:
e_xml_document_start_element (xml, NULL, "remove");
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, change->ns_uri, change->name);
- e_xml_document_end_element (xml); /* change->name */
+ e_xml_document_add_empty_element (xml, change->ns_uri, change->name);
e_xml_document_end_element (xml); /* prop */
- e_xml_document_end_element (xml); /* set */
+ e_xml_document_end_element (xml); /* remove */
break;
}
}
@@ -3595,19 +3703,16 @@ e_webdav_session_lock_resource_sync (EWebDAVSession *webdav,
e_xml_document_start_element (xml, NULL, "lockscope");
switch (lock_scope) {
case E_WEBDAV_LOCK_EXCLUSIVE:
- e_xml_document_start_element (xml, NULL, "exclusive");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "exclusive");
break;
case E_WEBDAV_LOCK_SHARED:
- e_xml_document_start_element (xml, NULL, "shared");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "shared");
break;
}
e_xml_document_end_element (xml); /* lockscope */
e_xml_document_start_element (xml, NULL, "locktype");
- e_xml_document_start_element (xml, NULL, "write");
- e_xml_document_end_element (xml); /* write */
+ e_xml_document_add_empty_element (xml, NULL, "write");
e_xml_document_end_element (xml); /* locktype */
e_xml_document_start_text_element (xml, NULL, "owner");
@@ -3830,7 +3935,7 @@ e_webdav_session_acl_sync (EWebDAVSession *webdav,
bytes = e_soup_session_send_request_simple_sync (E_SOUP_SESSION (webdav), request, cancellable,
error);
- success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to
get properties"), error) &&
+ success = !e_webdav_session_replace_with_detailed_error (webdav, request, bytes, TRUE, _("Failed to
get access control list"), error) &&
bytes != NULL;
if (bytes)
@@ -3880,8 +3985,7 @@ e_webdav_session_get_supported_privilege_set_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "supported-privilege-set");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "supported-privilege-set");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4013,8 +4117,7 @@ e_webdav_session_get_current_user_privilege_set_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "current-user-privilege-set");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "current-user-privilege-set");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4288,8 +4391,7 @@ e_webdav_session_get_acl_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "acl");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "acl");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4399,8 +4501,7 @@ e_webdav_session_get_acl_restrictions_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "acl-restrictions");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "acl-restrictions");
e_xml_document_end_element (xml); /* prop */
ard.out_restrictions = out_restrictions;
@@ -4496,8 +4597,7 @@ e_webdav_session_get_principal_collection_set_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "principal-collection-set");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "principal-collection-set");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_propfind_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4622,28 +4722,23 @@ e_webdav_session_set_acl_sync (EWebDAVSession *webdav,
e_xml_document_end_element (xml);
break;
case E_WEBDAV_ACE_PRINCIPAL_ALL:
- e_xml_document_start_element (xml, NULL, "all");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "all");
break;
case E_WEBDAV_ACE_PRINCIPAL_AUTHENTICATED:
- e_xml_document_start_element (xml, NULL, "authenticated");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "authenticated");
break;
case E_WEBDAV_ACE_PRINCIPAL_UNAUTHENTICATED:
- e_xml_document_start_element (xml, NULL, "unauthenticated");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "unauthenticated");
break;
case E_WEBDAV_ACE_PRINCIPAL_PROPERTY:
g_warn_if_reached ();
break;
case E_WEBDAV_ACE_PRINCIPAL_SELF:
- e_xml_document_start_element (xml, NULL, "self");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "self");
break;
case E_WEBDAV_ACE_PRINCIPAL_OWNER:
e_xml_document_start_element (xml, NULL, "property");
- e_xml_document_start_element (xml, NULL, "owner");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "owner");
e_xml_document_end_element (xml);
break;
@@ -4671,8 +4766,7 @@ e_webdav_session_set_acl_sync (EWebDAVSession *webdav,
}
e_xml_document_start_element (xml, NULL, "privilege");
- e_xml_document_start_element (xml, privilege->ns_uri, privilege->name);
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, privilege->ns_uri, privilege->name);
e_xml_document_end_element (xml); /* privilege */
}
@@ -4733,7 +4827,9 @@ e_webdav_session_principal_property_search_cb (EWebDAVSession *webdav,
* @webdav: an #EWebDAVSession
* @uri: (nullable): URI to issue the request for, or %NULL to read from #ESource
* @apply_to_principal_collection_set: whether to apply to principal-collection-set
- * @match_displayname: a string to match DAV:displayname with
+ * @match_ns_uri: (nullable): namespace URI of the property to search in, or %NULL for %E_WEBDAV_NS_DAV
+ * @match_property: name of the property to search in
+ * @match_value: a string value to search for
* @out_principals: (out) (transfer full) (element-type EWebDAVResource): return location for matching
principals
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
@@ -4741,7 +4837,8 @@ e_webdav_session_principal_property_search_cb (EWebDAVSession *webdav,
* Issues a DAV:principal-property-search for the @uri, or, in case it's %NULL,
* for the URI defined in associated #ESource. The DAV:principal-property-search
* performs a search for all principals whose properties contain character data
- * that matches the search criteria @match_displayname.
+ * that matches the search criteria @match_value in @match_property property
+ * of namespace @match_ns_uri.
*
* By default, the function searches all members (at any depth) of the collection
* identified by the @uri. If @apply_to_principal_collection_set is set to %TRUE,
@@ -4765,7 +4862,9 @@ gboolean
e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
const gchar *uri,
gboolean apply_to_principal_collection_set,
- const gchar *match_displayname,
+ const gchar *match_ns_uri,
+ const gchar *match_property,
+ const gchar *match_value,
GSList **out_principals,
GCancellable *cancellable,
GError **error)
@@ -4774,7 +4873,8 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
gboolean success;
g_return_val_if_fail (E_IS_WEBDAV_SESSION (webdav), FALSE);
- g_return_val_if_fail (match_displayname != NULL, FALSE);
+ g_return_val_if_fail (match_property != NULL, FALSE);
+ g_return_val_if_fail (match_value != NULL, FALSE);
g_return_val_if_fail (out_principals != NULL, FALSE);
*out_principals = NULL;
@@ -4783,23 +4883,20 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
g_return_val_if_fail (xml != NULL, FALSE);
if (apply_to_principal_collection_set) {
- e_xml_document_start_element (xml, NULL, "apply-to-principal-collection-set");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "apply-to-principal-collection-set");
}
e_xml_document_start_element (xml, NULL, "property-search");
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "displayname");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, match_ns_uri, match_property);
e_xml_document_end_element (xml); /* prop */
e_xml_document_start_text_element (xml, NULL, "match");
- e_xml_document_write_string (xml, match_displayname);
+ e_xml_document_write_string (xml, match_value);
e_xml_document_end_element (xml); /* match */
e_xml_document_end_element (xml); /* property-search */
e_xml_document_start_element (xml, NULL, "prop");
- e_xml_document_start_element (xml, NULL, "displayname");
- e_xml_document_end_element (xml);
+ e_xml_document_add_empty_element (xml, NULL, "displayname");
e_xml_document_end_element (xml); /* prop */
success = e_webdav_session_report_sync (webdav, uri, E_WEBDAV_DEPTH_THIS, xml,
@@ -4815,7 +4912,7 @@ e_webdav_session_principal_property_search_sync (EWebDAVSession *webdav,
/**
* e_webdav_session_util_maybe_dequote:
- * @text: (inout): text to dequote
+ * @text: (inout) (nullable): text to dequote
*
* Dequotes @text, if it's enclosed in double-quotes. The function
* changes @text, it doesn't allocate new string. The function does
diff --git a/src/libedataserver/e-webdav-session.h b/src/libedataserver/e-webdav-session.h
index abbec06..8d15080 100644
--- a/src/libedataserver/e-webdav-session.h
+++ b/src/libedataserver/e-webdav-session.h
@@ -63,20 +63,6 @@ G_BEGIN_DECLS
#define E_WEBDAV_CAPABILITY_CALENDAR_AUTO_SCHEDULE "calendar-auto-schedule"
#define E_WEBDAV_CAPABILITY_CALENDAR_PROXY "calendar-proxy"
-#define E_WEBDAV_ALLOW_OPTIONS "OPTIONS"
-#define E_WEBDAV_ALLOW_PROPFIND "PROPFIND"
-#define E_WEBDAV_ALLOW_REPORT "REPORT"
-#define E_WEBDAV_ALLOW_DELETE "DELETE"
-#define E_WEBDAV_ALLOW_GET "GET"
-#define E_WEBDAV_ALLOW_PUT "PUT"
-#define E_WEBDAV_ALLOW_HEAD "HEAD"
-#define E_WEBDAV_ALLOW_ACL "ACL"
-#define E_WEBDAV_ALLOW_LOCK "LOCK"
-#define E_WEBDAV_ALLOW_UNLOCK "UNLOCK"
-#define E_WEBDAV_ALLOW_MOVE "MOVE"
-#define E_WEBDAV_ALLOW_MKTICKET "MKTICKET"
-#define E_WEBDAV_ALLOW_DELTICKET "DELTICKET"
-
#define E_WEBDAV_DEPTH_THIS "0"
#define E_WEBDAV_DEPTH_THIS_AND_CHILDREN "1"
#define E_WEBDAV_DEPTH_INFINITY "infinity"
@@ -369,6 +355,14 @@ gboolean e_webdav_session_options_sync (EWebDAVSession *webdav,
GHashTable **out_allows,
GCancellable *cancellable,
GError **error);
+gboolean e_webdav_session_post_sync (EWebDAVSession *webdav,
+ const gchar *uri,
+ const gchar *data,
+ gsize data_length,
+ gchar **out_content_type,
+ GByteArray **out_content,
+ GCancellable *cancellable,
+ GError **error);
gboolean e_webdav_session_propfind_sync (EWebDAVSession *webdav,
const gchar *uri,
const gchar *depth,
@@ -574,7 +568,9 @@ gboolean e_webdav_session_principal_property_search_sync
(EWebDAVSession *webdav,
const gchar *uri,
gboolean apply_to_principal_collection_set,
- const gchar *match_displayname,
+ const gchar *match_ns_uri,
+ const gchar *match_property,
+ const gchar *match_value,
GSList **out_principals, /* EWebDAVResource * */
GCancellable *cancellable,
GError **error);
diff --git a/src/libedataserver/e-xml-document.c b/src/libedataserver/e-xml-document.c
index 9212df4..c195699 100644
--- a/src/libedataserver/e-xml-document.c
+++ b/src/libedataserver/e-xml-document.c
@@ -424,6 +424,33 @@ e_xml_document_end_element (EXmlDocument *xml)
}
/**
+ * e_xml_document_add_empty_element:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new element, or %NULL
+ * @name: name of the new element
+ *
+ * Adds an empty element, which is an element with no attribute and no value.
+ *
+ * It's the same as calling e_xml_document_start_element() immediately
+ * followed by e_xml_document_end_element().
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_empty_element (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name)
+{
+ g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (*name);
+ g_return_if_fail (xml->priv->current_element != NULL);
+
+ e_xml_document_start_element (xml, ns_href, name);
+ e_xml_document_end_element (xml);
+}
+
+/**
* e_xml_document_add_attribute:
* @xml: an #EXmlDocument
* @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
@@ -456,6 +483,104 @@ e_xml_document_add_attribute (EXmlDocument *xml,
}
/**
+ * e_xml_document_add_attribute_int:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: integer value of the attribute
+ *
+ * Adds a new attribute with an integer value to the current element.
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_int (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ gint64 value)
+{
+ gchar *strvalue;
+
+ g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+ g_return_if_fail (xml->priv->current_element != NULL);
+ g_return_if_fail (name != NULL);
+
+ strvalue = g_strdup_printf ("%" G_GINT64_FORMAT, value);
+ e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+ g_free (strvalue);
+}
+
+/**
+ * e_xml_document_add_attribute_double:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: double value of the attribute
+ *
+ * Adds a new attribute with a double value to the current element.
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_double (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ gdouble value)
+{
+ gchar *strvalue;
+
+ g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+ g_return_if_fail (xml->priv->current_element != NULL);
+ g_return_if_fail (name != NULL);
+
+ strvalue = g_strdup_printf ("%f", value);
+ e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+ g_free (strvalue);
+}
+
+/**
+ * e_xml_document_add_attribute_time:
+ * @xml: an #EXmlDocument
+ * @ns_href: (nullable): optional namespace href for the new attribute, or %NULL
+ * @name: name of the attribute
+ * @value: time_t value of the attribute
+ *
+ * Adds a new attribute with a time_t value in ISO 8601 format to the current element.
+ * The format is "YYYY-MM-DDTHH:MM:SSZ".
+ * Use %NULL @ns_href, to use the default namespace, otherwise either previously
+ * added namespace with the same href from e_xml_document_add_namespaces() is picked,
+ * or a new namespace with generated prefix is added.
+ *
+ * Since: 3.26
+ **/
+void
+e_xml_document_add_attribute_time (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ time_t value)
+{
+ GTimeVal tv;
+ gchar *strvalue;
+
+ g_return_if_fail (E_IS_XML_DOCUMENT (xml));
+ g_return_if_fail (xml->priv->current_element != NULL);
+ g_return_if_fail (name != NULL);
+
+ tv.tv_usec = 0;
+ tv.tv_sec = value;
+
+ strvalue = g_time_val_to_iso8601 (&tv);
+ e_xml_document_add_attribute (xml, ns_href, name, strvalue);
+ g_free (strvalue);
+}
+
+/**
* e_xml_document_write_int:
* @xml: an #EXmlDocument
* @value: value to write as the content
@@ -533,6 +658,7 @@ e_xml_document_write_base64 (EXmlDocument *xml,
* @value: value to write as the content
*
* Writes @value in ISO 8601 format as content of the current element.
+ * The format is "YYYY-MM-DDTHH:MM:SSZ".
*
* Since: 3.26
**/
diff --git a/src/libedataserver/e-xml-document.h b/src/libedataserver/e-xml-document.h
index afadf2e..e1fcf94 100644
--- a/src/libedataserver/e-xml-document.h
+++ b/src/libedataserver/e-xml-document.h
@@ -91,10 +91,29 @@ void e_xml_document_start_text_element
const gchar *ns_href,
const gchar *name);
void e_xml_document_end_element (EXmlDocument *xml);
+void e_xml_document_add_empty_element
+ (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name);
void e_xml_document_add_attribute (EXmlDocument *xml,
const gchar *ns_href,
const gchar *name,
const gchar *value);
+void e_xml_document_add_attribute_int
+ (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ gint64 value);
+void e_xml_document_add_attribute_double
+ (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ gdouble value);
+void e_xml_document_add_attribute_time
+ (EXmlDocument *xml,
+ const gchar *ns_href,
+ const gchar *name,
+ time_t value);
void e_xml_document_write_int (EXmlDocument *xml,
gint64 value);
void e_xml_document_write_double (EXmlDocument *xml,
diff --git a/tests/libedata-cal/test-cal-meta-backend.c b/tests/libedata-cal/test-cal-meta-backend.c
index 8524c60..63e481d 100644
--- a/tests/libedata-cal/test-cal-meta-backend.c
+++ b/tests/libedata-cal/test-cal-meta-backend.c
@@ -318,6 +318,7 @@ e_cal_meta_backend_test_disconnect_sync (ECalMetaBackend *meta_backend,
static gboolean
e_cal_meta_backend_test_get_changes_sync (ECalMetaBackend *meta_backend,
const gchar *last_sync_tag,
+ gboolean is_repeat,
gchar **out_new_sync_tag,
gboolean *out_repeat,
GSList **out_created_objects,
@@ -352,7 +353,7 @@ e_cal_meta_backend_test_get_changes_sync (ECalMetaBackend *meta_backend,
/* Nothing to do here at the moment, left the work to the parent class,
which calls list_existing_sync() internally. */
return E_CAL_META_BACKEND_CLASS (e_cal_meta_backend_test_parent_class)->get_changes_sync
(meta_backend,
- last_sync_tag, out_new_sync_tag, out_repeat, out_created_objects,
+ last_sync_tag, is_repeat, out_new_sync_tag, out_repeat, out_created_objects,
out_modified_objects, out_removed_objects, cancellable, error);
}
@@ -395,7 +396,7 @@ e_cal_meta_backend_test_list_existing_sync (ECalMetaBackend *meta_backend,
uid = icalcomponent_get_uid (icalcomp);
revision = e_cal_cache_dup_component_revision (cal_cache, icalcomp);
- nfo = e_cal_meta_backend_info_new (uid, NULL, revision, NULL);
+ nfo = e_cal_meta_backend_info_new (uid, revision, NULL, NULL);
*out_existing_objects = g_slist_prepend (*out_existing_objects, nfo);
g_free (revision);
@@ -413,6 +414,7 @@ e_cal_meta_backend_test_save_component_sync (ECalMetaBackend *meta_backend,
const GSList *instances,
const gchar *extra,
gchar **out_new_uid,
+ gchar **out_new_extra,
GCancellable *cancellable,
GError **error)
{
@@ -476,6 +478,7 @@ e_cal_meta_backend_test_save_component_sync (ECalMetaBackend *meta_backend,
static gboolean
e_cal_meta_backend_test_load_component_sync (ECalMetaBackend *meta_backend,
const gchar *uid,
+ const gchar *extra,
icalcomponent **out_instances,
gchar **out_extra,
GCancellable *cancellable,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]