[evolution-data-server] Bug #686528 - Pickup ownCloud accounts from GOA



commit 00740be9b52cce08611062a4b3e5b5e75e7b8c86
Author: Milan Crha <mcrha redhat com>
Date:   Tue Feb 12 13:21:06 2013 +0100

    Bug #686528 - Pickup ownCloud accounts from GOA

 configure.ac                                       |    1 +
 libedataserver/e-source-goa.c                      |  231 ++++++++-
 libedataserver/e-source-goa.h                      |    8 +
 libedataserver/e-source-webdav.c                   |   84 +++-
 libedataserver/e-source-webdav.h                   |    6 +
 modules/Makefile.am                                |    1 +
 .../module-gnome-online-accounts.c                 |  116 +++--
 modules/owncloud-backend/Makefile.am               |   31 ++
 modules/owncloud-backend/module-owncloud-backend.c |  344 ++++++++++++
 modules/owncloud-backend/owncloud-utils.c          |  558 ++++++++++++++++++++
 modules/owncloud-backend/owncloud-utils.h          |   47 ++
 11 files changed, 1367 insertions(+), 60 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 77d035d..710fa98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1701,6 +1701,7 @@ modules/Makefile
 modules/cache-reaper/Makefile
 modules/gnome-online-accounts/Makefile
 modules/google-backend/Makefile
+modules/owncloud-backend/Makefile
 modules/ubuntu-online-accounts/Makefile
 modules/ubuntu-online-accounts/calendar.service-type.in
 modules/ubuntu-online-accounts/contacts.service-type.in
