[evolution-data-server] EBookBackendWebdav cleanups.
- From: Matthew Barnes <mbarnes src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] EBookBackendWebdav cleanups.
- Date: Fri, 5 Apr 2013 14:36:40 +0000 (UTC)
commit d6f4e33c7983601bd283defc4901f4b8273f0a19
Author: Matthew Barnes <mbarnes redhat com>
Date: Fri Apr 5 09:27:36 2013 -0400
EBookBackendWebdav cleanups.
.../backends/webdav/e-book-backend-webdav.c | 1851 ++++++++++----------
1 files changed, 924 insertions(+), 927 deletions(-)
---
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c
b/addressbook/backends/webdav/e-book-backend-webdav.c
index 1eb71a1..04b7cab 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -364,101 +364,6 @@ webdav_handle_auth_request (EBookBackendWebdav *webdav)
}
}
-static void
-e_book_backend_webdav_create_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *vcards)
-{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- EContact *contact;
- gchar *uid;
- guint status;
- gchar *status_reason = NULL;
- const gchar *vcard = (const gchar *) vcards->data;
- GSList added_contacts = {NULL,};
-
- /* We make the assumption that the vCard list we're passed is always exactly one element long, since
we haven't specified "bulk-adds"
- * in our static capability list. This is because there is no clean way to roll back changes in case
of an error. */
- if (vcards->next != NULL) {
- e_data_book_respond_create_contacts (
- book, opid,
- EDB_ERROR_EX (NOT_SUPPORTED,
- _("The backend does not support bulk additions")),
- NULL);
- return;
- }
-
- if (!e_backend_get_online (E_BACKEND (backend))) {
- e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
-
- /* 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",
- priv->uri, rand (), rand (), rand ());
-
- contact = e_contact_new_from_vcard_with_uid (vcard, uid);
-
- /* kill revision field (might have been set by some other backend) */
- e_contact_set (contact, E_CONTACT_REV, NULL);
-
- status = upload_contact (webdav, contact, &status_reason, cancellable);
- if (status != 201 && status != 204) {
- g_object_unref (contact);
- if (status == 401 || status == 407) {
- e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav),
NULL);
- } else {
- e_data_book_respond_create_contacts (
- book, opid,
- e_data_book_create_error_fmt (
- E_DATA_BOOK_STATUS_OTHER_ERROR,
- _("Create resource '%s' failed with HTTP status %d (%s)"),
- uid, status, status_reason),
- NULL);
- }
- g_free (uid);
- g_free (status_reason);
- return;
- }
-
- g_free (status_reason);
-
- /* PUT request didn't return an etag? try downloading to get one */
- if (e_contact_get_const (contact, E_CONTACT_REV) == NULL) {
- const gchar *new_uid;
- EContact *new_contact;
-
- 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) {
- e_data_book_respond_create_contacts (
- book, opid,
- EDB_ERROR (OTHER_ERROR), NULL);
- g_free (uid);
- return;
- }
- contact = new_contact;
- }
-
- g_mutex_lock (&priv->cache_lock);
- e_book_backend_cache_add_contact (priv->cache, contact);
- g_mutex_unlock (&priv->cache_lock);
-
- added_contacts.data = contact;
- e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);
-
- g_object_unref (contact);
- g_free (uid);
-}
-
static guint
delete_contact (EBookBackendWebdav *webdav,
const gchar *uri,
@@ -477,773 +382,1094 @@ delete_contact (EBookBackendWebdav *webdav,
return status;
}
-static void
-e_book_backend_webdav_remove_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *id_list)
+typedef struct parser_strings_t {
+ const xmlChar *multistatus;
+ const xmlChar *dav;
+ const xmlChar *href;
+ const xmlChar *response;
+ const xmlChar *propstat;
+ const xmlChar *prop;
+ const xmlChar *getetag;
+} parser_strings_t;
+
+typedef struct response_element_t response_element_t;
+struct response_element_t {
+ xmlChar *href;
+ xmlChar *etag;
+ response_element_t *next;
+};
+
+static response_element_t *
+parse_response_tag (const parser_strings_t *strings,
+ xmlTextReaderPtr reader)
{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- gchar *uid = id_list->data;
- GSList deleted_ids = {NULL,};
- guint status;
+ xmlChar *href = NULL;
+ xmlChar *etag = NULL;
+ gint depth = xmlTextReaderDepth (reader);
+ response_element_t *element;
- if (!e_backend_get_online (E_BACKEND (backend))) {
- e_data_book_respond_remove_contacts (
- book, opid,
- EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
- }
+ while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth) {
+ const xmlChar *tag_name;
+ if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+ continue;
- /* We make the assumption that the ID list we're passed is always exactly one element long, since we
haven't specified "bulk-removes"
- * in our static capability list. */
- if (id_list->next != NULL) {
- e_data_book_respond_remove_contacts (
- book, opid,
- EDB_ERROR_EX (NOT_SUPPORTED,
- _("The backend does not support bulk removals")),
- NULL);
- return;
- }
+ if (xmlTextReaderConstNamespaceUri (reader) != strings->dav)
+ continue;
- status = delete_contact (webdav, uid, cancellable);
- if (status != 204) {
- if (status == 401 || status == 407) {
- e_data_book_respond_remove_contacts (
- book, opid,
- webdav_handle_auth_request (webdav), NULL);
- } else {
- g_warning ("DELETE failed with HTTP status %d", status);
- e_data_book_respond_remove_contacts (
- book, opid,
- e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
- _("DELETE failed with HTTP status %d"), status),
- NULL);
+ tag_name = xmlTextReaderConstLocalName (reader);
+ if (tag_name == strings->href) {
+ if (href != NULL) {
+ /* multiple href elements?!? */
+ xmlFree (href);
+ }
+ href = xmlTextReaderReadString (reader);
+ } else if (tag_name == strings->propstat) {
+ /* find <propstat><prop><getetag> hierarchy */
+ gint depth2 = xmlTextReaderDepth (reader);
+ while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth2) {
+ gint depth3;
+ if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+ continue;
+
+ if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
+ || xmlTextReaderConstLocalName (reader) != strings->prop)
+ continue;
+
+ depth3 = xmlTextReaderDepth (reader);
+ while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) >
depth3) {
+ if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+ continue;
+
+ if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
+ || xmlTextReaderConstLocalName (reader)
+ != strings->getetag)
+ continue;
+
+ if (etag != NULL) {
+ /* multiple etag elements?!? */
+ xmlFree (etag);
+ }
+ etag = xmlTextReaderReadString (reader);
+ }
+ }
}
- return;
}
- g_mutex_lock (&priv->cache_lock);
- e_book_backend_cache_remove_contact (priv->cache, uid);
- g_mutex_unlock (&priv->cache_lock);
+ if (href == NULL) {
+ g_warning ("webdav returned response element without href");
+ return NULL;
+ }
- deleted_ids.data = uid;
- e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (SUCCESS), &deleted_ids);
+ /* append element to list */
+ element = g_malloc (sizeof (element[0]));
+ element->href = href;
+ element->etag = etag;
+ return element;
}
-static void
-e_book_backend_webdav_modify_contacts (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const GSList *vcards)
+static response_element_t *
+parse_propfind_response (xmlTextReaderPtr reader)
{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- EContact *contact;
- GSList modified_contacts = {NULL,};
- const gchar *uid;
- const gchar *etag;
- guint status;
- gchar *status_reason = NULL;
- const gchar *vcard = vcards->data;
+ parser_strings_t strings;
+ response_element_t *elements;
- if (!e_backend_get_online (E_BACKEND (backend))) {
- e_data_book_respond_create_contacts (
- book, opid,
- EDB_ERROR (REPOSITORY_OFFLINE), NULL);
- return;
+ /* get internalized versions of some strings to avoid strcmp while
+ * parsing */
+ strings.multistatus
+ = xmlTextReaderConstString (reader, BAD_CAST "multistatus");
+ strings.dav = xmlTextReaderConstString (reader, BAD_CAST "DAV:");
+ strings.href = xmlTextReaderConstString (reader, BAD_CAST "href");
+ strings.response = xmlTextReaderConstString (reader, BAD_CAST "response");
+ strings.propstat = xmlTextReaderConstString (reader, BAD_CAST "propstat");
+ strings.prop = xmlTextReaderConstString (reader, BAD_CAST "prop");
+ strings.getetag = xmlTextReaderConstString (reader, BAD_CAST "getetag");
+
+ while (xmlTextReaderRead (reader) == 1 && xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT) {
}
- /* We make the assumption that the vCard list we're passed is always exactly one element long, since
we haven't specified "bulk-modifies"
- * in our static capability list. This is because there is no clean way to roll back changes in case
of an error. */
- if (vcards->next != NULL) {
- e_data_book_respond_modify_contacts (
- book, opid,
- EDB_ERROR_EX (
- NOT_SUPPORTED,
- _("The backend does not support bulk modifications")),
- NULL);
- return;
+ if (xmlTextReaderConstLocalName (reader) != strings.multistatus
+ || xmlTextReaderConstNamespaceUri (reader) != strings.dav) {
+ g_warning ("webdav PROPFIND result is not <DAV:multistatus>");
+ return NULL;
}
- /* modify contact */
- contact = e_contact_new_from_vcard (vcard);
- status = upload_contact (webdav, contact, &status_reason, cancellable);
- if (status != 201 && status != 204) {
- g_object_unref (contact);
- if (status == 401 || status == 407) {
- e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav),
NULL);
- g_free (status_reason);
- return;
- }
- /* data changed on server while we were editing */
- if (status == 412) {
- /* too bad no special error code in evolution for this... */
- e_data_book_respond_modify_contacts (book, opid,
- e_data_book_create_error_fmt (
- E_DATA_BOOK_STATUS_OTHER_ERROR,
- _("Contact on server changed -> not modifying")),
- NULL);
- g_free (status_reason);
- return;
- }
+ elements = NULL;
- e_data_book_respond_modify_contacts (book, opid,
- e_data_book_create_error_fmt (
- E_DATA_BOOK_STATUS_OTHER_ERROR,
- _("Modify contact failed with HTTP status %d (%s)"),
- status, status_reason),
- NULL);
- g_free (status_reason);
- return;
+ /* parse all DAV:response tags */
+ while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 0) {
+ response_element_t *element;
+
+ if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
+ continue;
+
+ if (xmlTextReaderConstLocalName (reader) != strings.response
+ || xmlTextReaderConstNamespaceUri (reader) != strings.dav)
+ continue;
+
+ element = parse_response_tag (&strings, reader);
+ if (element == NULL)
+ continue;
+
+ element->next = elements;
+ elements = element;
}
- g_free (status_reason);
+ return elements;
+}
- uid = e_contact_get_const (contact, E_CONTACT_UID);
- g_mutex_lock (&priv->cache_lock);
- e_book_backend_cache_remove_contact (priv->cache, uid);
+static SoupMessage *
+send_propfind (EBookBackendWebdav *webdav,
+ GCancellable *cancellable)
+{
+ SoupMessage *message;
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ const gchar *request =
+ "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
+ "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>";
- etag = e_contact_get_const (contact, E_CONTACT_REV);
+ message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
+ soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
+ soup_message_headers_append (message->request_headers, "Connection", "close");
+ soup_message_headers_append (message->request_headers, "Depth", "1");
+ soup_message_set_request (
+ message, "text/xml", SOUP_MEMORY_TEMPORARY,
+ (gchar *) request, strlen (request));
- /* PUT request didn't return an etag? try downloading to get one */
- if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
- EContact *new_contact;
+ send_and_handle_ssl (webdav, message, cancellable);
- g_warning ("Server didn't return etag for modified address resource");
- new_contact = download_contact (webdav, uid, cancellable);
- if (new_contact != NULL) {
- contact = new_contact;
- }
+ return message;
+}
+
+static xmlXPathObjectPtr
+xpath_eval (xmlXPathContextPtr ctx,
+ const gchar *format,
+ ...)
+{
+ xmlXPathObjectPtr result;
+ va_list args;
+ gchar *expr;
+
+ if (ctx == NULL) {
+ return NULL;
}
- e_book_backend_cache_add_contact (priv->cache, contact);
- g_mutex_unlock (&priv->cache_lock);
- modified_contacts.data = contact;
- e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);
+ va_start (args, format);
+ expr = g_strdup_vprintf (format, args);
+ va_end (args);
- g_object_unref (contact);
+ 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;
}
-static void
-e_book_backend_webdav_get_contact (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const gchar *uid)
+static gchar *
+xp_object_get_string (xmlXPathObjectPtr result)
{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- EContact *contact;
- gchar *vcard;
+ gchar *ret = NULL;
- if (!e_backend_get_online (E_BACKEND (backend))) {
- g_mutex_lock (&priv->cache_lock);
- contact = e_book_backend_cache_get_contact (priv->cache, uid);
- g_mutex_unlock (&priv->cache_lock);
- } else {
- contact = download_contact (webdav, uid, cancellable);
- /* update cache as we possibly have changes */
- if (contact != NULL) {
- g_mutex_lock (&priv->cache_lock);
- e_book_backend_cache_remove_contact (priv->cache, uid);
- e_book_backend_cache_add_contact (priv->cache, contact);
- g_mutex_unlock (&priv->cache_lock);
- }
- }
+ if (result == NULL)
+ return ret;
- if (contact == NULL) {
- e_data_book_respond_get_contact (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
- return;
+ if (result->type == XPATH_STRING) {
+ ret = g_strdup ((gchar *) result->stringval);
}
- vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
- e_data_book_respond_get_contact (book, opid, EDB_ERROR (SUCCESS), vcard);
- g_free (vcard);
- g_object_unref (contact);
+ xmlXPathFreeObject (result);
+ return ret;
}
-typedef struct parser_strings_t {
- const xmlChar *multistatus;
- const xmlChar *dav;
- const xmlChar *href;
- const xmlChar *response;
- const xmlChar *propstat;
- const xmlChar *prop;
- const xmlChar *getetag;
-} parser_strings_t;
+static guint
+xp_object_get_status (xmlXPathObjectPtr result)
+{
+ gboolean res;
+ guint ret = 0;
-typedef struct response_element_t response_element_t;
-struct response_element_t {
- xmlChar *href;
- xmlChar *etag;
- response_element_t *next;
-};
+ if (result == NULL)
+ return ret;
-static response_element_t *
-parse_response_tag (const parser_strings_t *strings,
- xmlTextReaderPtr reader)
+ if (result->type == XPATH_STRING) {
+ res = soup_headers_parse_status_line ((gchar *) result->stringval, NULL, &ret, NULL);
+ if (!res) {
+ ret = 0;
+ }
+ }
+
+ xmlXPathFreeObject (result);
+ return ret;
+}
+
+static gboolean
+check_addressbook_changed (EBookBackendWebdav *webdav,
+ gchar **new_ctag,
+ GCancellable *cancellable)
{
- xmlChar *href = NULL;
- xmlChar *etag = NULL;
- gint depth = xmlTextReaderDepth (reader);
- response_element_t *element;
+ gboolean res = TRUE;
+ const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind
xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
+ EBookBackendWebdavPrivate *priv;
+ SoupMessage *message;
- while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth) {
- const xmlChar *tag_name;
- if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
- continue;
+ g_return_val_if_fail (webdav != NULL, TRUE);
+ g_return_val_if_fail (new_ctag != NULL, TRUE);
- if (xmlTextReaderConstNamespaceUri (reader) != strings->dav)
- continue;
+ *new_ctag = NULL;
+ priv = webdav->priv;
- tag_name = xmlTextReaderConstLocalName (reader);
- if (tag_name == strings->href) {
- if (href != NULL) {
- /* multiple href elements?!? */
- xmlFree (href);
- }
- href = xmlTextReaderReadString (reader);
- } else if (tag_name == strings->propstat) {
- /* find <propstat><prop><getetag> hierarchy */
- gint depth2 = xmlTextReaderDepth (reader);
- while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > depth2) {
- gint depth3;
- if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
- continue;
+ if (!priv->supports_getctag)
+ return TRUE;
- if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
- || xmlTextReaderConstLocalName (reader) != strings->prop)
- continue;
+ priv->supports_getctag = FALSE;
- depth3 = xmlTextReaderDepth (reader);
- while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) >
depth3) {
- if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
- continue;
+ message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
+ if (!message)
+ return TRUE;
- if (xmlTextReaderConstNamespaceUri (reader) != strings->dav
- || xmlTextReaderConstLocalName (reader)
- != strings->getetag)
- continue;
+ soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
+ soup_message_headers_append (message->request_headers, "Connection", "close");
+ soup_message_headers_append (message->request_headers, "Depth", "0");
+ soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen
(request));
+ send_and_handle_ssl (webdav, message, cancellable);
- if (etag != NULL) {
- /* multiple etag elements?!? */
- xmlFree (etag);
+ if (message->status_code == 207 && message->response_body) {
+ xmlDocPtr xml;
+
+ xml = xmlReadMemory (message->response_body->data, message->response_body->length, NULL,
NULL, XML_PARSE_NOWARNING);
+ if (xml) {
+ const gchar *GETCTAG_XPATH_STATUS =
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag/../../D:status)";
+ const gchar *GETCTAG_XPATH_VALUE =
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag)";
+ xmlXPathContextPtr xpctx;
+
+ xpctx = xmlXPathNewContext (xml);
+ xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
+
+ if (xp_object_get_status (xpath_eval (xpctx, GETCTAG_XPATH_STATUS)) == 200) {
+ gchar *txt = xp_object_get_string (xpath_eval (xpctx, GETCTAG_XPATH_VALUE));
+
+ if (txt && *txt) {
+ gint len = strlen (txt);
+
+ if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
+ /* dequote */
+ *new_ctag = g_strndup (txt + 1, len - 2);
+ } else {
+ *new_ctag = txt;
+ txt = NULL;
+ }
+
+ if (*new_ctag) {
+ const gchar *my_ctag;
+
+ g_mutex_lock (&priv->cache_lock);
+ my_ctag = e_file_cache_get_object (E_FILE_CACHE
(priv->cache), WEBDAV_CTAG_KEY);
+ res = !my_ctag || !g_str_equal (my_ctag, *new_ctag);
+ priv->supports_getctag = TRUE;
+ g_mutex_unlock (&priv->cache_lock);
}
- etag = xmlTextReaderReadString (reader);
}
+
+ g_free (txt);
+ }
+
+ xmlXPathFreeContext (xpctx);
+ xmlFreeDoc (xml);
+ }
+ }
+
+ g_object_unref (message);
+
+ return res;
+}
+
+static GError *
+download_contacts (EBookBackendWebdav *webdav,
+ EFlag *running,
+ EDataBookView *book_view,
+ GCancellable *cancellable)
+{
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ EBookBackend *book_backend;
+ SoupMessage *message;
+ guint status;
+ xmlTextReaderPtr reader;
+ response_element_t *elements;
+ response_element_t *element;
+ response_element_t *next;
+ gint count;
+ gint i;
+ gchar *new_ctag = NULL;
+
+ g_mutex_lock (&priv->update_lock);
+
+ if (!check_addressbook_changed (webdav, &new_ctag, cancellable)) {
+ g_free (new_ctag);
+ g_mutex_unlock (&priv->update_lock);
+ return EDB_ERROR (SUCCESS);
+ }
+
+ book_backend = E_BOOK_BACKEND (webdav);
+
+ if (book_view != NULL) {
+ e_data_book_view_notify_progress (book_view, -1,
+ _("Loading Addressbook summary..."));
+ }
+
+ message = send_propfind (webdav, cancellable);
+ status = message->status_code;
+
+ if (status == 401 || status == 407) {
+ g_object_unref (message);
+ g_free (new_ctag);
+ if (book_view)
+ e_data_book_view_notify_progress (book_view, -1, NULL);
+ g_mutex_unlock (&priv->update_lock);
+ return webdav_handle_auth_request (webdav);
+ }
+ if (status != 207) {
+ GError *error;
+
+ error = e_data_book_create_error_fmt (
+ E_DATA_BOOK_STATUS_OTHER_ERROR,
+ _("PROPFIND on webdav failed with HTTP status %d (%s)"),
+ status,
+ 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")));
+
+ g_object_unref (message);
+ g_free (new_ctag);
+
+ if (book_view)
+ e_data_book_view_notify_progress (book_view, -1, NULL);
+
+ g_mutex_unlock (&priv->update_lock);
+
+ return error;
+ }
+ if (message->response_body == NULL) {
+ g_warning ("No response body in webdav PROPFIND result");
+ g_object_unref (message);
+ g_free (new_ctag);
+
+ if (book_view)
+ e_data_book_view_notify_progress (book_view, -1, NULL);
+
+ g_mutex_unlock (&priv->update_lock);
+
+ return e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, _("No response body in
webdav PROPFIND result"));
+ }
+
+ /* parse response */
+ reader = xmlReaderForMemory (
+ message->response_body->data,
+ message->response_body->length, NULL, NULL,
+ XML_PARSE_NOWARNING);
+
+ elements = parse_propfind_response (reader);
+
+ /* count contacts */
+ count = 0;
+ for (element = elements; element != NULL; element = element->next) {
+ ++count;
+ }
+
+ /* download contacts */
+ i = 0;
+ for (element = elements; element != NULL; element = element->next, ++i) {
+ const gchar *uri;
+ const gchar *etag;
+ EContact *contact;
+ gchar *complete_uri;
+
+ /* stop downloading if search was aborted */
+ if (running != NULL && !e_flag_is_set (running))
+ break;
+
+ if (book_view != NULL) {
+ gfloat percent = 100.0 / count * i;
+ gchar buf[100];
+ snprintf (buf, sizeof (buf), _("Loading Contacts (%d%%)"), (gint) percent);
+ e_data_book_view_notify_progress (book_view, -1, buf);
+ }
+
+ /* skip collections */
+ uri = (const gchar *) element->href;
+ if (uri[strlen (uri) - 1] == '/')
+ continue;
+
+ /* uri might be relative, construct complete one */
+ if (uri[0] == '/') {
+ SoupURI *soup_uri = soup_uri_new (priv->uri);
+ soup_uri->path = g_strdup (uri);
+
+ complete_uri = soup_uri_to_string (soup_uri, FALSE);
+ soup_uri_free (soup_uri);
+ } else {
+ complete_uri = g_strdup (uri);
+ }
+
+ 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);
+
+ /* download contact if it is not cached or its ETag changed */
+ if (contact == NULL || etag == NULL ||
+ strcmp (e_contact_get_const (contact, E_CONTACT_REV), etag) != 0) {
+ contact = download_contact (webdav, complete_uri, cancellable);
+ if (contact != NULL) {
+ 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);
+ e_book_backend_cache_add_contact (priv->cache, contact);
+ g_mutex_unlock (&priv->cache_lock);
+ e_book_backend_notify_update (book_backend, contact);
}
}
+
+ g_free (complete_uri);
+ }
+
+ /* free element list */
+ for (element = elements; element != NULL; element = next) {
+ next = element->next;
+
+ xmlFree (element->href);
+ xmlFree (element->etag);
+ g_free (element);
+ }
+
+ xmlFreeTextReader (reader);
+ g_object_unref (message);
+
+ if (new_ctag) {
+ g_mutex_lock (&priv->cache_lock);
+ if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag))
+ e_file_cache_add_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag);
+ g_mutex_unlock (&priv->cache_lock);
}
+ g_free (new_ctag);
- if (href == NULL) {
- g_warning ("webdav returned response element without href");
- return NULL;
- }
+ if (book_view)
+ e_data_book_view_notify_progress (book_view, -1, NULL);
- /* append element to list */
- element = g_malloc (sizeof (element[0]));
- element->href = href;
- element->etag = etag;
- return element;
+ g_mutex_unlock (&priv->update_lock);
+
+ return EDB_ERROR (SUCCESS);
}
-static response_element_t *
-parse_propfind_response (xmlTextReaderPtr reader)
+static gpointer
+book_view_thread (gpointer data)
{
- parser_strings_t strings;
- response_element_t *elements;
+ EDataBookView *book_view = data;
+ WebdavBackendSearchClosure *closure = get_closure (book_view);
+ EBookBackendWebdav *webdav = closure->webdav;
+ GError *error;
- /* get internalized versions of some strings to avoid strcmp while
- * parsing */
- strings.multistatus
- = xmlTextReaderConstString (reader, BAD_CAST "multistatus");
- strings.dav = xmlTextReaderConstString (reader, BAD_CAST "DAV:");
- strings.href = xmlTextReaderConstString (reader, BAD_CAST "href");
- strings.response = xmlTextReaderConstString (reader, BAD_CAST "response");
- strings.propstat = xmlTextReaderConstString (reader, BAD_CAST "propstat");
- strings.prop = xmlTextReaderConstString (reader, BAD_CAST "prop");
- strings.getetag = xmlTextReaderConstString (reader, BAD_CAST "getetag");
+ e_flag_set (closure->running);
- while (xmlTextReaderRead (reader) == 1 && xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT) {
- }
+ /* ref the book view because it'll be removed and unrefed when/if
+ * it's stopped */
+ g_object_ref (book_view);
- if (xmlTextReaderConstLocalName (reader) != strings.multistatus
- || xmlTextReaderConstNamespaceUri (reader) != strings.dav) {
- g_warning ("webdav PROPFIND result is not <DAV:multistatus>");
- return NULL;
- }
+ error = download_contacts (webdav, closure->running, book_view, NULL);
- elements = NULL;
+ g_object_unref (book_view);
- /* parse all DAV:response tags */
- while (xmlTextReaderRead (reader) == 1 && xmlTextReaderDepth (reader) > 0) {
- response_element_t *element;
+ if (error)
+ g_error_free (error);
- if (xmlTextReaderNodeType (reader) != XML_READER_TYPE_ELEMENT)
- continue;
+ return NULL;
+}
- if (xmlTextReaderConstLocalName (reader) != strings.response
- || xmlTextReaderConstNamespaceUri (reader) != strings.dav)
- continue;
+static void
+e_book_backend_webdav_start_view (EBookBackend *backend,
+ EDataBookView *book_view)
+{
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ EBookBackendSExp *sexp;
+ const gchar *query;
+ GList *contacts;
+ GList *l;
- element = parse_response_tag (&strings, reader);
- if (element == NULL)
- continue;
+ sexp = e_data_book_view_get_sexp (book_view);
+ query = e_book_backend_sexp_text (sexp);
- element->next = elements;
- elements = element;
- }
+ g_mutex_lock (&priv->cache_lock);
+ contacts = e_book_backend_cache_get_contacts (priv->cache, query);
+ g_mutex_unlock (&priv->cache_lock);
- return elements;
-}
+ for (l = contacts; l != NULL; l = g_list_next (l)) {
+ EContact *contact = l->data;
+ e_data_book_view_notify_update (book_view, contact);
+ g_object_unref (contact);
+ }
+ g_list_free (contacts);
-static SoupMessage *
-send_propfind (EBookBackendWebdav *webdav,
- GCancellable *cancellable)
-{
- SoupMessage *message;
- EBookBackendWebdavPrivate *priv = webdav->priv;
- const gchar *request =
- "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
- "<propfind xmlns=\"DAV:\"><prop><getetag/></prop></propfind>";
+ /* this way the UI is notified about cached contacts immediately,
+ * and the update thread notifies about possible changes only */
+ e_data_book_view_notify_complete (book_view, NULL /* Success */);
- message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
- soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
- soup_message_headers_append (message->request_headers, "Connection", "close");
- soup_message_headers_append (message->request_headers, "Depth", "1");
- soup_message_set_request (
- message, "text/xml", SOUP_MEMORY_TEMPORARY,
- (gchar *) request, strlen (request));
+ if (e_backend_get_online (E_BACKEND (backend))) {
+ WebdavBackendSearchClosure *closure
+ = init_closure (book_view, E_BOOK_BACKEND_WEBDAV (backend));
- send_and_handle_ssl (webdav, message, cancellable);
+ closure->thread
+ = g_thread_new (NULL, book_view_thread, book_view);
- return message;
+ e_flag_wait (closure->running);
+ }
}
-static xmlXPathObjectPtr
-xpath_eval (xmlXPathContextPtr ctx,
- const gchar *format,
- ...)
+static void
+e_book_backend_webdav_stop_view (EBookBackend *backend,
+ EDataBookView *book_view)
{
- xmlXPathObjectPtr result;
- va_list args;
- gchar *expr;
-
- if (ctx == NULL) {
- return NULL;
- }
+ WebdavBackendSearchClosure *closure;
+ gboolean need_join;
- va_start (args, format);
- expr = g_strdup_vprintf (format, args);
- va_end (args);
+ if (!e_backend_get_online (E_BACKEND (backend)))
+ return;
- result = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
- g_free (expr);
+ closure = get_closure (book_view);
+ if (closure == NULL)
+ return;
- if (result == NULL) {
- return NULL;
- }
+ need_join = e_flag_is_set (closure->running);
+ e_flag_clear (closure->running);
- if (result->type == XPATH_NODESET &&
- xmlXPathNodeSetIsEmpty (result->nodesetval)) {
- xmlXPathFreeObject (result);
- return NULL;
+ if (need_join) {
+ g_thread_join (closure->thread);
+ closure->thread = NULL;
}
-
- return result;
}
-static gchar *
-xp_object_get_string (xmlXPathObjectPtr result)
+/** authentication callback for libsoup */
+static void
+soup_authenticate (SoupSession *session,
+ SoupMessage *message,
+ SoupAuth *auth,
+ gboolean retrying,
+ gpointer data)
{
- gchar *ret = NULL;
+ EBookBackendWebdav *webdav = data;
+ EBookBackendWebdavPrivate *priv = webdav->priv;
- if (result == NULL)
- return ret;
+ if (retrying)
+ return;
- if (result->type == XPATH_STRING) {
- ret = g_strdup ((gchar *) result->stringval);
+ if (priv->username != NULL) {
+ soup_auth_authenticate (auth, priv->username, priv->password);
}
-
- xmlXPathFreeObject (result);
- return ret;
}
-static guint
-xp_object_get_status (xmlXPathObjectPtr result)
+static void
+proxy_settings_changed (EProxy *proxy,
+ gpointer user_data)
{
- gboolean res;
- guint ret = 0;
+ SoupURI *proxy_uri = NULL;
+ EBookBackendWebdavPrivate *priv = (EBookBackendWebdavPrivate *) user_data;
- if (result == NULL)
- return ret;
+ if (!priv || !priv->uri || !priv->session)
+ return;
- if (result->type == XPATH_STRING) {
- res = soup_headers_parse_status_line ((gchar *) result->stringval, NULL, &ret, NULL);
- if (!res) {
- ret = 0;
- }
+ /* use proxy if necessary */
+ if (e_proxy_require_proxy_for_uri (proxy, priv->uri)) {
+ proxy_uri = e_proxy_peek_uri_for (proxy, priv->uri);
}
- xmlXPathFreeObject (result);
- return ret;
+ g_object_set (priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
}
-static gboolean
-check_addressbook_changed (EBookBackendWebdav *webdav,
- gchar **new_ctag,
- GCancellable *cancellable)
+static void
+e_book_backend_webdav_notify_online_cb (EBookBackend *backend,
+ GParamSpec *pspec)
{
- gboolean res = TRUE;
- const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind
xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
- EBookBackendWebdavPrivate *priv;
- SoupMessage *message;
-
- g_return_val_if_fail (webdav != NULL, TRUE);
- g_return_val_if_fail (new_ctag != NULL, TRUE);
+ gboolean online;
- *new_ctag = NULL;
- priv = webdav->priv;
+ /* set_mode is called before the backend is loaded */
+ if (!e_book_backend_is_opened (backend))
+ return;
- if (!priv->supports_getctag)
- return TRUE;
+ /* XXX Could just use a property binding for this.
+ * EBackend:online --> EBookBackend:writable */
+ online = e_backend_get_online (E_BACKEND (backend));
+ e_book_backend_set_writable (backend, online);
+}
- priv->supports_getctag = FALSE;
+static void
+book_backend_webdav_dispose (GObject *object)
+{
+ EBookBackendWebdavPrivate *priv;
- message = soup_message_new (SOUP_METHOD_PROPFIND, priv->uri);
- if (!message)
- return TRUE;
+ priv = E_BOOK_BACKEND_WEBDAV_GET_PRIVATE (object);
- soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
- soup_message_headers_append (message->request_headers, "Connection", "close");
- soup_message_headers_append (message->request_headers, "Depth", "0");
- soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen
(request));
- send_and_handle_ssl (webdav, message, cancellable);
+ g_clear_object (&priv->session);
+ g_clear_object (&priv->proxy);
+ g_clear_object (&priv->cache);
- if (message->status_code == 207 && message->response_body) {
- xmlDocPtr xml;
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
+}
- xml = xmlReadMemory (message->response_body->data, message->response_body->length, NULL,
NULL, XML_PARSE_NOWARNING);
- if (xml) {
- const gchar *GETCTAG_XPATH_STATUS =
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag/../../D:status)";
- const gchar *GETCTAG_XPATH_VALUE =
"string(/D:multistatus/D:response/D:propstat/D:prop/D:getctag)";
- xmlXPathContextPtr xpctx;
+static void
+book_backend_webdav_finalize (GObject *object)
+{
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
- xpctx = xmlXPathNewContext (xml);
- xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
+ g_free (priv->uri);
+ g_free (priv->username);
+ g_free (priv->password);
- if (xp_object_get_status (xpath_eval (xpctx, GETCTAG_XPATH_STATUS)) == 200) {
- gchar *txt = xp_object_get_string (xpath_eval (xpctx, GETCTAG_XPATH_VALUE));
+ g_mutex_clear (&priv->cache_lock);
+ g_mutex_clear (&priv->update_lock);
- if (txt && *txt) {
- gint len = strlen (txt);
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->finalize (object);
+}
- if (*txt == '\"' && len > 2 && txt[len - 1] == '\"') {
- /* dequote */
- *new_ctag = g_strndup (txt + 1, len - 2);
- } else {
- *new_ctag = txt;
- txt = NULL;
- }
+static gchar *
+book_backend_webdav_get_backend_property (EBookBackend *backend,
+ const gchar *prop_name)
+{
+ g_return_val_if_fail (prop_name != NULL, NULL);
- if (*new_ctag) {
- const gchar *my_ctag;
+ if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
+ return g_strdup ("net,do-initial-query,contact-lists");
- g_mutex_lock (&priv->cache_lock);
- my_ctag = e_file_cache_get_object (E_FILE_CACHE
(priv->cache), WEBDAV_CTAG_KEY);
- res = !my_ctag || !g_str_equal (my_ctag, *new_ctag);
- priv->supports_getctag = TRUE;
- g_mutex_unlock (&priv->cache_lock);
- }
- }
+ } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
+ return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
- g_free (txt);
- }
+ } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
+ GString *fields;
+ gint ii;
- xmlXPathFreeContext (xpctx);
- xmlFreeDoc (xml);
+ fields = g_string_sized_new (1024);
+
+ /* we support everything */
+ for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
+ if (fields->len > 0)
+ g_string_append_c (fields, ',');
+ g_string_append (fields, e_contact_field_name (ii));
}
- }
- g_object_unref (message);
+ return g_string_free (fields, FALSE);
+ }
- return res;
+ /* Chain up to parent's get_backend_property() method. */
+ return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->
+ get_backend_property (backend, prop_name);
}
-static GError *
-download_contacts (EBookBackendWebdav *webdav,
- EFlag *running,
- EDataBookView *book_view,
- GCancellable *cancellable)
+static void
+book_backend_webdav_open (EBookBackend *backend,
+ EDataBook *book,
+ guint opid,
+ GCancellable *cancellable,
+ gboolean only_if_exists)
{
- EBookBackendWebdavPrivate *priv = webdav->priv;
- EBookBackend *book_backend;
- SoupMessage *message;
- guint status;
- xmlTextReaderPtr reader;
- response_element_t *elements;
- response_element_t *element;
- response_element_t *next;
- gint count;
- gint i;
- gchar *new_ctag = NULL;
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ ESourceAuthentication *auth_extension;
+ ESourceOffline *offline_extension;
+ ESourceWebdav *webdav_extension;
+ ESource *source;
+ const gchar *extension_name;
+ const gchar *cache_dir;
+ gchar *filename;
+ SoupSession *session;
+ SoupURI *suri;
+ GError *error = NULL;
- g_mutex_lock (&priv->update_lock);
+ /* will try fetch ctag for the first time, if it fails then sets this to FALSE */
+ priv->supports_getctag = TRUE;
- if (!check_addressbook_changed (webdav, &new_ctag, cancellable)) {
- g_free (new_ctag);
- g_mutex_unlock (&priv->update_lock);
- return EDB_ERROR (SUCCESS);
+ source = e_backend_get_source (E_BACKEND (backend));
+ cache_dir = e_book_backend_get_cache_dir (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);
+
+ priv->marked_for_offline =
+ e_source_offline_get_stay_synchronized (offline_extension);
+
+ if (!e_backend_get_online (E_BACKEND (backend)) && !priv->marked_for_offline ) {
+ e_data_book_respond_open (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE));
+ return;
}
- book_backend = E_BOOK_BACKEND (webdav);
+ suri = e_source_webdav_dup_soup_uri (webdav_extension);
- if (book_view != NULL) {
- e_data_book_view_notify_progress (book_view, -1,
- _("Loading Addressbook summary..."));
+ priv->uri = soup_uri_to_string (suri, FALSE);
+ if (!priv->uri || !*priv->uri) {
+ g_free (priv->uri);
+ priv->uri = NULL;
+ soup_uri_free (suri);
+ e_data_book_respond_open (book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Cannot transform SoupURI
to string")));
+ return;
}
- message = send_propfind (webdav, cancellable);
- status = message->status_code;
+ g_mutex_lock (&priv->cache_lock);
- if (status == 401 || status == 407) {
- g_object_unref (message);
- g_free (new_ctag);
- if (book_view)
- e_data_book_view_notify_progress (book_view, -1, NULL);
- g_mutex_unlock (&priv->update_lock);
- return webdav_handle_auth_request (webdav);
+ /* make sure the priv->uri ends with a forward slash */
+ if (priv->uri[strlen (priv->uri) - 1] != '/') {
+ gchar *tmp = priv->uri;
+ priv->uri = g_strconcat (tmp, "/", NULL);
+ g_free (tmp);
}
- if (status != 207) {
- GError *error;
- error = e_data_book_create_error_fmt (
- E_DATA_BOOK_STATUS_OTHER_ERROR,
- _("PROPFIND on webdav failed with HTTP status %d (%s)"),
- status,
- 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->cache) {
+ filename = g_build_filename (cache_dir, "cache.xml", NULL);
+ priv->cache = e_book_backend_cache_new (filename);
+ g_free (filename);
+ }
+ g_mutex_unlock (&priv->cache_lock);
- g_object_unref (message);
- g_free (new_ctag);
+ session = soup_session_sync_new ();
+ g_object_set (
+ session,
+ SOUP_SESSION_TIMEOUT, 90,
+ SOUP_SESSION_SSL_STRICT, TRUE,
+ SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+ NULL);
- if (book_view)
- e_data_book_view_notify_progress (book_view, -1, NULL);
+ e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
- g_mutex_unlock (&priv->update_lock);
+ g_signal_connect (
+ session, "authenticate",
+ G_CALLBACK (soup_authenticate), webdav);
- return error;
- }
- if (message->response_body == NULL) {
- g_warning ("No response body in webdav PROPFIND result");
- g_object_unref (message);
- g_free (new_ctag);
+ priv->session = session;
+ priv->proxy = e_proxy_new ();
+ e_proxy_setup_proxy (priv->proxy);
+ g_signal_connect (
+ priv->proxy, "changed",
+ G_CALLBACK (proxy_settings_changed), priv);
+ proxy_settings_changed (priv->proxy, priv);
+ webdav_debug_setup (priv->session);
- if (book_view)
- e_data_book_view_notify_progress (book_view, -1, NULL);
+ e_backend_set_online (E_BACKEND (backend), TRUE);
+ e_book_backend_set_writable (backend, TRUE);
- g_mutex_unlock (&priv->update_lock);
+ if (e_source_authentication_required (auth_extension))
+ e_backend_authenticate_sync (
+ E_BACKEND (backend),
+ E_SOURCE_AUTHENTICATOR (backend),
+ cancellable, &error);
- return e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR, _("No response body in
webdav PROPFIND result"));
- }
+ soup_uri_free (suri);
- /* parse response */
- reader = xmlReaderForMemory (
- message->response_body->data,
- message->response_body->length, NULL, NULL,
- XML_PARSE_NOWARNING);
+ /* This function frees the GError passed to it. */
+ e_data_book_respond_open (book, opid, error);
+}
- elements = parse_propfind_response (reader);
+static void
+book_backend_webdav_create_contacts (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const GSList *vcards)
+{
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ EContact *contact;
+ gchar *uid;
+ guint status;
+ gchar *status_reason = NULL;
+ const gchar *vcard = (const gchar *) vcards->data;
+ GSList added_contacts = {NULL,};
- /* count contacts */
- count = 0;
- for (element = elements; element != NULL; element = element->next) {
- ++count;
+ /* We make the assumption that the vCard list we're passed is always exactly one element long, since
we haven't specified "bulk-adds"
+ * in our static capability list. This is because there is no clean way to roll back changes in case
of an error. */
+ if (vcards->next != NULL) {
+ e_data_book_respond_create_contacts (
+ book, opid,
+ EDB_ERROR_EX (NOT_SUPPORTED,
+ _("The backend does not support bulk additions")),
+ NULL);
+ return;
}
- /* download contacts */
- i = 0;
- for (element = elements; element != NULL; element = element->next, ++i) {
- const gchar *uri;
- const gchar *etag;
- EContact *contact;
- gchar *complete_uri;
-
- /* stop downloading if search was aborted */
- if (running != NULL && !e_flag_is_set (running))
- break;
+ if (!e_backend_get_online (E_BACKEND (backend))) {
+ e_data_book_respond_create_contacts (book, opid, EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+ return;
+ }
- if (book_view != NULL) {
- gfloat percent = 100.0 / count * i;
- gchar buf[100];
- snprintf (buf, sizeof (buf), _("Loading Contacts (%d%%)"), (gint) percent);
- e_data_book_view_notify_progress (book_view, -1, buf);
- }
+ /* 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",
+ priv->uri, rand (), rand (), rand ());
- /* skip collections */
- uri = (const gchar *) element->href;
- if (uri[strlen (uri) - 1] == '/')
- continue;
+ contact = e_contact_new_from_vcard_with_uid (vcard, uid);
- /* uri might be relative, construct complete one */
- if (uri[0] == '/') {
- SoupURI *soup_uri = soup_uri_new (priv->uri);
- soup_uri->path = g_strdup (uri);
+ /* kill revision field (might have been set by some other backend) */
+ e_contact_set (contact, E_CONTACT_REV, NULL);
- complete_uri = soup_uri_to_string (soup_uri, FALSE);
- soup_uri_free (soup_uri);
+ status = upload_contact (webdav, contact, &status_reason, cancellable);
+ if (status != 201 && status != 204) {
+ g_object_unref (contact);
+ if (status == 401 || status == 407) {
+ e_data_book_respond_create_contacts (book, opid, webdav_handle_auth_request (webdav),
NULL);
} else {
- complete_uri = g_strdup (uri);
+ e_data_book_respond_create_contacts (
+ book, opid,
+ e_data_book_create_error_fmt (
+ E_DATA_BOOK_STATUS_OTHER_ERROR,
+ _("Create resource '%s' failed with HTTP status %d (%s)"),
+ uid, status, status_reason),
+ NULL);
}
+ g_free (uid);
+ g_free (status_reason);
+ return;
+ }
- etag = (const gchar *) element->etag;
+ g_free (status_reason);
- g_mutex_lock (&priv->cache_lock);
- contact = e_book_backend_cache_get_contact (priv->cache, complete_uri);
- g_mutex_unlock (&priv->cache_lock);
+ /* PUT request didn't return an etag? try downloading to get one */
+ if (e_contact_get_const (contact, E_CONTACT_REV) == NULL) {
+ const gchar *new_uid;
+ EContact *new_contact;
- /* download contact if it is not cached or its ETag changed */
- if (contact == NULL || etag == NULL ||
- strcmp (e_contact_get_const (contact, E_CONTACT_REV), etag) != 0) {
- contact = download_contact (webdav, complete_uri, cancellable);
- if (contact != NULL) {
- 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);
- e_book_backend_cache_add_contact (priv->cache, contact);
- g_mutex_unlock (&priv->cache_lock);
- e_book_backend_notify_update (book_backend, contact);
- }
- }
+ 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);
- g_free (complete_uri);
+ if (new_contact == NULL) {
+ e_data_book_respond_create_contacts (
+ book, opid,
+ EDB_ERROR (OTHER_ERROR), NULL);
+ g_free (uid);
+ return;
+ }
+ contact = new_contact;
}
- /* free element list */
- for (element = elements; element != NULL; element = next) {
- next = element->next;
+ g_mutex_lock (&priv->cache_lock);
+ e_book_backend_cache_add_contact (priv->cache, contact);
+ g_mutex_unlock (&priv->cache_lock);
- xmlFree (element->href);
- xmlFree (element->etag);
- g_free (element);
- }
+ added_contacts.data = contact;
+ e_data_book_respond_create_contacts (book, opid, EDB_ERROR (SUCCESS), &added_contacts);
- xmlFreeTextReader (reader);
- g_object_unref (message);
+ g_object_unref (contact);
+ g_free (uid);
+}
- if (new_ctag) {
- g_mutex_lock (&priv->cache_lock);
- if (!e_file_cache_replace_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag))
- e_file_cache_add_object (E_FILE_CACHE (priv->cache), WEBDAV_CTAG_KEY, new_ctag);
- g_mutex_unlock (&priv->cache_lock);
+static void
+book_backend_webdav_modify_contacts (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const GSList *vcards)
+{
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ EContact *contact;
+ GSList modified_contacts = {NULL,};
+ const gchar *uid;
+ const gchar *etag;
+ guint status;
+ gchar *status_reason = NULL;
+ const gchar *vcard = vcards->data;
+
+ if (!e_backend_get_online (E_BACKEND (backend))) {
+ e_data_book_respond_create_contacts (
+ book, opid,
+ EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+ return;
}
- g_free (new_ctag);
- if (book_view)
- e_data_book_view_notify_progress (book_view, -1, NULL);
+ /* We make the assumption that the vCard list we're passed is always exactly one element long, since
we haven't specified "bulk-modifies"
+ * in our static capability list. This is because there is no clean way to roll back changes in case
of an error. */
+ if (vcards->next != NULL) {
+ e_data_book_respond_modify_contacts (
+ book, opid,
+ EDB_ERROR_EX (
+ NOT_SUPPORTED,
+ _("The backend does not support bulk modifications")),
+ NULL);
+ return;
+ }
- g_mutex_unlock (&priv->update_lock);
+ /* modify contact */
+ contact = e_contact_new_from_vcard (vcard);
+ status = upload_contact (webdav, contact, &status_reason, cancellable);
+ if (status != 201 && status != 204) {
+ g_object_unref (contact);
+ if (status == 401 || status == 407) {
+ e_data_book_respond_modify_contacts (book, opid, webdav_handle_auth_request (webdav),
NULL);
+ g_free (status_reason);
+ return;
+ }
+ /* data changed on server while we were editing */
+ if (status == 412) {
+ /* too bad no special error code in evolution for this... */
+ e_data_book_respond_modify_contacts (book, opid,
+ e_data_book_create_error_fmt (
+ E_DATA_BOOK_STATUS_OTHER_ERROR,
+ _("Contact on server changed -> not modifying")),
+ NULL);
+ g_free (status_reason);
+ return;
+ }
- return EDB_ERROR (SUCCESS);
-}
+ e_data_book_respond_modify_contacts (book, opid,
+ e_data_book_create_error_fmt (
+ E_DATA_BOOK_STATUS_OTHER_ERROR,
+ _("Modify contact failed with HTTP status %d (%s)"),
+ status, status_reason),
+ NULL);
+ g_free (status_reason);
+ return;
+ }
-static gpointer
-book_view_thread (gpointer data)
-{
- EDataBookView *book_view = data;
- WebdavBackendSearchClosure *closure = get_closure (book_view);
- EBookBackendWebdav *webdav = closure->webdav;
- GError *error;
+ g_free (status_reason);
- e_flag_set (closure->running);
+ uid = e_contact_get_const (contact, E_CONTACT_UID);
+ g_mutex_lock (&priv->cache_lock);
+ e_book_backend_cache_remove_contact (priv->cache, uid);
- /* ref the book view because it'll be removed and unrefed when/if
- * it's stopped */
- g_object_ref (book_view);
+ etag = e_contact_get_const (contact, E_CONTACT_REV);
- error = download_contacts (webdav, closure->running, book_view, NULL);
+ /* PUT request didn't return an etag? try downloading to get one */
+ if (etag == NULL || (etag[0] == 'W' && etag[1] == '/')) {
+ EContact *new_contact;
- g_object_unref (book_view);
+ g_warning ("Server didn't return etag for modified address resource");
+ new_contact = download_contact (webdav, uid, cancellable);
+ if (new_contact != NULL) {
+ contact = new_contact;
+ }
+ }
+ e_book_backend_cache_add_contact (priv->cache, contact);
+ g_mutex_unlock (&priv->cache_lock);
- if (error)
- g_error_free (error);
+ modified_contacts.data = contact;
+ e_data_book_respond_modify_contacts (book, opid, EDB_ERROR (SUCCESS), &modified_contacts);
- return NULL;
+ g_object_unref (contact);
}
static void
-e_book_backend_webdav_start_view (EBookBackend *backend,
- EDataBookView *book_view)
+book_backend_webdav_remove_contacts (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const GSList *id_list)
{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- EBookBackendSExp *sexp;
- const gchar *query;
- GList *contacts;
- GList *l;
-
- sexp = e_data_book_view_get_sexp (book_view);
- query = e_book_backend_sexp_text (sexp);
-
- g_mutex_lock (&priv->cache_lock);
- contacts = e_book_backend_cache_get_contacts (priv->cache, query);
- g_mutex_unlock (&priv->cache_lock);
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ gchar *uid = id_list->data;
+ GSList deleted_ids = {NULL,};
+ guint status;
- for (l = contacts; l != NULL; l = g_list_next (l)) {
- EContact *contact = l->data;
- e_data_book_view_notify_update (book_view, contact);
- g_object_unref (contact);
+ if (!e_backend_get_online (E_BACKEND (backend))) {
+ e_data_book_respond_remove_contacts (
+ book, opid,
+ EDB_ERROR (REPOSITORY_OFFLINE), NULL);
+ return;
}
- g_list_free (contacts);
- /* this way the UI is notified about cached contacts immediately,
- * and the update thread notifies about possible changes only */
- e_data_book_view_notify_complete (book_view, NULL /* Success */);
+ /* We make the assumption that the ID list we're passed is always exactly one element long, since we
haven't specified "bulk-removes"
+ * in our static capability list. */
+ if (id_list->next != NULL) {
+ e_data_book_respond_remove_contacts (
+ book, opid,
+ EDB_ERROR_EX (NOT_SUPPORTED,
+ _("The backend does not support bulk removals")),
+ NULL);
+ return;
+ }
- if (e_backend_get_online (E_BACKEND (backend))) {
- WebdavBackendSearchClosure *closure
- = init_closure (book_view, E_BOOK_BACKEND_WEBDAV (backend));
+ status = delete_contact (webdav, uid, cancellable);
+ if (status != 204) {
+ if (status == 401 || status == 407) {
+ e_data_book_respond_remove_contacts (
+ book, opid,
+ webdav_handle_auth_request (webdav), NULL);
+ } else {
+ g_warning ("DELETE failed with HTTP status %d", status);
+ e_data_book_respond_remove_contacts (
+ book, opid,
+ e_data_book_create_error_fmt (E_DATA_BOOK_STATUS_OTHER_ERROR,
+ _("DELETE failed with HTTP status %d"), status),
+ NULL);
+ }
+ return;
+ }
- closure->thread
- = g_thread_new (NULL, book_view_thread, book_view);
+ g_mutex_lock (&priv->cache_lock);
+ e_book_backend_cache_remove_contact (priv->cache, uid);
+ g_mutex_unlock (&priv->cache_lock);
- e_flag_wait (closure->running);
- }
+ deleted_ids.data = uid;
+ e_data_book_respond_remove_contacts (book, opid, EDB_ERROR (SUCCESS), &deleted_ids);
}
static void
-e_book_backend_webdav_stop_view (EBookBackend *backend,
- EDataBookView *book_view)
+book_backend_webdav_get_contact (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *uid)
{
- WebdavBackendSearchClosure *closure;
- gboolean need_join;
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
+ EBookBackendWebdavPrivate *priv = webdav->priv;
+ EContact *contact;
+ gchar *vcard;
- if (!e_backend_get_online (E_BACKEND (backend)))
- return;
+ if (!e_backend_get_online (E_BACKEND (backend))) {
+ g_mutex_lock (&priv->cache_lock);
+ contact = e_book_backend_cache_get_contact (priv->cache, uid);
+ g_mutex_unlock (&priv->cache_lock);
+ } else {
+ contact = download_contact (webdav, uid, cancellable);
+ /* update cache as we possibly have changes */
+ if (contact != NULL) {
+ g_mutex_lock (&priv->cache_lock);
+ e_book_backend_cache_remove_contact (priv->cache, uid);
+ e_book_backend_cache_add_contact (priv->cache, contact);
+ g_mutex_unlock (&priv->cache_lock);
+ }
+ }
- closure = get_closure (book_view);
- if (closure == NULL)
+ if (contact == NULL) {
+ e_data_book_respond_get_contact (book, opid, EDB_ERROR (CONTACT_NOT_FOUND), NULL);
return;
-
- need_join = e_flag_is_set (closure->running);
- e_flag_clear (closure->running);
-
- if (need_join) {
- g_thread_join (closure->thread);
- closure->thread = NULL;
}
+
+ vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
+ e_data_book_respond_get_contact (book, opid, EDB_ERROR (SUCCESS), vcard);
+ g_free (vcard);
+ g_object_unref (contact);
}
static void
-e_book_backend_webdav_get_contact_list (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const gchar *query)
+book_backend_webdav_get_contact_list (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *query)
{
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
EBookBackendWebdavPrivate *priv = webdav->priv;
@@ -1283,11 +1509,11 @@ e_book_backend_webdav_get_contact_list (EBookBackend *backend,
}
static void
-e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
- EDataBook *book,
- guint32 opid,
- GCancellable *cancellable,
- const gchar *query)
+book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
+ EDataBook *book,
+ guint32 opid,
+ GCancellable *cancellable,
+ const gchar *query)
{
EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
EBookBackendWebdavPrivate *priv = webdav->priv;
@@ -1326,233 +1552,6 @@ e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
g_slist_free (uids_list);
}
-/** authentication callback for libsoup */
-static void
-soup_authenticate (SoupSession *session,
- SoupMessage *message,
- SoupAuth *auth,
- gboolean retrying,
- gpointer data)
-{
- EBookBackendWebdav *webdav = data;
- EBookBackendWebdavPrivate *priv = webdav->priv;
-
- if (retrying)
- return;
-
- if (priv->username != NULL) {
- soup_auth_authenticate (auth, priv->username, priv->password);
- }
-}
-
-static void
-proxy_settings_changed (EProxy *proxy,
- gpointer user_data)
-{
- SoupURI *proxy_uri = NULL;
- EBookBackendWebdavPrivate *priv = (EBookBackendWebdavPrivate *) user_data;
-
- if (!priv || !priv->uri || !priv->session)
- return;
-
- /* use proxy if necessary */
- if (e_proxy_require_proxy_for_uri (proxy, priv->uri)) {
- proxy_uri = e_proxy_peek_uri_for (proxy, priv->uri);
- }
-
- g_object_set (priv->session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
-}
-
-static void
-e_book_backend_webdav_open (EBookBackend *backend,
- EDataBook *book,
- guint opid,
- GCancellable *cancellable,
- gboolean only_if_exists)
-{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
- EBookBackendWebdavPrivate *priv = webdav->priv;
- ESourceAuthentication *auth_extension;
- ESourceOffline *offline_extension;
- ESourceWebdav *webdav_extension;
- ESource *source;
- const gchar *extension_name;
- const gchar *cache_dir;
- gchar *filename;
- SoupSession *session;
- SoupURI *suri;
- GError *error = NULL;
-
- /* will try fetch ctag for the first time, if it fails then sets this to FALSE */
- priv->supports_getctag = TRUE;
-
- source = e_backend_get_source (E_BACKEND (backend));
- cache_dir = e_book_backend_get_cache_dir (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);
-
- priv->marked_for_offline =
- e_source_offline_get_stay_synchronized (offline_extension);
-
- if (!e_backend_get_online (E_BACKEND (backend)) && !priv->marked_for_offline ) {
- e_data_book_respond_open (book, opid, EDB_ERROR (OFFLINE_UNAVAILABLE));
- return;
- }
-
- suri = e_source_webdav_dup_soup_uri (webdav_extension);
-
- priv->uri = soup_uri_to_string (suri, FALSE);
- if (!priv->uri || !*priv->uri) {
- g_free (priv->uri);
- priv->uri = NULL;
- soup_uri_free (suri);
- e_data_book_respond_open (book, opid, EDB_ERROR_EX (OTHER_ERROR, _("Cannot transform SoupURI
to string")));
- return;
- }
-
- g_mutex_lock (&priv->cache_lock);
-
- /* make sure the priv->uri ends with a forward slash */
- if (priv->uri[strlen (priv->uri) - 1] != '/') {
- gchar *tmp = priv->uri;
- priv->uri = g_strconcat (tmp, "/", NULL);
- g_free (tmp);
- }
-
- if (!priv->cache) {
- filename = g_build_filename (cache_dir, "cache.xml", NULL);
- priv->cache = e_book_backend_cache_new (filename);
- g_free (filename);
- }
- g_mutex_unlock (&priv->cache_lock);
-
- session = soup_session_sync_new ();
- g_object_set (
- session,
- SOUP_SESSION_TIMEOUT, 90,
- SOUP_SESSION_SSL_STRICT, TRUE,
- SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
- NULL);
-
- e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
-
- g_signal_connect (
- session, "authenticate",
- G_CALLBACK (soup_authenticate), webdav);
-
- priv->session = session;
- priv->proxy = e_proxy_new ();
- e_proxy_setup_proxy (priv->proxy);
- g_signal_connect (
- priv->proxy, "changed",
- G_CALLBACK (proxy_settings_changed), priv);
- proxy_settings_changed (priv->proxy, priv);
- webdav_debug_setup (priv->session);
-
- e_backend_set_online (E_BACKEND (backend), TRUE);
- e_book_backend_set_writable (backend, TRUE);
-
- if (e_source_authentication_required (auth_extension))
- e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, &error);
-
- soup_uri_free (suri);
-
- /* This function frees the GError passed to it. */
- e_data_book_respond_open (book, opid, error);
-}
-
-static void
-e_book_backend_webdav_notify_online_cb (EBookBackend *backend,
- GParamSpec *pspec)
-{
- gboolean online;
-
- /* set_mode is called before the backend is loaded */
- if (!e_book_backend_is_opened (backend))
- return;
-
- /* XXX Could just use a property binding for this.
- * EBackend:online --> EBookBackend:writable */
- online = e_backend_get_online (E_BACKEND (backend));
- e_book_backend_set_writable (backend, online);
-}
-
-static gchar *
-e_book_backend_webdav_get_backend_property (EBookBackend *backend,
- const gchar *prop_name)
-{
- g_return_val_if_fail (prop_name != NULL, NULL);
-
- if (g_str_equal (prop_name, CLIENT_BACKEND_PROPERTY_CAPABILITIES)) {
- return g_strdup ("net,do-initial-query,contact-lists");
-
- } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS)) {
- return g_strdup (e_contact_field_name (E_CONTACT_FILE_AS));
-
- } else if (g_str_equal (prop_name, BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS)) {
- GString *fields;
- gint ii;
-
- fields = g_string_sized_new (1024);
-
- /* we support everything */
- for (ii = 1; ii < E_CONTACT_FIELD_LAST; ii++) {
- if (fields->len > 0)
- g_string_append_c (fields, ',');
- g_string_append (fields, e_contact_field_name (ii));
- }
-
- return g_string_free (fields, FALSE);
- }
-
- /* Chain up to parent's get_backend_property() method. */
- return E_BOOK_BACKEND_CLASS (e_book_backend_webdav_parent_class)->
- get_backend_property (backend, prop_name);
-}
-
-static void
-e_book_backend_webdav_dispose (GObject *object)
-{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
- EBookBackendWebdavPrivate *priv = webdav->priv;
-
- #define do_unref(x) { if (x) g_object_unref (x); x = NULL; }
-
- do_unref (priv->session);
- do_unref (priv->proxy);
- do_unref (priv->cache);
-
- g_free (priv->uri);
- g_free (priv->username);
- g_free (priv->password);
-
- #undef do_unref
-
- G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->dispose (object);
-}
-
-static void
-e_book_backend_webdav_finalize (GObject *object)
-{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (object);
- EBookBackendWebdavPrivate *priv = webdav->priv;
-
- g_mutex_clear (&priv->cache_lock);
- g_mutex_clear (&priv->update_lock);
-
- G_OBJECT_CLASS (e_book_backend_webdav_parent_class)->finalize (object);
-}
-
static ESourceAuthenticationResult
book_backend_webdav_try_password_sync (ESourceAuthenticator *authenticator,
const GString *password,
@@ -1614,23 +1613,21 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *class)
g_type_class_add_private (class, sizeof (EBookBackendWebdavPrivate));
- backend_class = E_BOOK_BACKEND_CLASS (class);
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = book_backend_webdav_dispose;
+ object_class->finalize = book_backend_webdav_finalize;
- /* Set the virtual methods. */
- backend_class->open = e_book_backend_webdav_open;
- backend_class->get_backend_property = e_book_backend_webdav_get_backend_property;
-
- backend_class->create_contacts = e_book_backend_webdav_create_contacts;
- backend_class->remove_contacts = e_book_backend_webdav_remove_contacts;
- backend_class->modify_contacts = e_book_backend_webdav_modify_contacts;
- backend_class->get_contact = e_book_backend_webdav_get_contact;
- backend_class->get_contact_list = e_book_backend_webdav_get_contact_list;
- backend_class->get_contact_list_uids = e_book_backend_webdav_get_contact_list_uids;
- backend_class->start_view = e_book_backend_webdav_start_view;
- backend_class->stop_view = e_book_backend_webdav_stop_view;
-
- object_class->dispose = e_book_backend_webdav_dispose;
- object_class->finalize = e_book_backend_webdav_finalize;
+ backend_class = E_BOOK_BACKEND_CLASS (class);
+ backend_class->get_backend_property = book_backend_webdav_get_backend_property;
+ backend_class->open = book_backend_webdav_open;
+ backend_class->create_contacts = book_backend_webdav_create_contacts;
+ backend_class->modify_contacts = book_backend_webdav_modify_contacts;
+ backend_class->remove_contacts = book_backend_webdav_remove_contacts;
+ backend_class->get_contact = book_backend_webdav_get_contact;
+ backend_class->get_contact_list = book_backend_webdav_get_contact_list;
+ backend_class->get_contact_list_uids = book_backend_webdav_get_contact_list_uids;
+ backend_class->start_view = e_book_backend_webdav_start_view;
+ backend_class->stop_view = e_book_backend_webdav_stop_view;
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]