Bug #686528 - Pickup ownCloud accounts from GOA
authorMilan Crha <mcrha@redhat.com>
Tue, 12 Feb 2013 12:21:06 +0000 (13:21 +0100)
committerMilan Crha <mcrha@redhat.com>
Tue, 12 Feb 2013 12:21:06 +0000 (13:21 +0100)
configure.ac
libedataserver/e-source-goa.c
libedataserver/e-source-goa.h
libedataserver/e-source-webdav.c
libedataserver/e-source-webdav.h
modules/Makefile.am
modules/gnome-online-accounts/module-gnome-online-accounts.c
modules/owncloud-backend/Makefile.am [new file with mode: 0644]
modules/owncloud-backend/module-owncloud-backend.c [new file with mode: 0644]
modules/owncloud-backend/owncloud-utils.c [new file with mode: 0644]
modules/owncloud-backend/owncloud-utils.h [new file with mode: 0644]

index 77d035d..710fa98 100644 (file)
@@ -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
index 5cff646..2856ab8 100644 (file)
 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");
+}
index bbbbb67..02fa321 100644 (file)
@@ -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
 
index d396860..482d5e4 100644 (file)
@@ -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)
index 93cd8c5..d6a4957 100644 (file)
@@ -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,
index a9014e1..19d96b2 100644 (file)
@@ -15,6 +15,7 @@ endif
 SUBDIRS = \
        cache-reaper \
        google-backend \
+       owncloud-backend \
        yahoo-backend \
        $(TRUST_PROMPT_DIR) \
        $(GNOME_ONLINE_ACCOUNTS_DIR) \
index 16a96be..62c06e5 100644 (file)
@@ -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 (file)
index 0000000..14bf232
--- /dev/null
@@ -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 (file)
index 0000000..2c04bd3
--- /dev/null
@@ -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 (file)
index 0000000..8b652c5
--- /dev/null
@@ -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 (file)
index 0000000..7c49fac
--- /dev/null
@@ -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 */