diff --git a/libedataserver/e-source-goa.c b/libedataserver/e-source-goa.c
index 5cff646..2856ab8 100644
--- a/libedataserver/e-source-goa.c
+++ b/libedataserver/e-source-goa.c
@@ -47,11 +47,15 @@
 struct _ESourceGoaPrivate {
 	GMutex property_lock;
 	gchar *account_id;
+	gchar *calendar_url;
+	gchar *contacts_url;
 };
 
 enum {
 	PROP_0,
-	PROP_ACCOUNT_ID
+	PROP_ACCOUNT_ID,
+	PROP_CALENDAR_URL,
+	PROP_CONTACTS_URL
 };
 
 G_DEFINE_TYPE (
@@ -71,6 +75,18 @@ source_goa_set_property (GObject *object,
 				E_SOURCE_GOA (object),
 				g_value_get_string (value));
 			return;
+
+		case PROP_CALENDAR_URL:
+			e_source_goa_set_calendar_url (
+				E_SOURCE_GOA (object),
+				g_value_get_string (value));
+			return;
+
+		case PROP_CONTACTS_URL:
+			e_source_goa_set_contacts_url (
+				E_SOURCE_GOA (object),
+				g_value_get_string (value));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -89,6 +105,20 @@ source_goa_get_property (GObject *object,
 				e_source_goa_dup_account_id (
 				E_SOURCE_GOA (object)));
 			return;
+
+		case PROP_CALENDAR_URL:
+			g_value_take_string (
+				value,
+				e_source_goa_dup_calendar_url (
+				E_SOURCE_GOA (object)));
+			return;
+
+		case PROP_CONTACTS_URL:
+			g_value_take_string (
+				value,
+				e_source_goa_dup_contacts_url (
+				E_SOURCE_GOA (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -104,6 +134,8 @@ source_goa_finalize (GObject *object)
 	g_mutex_clear (&priv->property_lock);
 
 	g_free (priv->account_id);
+	g_free (priv->calendar_url);
+	g_free (priv->contacts_url);
 
 	/* Chain up to parent's finalize() method. */
 	G_OBJECT_CLASS (e_source_goa_parent_class)->finalize (object);
@@ -137,6 +169,32 @@ e_source_goa_class_init (ESourceGoaClass *class)
 			G_PARAM_CONSTRUCT |
 			G_PARAM_STATIC_STRINGS |
 			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CALENDAR_URL,
+		g_param_spec_string (
+			"calendar-url",
+			"Calendar URL",
+			"GNOME Online Calendar URL",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_CONTACTS_URL,
+		g_param_spec_string (
+			"contacts-url",
+			"Contacts URL",
+			"GNOME Online Contacts URL",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			G_PARAM_STATIC_STRINGS |
+			E_SOURCE_PARAM_SETTING));
 }
 
 static void
@@ -231,3 +289,174 @@ e_source_goa_set_account_id (ESourceGoa *extension,
 	g_object_notify (G_OBJECT (extension), "account-id");
 }
 
+/**
+ * e_source_goa_get_calendar_url:
+ * @extension: an #ESourceGoa
+ *
+ * Returns the calendar URL string of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs. Can be %NULL or an empty
+ * string for accounts not supporting this property.
+ *
+ * Returns: the associated GNOME Online Account calendar URL
+ *
+ * Since: 3.8
+ **/
+const gchar *
+e_source_goa_get_calendar_url (ESourceGoa *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	return extension->priv->calendar_url;
+}
+
+/**
+ * e_source_goa_dup_calendar_url:
+ * @extension: an #ESourceGoa
+ *
+ * Thread-safe variation of e_source_goa_get_calendar_url().
+ * Use this function when accessing @extension from multiple threads.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceGoa:calendar-url
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_source_goa_dup_calendar_url (ESourceGoa *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	protected = e_source_goa_get_calendar_url (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_goa_set_calendar_url:
+ * @extension: an #ESourceGoa
+ * @calendar_url: (allow-none): the associated GNOME Online Account calendar URL, or %NULL
+ *
+ * Sets the calendar URL of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs.
+ *
+ * The internal copy of @calendar_url is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.8
+ **/
+void
+e_source_goa_set_calendar_url (ESourceGoa *extension,
+			       const gchar *calendar_url)
+{
+	g_return_if_fail (E_IS_SOURCE_GOA (extension));
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	if (g_strcmp0 (extension->priv->calendar_url, calendar_url) == 0) {
+		g_mutex_unlock (&extension->priv->property_lock);
+		return;
+	}
+
+	g_free (extension->priv->calendar_url);
+	extension->priv->calendar_url = e_util_strdup_strip (calendar_url);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "calendar-url");
+}
+
+/**
+ * e_source_goa_get_contacts_url:
+ * @extension: an #ESourceGoa
+ *
+ * Returns the contacts URL string of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs. Can be %NULL or an empty
+ * string for accounts not supporting this property.
+ *
+ * Returns: the associated GNOME Online Account contacts URL
+ *
+ * Since: 3.8
+ **/
+const gchar *
+e_source_goa_get_contacts_url (ESourceGoa *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	return extension->priv->contacts_url;
+}
+
+/**
+ * e_source_goa_dup_contacts_url:
+ * @extension: an #ESourceGoa
+ *
+ * Thread-safe variation of e_source_goa_get_contacts_url().
+ * Use this function when accessing @extension from multiple threads.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceGoa:contacts-url
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_source_goa_dup_contacts_url (ESourceGoa *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_GOA (extension), NULL);
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	protected = e_source_goa_get_contacts_url (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_goa_set_contacts_url:
+ * @extension: an #ESourceGoa
+ * @contacts_url: (allow-none): the associated GNOME Online Account contacts URL, or %NULL
+ *
+ * Sets the contacts URL of the GNOME Online Account associated
+ * with the #ESource to which @extension belongs.
+ *
+ * The internal copy of @contacts_url is automatically stripped of leading
+ * and trailing whitespace.  If the resulting string is empty, %NULL is set
+ * instead.
+ *
+ * Since: 3.8
+ **/
+void
+e_source_goa_set_contacts_url (ESourceGoa *extension,
+			       const gchar *contacts_url)
+{
+	g_return_if_fail (E_IS_SOURCE_GOA (extension));
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	if (g_strcmp0 (extension->priv->contacts_url, contacts_url) == 0) {
+		g_mutex_unlock (&extension->priv->property_lock);
+		return;
+	}
+
+	g_free (extension->priv->contacts_url);
+	extension->priv->contacts_url = e_util_strdup_strip (contacts_url);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "contacts-url");
+}
diff --git a/libedataserver/e-source-goa.h b/libedataserver/e-source-goa.h
index bbbbb67..02fa321 100644
--- a/libedataserver/e-source-goa.h
+++ b/libedataserver/e-source-goa.h
@@ -82,6 +82,14 @@ const gchar *	e_source_goa_get_account_id	(ESourceGoa *extension);
 gchar *		e_source_goa_dup_account_id	(ESourceGoa *extension);
 void		e_source_goa_set_account_id	(ESourceGoa *extension,
 						 const gchar *account_id);
+const gchar *	e_source_goa_get_calendar_url	(ESourceGoa *extension);
+gchar *		e_source_goa_dup_calendar_url	(ESourceGoa *extension);
+void		e_source_goa_set_calendar_url	(ESourceGoa *extension,
+						 const gchar *calendar_url);
+const gchar *	e_source_goa_get_contacts_url	(ESourceGoa *extension);
+gchar *		e_source_goa_dup_contacts_url	(ESourceGoa *extension);
+void		e_source_goa_set_contacts_url	(ESourceGoa *extension,
+						 const gchar *contacts_url);
 
 G_END_DECLS
 
diff --git a/libedataserver/e-source-webdav.c b/libedataserver/e-source-webdav.c
index d396860..482d5e4 100644
--- a/libedataserver/e-source-webdav.c
+++ b/libedataserver/e-source-webdav.c
@@ -1369,6 +1369,61 @@ e_source_webdav_prepare_ssl_trust_prompt (ESourceWebdav *extension,
                                           ESourceRegistry *registry,
                                           ENamedParameters *parameters)
 {
+	ESource *source, *parent_source = NULL;
+	ETrustPromptResponse res;
+
+	g_return_val_if_fail (
+		E_IS_SOURCE_WEBDAV (extension),
+		E_TRUST_PROMPT_RESPONSE_REJECT);
+	g_return_val_if_fail (
+		SOUP_IS_MESSAGE (message),
+		E_TRUST_PROMPT_RESPONSE_REJECT);
+	g_return_val_if_fail (
+		E_IS_SOURCE_REGISTRY (registry),
+		E_TRUST_PROMPT_RESPONSE_REJECT);
+	g_return_val_if_fail (
+		parameters != NULL,
+		E_TRUST_PROMPT_RESPONSE_REJECT);
+
+	source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
+	if (source != NULL) {
+		const gchar *parent_uid;
+
+		parent_uid = e_source_get_parent (source);
+
+		if (parent_uid != NULL)
+			parent_source = e_source_registry_ref_source (registry, parent_uid);
+
+		g_object_unref (source);
+	}
+
+	res = e_source_webdav_prepare_ssl_trust_prompt_with_parent (extension, message, parent_source, parameters);
+
+	if (parent_source)
+		g_object_unref (parent_source);
+
+	return res;
+}
+
+/**
+ * e_source_webdav_prepare_ssl_trust_prompt_with_parent:
+ * @extension: an #ESourceWebdav
+ * @message: a #SoupMessage with #SOUP_STATUS_SSL_FAILED status code
+ * @parent_source: an #ESource, parent of the @extension<!-- -->'s source
+ * @parameters: an #ENamedParameters to be populated
+ *
+ * The same as e_source_webdav_prepare_ssl_trust_prompt(), only takes @parent_source
+ * directly, instead of an #ESourceRegistry. See e_source_webdav_prepare_ssl_trust_prompt()
+ * for more details.
+ *
+ * Since: 3.8
+ **/
+ETrustPromptResponse
+e_source_webdav_prepare_ssl_trust_prompt_with_parent (ESourceWebdav *extension,
+						      SoupMessage *message,
+						      ESource *parent_source,
+						      ENamedParameters *parameters)
+{
 	ETrustPromptResponse response;
 	ESource *source;
 	GTlsCertificate *cert = NULL;
@@ -1385,9 +1440,10 @@ e_source_webdav_prepare_ssl_trust_prompt (ESourceWebdav *extension,
 	g_return_val_if_fail (
 		SOUP_IS_MESSAGE (message),
 		E_TRUST_PROMPT_RESPONSE_REJECT);
-	g_return_val_if_fail (
-		E_IS_SOURCE_REGISTRY (registry),
-		E_TRUST_PROMPT_RESPONSE_REJECT);
+	if (parent_source)
+		g_return_val_if_fail (
+			E_IS_SOURCE (parent_source),
+			E_TRUST_PROMPT_RESPONSE_REJECT);
 	g_return_val_if_fail (
 		parameters != NULL,
 		E_TRUST_PROMPT_RESPONSE_REJECT);
@@ -1439,30 +1495,16 @@ e_source_webdav_prepare_ssl_trust_prompt (ESourceWebdav *extension,
 	source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
 	if (source != NULL) {
 		const gchar *display_name;
-		const gchar *parent_uid;
 		gchar *bhost = g_strconcat ("<b>", host, "</b>", NULL);
 		gchar *bname = NULL;
 
 		display_name = e_source_get_display_name (source);
-		parent_uid = e_source_get_parent (source);
-
-		if (parent_uid != NULL) {
-			ESource *parent = NULL;
 
-			parent = e_source_registry_ref_source (
-				registry, parent_uid);
+		if (parent_source != NULL) {
+			const gchar *parent_display_name;
 
-			if (parent != NULL) {
-				const gchar *parent_display_name;
-
-				parent_display_name =
-					e_source_get_display_name (parent);
-				bname = g_strdup_printf (
-					"<b>%s: %s</b>",
-					parent_display_name,
-					display_name);
-				g_object_unref (parent);
-			}
+			parent_display_name = e_source_get_display_name (parent_source);
+			bname = g_strdup_printf ("<b>%s: %s</b>", parent_display_name, display_name);
 		}
 
 		if (bname == NULL)
diff --git a/libedataserver/e-source-webdav.h b/libedataserver/e-source-webdav.h
index 93cd8c5..d6a4957 100644
--- a/libedataserver/e-source-webdav.h
+++ b/libedataserver/e-source-webdav.h
@@ -135,6 +135,12 @@ ETrustPromptResponse
 						 SoupMessage *message,
 						 struct _ESourceRegistry *registry,
 						 struct _ENamedParameters *parameters);
+ETrustPromptResponse
+		e_source_webdav_prepare_ssl_trust_prompt_with_parent
+						(ESourceWebdav *extension,
+						 SoupMessage *message,
+						 ESource *parent_source,
+						 struct _ENamedParameters *parameters);
 void		e_source_webdav_store_ssl_trust_prompt
 						(ESourceWebdav *extension,
 						 SoupMessage *message,
diff --git a/modules/Makefile.am b/modules/Makefile.am
index a9014e1..19d96b2 100644
--- a/modules/Makefile.am
+++ b/modules/Makefile.am
@@ -15,6 +15,7 @@ endif
 SUBDIRS = \
 	cache-reaper \
 	google-backend \
+	owncloud-backend \
 	yahoo-backend \
 	$(TRUST_PROMPT_DIR) \
 	$(GNOME_ONLINE_ACCOUNTS_DIR) \
diff --git a/modules/gnome-online-accounts/module-gnome-online-accounts.c b/modules/gnome-online-accounts/module-gnome-online-accounts.c
index 16a96be..62c06e5 100644
--- a/modules/gnome-online-accounts/module-gnome-online-accounts.c
+++ b/modules/gnome-online-accounts/module-gnome-online-accounts.c
@@ -117,6 +117,9 @@ gnome_online_accounts_get_backend_name (const gchar *goa_provider_type)
 	if (g_str_equal (goa_provider_type, "yahoo"))
 		eds_backend_name = "yahoo";
 
+	if (g_str_equal (goa_provider_type, "owncloud"))
+		eds_backend_name = "owncloud";
+
 	return eds_backend_name;
 }
 
@@ -500,8 +503,12 @@ gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
 	GoaAccount *goa_account;
 	ESourceExtension *source_extension;
 	const gchar *extension_name;
+	const gchar *provider_type;
+	const gchar *backend_name;
 
 	goa_account = goa_object_get_account (goa_object);
+	provider_type = goa_account_get_provider_type (goa_account);
+	backend_name = gnome_online_accounts_get_backend_name (provider_type);
 
 	g_object_bind_property (
 		goa_account, "presentation-identity",
@@ -516,6 +523,33 @@ gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
 		source_extension, "account-id",
 		G_BINDING_SYNC_CREATE);
 
+	/* requires more properties from ownCould, but these are not
+	   available before ownCloud was introduced, thus workaround
+	   it with the backend_name check
+	*/
+	if (g_strcmp0 (backend_name, "owncloud") == 0) {
+		GoaCalendar *goa_calendar;
+		GoaContacts *goa_contacts;
+
+		goa_calendar = goa_object_get_calendar (goa_object);
+		if (goa_calendar) {
+			g_object_bind_property (
+				goa_calendar, "uri",
+				source_extension, "calendar-url",
+				G_BINDING_SYNC_CREATE);
+			g_object_unref (goa_calendar);
+		}
+
+		goa_contacts = goa_object_get_contacts (goa_object);
+		if (goa_contacts) {
+			g_object_bind_property (
+				goa_contacts, "uri",
+				source_extension, "contacts-url",
+				G_BINDING_SYNC_CREATE);
+			g_object_unref (goa_contacts);
+		}
+	}
+
 	extension_name = E_SOURCE_EXTENSION_COLLECTION;
 	source_extension = e_source_get_extension (source, extension_name);
 
@@ -705,9 +739,9 @@ gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
 	GoaAccount *goa_account;
 	ESourceRegistryServer *server;
 	ESource *collection_source;
-	ESource *mail_account_source;
-	ESource *mail_identity_source;
-	ESource *mail_transport_source;
+	ESource *mail_account_source = NULL;
+	ESource *mail_identity_source = NULL;
+	ESource *mail_transport_source = NULL;
 	const gchar *account_id;
 	const gchar *parent_uid;
 
@@ -716,43 +750,53 @@ gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
 	collection_source = gnome_online_accounts_new_source (extension);
 	g_return_if_fail (E_IS_SOURCE (collection_source));
 
-	mail_account_source = gnome_online_accounts_new_source (extension);
-	g_return_if_fail (E_IS_SOURCE (mail_account_source));
+	gnome_online_accounts_config_collection (extension, collection_source, goa_object);
+	parent_uid = e_source_get_uid (collection_source);
 
-	mail_identity_source = gnome_online_accounts_new_source (extension);
-	g_return_if_fail (E_IS_SOURCE (mail_identity_source));
+	if (goa_object_peek_mail (goa_object)) {
+		mail_account_source = gnome_online_accounts_new_source (extension);
+		g_return_if_fail (E_IS_SOURCE (mail_account_source));
 
-	mail_transport_source = gnome_online_accounts_new_source (extension);
-	g_return_if_fail (E_IS_SOURCE (mail_transport_source));
+		mail_identity_source = gnome_online_accounts_new_source (extension);
+		g_return_if_fail (E_IS_SOURCE (mail_identity_source));
 
-	/* Configure parent/child relationships. */
-	parent_uid = e_source_get_uid (collection_source);
-	e_source_set_parent (mail_account_source, parent_uid);
-	e_source_set_parent (mail_identity_source, parent_uid);
-	e_source_set_parent (mail_transport_source, parent_uid);
-
-	/* Give the factory first crack at mail configuration. */
-	e_collection_backend_factory_prepare_mail (
-		E_COLLECTION_BACKEND_FACTORY (backend_factory),
-		mail_account_source,
-		mail_identity_source,
-		mail_transport_source);
-
-	/* Now it's our turn. */
-	gnome_online_accounts_config_collection (
-		extension, collection_source, goa_object);
-	gnome_online_accounts_config_mail_account (
-		extension, mail_account_source, goa_object);
-	gnome_online_accounts_config_mail_identity (
-		extension, mail_identity_source, goa_object);
-	gnome_online_accounts_config_mail_transport (
-		extension, mail_transport_source, goa_object);
+		mail_transport_source = gnome_online_accounts_new_source (extension);
+		g_return_if_fail (E_IS_SOURCE (mail_transport_source));
+
+		/* Configure parent/child relationships. */
+		e_source_set_parent (mail_account_source, parent_uid);
+		e_source_set_parent (mail_identity_source, parent_uid);
+		e_source_set_parent (mail_transport_source, parent_uid);
+
+		/* Give the factory first crack at mail configuration. */
+		e_collection_backend_factory_prepare_mail (
+			E_COLLECTION_BACKEND_FACTORY (backend_factory),
+			mail_account_source,
+			mail_identity_source,
+			mail_transport_source);
+
+		gnome_online_accounts_config_mail_account (extension, mail_account_source, goa_object);
+		gnome_online_accounts_config_mail_identity (extension, mail_identity_source, goa_object);
+		gnome_online_accounts_config_mail_transport (extension, mail_transport_source, goa_object);
+	}
 
 	/* Export the new source collection. */
 	e_source_registry_server_add_source (server, collection_source);
-	e_source_registry_server_add_source (server, mail_account_source);
-	e_source_registry_server_add_source (server, mail_identity_source);
-	e_source_registry_server_add_source (server, mail_transport_source);
+
+	if (mail_account_source) {
+		e_source_registry_server_add_source (server, mail_account_source);
+		g_object_unref (mail_account_source);
+	}
+
+	if (mail_identity_source) {
+		e_source_registry_server_add_source (server, mail_identity_source);
+		g_object_unref (mail_identity_source);
+	}
+
+	if (mail_transport_source) {
+		e_source_registry_server_add_source (server, mail_transport_source);
+		g_object_unref (mail_transport_source);
+	}
 
 	goa_account = goa_object_get_account (goa_object);
 	account_id = goa_account_get_id (goa_account);
@@ -763,11 +807,7 @@ gnome_online_accounts_create_collection (EGnomeOnlineAccounts *extension,
 		g_strdup (parent_uid));
 
 	g_object_unref (goa_account);
-
 	g_object_unref (collection_source);
-	g_object_unref (mail_account_source);
-	g_object_unref (mail_identity_source);
-	g_object_unref (mail_transport_source);
 }
 
 static void
diff --git a/modules/owncloud-backend/Makefile.am b/modules/owncloud-backend/Makefile.am
new file mode 100644
index 0000000..14bf232
--- /dev/null
+++ b/modules/owncloud-backend/Makefile.am
@@ -0,0 +1,31 @@
+NULL =
+
+module_LTLIBRARIES = module-owncloud-backend.la
+
+module_owncloud_backend_la_CPPFLAGS = \
+	$(AM_CPPFLAGS) \
+	-I$(top_srcdir) \
+	-DG_LOG_DOMAIN=\"module-owncloud-backend\" \
+	$(E_BACKEND_CFLAGS) \
+	$(E_DATA_SERVER_CFLAGS) \
+	$(NULL)
+
+module_owncloud_backend_la_SOURCES = \
+	module-owncloud-backend.c \
+	owncloud-utils.c \
+	owncloud-utils.h \
+	$(NULL)
+
+module_owncloud_backend_la_LIBADD = \
+	$(top_builddir)/libebackend/libebackend-1.2.la \
+	$(top_builddir)/libedataserver/libedataserver-1.2.la \
+	$(top_builddir)/camel/libcamel-1.2.la \
+	$(E_BACKEND_LIBS) \
+	$(E_DATA_SERVER_LIBS) \
+	$(NULL)
+
+module_owncloud_backend_la_LDFLAGS = \
+	-module -avoid-version $(NO_UNDEFINED) \
+	$(NULL)
+
+-include $(top_srcdir)/git.mk
diff --git a/modules/owncloud-backend/module-owncloud-backend.c b/modules/owncloud-backend/module-owncloud-backend.c
new file mode 100644
index 0000000..2c04bd3
--- /dev/null
+++ b/modules/owncloud-backend/module-owncloud-backend.c
@@ -0,0 +1,344 @@
+/*
+ * module-owncloud-backend.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <libebackend/libebackend.h>
+
+#include "owncloud-utils.h"
+
+/* Standard GObject macros */
+#define E_TYPE_OWNCLOUD_BACKEND \
+	(e_owncloud_backend_get_type ())
+#define E_OWNCLOUD_BACKEND(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_OWNCLOUD_BACKEND, EOwncloudBackend))
+
+typedef struct _EOwncloudBackend EOwncloudBackend;
+typedef struct _EOwncloudBackendClass EOwncloudBackendClass;
+
+typedef struct _EOwncloudBackendFactory EOwncloudBackendFactory;
+typedef struct _EOwncloudBackendFactoryClass EOwncloudBackendFactoryClass;
+
+struct _EOwncloudBackend {
+	ECollectionBackend parent;
+};
+
+struct _EOwncloudBackendClass {
+	ECollectionBackendClass parent_class;
+};
+
+struct _EOwncloudBackendFactory {
+	ECollectionBackendFactory parent;
+};
+
+struct _EOwncloudBackendFactoryClass {
+	ECollectionBackendFactoryClass parent_class;
+};
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+/* Forward Declarations */
+GType e_owncloud_backend_get_type (void);
+GType e_owncloud_backend_factory_get_type (void);
+
+G_DEFINE_DYNAMIC_TYPE (
+	EOwncloudBackend,
+	e_owncloud_backend,
+	E_TYPE_COLLECTION_BACKEND)
+
+G_DEFINE_DYNAMIC_TYPE (
+	EOwncloudBackendFactory,
+	e_owncloud_backend_factory,
+	E_TYPE_COLLECTION_BACKEND_FACTORY)
+
+static void
+owncloud_remove_unknown_sources_cb (gpointer resource_id,
+				    gpointer uid,
+				    gpointer user_data)
+{
+	ESourceRegistryServer *server = user_data;
+	ESource *source;
+
+	source = e_source_registry_server_ref_source (server, uid);
+
+	if (source) {
+		e_source_registry_server_remove_source (server, source);
+		g_object_unref (source);
+	}
+}
+
+static void
+owncloud_source_found_cb (ECollectionBackend *collection,
+			  OwnCloudSourceType source_type,
+			  SoupURI *uri,
+			  const gchar *display_name,
+			  const gchar *color,
+			  gpointer user_data)
+{
+	GHashTable *known_sources = user_data;
+	ESourceRegistryServer *server;
+	ESourceBackend *backend;
+	ESource *source = NULL;
+	const gchar *backend_name = NULL;
+	const gchar *provider = NULL;
+	const gchar *identity_prefix = NULL;
+	const gchar *source_uid;
+	gboolean is_new;
+	gchar *url;
+	gchar *identity;
+
+	g_return_if_fail (collection != NULL);
+	g_return_if_fail (uri != NULL);
+	g_return_if_fail (display_name != NULL);
+	g_return_if_fail (known_sources != NULL);
+
+	switch (source_type) {
+	case OwnCloud_Source_Contacts:
+		backend_name = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+		provider = "webdav";
+		identity_prefix = "contacts";
+		break;
+	case OwnCloud_Source_Events:
+		backend_name = E_SOURCE_EXTENSION_CALENDAR;
+		provider = "caldav";
+		identity_prefix = "events";
+		break;
+	case OwnCloud_Source_Memos:
+		backend_name = E_SOURCE_EXTENSION_MEMO_LIST;
+		provider = "caldav";
+		identity_prefix = "memos";
+		break;
+	case OwnCloud_Source_Tasks:
+		backend_name = E_SOURCE_EXTENSION_TASK_LIST;
+		provider = "caldav";
+		identity_prefix = "tasks";
+		break;
+	}
+
+	g_return_if_fail (backend_name != NULL);
+
+	server = e_collection_backend_ref_server (collection);
+
+	url = soup_uri_to_string (uri, FALSE);
+	identity = g_strconcat (identity_prefix, "::", url, NULL);
+	source_uid = g_hash_table_lookup (known_sources, identity);
+	is_new = !source_uid;
+	if (is_new) {
+		ESource *master_source;
+
+		source = e_collection_backend_new_child (collection, identity);
+		g_warn_if_fail (source != NULL);
+
+		if (source) {
+			ESourceResource *resource;
+			ESourceWebdav *master_webdav, *child_webdav;
+
+			master_source = e_backend_get_source (E_BACKEND (collection));
+			master_webdav = e_source_get_extension (master_source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+			child_webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+			resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+
+			e_source_webdav_set_soup_uri (child_webdav, uri);
+			e_source_resource_set_identity (resource, identity);
+
+			/* inherit ssl trust options */
+			e_source_webdav_set_ssl_trust (child_webdav, e_source_webdav_get_ssl_trust (master_webdav));
+		}
+	} else {
+		source = e_source_registry_server_ref_source (server, source_uid);
+		g_warn_if_fail (source != NULL);
+
+		g_hash_table_remove (known_sources, identity);
+	}
+
+	g_free (identity);
+	g_free (url);
+
+	/* these properties are synchronized always */
+	if (source) {
+		backend = e_source_get_extension (source, backend_name);
+		e_source_backend_set_backend_name (backend, provider);
+
+		e_source_set_display_name (source, display_name);
+		if (source_type != OwnCloud_Source_Contacts && color)
+			e_source_selectable_set_color (E_SOURCE_SELECTABLE (backend), color);
+
+		if (is_new)
+			e_source_registry_server_add_source (server, source);
+
+		g_object_unref (source);
+	}
+
+	g_object_unref (server);
+}
+
+static void
+owncloud_add_uid_to_hashtable (gpointer source,
+			       gpointer known_sources)
+{
+	ESourceResource *resource;
+	gchar *uid, *rid;
+
+	if (!e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE))
+		return;
+
+	resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+
+	uid = e_source_dup_uid (source);
+	if (!uid || !*uid) {
+		g_free (uid);
+		return;
+	}
+
+	rid = e_source_resource_dup_identity (resource);
+	if (!rid || !*rid) {
+		g_free (rid);
+		g_free (uid);
+		return;
+	}
+
+	g_hash_table_insert (known_sources, rid, uid);
+}
+
+static gpointer
+owncloud_populate_thread (gpointer data)
+{
+	ECollectionBackend *collection = data;
+	GHashTable *known_sources;
+	GList *sources;
+
+	g_return_val_if_fail (collection != NULL, NULL);
+
+	/* resource-id => source's UID */
+	known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+
+	sources = e_collection_backend_list_calendar_sources (collection);
+	g_list_foreach (sources, owncloud_add_uid_to_hashtable, known_sources);
+	g_list_free_full (sources, g_object_unref);
+
+	sources = e_collection_backend_list_contacts_sources (collection);
+	g_list_foreach (sources, owncloud_add_uid_to_hashtable, known_sources);
+	g_list_free_full (sources, g_object_unref);
+
+	if (owncloud_utils_search_server (collection, owncloud_source_found_cb, known_sources)) {
+		ESourceRegistryServer *server;
+
+		server = e_collection_backend_ref_server (collection);
+
+		g_hash_table_foreach (known_sources, owncloud_remove_unknown_sources_cb, server);
+
+		g_object_unref (server);
+	}
+
+	g_hash_table_destroy (known_sources);
+	g_object_unref (collection);
+
+	return NULL;
+}
+
+static void
+owncloud_backend_populate (ECollectionBackend *collection)
+{
+	GList *list, *liter;
+	ESourceRegistryServer *server;
+	GThread *thread;
+
+	/* Chain up to parent's populate() method. */
+	E_COLLECTION_BACKEND_CLASS (e_owncloud_backend_parent_class)->populate (collection);
+
+	server = e_collection_backend_ref_server (collection);
+	list = e_collection_backend_claim_all_resources (collection);
+
+	for (liter = list; liter; liter = g_list_next (liter)) {
+		ESource *source = liter->data;
+
+		if (e_source_has_extension (source, E_SOURCE_EXTENSION_RESOURCE)) {
+			ESourceResource *resource;
+			ESource *child;
+
+			resource = e_source_get_extension (source, E_SOURCE_EXTENSION_RESOURCE);
+			child = e_collection_backend_new_child (collection, e_source_resource_get_identity (resource));
+			if (child) {
+				e_source_registry_server_add_source (server, source);
+				g_object_unref (child);
+			}
+		}
+	}
+
+	g_list_free_full (list, g_object_unref);
+	g_object_unref (server);
+
+	thread = g_thread_new (NULL, owncloud_populate_thread, g_object_ref (collection));
+	g_thread_unref (thread);
+}
+
+static void
+e_owncloud_backend_class_init (EOwncloudBackendClass *class)
+{
+	ECollectionBackendClass *backend_class;
+
+	backend_class = E_COLLECTION_BACKEND_CLASS (class);
+	backend_class->populate = owncloud_backend_populate;
+}
+
+static void
+e_owncloud_backend_class_finalize (EOwncloudBackendClass *class)
+{
+}
+
+static void
+e_owncloud_backend_init (EOwncloudBackend *backend)
+{
+}
+
+static void
+e_owncloud_backend_factory_class_init (EOwncloudBackendFactoryClass *class)
+{
+	ECollectionBackendFactoryClass *factory_class;
+
+	factory_class = E_COLLECTION_BACKEND_FACTORY_CLASS (class);
+	factory_class->factory_name = "owncloud";
+	factory_class->backend_type = E_TYPE_OWNCLOUD_BACKEND;
+}
+
+static void
+e_owncloud_backend_factory_class_finalize (EOwncloudBackendFactoryClass *class)
+{
+}
+
+static void
+e_owncloud_backend_factory_init (EOwncloudBackendFactory *factory)
+{
+}
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+	e_owncloud_backend_register_type (type_module);
+	e_owncloud_backend_factory_register_type (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/owncloud-backend/owncloud-utils.c b/modules/owncloud-backend/owncloud-utils.c
new file mode 100644
index 0000000..8b652c5
--- /dev/null
+++ b/modules/owncloud-backend/owncloud-utils.c
@@ -0,0 +1,558 @@
+/*
+ * owncloud-utils.c
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <libsoup/soup.h>
+#include <string.h>
+
+#include <libxml/parser.h>
+#include <libxml/xpath.h>
+#include <libxml/xpathInternals.h>
+
+#include <libebackend/libebackend.h>
+
+#include "owncloud-utils.h"
+
+typedef struct _EOwncloudAuthenticator EOwncloudAuthenticator;
+typedef struct _EOwncloudAuthenticatorClass EOwncloudAuthenticatorClass;
+
+struct _EOwncloudAuthenticator {
+	GObject parent;
+
+	ECollectionBackend *collection;
+	gchar *username;
+	GString *password;
+};
+
+struct _EOwncloudAuthenticatorClass {
+	GObjectClass parent_class;
+};
+
+static ESourceAuthenticationResult
+owncloud_authenticator_try_password_sync (ESourceAuthenticator *auth,
+					  const GString *password,
+					  GCancellable *cancellable,
+					  GError **error)
+{
+	EOwncloudAuthenticator *authenticator = (EOwncloudAuthenticator *) auth;
+
+	if (authenticator->password)
+		g_string_free (authenticator->password, TRUE);
+	authenticator->password = g_string_new (password->str);
+
+	return E_SOURCE_AUTHENTICATION_ACCEPTED;
+}
+
+#define E_TYPE_OWNCLOUD_AUTHENTICATOR (e_owncloud_authenticator_get_type ())
+
+GType e_owncloud_authenticator_get_type (void) G_GNUC_CONST;
+
+static void e_owncloud_authenticator_authenticator_init (ESourceAuthenticatorInterface *interface);
+
+G_DEFINE_TYPE_EXTENDED (EOwncloudAuthenticator, e_owncloud_authenticator, G_TYPE_OBJECT, 0,
+	G_IMPLEMENT_INTERFACE (E_TYPE_SOURCE_AUTHENTICATOR, e_owncloud_authenticator_authenticator_init))
+
+static void
+owncloud_authenticator_finalize (GObject *object)
+{
+	EOwncloudAuthenticator *authenticator = (EOwncloudAuthenticator *) object;
+
+	g_free (authenticator->username);
+	if (authenticator->password)
+		g_string_free (authenticator->password, TRUE);
+
+	G_OBJECT_CLASS (e_owncloud_authenticator_parent_class)->finalize (object);
+}
+
+static void
+e_owncloud_authenticator_class_init (EOwncloudAuthenticatorClass *class)
+{
+	GObjectClass *object_class;
+
+	object_class = G_OBJECT_CLASS (class);
+	object_class->finalize = owncloud_authenticator_finalize;
+}
+
+static void
+e_owncloud_authenticator_authenticator_init (ESourceAuthenticatorInterface *interface)
+{
+	interface->try_password_sync = owncloud_authenticator_try_password_sync;
+}
+
+static void
+e_owncloud_authenticator_init (EOwncloudAuthenticator *authenticator)
+{
+}
+
+#define XPATH_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:status)"
+#define XPATH_HREF "string(/D:multistatus/D:response[%d]/D:href)"
+#define XPATH_DISPLAY_NAME "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:displayname)"
+#define XPATH_CALENDAR_COLOR "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/APL:calendar-color)"
+#define XPATH_RESOURCE_TYPE_ADDRESSBOOK "/D:multistatus/D:response[%d]/D:propstat/D:prop/D:resourcetype/B:addressbook"
+#define XPATH_RESOURCE_TYPE_CALENDAR "/D:multistatus/D:response[%d]/D:propstat/D:prop/D:resourcetype/C:calendar"
+#define XPATH_SUPPORTED_CALENDAR_COMPONENT_SET "/D:multistatus/D:response[%d]/D:propstat/D:prop/C:supported-calendar-component-set/C:comp"
+#define XPATH_CALENDAR_COMP_TYPE "string(" XPATH_SUPPORTED_CALENDAR_COMPONENT_SET "[%d]/@name)"
+
+static xmlXPathObjectPtr
+xpath_eval (xmlXPathContextPtr ctx,
+            const gchar *format,
+            ...)
+{
+	xmlXPathObjectPtr xpres;
+	va_list args;
+	gchar *expr;
+
+	if (ctx == NULL)
+		return NULL;
+
+	va_start (args, format);
+	expr = g_strdup_vprintf (format, args);
+	va_end (args);
+
+	xpres = xmlXPathEvalExpression ((xmlChar *) expr, ctx);
+	g_free (expr);
+
+	if (xpres == NULL)
+		return NULL;
+
+	if (xpres->type == XPATH_NODESET &&
+	    xmlXPathNodeSetIsEmpty (xpres->nodesetval)) {
+		xmlXPathFreeObject (xpres);
+		return NULL;
+	}
+
+	return xpres;
+}
+
+static guint
+xp_object_get_status (xmlXPathObjectPtr xpres)
+{
+	gboolean res;
+	guint ret = 0;
+
+	if (xpres == NULL)
+		return ret;
+
+	if (xpres->type == XPATH_STRING) {
+		res = soup_headers_parse_status_line (
+			(const gchar *) xpres->stringval,
+			NULL,
+			&ret,
+			NULL);
+
+		if (!res)
+			ret = 0;
+	}
+
+	xmlXPathFreeObject (xpres);
+
+	return ret;
+}
+
+static gchar *
+xp_object_get_string (xmlXPathObjectPtr xpres)
+{
+	gchar *ret = NULL;
+
+	if (xpres == NULL)
+		return ret;
+
+	if (xpres->type == XPATH_STRING) {
+		ret = g_strdup ((gchar *) xpres->stringval);
+	}
+
+	xmlXPathFreeObject (xpres);
+
+	return ret;
+}
+
+static void
+add_source (ECollectionBackend *collection,
+	    OwnCloudSourceFoundCb found_cb,
+	    gpointer user_data,
+	    OwnCloudSourceType source_type,
+	    SoupURI *base_uri,
+	    const gchar *href,
+	    const gchar *display_name,
+	    const gchar *color)
+{
+	SoupURI *uri = NULL;
+
+	if (!href || !display_name)
+		return;
+
+	if (!strstr (href, "://")) {
+		soup_uri_set_path (base_uri, href);
+	} else {
+		uri = soup_uri_new (href);
+	}
+
+	found_cb (collection, source_type, uri ? uri : base_uri, display_name, color, user_data);
+
+	if (uri)
+		soup_uri_free (uri);
+}
+
+static void
+enum_calendars (ECollectionBackend *collection,
+		OwnCloudSourceFoundCb found_cb,
+		gpointer user_data,
+		/* const */ xmlXPathContextPtr xpctx,
+		/* const */ xmlXPathObjectPtr xpathobj,
+		gint response_index,
+		SoupURI *base_uri,
+		const gchar *href,
+		const gchar *display_name,
+		const gchar *color)
+{
+	gint ii, nn;
+
+	if (!href || !display_name || !xpctx || !xpathobj || xpathobj->type != XPATH_NODESET)
+		return;
+
+	nn = xmlXPathNodeSetGetLength (xpathobj->nodesetval);
+	for (ii = 0; ii < nn; ii++) {
+		xmlXPathObjectPtr xpres;
+		gchar *comp_type;
+
+		xpres = xpath_eval (xpctx, XPATH_CALENDAR_COMP_TYPE, response_index, ii + 1);
+		comp_type = xp_object_get_string (xpres);
+
+		if (g_strcmp0 (comp_type, "VEVENT") == 0) {
+			add_source (collection, found_cb, user_data, OwnCloud_Source_Events, base_uri, href, display_name, color);
+		} else if (g_strcmp0 (comp_type, "VTODO") == 0) {
+			add_source (collection, found_cb, user_data, OwnCloud_Source_Tasks, base_uri, href, display_name, color);
+		} else if (g_strcmp0 (comp_type, "VJOURNAL") == 0) {
+			add_source (collection, found_cb, user_data, OwnCloud_Source_Memos, base_uri, href, display_name, color);
+		}
+
+		g_free (comp_type);
+	}
+}
+
+static void
+parse_propfind_response (ECollectionBackend *collection,
+			 OwnCloudSourceFoundCb found_cb,
+			 gpointer user_data,
+			 SoupURI *base_uri,
+			 const gchar *body_str,
+			 glong body_len)
+{
+	xmlXPathContextPtr xpctx;
+	xmlXPathObjectPtr xpathobj;
+	xmlDocPtr doc;
+
+	if (!body_str || !body_len || !base_uri)
+		return;
+
+	doc = xmlReadMemory (body_str, body_len, "response.xml", NULL, 0);
+	if (!doc)
+		return;
+
+	xpctx = xmlXPathNewContext (doc);
+	xmlXPathRegisterNs (xpctx, (xmlChar *) "D", (xmlChar *) "DAV:");
+	xmlXPathRegisterNs (xpctx, (xmlChar *) "B", (xmlChar *) "urn:ietf:params:xml:ns:carddav");
+	xmlXPathRegisterNs (xpctx, (xmlChar *) "C", (xmlChar *) "urn:ietf:params:xml:ns:caldav");
+	xmlXPathRegisterNs (xpctx, (xmlChar *) "CS", (xmlChar *) "http://calendarserver.org/ns/";);
+	xmlXPathRegisterNs (xpctx, (xmlChar *) "APL", (xmlChar *) "http://apple.com/ns/ical/";);
+
+	xpathobj = xpath_eval (xpctx, "/D:multistatus/D:response");
+	if (xpathobj && xpathobj->type == XPATH_NODESET) {
+		gint ii, nn;
+		gchar *href, *display_name, *color;
+
+		nn = xmlXPathNodeSetGetLength (xpathobj->nodesetval);
+		for (ii = 0; ii < nn; ii++) {
+			xmlXPathObjectPtr xpres;
+
+			xpres = xpath_eval (xpctx, XPATH_STATUS, ii + 1);
+			if (xp_object_get_status (xpres) != 200)
+				continue;
+
+			xpres = xpath_eval (xpctx, XPATH_HREF, ii + 1);
+			href = xp_object_get_string (xpres);
+
+			if (!href)
+				continue;
+
+			xpres = xpath_eval (xpctx, XPATH_DISPLAY_NAME, ii + 1);
+			display_name = xp_object_get_string (xpres);
+
+			xpres = xpath_eval (xpctx, XPATH_CALENDAR_COLOR, ii + 1);
+			color = xp_object_get_string (xpres);
+
+			if (display_name && *display_name) {
+				xpres = xpath_eval (xpctx, XPATH_RESOURCE_TYPE_ADDRESSBOOK, ii + 1);
+				if (xpres) {
+					add_source (collection, found_cb, user_data, OwnCloud_Source_Contacts, base_uri, href, display_name, NULL);
+					xmlXPathFreeObject (xpres);
+				}
+
+				xpres = xpath_eval (xpctx, XPATH_RESOURCE_TYPE_CALENDAR, ii + 1);
+				if (xpres) {
+					xmlXPathFreeObject (xpres);
+
+					xpres = xpath_eval (xpctx, XPATH_SUPPORTED_CALENDAR_COMPONENT_SET, ii + 1);
+					if (xpres) {
+						enum_calendars (collection, found_cb, user_data, xpctx, xpres, ii + 1, base_uri, href, display_name, color);
+						xmlXPathFreeObject (xpres);
+					}
+				}
+			}
+
+			g_free (display_name);
+			g_free (color);
+			g_free (href);
+		}
+	}
+
+	if (xpathobj)
+		xmlXPathFreeObject (xpathobj);
+	xmlXPathFreeContext (xpctx);
+	xmlFreeDoc (doc);
+}
+
+static void
+authenticate_cb (SoupSession *session,
+		 SoupMessage *msg,
+		 SoupAuth *auth,
+		 gboolean retrying,
+		 gpointer user_data)
+{
+	EOwncloudAuthenticator *authenticator = user_data;
+
+	g_return_if_fail (authenticator != NULL);
+
+	if (retrying || !authenticator->password) {
+		ESourceRegistryServer *server;
+		EAuthenticationSession *auth_session;
+		ESource *source;
+
+		source = e_backend_get_source (E_BACKEND (authenticator->collection));
+		server = e_collection_backend_ref_server (authenticator->collection);
+
+		auth_session = e_source_registry_server_new_auth_session (server, E_SOURCE_AUTHENTICATOR (authenticator), e_source_get_uid (source));
+		if (!e_source_registry_server_authenticate_sync (server, auth_session, NULL, NULL)) {
+			if (authenticator->password)
+				g_string_free (authenticator->password, TRUE);
+			authenticator->password = NULL;
+		}
+
+		g_object_unref (auth_session);
+		g_object_unref (server);
+	}
+
+	if (authenticator->username && authenticator->password)
+		soup_auth_authenticate (auth, authenticator->username, authenticator->password->str);
+}
+
+static ETrustPromptResponse
+trust_prompt_sync (const ENamedParameters *parameters,
+		   GCancellable *cancellable,
+		   GError **error)
+{
+	EUserPrompter *prompter;
+	gint response;
+
+	g_return_val_if_fail (parameters != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	prompter = e_user_prompter_new ();
+	g_return_val_if_fail (prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	response = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters, NULL, cancellable, error);
+
+	g_object_unref (prompter);
+
+	if (response == 0)
+		return E_TRUST_PROMPT_RESPONSE_REJECT;
+	if (response == 1)
+		return E_TRUST_PROMPT_RESPONSE_ACCEPT;
+	if (response == 2)
+		return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
+	if (response == -1)
+		return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
+
+	return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+}
+
+static gboolean
+find_sources (ECollectionBackend *collection,
+	      OwnCloudSourceFoundCb found_cb,
+	      gpointer user_data,
+	      const gchar *base_url,
+	      const gchar *base_collection_path,
+	      EOwncloudAuthenticator *authenticator)
+{
+	const gchar *req_body =
+		"<D:propfind "
+			"xmlns:C=\"urn:ietf:params:xml:ns:caldav\" "
+			"xmlns:IC=\"http://apple.com/ns/ical/\"; "
+			"xmlns:D=\"DAV:\">\n"
+		"  <D:prop>\n"
+		"    <D:displayname/>\n"
+		"    <D:resourcetype/>\n"
+		"    <C:supported-calendar-component-set/>\n"
+		"    <IC:calendar-color/>\n"
+		"  </D:prop>\n"
+		"</D:propfind>\n";
+
+	SoupSession *session;
+	SoupMessage *msg;
+	GString *url;
+	EProxy *proxy;
+	gboolean tested = FALSE;
+
+	g_return_val_if_fail (base_url && *base_url, FALSE);
+	g_return_val_if_fail (base_collection_path && *base_collection_path, FALSE);
+	g_return_val_if_fail (authenticator, FALSE);
+
+	url = g_string_new (base_url);
+	if (url->str[url->len - 1] != '/')
+		g_string_append_c (url, '/');
+	g_string_append (url, base_collection_path);
+	g_string_append_c (url, '/');
+	g_string_append (url, authenticator->username);
+	g_string_append_c (url, '/');
+
+	msg = soup_message_new ("PROPFIND", url->str);
+
+	if (!msg) {
+		g_string_free (url, TRUE);
+		return FALSE;
+	}
+
+	session = soup_session_sync_new ();
+	g_object_set (session,
+		SOUP_SESSION_TIMEOUT, 90,
+		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+		SOUP_SESSION_SSL_STRICT, TRUE,
+		NULL);
+	g_signal_connect (session, "authenticate", G_CALLBACK (authenticate_cb), authenticator);
+
+	proxy = e_proxy_new ();
+	e_proxy_setup_proxy (proxy);
+
+	if (e_proxy_require_proxy_for_uri (proxy, url->str)) {
+		SoupURI *proxy_uri;
+
+		proxy_uri = e_proxy_peek_uri_for (proxy, url->str);
+		g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL);
+	} else {
+		g_object_set (session, SOUP_SESSION_PROXY_URI, NULL, NULL);
+	}
+
+	g_string_free (url, TRUE);
+
+	soup_message_set_request (msg, "application/xml; charset=utf-8", SOUP_MEMORY_STATIC, req_body, strlen (req_body));
+
+	if (soup_session_send_message (session, msg) == SOUP_STATUS_SSL_FAILED) {
+		ETrustPromptResponse response;
+		ENamedParameters *parameters;
+		ESourceWebdav *extension;
+		ESource *source;
+
+		source = e_backend_get_source (E_BACKEND (collection));
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+		parameters = e_named_parameters_new ();
+
+		/* this is the master source, thus there is no parent_source */
+		response = e_source_webdav_prepare_ssl_trust_prompt_with_parent (extension, msg, NULL, parameters);
+		if (response == E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+			response = trust_prompt_sync (parameters, NULL, NULL);
+			if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
+				e_source_webdav_store_ssl_trust_prompt (extension, msg, response);
+		}
+
+		e_named_parameters_free (parameters);
+
+		if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
+		    response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) {
+			g_object_set (session, SOUP_SESSION_SSL_STRICT, FALSE, NULL);
+
+			soup_session_send_message (session, msg);
+		}
+	}
+
+	if (msg->status_code == SOUP_STATUS_MULTI_STATUS &&
+	    msg->response_body && msg->response_body->length) {
+		SoupURI *suri = soup_message_get_uri (msg);
+
+		suri = soup_uri_copy (suri);
+
+		parse_propfind_response (collection, found_cb, user_data, suri, msg->response_body->data, msg->response_body->length);
+
+		soup_uri_free (suri);
+		tested = TRUE;
+	}
+
+	g_object_unref (msg);
+	g_object_unref (proxy);
+	g_object_unref (session);
+
+	return tested;
+}
+
+gboolean
+owncloud_utils_search_server (ECollectionBackend *collection,
+			      OwnCloudSourceFoundCb found_cb,
+			      gpointer user_data)
+{
+	ESourceCollection *collection_extension;
+	ESourceGoa *goa_extension;
+	ESource *source;
+	EOwncloudAuthenticator *authenticator;
+	gchar *url;
+	gboolean res = TRUE;
+
+	g_return_val_if_fail (collection != NULL, FALSE);
+	g_return_val_if_fail (found_cb != NULL, FALSE);
+
+	source = e_backend_get_source (E_BACKEND (collection));
+	collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
+	goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
+
+	authenticator = g_object_new (E_TYPE_OWNCLOUD_AUTHENTICATOR, NULL);
+	authenticator->collection = collection;
+	authenticator->username = e_source_collection_dup_identity (collection_extension);
+
+	if (res && e_source_collection_get_calendar_enabled (collection_extension)) {
+		url = e_source_goa_dup_calendar_url (goa_extension);
+
+		if (url && *url)
+			res = find_sources (collection, found_cb, user_data, url, "calendars", authenticator);
+
+		g_free (url);
+	}
+
+	if (res && e_source_collection_get_contacts_enabled (collection_extension)) {
+		url = e_source_goa_dup_contacts_url (goa_extension);
+
+		if (url && *url)
+			res = find_sources (collection, found_cb, user_data, url, "addressbooks", authenticator);
+
+		g_free (url);
+	}
+
+	g_object_unref (authenticator);
+
+	return res;
+}
diff --git a/modules/owncloud-backend/owncloud-utils.h b/modules/owncloud-backend/owncloud-utils.h
new file mode 100644
index 0000000..7c49fac
--- /dev/null
+++ b/modules/owncloud-backend/owncloud-utils.h
@@ -0,0 +1,47 @@
+/*
+ * owncloud-utils.h
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with the program; if not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef OWNCLOUD_UTILS_H
+#define OWNCLOUD_UTILS_H
+
+#include <libebackend/libebackend.h>
+#include <libsoup/soup.h>
+
+G_BEGIN_DECLS
+
+typedef enum {
+	OwnCloud_Source_Contacts = 1,
+	OwnCloud_Source_Events,
+	OwnCloud_Source_Memos,
+	OwnCloud_Source_Tasks
+} OwnCloudSourceType;
+
+typedef void	(*OwnCloudSourceFoundCb)	(ECollectionBackend *collection,
+						 OwnCloudSourceType source_type,
+						 SoupURI *uri,
+						 const gchar *display_name,
+						 const gchar *color,
+						 gpointer user_data);
+
+gboolean	owncloud_utils_search_server	(ECollectionBackend *collection,
+						 OwnCloudSourceFoundCb found_cb,
+						 gpointer user_data);
+
+G_END_DECLS
+
+#endif /* OWNCLOUD_UTILS_H */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]