[evolution-ews] Bug 625149 - Support offline for books/calendars
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-ews] Bug 625149 - Support offline for books/calendars
- Date: Mon, 19 Jun 2017 16:33:43 +0000 (UTC)
commit aea7a676fcbaf19c725f96fd981326e24e02681a
Author: Milan Crha <mcrha redhat com>
Date: Mon Jun 19 18:33:00 2017 +0200
Bug 625149 - Support offline for books/calendars
src/addressbook/e-book-backend-ews.c | 137 +++++++++++++++++++++++++++++++++-
src/calendar/e-cal-backend-ews.c | 90 ++++++++++++++++++++++-
src/server/e-ews-connection.c | 3 +-
3 files changed, 227 insertions(+), 3 deletions(-)
---
diff --git a/src/addressbook/e-book-backend-ews.c b/src/addressbook/e-book-backend-ews.c
index c37a79d..205c9e4 100644
--- a/src/addressbook/e-book-backend-ews.c
+++ b/src/addressbook/e-book-backend-ews.c
@@ -56,6 +56,8 @@
#define EDB_ERROR(_code) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, NULL)
#define EDB_ERROR_EX(_code,_msg) e_data_book_create_error (E_DATA_BOOK_STATUS_ ## _code, _msg)
+#define X_EWS_ORIGINAL_VCARD "X-EWS-ORIGINAL-VCARD"
+
#define EWS_MAX_FETCH_COUNT 500
#define ELEMENT_TYPE_SIMPLE 0x01 /* simple string fields */
@@ -132,6 +134,9 @@ ebb_ews_convert_error_to_edb_error (GError **perror)
case EWS_CONNECTION_ERROR_ITEMNOTFOUND:
error = EDB_ERROR_EX (CONTACT_NOT_FOUND, (*perror)->message);
break;
+ case EWS_CONNECTION_ERROR_UNAVAILABLE:
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
(*perror)->message);
+ break;
}
if (!error)
@@ -1839,6 +1844,8 @@ ebb_ews_unset_connection (EBookBackendEws *bbews)
}
}
+ g_clear_object (&bbews->priv->cnc);
+
g_rec_mutex_unlock (&bbews->priv->cnc_lock);
}
@@ -2202,6 +2209,61 @@ ebb_ews_check_gal_changes (EBookBackendEws *bbews,
return success;
}
+static void
+ebb_ews_remove_original_vcard (EContact *contact)
+{
+ g_return_if_fail (E_IS_CONTACT (contact));
+
+ e_vcard_remove_attributes (E_VCARD (contact), NULL, X_EWS_ORIGINAL_VCARD);
+}
+
+static void
+ebb_ews_store_original_vcard (EContact *contact)
+{
+ EVCard *vcard;
+ EVCardAttribute *attr;
+ gchar *vcard_str;
+
+ g_return_if_fail (E_IS_CONTACT (contact));
+
+ ebb_ews_remove_original_vcard (contact);
+
+ vcard = E_VCARD (contact);
+
+ vcard_str = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
+
+ attr = e_vcard_attribute_new ("", X_EWS_ORIGINAL_VCARD);
+ e_vcard_attribute_add_value (attr, vcard_str);
+ e_vcard_add_attribute (vcard, attr);
+
+ g_free (vcard_str);
+}
+
+static const gchar *
+ebb_ews_get_original_vcard (EContact *contact)
+{
+ EVCardAttribute *attr;
+ GList *values = NULL;
+ const gchar *vcard;
+
+ g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+ attr = e_vcard_get_attribute (E_VCARD (contact), X_EWS_ORIGINAL_VCARD);
+ if (!attr)
+ return NULL;
+
+ values = e_vcard_attribute_get_values (attr);
+ if (!values)
+ return NULL;
+
+ vcard = values->data;
+
+ if (vcard && *vcard)
+ return vcard;
+
+ return NULL;
+}
+
typedef struct {
/* For future use */
gpointer restriction;
@@ -2533,6 +2595,8 @@ ebb_ews_update_cache_for_expression (EBookBackendEws *bbews,
}
}
+ ebb_ews_store_original_vcard (contact);
+
nfo = e_book_meta_backend_info_new (e_contact_get_const (contact,
E_CONTACT_UID),
e_contact_get_const (contact, E_CONTACT_REV), NULL, NULL);
nfo->object = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
@@ -2619,6 +2683,8 @@ ebb_ews_contacts_to_infos (const GSList *contacts) /* EContact * */
if (!E_IS_CONTACT (contact))
continue;
+ ebb_ews_store_original_vcard (contact);
+
nfo = e_book_meta_backend_info_new (
e_contact_get_const (contact, E_CONTACT_UID),
e_contact_get_const (contact, E_CONTACT_REV),
@@ -2996,9 +3062,12 @@ ebb_ews_load_contact_sync (EBookMetaBackend *meta_backend,
GSList *contacts = NULL;
success = ebb_ews_fetch_items_sync (bbews, items, &contacts, cancellable, error);
- if (success && contacts)
+ if (success && contacts) {
*out_contact = g_object_ref (contacts->data);
+ ebb_ews_store_original_vcard (*out_contact);
+ }
+
g_slist_free_full (contacts, g_object_unref);
}
@@ -3057,6 +3126,25 @@ ebb_ews_save_contact_sync (EBookMetaBackend *meta_backend,
success = e_book_cache_get_contact (book_cache, e_contact_get_const (contact, E_CONTACT_UID),
FALSE, &old_contact, cancellable, error);
if (success) {
+ const gchar *original_vcard;
+
+ /* This is for offline changes, where the EContact in the cache
+ is already modified, while the original, the one on the server,
+ is different. Using the cached EContact in this case generates
+ empty UpdateItem request and nothing is saved. */
+ original_vcard = ebb_ews_get_original_vcard (old_contact);
+ if (original_vcard) {
+ EContact *tmp;
+
+ tmp = e_contact_new_from_vcard (original_vcard);
+ if (tmp) {
+ g_object_unref (old_contact);
+ old_contact = tmp;
+ }
+ }
+ }
+
+ if (success) {
ConvertData cd;
const gchar *conflict_res = "AlwaysOverwrite";
@@ -3271,6 +3359,49 @@ ebb_ews_get_backend_property (EBookBackend *book_backend,
return E_BOOK_BACKEND_CLASS (e_book_backend_ews_parent_class)->get_backend_property (book_backend,
prop_name);
}
+static gboolean
+ebb_ews_get_destination_address (EBackend *backend,
+ gchar **host,
+ guint16 *port)
+{
+ CamelEwsSettings *ews_settings;
+ SoupURI *soup_uri;
+ gchar *host_url;
+ gboolean result = FALSE;
+
+ g_return_val_if_fail (port != NULL, FALSE);
+ g_return_val_if_fail (host != NULL, FALSE);
+
+ /* Sanity checking */
+ if (!e_book_backend_get_registry (E_BOOK_BACKEND (backend)) ||
+ !e_backend_get_source (backend))
+ return FALSE;
+
+ ews_settings = ebb_ews_get_collection_settings (E_BOOK_BACKEND_EWS (backend));
+ g_return_val_if_fail (ews_settings != NULL, FALSE);
+
+ host_url = camel_ews_settings_dup_hosturl (ews_settings);
+ g_return_val_if_fail (host_url != NULL, FALSE);
+
+ soup_uri = soup_uri_new (host_url);
+ if (soup_uri) {
+ *host = g_strdup (soup_uri_get_host (soup_uri));
+ *port = soup_uri_get_port (soup_uri);
+
+ result = *host && **host;
+ if (!result) {
+ g_free (*host);
+ *host = NULL;
+ }
+
+ soup_uri_free (soup_uri);
+ }
+
+ g_free (host_url);
+
+ return result;
+}
+
static void
e_book_backend_ews_constructed (GObject *object)
{
@@ -3334,6 +3465,7 @@ static void
e_book_backend_ews_class_init (EBookBackendEwsClass *klass)
{
GObjectClass *object_class;
+ EBackendClass *backend_class;
EBookBackendClass *book_backend_class;
EBookMetaBackendClass *book_meta_backend_class;
@@ -3354,6 +3486,9 @@ e_book_backend_ews_class_init (EBookBackendEwsClass *klass)
book_backend_class = E_BOOK_BACKEND_CLASS (klass);
book_backend_class->get_backend_property = ebb_ews_get_backend_property;
+ backend_class = E_BACKEND_CLASS (klass);
+ backend_class->get_destination_address = ebb_ews_get_destination_address;
+
object_class = G_OBJECT_CLASS (klass);
object_class->constructed = e_book_backend_ews_constructed;
object_class->dispose = e_book_backend_ews_dispose;
diff --git a/src/calendar/e-cal-backend-ews.c b/src/calendar/e-cal-backend-ews.c
index 57495a6..7adac7b 100644
--- a/src/calendar/e-cal-backend-ews.c
+++ b/src/calendar/e-cal-backend-ews.c
@@ -73,6 +73,8 @@ struct _ECalBackendEwsPrivate {
gchar *attachments_dir;
};
+#define X_EWS_ORIGINAL_COMP "X-EWS-ORIGINAL-COMP"
+
#define EWS_MAX_FETCH_COUNT 100
#define GET_ITEMS_SYNC_PROPERTIES \
@@ -165,6 +167,9 @@ ecb_ews_convert_error_to_edc_error (GError **perror)
case EWS_CONNECTION_ERROR_ITEMNOTFOUND:
error = EDC_ERROR_EX (ObjectNotFound, (*perror)->message);
break;
+ case EWS_CONNECTION_ERROR_UNAVAILABLE:
+ g_set_error_literal (&error, G_IO_ERROR, G_IO_ERROR_HOST_NOT_FOUND,
(*perror)->message);
+ break;
}
if (!error)
@@ -235,6 +240,8 @@ ecb_ews_unset_connection (ECalBackendEws *cbews)
}
}
+ g_clear_object (&cbews->priv->cnc);
+
g_rec_mutex_unlock (&cbews->priv->cnc_lock);
}
@@ -860,6 +867,65 @@ ecb_ews_item_to_component_sync (ECalBackendEws *cbews,
return res_component;
}
+static void
+ecb_ews_store_original_comp (ECalComponent *comp)
+{
+ gchar *comp_str;
+ gchar *base64;
+
+ g_return_if_fail (E_IS_CAL_COMPONENT (comp));
+
+ comp_str = e_cal_component_get_as_string (comp);
+ g_return_if_fail (comp_str != NULL);
+
+ /* Include NUL-terminator */
+ base64 = g_base64_encode ((const guchar *) comp_str, strlen (comp_str) + 1);
+
+ e_cal_util_set_x_property (e_cal_component_get_icalcomponent (comp),
+ X_EWS_ORIGINAL_COMP, base64);
+
+ g_free (base64);
+ g_free (comp_str);
+}
+
+static ECalComponent * /* free with g_object_unref(), if not NULL */
+ecb_ews_restore_original_comp (ECalComponent *from_comp)
+{
+ ECalComponent *comp = NULL;
+ const gchar *original_base64;
+ guchar *decoded;
+ gsize len = -1;
+
+ g_return_val_if_fail (E_IS_CAL_COMPONENT (from_comp), NULL);
+
+ original_base64 = e_cal_util_get_x_property (e_cal_component_get_icalcomponent (from_comp),
X_EWS_ORIGINAL_COMP);
+
+ if (!original_base64 || !*original_base64)
+ return NULL;
+
+ decoded = g_base64_decode (original_base64, &len);
+ if (!decoded || !*decoded || len <= 0) {
+ g_free (decoded);
+ return NULL;
+ }
+
+ if (decoded[len - 1] != '\0') {
+ gchar *tmp;
+
+ tmp = g_strndup ((const gchar *) decoded, len);
+
+ g_free (decoded);
+ decoded = (guchar *) tmp;
+ }
+
+ if (decoded && *decoded)
+ comp = e_cal_component_new_from_string ((const gchar *) decoded);
+
+ g_free (decoded);
+
+ return comp;
+}
+
static gboolean
ecb_ews_get_items_sync (ECalBackendEws *cbews,
const GSList *item_ids, /* gchar * */
@@ -953,6 +1019,8 @@ ecb_ews_get_items_sync (ECalBackendEws *cbews,
break;
}
+ ecb_ews_store_original_comp (comp);
+
*out_components = g_slist_prepend (*out_components, comp);
}
}
@@ -1122,6 +1190,8 @@ ecb_ews_components_to_infos (ECalMetaBackend *meta_backend,
if (!uid)
continue;
+ ecb_ews_store_original_comp (comp);
+
instances = g_hash_table_lookup (sorted_by_uids, uid);
g_hash_table_insert (sorted_by_uids, (gpointer) uid, g_slist_prepend (instances, comp));
}
@@ -1850,7 +1920,7 @@ ecb_ews_filter_out_unchanged_instances (const GSList *to_save_instances,
for (link = (GSList *) existing_instances; link; link = g_slist_next (link)) {
ECalComponent *comp = link->data;
- ECalComponentId *id = NULL;
+ ECalComponentId *id;
id = e_cal_component_get_id (comp);
if (id)
@@ -2404,6 +2474,24 @@ ecb_ews_save_component_sync (ECalMetaBackend *meta_backend,
success = uid && e_cal_cache_get_components_by_uid (cal_cache, uid, &existing, cancellable,
error) && existing;
+ if (success) {
+ GSList *link;
+
+ /* This is for offline changes, where the component in the cache
+ is already modified, while the original, the one on the server,
+ is different. Using the cached component in this case generates
+ empty UpdateItem request and nothing is saved. */
+ for (link = existing; link; link = g_slist_next (link)) {
+ ECalComponent *comp = link->data;
+
+ comp = ecb_ews_restore_original_comp (comp);
+ if (comp) {
+ g_object_unref (link->data);
+ link->data = comp;
+ }
+ }
+ }
+
if (success)
ecb_ews_filter_out_unchanged_instances (instances, existing, &changed_instances,
&removed_instances);
diff --git a/src/server/e-ews-connection.c b/src/server/e-ews-connection.c
index 48517ca..0c15ec5 100644
--- a/src/server/e-ews-connection.c
+++ b/src/server/e-ews-connection.c
@@ -797,7 +797,8 @@ ews_response_cb (SoupSession *session,
} else if (msg->status_code == SOUP_STATUS_CANT_RESOLVE ||
msg->status_code == SOUP_STATUS_CANT_RESOLVE_PROXY ||
msg->status_code == SOUP_STATUS_CANT_CONNECT ||
- msg->status_code == SOUP_STATUS_CANT_CONNECT_PROXY) {
+ msg->status_code == SOUP_STATUS_CANT_CONNECT_PROXY ||
+ msg->status_code == SOUP_STATUS_IO_ERROR) {
g_simple_async_result_set_error (
enode->simple,
EWS_CONNECTION_ERROR,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]