[evolution-data-server] Bug #711053 - WebDAV: Do not overwrite UID of a contact
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug #711053 - WebDAV: Do not overwrite UID of a contact
- Date: Tue, 11 Mar 2014 16:28:01 +0000 (UTC)
commit 2cce4fb4f5397a9d0aa4bec4ec0574ecddd5d685
Author: Milan Crha <mcrha redhat com>
Date: Tue Mar 11 17:23:15 2014 +0100
Bug #711053 - WebDAV: Do not overwrite UID of a contact
Apart of aforementioned fix to not overwrite contact UIDs, this also fixes:
- store href in a new vCard attribute, similar to etag
- possible slowness and busy loop of constant rewrite of a local cache on update
- removal of deleted contacts on the server from local cache
- preserve contact UID on create, if not used already
- be able to get all contacts with e_book_backend_cache_get_contacts()
.../backends/webdav/e-book-backend-webdav.c | 238 ++++++++++++++++----
addressbook/libedata-book/e-book-backend-cache.c | 2 +-
2 files changed, 192 insertions(+), 48 deletions(-)
---
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c
b/addressbook/backends/webdav/e-book-backend-webdav.c
index 328a1db..2cdae05 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -47,8 +47,9 @@
#define WEBDAV_CLOSURE_NAME "EBookBackendWebdav.BookView::closure"
#define WEBDAV_CTAG_KEY "WEBDAV_CTAG"
#define WEBDAV_CACHE_VERSION_KEY "WEBDAV_CACHE_VERSION"
-#define WEBDAV_CACHE_VERSION "1"
+#define WEBDAV_CACHE_VERSION "2"
#define WEBDAV_CONTACT_ETAG "X-EVOLUTION-WEBDAV-ETAG"
+#define WEBDAV_CONTACT_HREF "X-EVOLUTION-WEBDAV-HREF"
/* Forward Declarations */
static void e_book_backend_webdav_source_authenticator_init
@@ -148,6 +149,47 @@ webdav_contact_get_etag (EContact *contact)
}
static void
+webdav_contact_set_href (EContact *contact,
+ const gchar *href)
+{
+ EVCardAttribute *attr;
+
+ g_return_if_fail (E_IS_CONTACT (contact));
+
+ attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_HREF);
+
+ if (attr) {
+ e_vcard_attribute_remove_values (attr);
+ if (href) {
+ e_vcard_attribute_add_value (attr, href);
+ } else {
+ e_vcard_remove_attribute (E_VCARD (contact), attr);
+ }
+ } else if (href) {
+ e_vcard_append_attribute_with_value (
+ E_VCARD (contact),
+ e_vcard_attribute_new (NULL, WEBDAV_CONTACT_HREF),
+ href);
+ }
+}
+
+static gchar *
+webdav_contact_get_href (EContact *contact)
+{
+ EVCardAttribute *attr;
+ GList *v = NULL;
+
+ g_return_val_if_fail (E_IS_CONTACT (contact), NULL);
+
+ attr = e_vcard_get_attribute (E_VCARD (contact), WEBDAV_CONTACT_HREF);
+
+ if (attr)
+ v = e_vcard_attribute_get_values (attr);
+
+ return ((v && v->data) ? g_strstrip (g_strdup (v->data)) : NULL);
+}
+
+static void
closure_destroy (WebdavBackendSearchClosure *closure)
{
e_flag_free (closure->running);
@@ -256,13 +298,14 @@ download_contact (EBookBackendWebdav *webdav,
etag = soup_message_headers_get_list (message->response_headers, "ETag");
/* we use our URI as UID */
- contact = e_contact_new_from_vcard_with_uid (message->response_body->data, uri);
+ contact = e_contact_new_from_vcard (message->response_body->data);
if (contact == NULL) {
g_warning ("Invalid vcard at '%s'", uri);
g_object_unref (message);
return NULL;
}
+ webdav_contact_set_href (contact, uri);
/* the etag is remembered in the WEBDAV_CONTACT_ETAG field */
if (etag != NULL) {
webdav_contact_set_etag (contact, etag);
@@ -274,6 +317,7 @@ download_contact (EBookBackendWebdav *webdav,
static guint
upload_contact (EBookBackendWebdav *webdav,
+ const gchar *uri,
EContact *contact,
gchar **reason,
GCancellable *cancellable)
@@ -281,7 +325,6 @@ upload_contact (EBookBackendWebdav *webdav,
ESource *source;
ESourceWebdav *webdav_extension;
SoupMessage *message;
- gchar *uri;
gchar *etag;
const gchar *new_etag, *redir_uri;
gchar *request;
@@ -289,17 +332,13 @@ upload_contact (EBookBackendWebdav *webdav,
gboolean avoid_ifmatch;
const gchar *extension_name;
+ g_return_val_if_fail (uri != NULL, SOUP_STATUS_BAD_REQUEST);
+
source = e_backend_get_source (E_BACKEND (webdav));
extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
webdav_extension = e_source_get_extension (source, extension_name);
- uri = e_contact_get (contact, E_CONTACT_UID);
- if (uri == NULL) {
- g_warning ("can't upload contact without UID");
- return 400;
- }
-
message = soup_message_new (SOUP_METHOD_PUT, uri);
soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
soup_message_headers_append (message->request_headers, "Connection", "close");
@@ -357,15 +396,15 @@ upload_contact (EBookBackendWebdav *webdav,
}
full_uri = soup_uri_to_string (suri, FALSE);
- e_contact_set (contact, E_CONTACT_UID, full_uri);
+ webdav_contact_set_href (contact, full_uri);
g_free (full_uri);
soup_uri_free (suri);
} else {
- e_contact_set (contact, E_CONTACT_UID, redir_uri);
+ webdav_contact_set_href (contact, redir_uri);
}
} else {
- e_contact_set (contact, E_CONTACT_UID, uri);
+ webdav_contact_set_href (contact, uri);
}
if (reason != NULL) {
@@ -382,7 +421,6 @@ upload_contact (EBookBackendWebdav *webdav,
g_object_unref (message);
g_free (request);
- g_free (uri);
return status;
}
@@ -765,6 +803,20 @@ check_addressbook_changed (EBookBackendWebdav *webdav,
return res;
}
+static void
+remove_unknown_contacts_cb (gpointer href,
+ gpointer pcontact,
+ gpointer pwebdav)
+{
+ EContact *contact = pcontact;
+ EBookBackendWebdav *webdav = pwebdav;
+ const gchar *uid;
+
+ uid = e_contact_get_const (contact, E_CONTACT_UID);
+ if (uid && e_book_backend_cache_remove_contact (webdav->priv->cache, uid))
+ e_book_backend_notify_remove ((EBookBackend *) webdav, uid);
+}
+
static gboolean
download_contacts (EBookBackendWebdav *webdav,
EFlag *running,
@@ -783,6 +835,8 @@ download_contacts (EBookBackendWebdav *webdav,
gint count;
gint i;
gchar *new_ctag = NULL;
+ GHashTable *href_to_contact;
+ GList *cached_contacts, *iter;
g_mutex_lock (&priv->update_lock);
@@ -860,6 +914,25 @@ download_contacts (EBookBackendWebdav *webdav,
++count;
}
+ href_to_contact = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
+ g_mutex_lock (&priv->cache_lock);
+ e_file_cache_freeze_changes (E_FILE_CACHE (priv->cache));
+ cached_contacts = e_book_backend_cache_get_contacts (priv->cache, NULL);
+ for (iter = cached_contacts; iter; iter = g_list_next (iter)) {
+ EContact *contact = iter->data;
+ gchar *href;
+
+ if (!contact)
+ continue;
+
+ href = webdav_contact_get_href (contact);
+
+ if (href)
+ g_hash_table_insert (href_to_contact, href, g_object_ref (contact));
+ }
+ g_list_free_full (cached_contacts, g_object_unref);
+ g_mutex_unlock (&priv->cache_lock);
+
/* download contacts */
i = 0;
for (element = elements; element != NULL; element = element->next, ++i) {
@@ -898,11 +971,15 @@ download_contacts (EBookBackendWebdav *webdav,
etag = (const gchar *) element->etag;
- g_mutex_lock (&priv->cache_lock);
- contact = e_book_backend_cache_get_contact (priv->cache, complete_uri);
- g_mutex_unlock (&priv->cache_lock);
+ contact = g_hash_table_lookup (href_to_contact, complete_uri);
+ if (contact) {
+ g_object_ref (contact);
+ g_hash_table_remove (href_to_contact, complete_uri);
+ stored_etag = webdav_contact_get_etag (contact);
+ } else {
+ stored_etag = NULL;
+ }
- stored_etag = webdav_contact_get_etag (contact);
/* download contact if it is not cached or its ETag changed */
if (contact == NULL || etag == NULL || !stored_etag ||
@@ -911,9 +988,13 @@ download_contacts (EBookBackendWebdav *webdav,
g_object_unref (contact);
contact = download_contact (webdav, complete_uri, cancellable);
if (contact != NULL) {
+ const gchar *uid;
+
+ uid = e_contact_get_const (contact, E_CONTACT_UID);
+
g_mutex_lock (&priv->cache_lock);
- if (e_book_backend_cache_remove_contact (priv->cache, complete_uri))
- e_book_backend_notify_remove (book_backend, complete_uri);
+ if (e_book_backend_cache_remove_contact (priv->cache, uid))
+ e_book_backend_notify_remove (book_backend, uid);
e_book_backend_cache_add_contact (priv->cache, contact);
g_mutex_unlock (&priv->cache_lock);
e_book_backend_notify_update (book_backend, contact);
@@ -949,8 +1030,14 @@ download_contacts (EBookBackendWebdav *webdav,
if (book_view)
e_data_book_view_notify_progress (book_view, -1, NULL);
+ g_mutex_lock (&priv->cache_lock);
+ g_hash_table_foreach (href_to_contact, remove_unknown_contacts_cb, webdav);
+ e_file_cache_thaw_changes (E_FILE_CACHE (priv->cache));
+ g_mutex_unlock (&priv->cache_lock);
g_mutex_unlock (&priv->update_lock);
+ g_hash_table_destroy (href_to_contact);
+
return TRUE;
}
@@ -1258,7 +1345,8 @@ book_backend_webdav_create_contacts_sync (EBookBackend *backend,
{
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
EContact *contact;
- gchar *uid;
+ gchar *uid, *href;
+ const gchar *orig_uid;
guint status;
gchar *status_reason = NULL, *stored_etag;
@@ -1283,18 +1371,26 @@ book_backend_webdav_create_contacts_sync (EBookBackend *backend,
return FALSE;
}
- /* do 3 rand() calls to construct a unique ID... poor way but should be
- * good enough for us */
- uid = g_strdup_printf (
- "%s%08X-%08X-%08X.vcf",
- webdav->priv->uri, rand (), rand (), rand ());
+ contact = e_contact_new_from_vcard (vcards[0]);
- contact = e_contact_new_from_vcard_with_uid (vcards[0], uid);
+ orig_uid = e_contact_get_const (contact, E_CONTACT_UID);
+ if (orig_uid && *orig_uid && !e_book_backend_cache_check_contact (webdav->priv->cache, orig_uid)) {
+ uid = g_strdup (orig_uid);
+ } else {
+ /* do 3 rand() calls to construct a unique ID... poor way but should be
+ * good enough for us */
+ uid = g_strdup_printf ("%08X-%08X-%08X", rand (), rand (), rand ());
+ e_contact_set (contact, E_CONTACT_UID, uid);
+ }
+ href = g_strconcat (webdav->priv->uri, uid, ".vcf", NULL);
/* kill WEBDAV_CONTACT_ETAG field (might have been set by some other backend) */
+ webdav_contact_set_href (contact, NULL);
webdav_contact_set_etag (contact, NULL);
- status = upload_contact (webdav, contact, &status_reason, cancellable);
+ status = upload_contact (webdav, href, contact, &status_reason, cancellable);
+ g_free (href);
+
if (status != 201 && status != 204) {
g_object_unref (contact);
if (status == 401 || status == 407) {
@@ -1312,16 +1408,20 @@ book_backend_webdav_create_contacts_sync (EBookBackend *backend,
}
g_free (status_reason);
+ g_free (uid);
/* PUT request didn't return an etag? try downloading to get one */
stored_etag = webdav_contact_get_etag (contact);
if (!stored_etag) {
- const gchar *new_uid;
- EContact *new_contact;
+ gchar *href;
+ EContact *new_contact = NULL;
+
+ href = webdav_contact_get_href (contact);
+ if (href) {
+ new_contact = download_contact (webdav, href, cancellable);
+ g_free (href);
+ }
- g_warning ("Server didn't return etag for new address resource");
- new_uid = e_contact_get_const (contact, E_CONTACT_UID);
- new_contact = download_contact (webdav, new_uid, cancellable);
g_object_unref (contact);
if (new_contact == NULL) {
@@ -1330,7 +1430,6 @@ book_backend_webdav_create_contacts_sync (EBookBackend *backend,
E_CLIENT_ERROR_OTHER_ERROR,
e_client_error_to_string (
E_CLIENT_ERROR_OTHER_ERROR));
- g_free (uid);
return FALSE;
}
contact = new_contact;
@@ -1345,7 +1444,6 @@ book_backend_webdav_create_contacts_sync (EBookBackend *backend,
g_queue_push_tail (out_contacts, g_object_ref (contact));
g_object_unref (contact);
- g_free (uid);
return TRUE;
}
@@ -1360,7 +1458,7 @@ book_backend_webdav_modify_contacts_sync (EBookBackend *backend,
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
EContact *contact;
const gchar *uid;
- gchar *etag;
+ gchar *href, *etag;
guint status;
gchar *status_reason = NULL;
@@ -1387,7 +1485,9 @@ book_backend_webdav_modify_contacts_sync (EBookBackend *backend,
/* modify contact */
contact = e_contact_new_from_vcard (vcards[0]);
- status = upload_contact (webdav, contact, &status_reason, cancellable);
+ href = webdav_contact_get_href (contact);
+ status = upload_contact (webdav, href, contact, &status_reason, cancellable);
+ g_free (href);
if (status != 201 && status != 204) {
g_object_unref (contact);
if (status == 401 || status == 407) {
@@ -1426,10 +1526,14 @@ book_backend_webdav_modify_contacts_sync (EBookBackend *backend,
/* PUT request didn't return an etag? try downloading to get one */
if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
- EContact *new_contact;
+ EContact *new_contact = NULL;
+
+ href = webdav_contact_get_href (contact);
+ if (href) {
+ new_contact = download_contact (webdav, href, cancellable);
+ g_free (href);
+ }
- g_warning ("Server didn't return etag for modified address resource");
- new_contact = download_contact (webdav, uid, cancellable);
if (new_contact != NULL) {
g_object_unref (contact);
contact = new_contact;
@@ -1455,6 +1559,8 @@ book_backend_webdav_remove_contacts_sync (EBookBackend *backend,
GError **error)
{
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EContact *contact;
+ gchar *href;
guint status;
/* We make the assumption that the ID list we're passed is
@@ -1477,7 +1583,34 @@ book_backend_webdav_remove_contacts_sync (EBookBackend *backend,
return FALSE;
}
- status = delete_contact (webdav, uids[0], cancellable);
+ g_mutex_lock (&webdav->priv->cache_lock);
+ contact = e_book_backend_cache_get_contact (webdav->priv->cache, uids[0]);
+ g_mutex_unlock (&webdav->priv->cache_lock);
+
+ if (!contact) {
+ g_set_error_literal (
+ error, E_BOOK_CLIENT_ERROR,
+ E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND,
+ e_book_client_error_to_string (
+ E_BOOK_CLIENT_ERROR_CONTACT_NOT_FOUND));
+ return FALSE;
+ }
+
+ href = webdav_contact_get_href (contact);
+ if (!href) {
+ g_object_unref (contact);
+ g_set_error (
+ error, E_CLIENT_ERROR,
+ E_CLIENT_ERROR_OTHER_ERROR,
+ _("DELETE failed with HTTP status %d"), SOUP_STATUS_MALFORMED);
+ return FALSE;
+ }
+
+ status = delete_contact (webdav, href, cancellable);
+
+ g_object_unref (contact);
+ g_free (href);
+
if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
if (status == 401 || status == 407) {
webdav_handle_auth_request (webdav, error);
@@ -1506,13 +1639,24 @@ book_backend_webdav_get_contact_sync (EBookBackend *backend,
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
EContact *contact;
- if (!e_backend_get_online (E_BACKEND (backend))) {
- g_mutex_lock (&webdav->priv->cache_lock);
- contact = e_book_backend_cache_get_contact (
- webdav->priv->cache, uid);
- g_mutex_unlock (&webdav->priv->cache_lock);
- } else {
- contact = download_contact (webdav, uid, cancellable);
+ g_mutex_lock (&webdav->priv->cache_lock);
+ contact = e_book_backend_cache_get_contact (
+ webdav->priv->cache, uid);
+ g_mutex_unlock (&webdav->priv->cache_lock);
+
+ if (contact && e_backend_get_online (E_BACKEND (backend))) {
+ gchar *href;
+
+ href = webdav_contact_get_href (contact);
+ g_object_unref (contact);
+
+ if (href) {
+ contact = download_contact (webdav, href, cancellable);
+ g_free (href);
+ } else {
+ contact = NULL;
+ }
+
/* update cache as we possibly have changes */
if (contact != NULL) {
g_mutex_lock (&webdav->priv->cache_lock);
diff --git a/addressbook/libedata-book/e-book-backend-cache.c
b/addressbook/libedata-book/e-book-backend-cache.c
index a072467..57db309 100644
--- a/addressbook/libedata-book/e-book-backend-cache.c
+++ b/addressbook/libedata-book/e-book-backend-cache.c
@@ -233,7 +233,7 @@ e_book_backend_cache_get_contacts (EBookBackendCache *cache,
if (vcard_str && !strncmp (vcard_str, "BEGIN:VCARD", 11)) {
contact = e_contact_new_from_vcard (vcard_str);
uid = e_contact_get_const (contact, E_CONTACT_UID);
- if (uid && *uid && (query && e_book_backend_sexp_match_contact (sexp, contact)))
+ if (uid && *uid && (!query || e_book_backend_sexp_match_contact (sexp, contact)))
list = g_list_prepend (list, contact);
else
g_object_unref (contact);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]