[evolution-data-server/wip/offline-cache] Add ESoupSession - hides some common parts into a descendant of SoupSession



commit 51474c1fdf3d0131004453a7839049ca9ce4b9ca
Author: Milan Crha <mcrha redhat com>
Date:   Thu Mar 23 15:30:25 2017 +0100

    Add ESoupSession - hides some common parts into a descendant of SoupSession

 src/calendar/backends/http/e-cal-backend-http.c |  140 ++-----
 src/libedataserver/CMakeLists.txt               |    4 +
 src/libedataserver/e-soup-session.c             |  574 +++++++++++++++++++++++
 src/libedataserver/e-soup-session.h             |  101 ++++
 src/libedataserver/e-webdav-session.c           |   74 +++
 src/libedataserver/e-webdav-session.h           |   83 ++++
 src/libedataserver/libedataserver.h             |    2 +
 7 files changed, 868 insertions(+), 110 deletions(-)
---
diff --git a/src/calendar/backends/http/e-cal-backend-http.c b/src/calendar/backends/http/e-cal-backend-http.c
index 57d1087..3e85504 100644
--- a/src/calendar/backends/http/e-cal-backend-http.c
+++ b/src/calendar/backends/http/e-cal-backend-http.c
@@ -34,17 +34,13 @@
 G_DEFINE_TYPE (ECalBackendHttp, e_cal_backend_http, E_TYPE_CAL_META_BACKEND)
 
 struct _ECalBackendHttpPrivate {
-       SoupSession *soup_session;
+       ESoupSession *session;
 
        SoupRequestHTTP *request;
        GInputStream *input_stream;
        GHashTable *components; /* gchar *uid ~> icalcomponent * */
-
-       ENamedParameters *credentials;
 };
 
-#define d(x)
-
 static gchar *
 ecb_http_webcal_to_http_method (const gchar *webcal_str,
                                gboolean secure)
@@ -95,66 +91,6 @@ ecb_http_dup_uri (ECalBackendHttp *cbhttp)
        return uri;
 }
 
-static void
-ecb_http_extract_ssl_failed_data (SoupMessage *msg,
-                                 gchar **out_certificate_pem,
-                                 GTlsCertificateFlags *out_certificate_errors)
-{
-       GTlsCertificate *certificate = NULL;
-
-       g_return_if_fail (SOUP_IS_MESSAGE (msg));
-
-       if (!out_certificate_pem || !out_certificate_errors)
-               return;
-
-       g_object_get (G_OBJECT (msg),
-               "tls-certificate", &certificate,
-               "tls-errors", out_certificate_errors,
-               NULL);
-
-       if (certificate) {
-               g_object_get (certificate, "certificate-pem", out_certificate_pem, NULL);
-               g_object_unref (certificate);
-       }
-}
-
-static void
-ecb_http_soup_authenticate (SoupSession *session,
-                           SoupMessage *msg,
-                           SoupAuth *auth,
-                           gboolean retrying,
-                           gpointer user_data)
-{
-       ECalBackendHttp *cbhttp;
-       const gchar *username;
-       gchar *auth_user = NULL;
-
-       if (retrying)
-               return;
-
-       cbhttp = E_CAL_BACKEND_HTTP (user_data);
-
-       username = cbhttp->priv->credentials ? e_named_parameters_get (cbhttp->priv->credentials, 
E_SOURCE_CREDENTIAL_USERNAME) : NULL;
-       if (!username || !*username) {
-               ESourceAuthentication *auth_extension;
-               ESource *source;
-
-               source = e_backend_get_source (E_BACKEND (cbhttp));
-               auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
-               auth_user = e_source_authentication_dup_user (auth_extension);
-
-               username = auth_user;
-       }
-
-       if (!username || !*username || !cbhttp->priv->credentials ||
-           !e_named_parameters_exists (cbhttp->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
-               soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
-       else
-               soup_auth_authenticate (auth, username, e_named_parameters_get (cbhttp->priv->credentials, 
E_SOURCE_CREDENTIAL_PASSWORD));
-
-       g_free (auth_user);
-}
-
 static gboolean
 ecb_http_connect_sync (ECalMetaBackend *meta_backend,
                       const ENamedParameters *credentials,
@@ -196,10 +132,9 @@ ecb_http_connect_sync (ECalMetaBackend *meta_backend,
 
        e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
 
-       e_named_parameters_free (cbhttp->priv->credentials);
-       cbhttp->priv->credentials = credentials ? e_named_parameters_new_clone (credentials) : NULL;
+       e_soup_session_set_credentials (cbhttp->priv->session, credentials);
 
-       request = soup_session_request_http (cbhttp->priv->soup_session, SOUP_METHOD_GET, uri, &local_error);
+       request = soup_session_request_http (SOUP_SESSION (cbhttp->priv->session), SOUP_METHOD_GET, uri, 
&local_error);
        success = request != NULL;
 
        if (success) {
@@ -211,7 +146,7 @@ ecb_http_connect_sync (ECalMetaBackend *meta_backend,
                        soup_message_headers_append (message->request_headers, "Connection", "close");
                }
 
-               input_stream = soup_request_send (SOUP_REQUEST (request), cancellable, &local_error);
+               input_stream = e_soup_session_send_request_sync (cbhttp->priv->session, SOUP_REQUEST 
(request), cancellable, &local_error);
 
                success = input_stream != NULL;
 
@@ -251,8 +186,7 @@ ecb_http_connect_sync (ECalMetaBackend *meta_backend,
                                *out_auth_result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
 
                                e_source_set_connection_status (source, 
E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
-                               if (message)
-                                       ecb_http_extract_ssl_failed_data (message, out_certificate_pem, 
out_certificate_errors);
+                               e_soup_session_get_ssl_error_details (cbhttp->priv->session, 
out_certificate_pem, out_certificate_errors);
                        } else {
                                e_source_set_connection_status (source, 
E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
                        }
@@ -294,8 +228,8 @@ ecb_http_disconnect_sync (ECalMetaBackend *meta_backend,
 
        cbhttp = E_CAL_BACKEND_HTTP (meta_backend);
 
-       if (cbhttp->priv->soup_session)
-               soup_session_abort (cbhttp->priv->soup_session);
+       if (cbhttp->priv->session)
+               soup_session_abort (SOUP_SESSION (cbhttp->priv->session));
 
        g_clear_object (&cbhttp->priv->input_stream);
        g_clear_object (&cbhttp->priv->request);
@@ -642,6 +576,24 @@ ecb_http_load_component_sync (ECalMetaBackend *meta_backend,
 }
 
 static void
+e_cal_backend_http_constructed (GObject *object)
+{
+       ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (object);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_cal_backend_http_parent_class)->constructed (object);
+
+       cbhttp->priv->session = e_soup_session_new (e_backend_get_source (E_BACKEND (cbhttp)));
+
+       e_soup_session_setup_logging (cbhttp->priv->session, g_getenv ("WEBCAL_DEBUG"));
+
+       e_binding_bind_property (
+               cbhttp, "proxy-resolver",
+               cbhttp->priv->session, "proxy-resolver",
+               G_BINDING_SYNC_CREATE);
+}
+
+static void
 e_cal_backend_http_dispose (GObject *object)
 {
        ECalBackendHttp *cbhttp;
@@ -651,9 +603,9 @@ e_cal_backend_http_dispose (GObject *object)
        g_clear_object (&cbhttp->priv->request);
        g_clear_object (&cbhttp->priv->input_stream);
 
-       if (cbhttp->priv->soup_session) {
-               soup_session_abort (cbhttp->priv->soup_session);
-               g_clear_object (&cbhttp->priv->soup_session);
+       if (cbhttp->priv->session) {
+               soup_session_abort (SOUP_SESSION (cbhttp->priv->session));
+               g_clear_object (&cbhttp->priv->session);
        }
 
        if (cbhttp->priv->components) {
@@ -670,8 +622,7 @@ e_cal_backend_http_finalize (GObject *object)
 {
        ECalBackendHttp *cbhttp = E_CAL_BACKEND_HTTP (object);
 
-       e_named_parameters_free (cbhttp->priv->credentials);
-       cbhttp->priv->credentials = NULL;
+       g_clear_object (&cbhttp->priv->session);
 
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (e_cal_backend_http_parent_class)->finalize (object);
@@ -680,41 +631,9 @@ e_cal_backend_http_finalize (GObject *object)
 static void
 e_cal_backend_http_init (ECalBackendHttp *cbhttp)
 {
-       SoupSession *soup_session;
-
        cbhttp->priv = G_TYPE_INSTANCE_GET_PRIVATE (cbhttp, E_TYPE_CAL_BACKEND_HTTP, ECalBackendHttpPrivate);
 
        e_cal_backend_set_writable (E_CAL_BACKEND (cbhttp), FALSE);
-
-       soup_session = soup_session_new ();
-       g_object_set (
-               soup_session,
-               SOUP_SESSION_TIMEOUT, 90,
-               SOUP_SESSION_SSL_STRICT, TRUE,
-               SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
-               SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
-               NULL);
-
-       if (g_getenv ("WEBCAL_DEBUG") != NULL) {
-               SoupLogger *logger;
-
-               logger = soup_logger_new (SOUP_LOGGER_LOG_BODY, 1024 * 1024);
-               soup_session_add_feature (soup_session, SOUP_SESSION_FEATURE (logger));
-               g_object_unref (logger);
-       }
-
-       cbhttp->priv->soup_session = soup_session;
-       cbhttp->priv->request = NULL;
-       cbhttp->priv->input_stream = NULL;
-
-       e_binding_bind_property (
-               cbhttp, "proxy-resolver",
-               cbhttp->priv->soup_session, "proxy-resolver",
-               G_BINDING_SYNC_CREATE);
-
-       g_signal_connect (
-               cbhttp->priv->soup_session, "authenticate",
-               G_CALLBACK (ecb_http_soup_authenticate), cbhttp);
 }
 
 static void
@@ -741,6 +660,7 @@ e_cal_backend_http_class_init (ECalBackendHttpClass *klass)
        cal_backend_sync_class->remove_objects_sync = NULL;
 
        object_class = G_OBJECT_CLASS (klass);
+       object_class->constructed = e_cal_backend_http_constructed;
        object_class->dispose = e_cal_backend_http_dispose;
        object_class->finalize = e_cal_backend_http_finalize;
 }
diff --git a/src/libedataserver/CMakeLists.txt b/src/libedataserver/CMakeLists.txt
index 8e26b83..33a0eb4 100644
--- a/src/libedataserver/CMakeLists.txt
+++ b/src/libedataserver/CMakeLists.txt
@@ -69,6 +69,7 @@ set(SOURCES
        e-secret-store.c
        e-sexp.c
        e-soup-auth-bearer.c
+       e-soup-session.c
        e-soup-ssl-trust.c
        e-source.c
        e-source-extension.c
@@ -117,6 +118,7 @@ set(SOURCES
        e-uid.c
        e-url.c
        e-webdav-discover.c
+       e-webdav-session.c
        e-data-server-util.c
        e-xml-utils.c
        e-xml-hash-utils.c
@@ -148,6 +150,7 @@ set(HEADERS
        e-secret-store.h
        e-sexp.h
        e-soup-auth-bearer.h
+       e-soup-session.h
        e-soup-ssl-trust.h
        e-source.h
        e-source-address-book.h
@@ -196,6 +199,7 @@ set(HEADERS
        e-uid.h
        e-url.h
        e-webdav-discover.h
+       e-webdav-session.h
        e-data-server-util.h
        e-xml-utils.h
        e-xml-hash-utils.h
diff --git a/src/libedataserver/e-soup-session.c b/src/libedataserver/e-soup-session.c
new file mode 100644
index 0000000..69edeeb
--- /dev/null
+++ b/src/libedataserver/e-soup-session.c
@@ -0,0 +1,574 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-soup-session
+ * @include: libedataserver/libedataserver.h
+ * @short_description: A SoupSession descendant
+ *
+ * The #ESoupSession is a #SoupSession descendant, which hides common
+ * tasks related to the way evolution-data-server works.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include "e-soup-ssl-trust.h"
+#include "e-source-authentication.h"
+#include "e-source-webdav.h"
+
+#include "e-soup-session.h"
+
+struct _ESoupSessionPrivate {
+       GMutex property_lock;
+       ESource *source;
+       ENamedParameters *credentials;
+
+       gboolean ssl_info_set;
+       gchar *ssl_certificate_pem;
+       GTlsCertificateFlags ssl_certificate_errors;
+};
+
+enum {
+       PROP_0,
+       PROP_SOURCE,
+       PROP_CREDENTIALS
+};
+
+G_DEFINE_TYPE (ESoupSession, e_soup_session, SOUP_TYPE_SESSION)
+
+static void
+e_soup_session_authenticate_cb (SoupSession *soup_session,
+                               SoupMessage *message,
+                               SoupAuth *auth,
+                               gboolean retrying,
+                               gpointer user_data)
+{
+       ESoupSession *session;
+       const gchar *username;
+       ENamedParameters *credentials;
+       gchar *auth_user = NULL;
+
+       g_return_if_fail (E_IS_SOUP_SESSION (soup_session));
+
+       if (retrying)
+               return;
+
+       session = E_SOUP_SESSION (soup_session);
+
+       credentials = e_soup_session_dup_credentials (session);
+
+       username = credentials ? e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME) : NULL;
+       if ((!username || !*username) &&
+           e_source_has_extension (session->priv->source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+               ESourceAuthentication *auth_extension;
+
+               auth_extension = e_source_get_extension (session->priv->source, 
E_SOURCE_EXTENSION_AUTHENTICATION);
+               auth_user = e_source_authentication_dup_user (auth_extension);
+
+               username = auth_user;
+       }
+
+       if (!username || !*username || !credentials ||
+           !e_named_parameters_exists (credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+               soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
+       else
+               soup_auth_authenticate (auth, username, e_named_parameters_get (credentials, 
E_SOURCE_CREDENTIAL_PASSWORD));
+
+       e_named_parameters_free (credentials);
+       g_free (auth_user);
+}
+
+static void
+e_soup_session_set_source (ESoupSession *session,
+                          ESource *source)
+{
+       g_return_if_fail (E_IS_SOUP_SESSION (session));
+       g_return_if_fail (E_IS_SOURCE (source));
+       g_return_if_fail (!session->priv->source);
+
+       session->priv->source = g_object_ref (source);
+}
+
+static void
+e_soup_session_set_property (GObject *object,
+                            guint property_id,
+                            const GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_SOURCE:
+                       e_soup_session_set_source (
+                               E_SOUP_SESSION (object),
+                               g_value_get_object (value));
+                       return;
+
+               case PROP_CREDENTIALS:
+                       e_soup_session_set_credentials (
+                               E_SOUP_SESSION (object),
+                               g_value_get_boxed (value));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_soup_session_get_property (GObject *object,
+                            guint property_id,
+                            GValue *value,
+                            GParamSpec *pspec)
+{
+       switch (property_id) {
+               case PROP_SOURCE:
+                       g_value_set_object (
+                               value,
+                               e_soup_session_get_source (
+                               E_SOUP_SESSION (object)));
+                       return;
+
+               case PROP_CREDENTIALS:
+                       g_value_take_boxed (
+                               value,
+                               e_soup_session_dup_credentials (
+                               E_SOUP_SESSION (object)));
+                       return;
+       }
+
+       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+e_soup_session_finalize (GObject *object)
+{
+       ESoupSession *session = E_SOUP_SESSION (object);
+
+       g_clear_object (&session->priv->source);
+       g_clear_pointer (&session->priv->credentials, e_named_parameters_free);
+       g_clear_pointer (&session->priv->ssl_certificate_pem, g_free);
+
+       g_mutex_clear (&session->priv->property_lock);
+
+       /* Chain up to parent's method. */
+       G_OBJECT_CLASS (e_soup_session_parent_class)->finalize (object);
+}
+
+static void
+e_soup_session_class_init (ESoupSessionClass *klass)
+{
+       GObjectClass *object_class;
+
+       g_type_class_add_private (klass, sizeof (ESoupSessionPrivate));
+
+       object_class = G_OBJECT_CLASS (klass);
+       object_class->set_property = e_soup_session_set_property;
+       object_class->get_property = e_soup_session_get_property;
+       object_class->finalize = e_soup_session_finalize;
+
+       /**
+        * ESoupSession:source:
+        *
+        * The #ESource being used for this soup session.
+        *
+        * Since: 3.26
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_SOURCE,
+               g_param_spec_object (
+                       "source",
+                       "Source",
+                       NULL,
+                       E_TYPE_SOURCE,
+                       G_PARAM_READWRITE |
+                       G_PARAM_CONSTRUCT_ONLY |
+                       G_PARAM_STATIC_STRINGS));
+
+       /**
+        * ESoupSession:credentials:
+        *
+        * The #ENamedParameters containing login credentials.
+        *
+        * Since: 3.26
+        **/
+       g_object_class_install_property (
+               object_class,
+               PROP_CREDENTIALS,
+               g_param_spec_boxed (
+                       "credentials",
+                       "Credentials",
+                       NULL,
+                       E_TYPE_NAMED_PARAMETERS,
+                       G_PARAM_READWRITE |
+                       G_PARAM_STATIC_STRINGS));
+}
+
+static void
+e_soup_session_init (ESoupSession *session)
+{
+       session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session, E_TYPE_SOUP_SESSION, ESoupSessionPrivate);
+       session->priv->ssl_info_set = FALSE;
+
+       g_mutex_init (&session->priv->property_lock);
+
+       g_object_set (
+               G_OBJECT (session),
+               SOUP_SESSION_TIMEOUT, 90,
+               SOUP_SESSION_SSL_STRICT, TRUE,
+               SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+               SOUP_SESSION_ACCEPT_LANGUAGE_AUTO, TRUE,
+               NULL);
+
+       g_signal_connect (session, "authenticate",
+               G_CALLBACK (e_soup_session_authenticate_cb), NULL);
+}
+
+/**
+ * e_soup_session_new:
+ * @source: an #ESource
+ *
+ * Creates a new #ESoupSession associated with given @source.
+ * The @source can be used to store and read SSL trust settings, but only if
+ * it already contains an #ESourceWebdav extension. Otherwise the SSL trust
+ * settings are ignored.
+ *
+ * Returns: (transfer full): a new #ESoupSession; free it with g_object_unref(),
+ *    when no longer needed.
+ *
+ * Since: 3.26
+ **/
+ESoupSession *
+e_soup_session_new (ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+       return g_object_new (E_TYPE_SOUP_SESSION,
+               "source", source,
+               NULL);
+}
+
+/**
+ * e_soup_session_setup_logging:
+ * @session: an #ESoupSession
+ * @logging_level: (nullable): logging level to setup, or %NULL
+ *
+ * Setups logging for the @session. The @logging_level can be one of:
+ * "all" - log whole raw communication;
+ * "body" - the same as "all";
+ * "headers" - log the headers only;
+ * "min" - minimal logging;
+ * "1" - the same as "all".
+ * Any other value, including %NULL, disables logging.
+ *
+ * Since: 3.26
+ **/
+void
+e_soup_session_setup_logging (ESoupSession *session,
+                             const gchar *logging_level)
+{
+       SoupLogger *logger;
+       SoupLoggerLogLevel level;
+
+       g_return_if_fail (E_IS_SOUP_SESSION (session));
+
+       soup_session_remove_feature_by_type (SOUP_SESSION (session), SOUP_TYPE_LOGGER);
+
+       if (!logging_level)
+               return;
+
+       if (g_ascii_strcasecmp (logging_level, "all") == 0 ||
+           g_ascii_strcasecmp (logging_level, "body") == 0 ||
+           g_ascii_strcasecmp (logging_level, "1") == 0)
+               level = SOUP_LOGGER_LOG_BODY;
+       else if (g_ascii_strcasecmp (logging_level, "headers") == 0)
+               level = SOUP_LOGGER_LOG_HEADERS;
+       else if (g_ascii_strcasecmp (logging_level, "min") == 0)
+               level = SOUP_LOGGER_LOG_MINIMAL;
+       else
+               return;
+
+       logger = soup_logger_new (level, 10 * 1024 * 1024);
+       soup_session_add_feature (SOUP_SESSION (session), SOUP_SESSION_FEATURE (logger));
+       g_object_unref (logger);
+}
+
+/**
+ * e_soup_session_get_source:
+ * @session: an #ESoupSession
+ *
+ * Returns: (transfer none): Associated #ESource with the @session.
+ *
+ * Since: 3.26
+ **/
+ESource *
+e_soup_session_get_source (ESoupSession *session)
+{
+       g_return_val_if_fail (E_IS_SOUP_SESSION (session), NULL);
+
+       return session->priv->source;
+}
+
+/**
+ * e_soup_session_set_credentials:
+ * @session: an #ESoupSession
+ * @credentials: (nullable): an #ENamedParameters with credentials to use, or %NULL
+ *
+ * Sets credentials to use for connection. Using %NULL for @credentials
+ * unsets previous value.
+ *
+ * Since: 3.26
+ **/
+void
+e_soup_session_set_credentials (ESoupSession *session,
+                               const ENamedParameters *credentials)
+{
+       g_return_if_fail (E_IS_SOUP_SESSION (session));
+
+       g_mutex_lock (&session->priv->property_lock);
+
+       if (credentials == session->priv->credentials) {
+               g_mutex_unlock (&session->priv->property_lock);
+               return;
+       }
+
+       e_named_parameters_free (session->priv->credentials);
+       if (credentials)
+               session->priv->credentials = e_named_parameters_new_clone (credentials);
+       else
+               session->priv->credentials = NULL;
+
+       g_mutex_unlock (&session->priv->property_lock);
+
+       g_object_notify (G_OBJECT (session), "credentials");
+}
+
+/**
+ * e_soup_session_dup_credentials:
+ * @session: an #ESoupSession
+ *
+ * Returns: (nullable) (transfer full): A copy of the credentials being
+ *    previously set with e_soup_session_set_credentials(), or %NULL when
+ *    none are set. Free the returned pointer with e_named_parameters_free(),
+ *    when no longer needed.
+ *
+ * Since: 3.26
+ **/
+ENamedParameters *
+e_soup_session_dup_credentials (ESoupSession *session)
+{
+       ENamedParameters *credentials;
+
+       g_return_val_if_fail (E_IS_SOUP_SESSION (session), NULL);
+
+       g_mutex_lock (&session->priv->property_lock);
+
+       if (session->priv->credentials)
+               credentials = e_named_parameters_new_clone (session->priv->credentials);
+       else
+               credentials = NULL;
+
+       g_mutex_unlock (&session->priv->property_lock);
+
+       return credentials;
+}
+
+/**
+ * e_soup_session_get_ssl_error_details:
+ * @session: an #ESoupSession
+ * @out_certificate_pem: (out): return location for a server TLS/SSL certificate
+ *   in PEM format, when the last operation failed with a TLS/SSL error
+ * @out_certificate_errors: (out): return location for a #GTlsCertificateFlags,
+ *   with certificate error flags when the the operation failed with a TLS/SSL error
+ *
+ * Populates @out_certificate_pem and @out_certificate_errors with the last values
+ * returned on #SOUP_STATUS_SSL_FAILED error.
+ *
+ * Returns: Whether the information was available and set to the out parameters.
+ *
+ * Since: 3.26
+ **/
+gboolean
+e_soup_session_get_ssl_error_details (ESoupSession *session,
+                                     gchar **out_certificate_pem,
+                                     GTlsCertificateFlags *out_certificate_errors)
+{
+       g_return_val_if_fail (E_IS_SOUP_SESSION (session), FALSE);
+       g_return_val_if_fail (out_certificate_pem != NULL, FALSE);
+       g_return_val_if_fail (out_certificate_errors != NULL, FALSE);
+
+       g_mutex_lock (&session->priv->property_lock);
+       if (!session->priv->ssl_info_set) {
+               g_mutex_unlock (&session->priv->property_lock);
+               return FALSE;
+       }
+
+       *out_certificate_pem = g_strdup (session->priv->ssl_certificate_pem);
+       *out_certificate_errors = session->priv->ssl_certificate_errors;
+
+       g_mutex_unlock (&session->priv->property_lock);
+
+       return TRUE;
+}
+
+/**
+ * e_soup_session_send_request_sync:
+ * @session: an #ESoupSession
+ * @request: a #SoupRequest to send
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Synchronously sends prepared request and returns #GInputStream
+ * that can be used to read its contents.
+ *
+ * This calls soup_request_send() internally, but it also setups
+ * the request according to #ESoupSession:source authentication
+ * settings. It also extracts information about used certificate,
+ * in case of SOUP_STATUS_SSL_FAILED error and keeps it for later use
+ * by e_soup_session_get_ssl_error_details().
+ *
+ * Use e_soup_session_send_request_simple_sync() to read whole
+ * content into a #GByteArray.
+ *
+ * Returns: (transfer full): A newly allocated #GInputStream,
+ *    that can be used to read from the URI pointed to by @request.
+ *    Free it with g_object_unref(), when no longer needed.
+ *
+ * Since: 3.26
+ **/
+GInputStream *
+e_soup_session_send_request_sync (ESoupSession *session,
+                                 SoupRequest *request,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       GInputStream *input_stream;
+       GError *local_error = NULL;
+
+       g_return_val_if_fail (E_IS_SOUP_SESSION (session), NULL);
+       g_return_val_if_fail (SOUP_IS_REQUEST (request), NULL);
+
+       g_mutex_lock (&session->priv->property_lock);
+       g_clear_pointer (&session->priv->ssl_certificate_pem, g_free);
+       session->priv->ssl_certificate_errors = 0;
+       session->priv->ssl_info_set = FALSE;
+       g_mutex_unlock (&session->priv->property_lock);
+
+       if (session->priv->source &&
+           e_source_has_extension (session->priv->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND) &&
+           SOUP_IS_REQUEST_HTTP (request)) {
+               SoupMessage *message;
+
+               message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+
+               e_soup_ssl_trust_connect (message, session->priv->source);
+
+               g_clear_object (&message);
+       }
+
+       input_stream = soup_request_send (request, cancellable, &local_error);
+       if (input_stream)
+               return input_stream;
+
+       if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+               GTlsCertificate *certificate = NULL;
+               SoupMessage *message;
+
+               message = soup_request_http_get_message (SOUP_REQUEST_HTTP (request));
+
+               g_mutex_lock (&session->priv->property_lock);
+
+               g_object_get (G_OBJECT (message),
+                       "tls-certificate", &certificate,
+                       "tls-errors", &session->priv->ssl_certificate_errors,
+                       NULL);
+
+               if (certificate) {
+                       g_object_get (certificate, "certificate-pem", &session->priv->ssl_certificate_pem, 
NULL);
+                       session->priv->ssl_info_set = TRUE;
+
+                       g_object_unref (certificate);
+               }
+
+               g_mutex_unlock (&session->priv->property_lock);
+               g_clear_object (&message);
+       }
+
+       if (local_error)
+               g_propagate_error (error, local_error);
+
+       return NULL;
+}
+
+/**
+ * e_soup_session_send_request_simple_sync:
+ * @session: an #ESoupSession
+ * @request: a #SoupRequest to send
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Similar to e_soup_session_send_request_sync(), except it reads
+ * whole response content into memory and returns it as a #GByteArray.
+ * Use e_soup_session_send_request_sync() when you want to have
+ * more control on the content read.
+ *
+ * Returns: (transfer full): A newly allocated #GByteArray,
+ *    which contains whole content from the URI pointed to by @request.
+ *
+ * Since: 3.26
+ **/
+GByteArray *
+e_soup_session_send_request_simple_sync (ESoupSession *session,
+                                        SoupRequest *request,
+                                        GCancellable *cancellable,
+                                        GError **error)
+{
+       GInputStream *input_stream;
+       GByteArray *bytes;
+       gint expected_length;
+       void *buffer;
+       gsize nread = 0;
+       gboolean success = FALSE;
+
+       g_return_val_if_fail (E_IS_SOUP_SESSION (session), NULL);
+       g_return_val_if_fail (SOUP_IS_REQUEST (request), NULL);
+
+       input_stream = e_soup_session_send_request_sync (session, request, cancellable, error);
+       if (!input_stream)
+               return NULL;
+
+       expected_length = soup_request_get_content_length (request);
+       if (expected_length > 0)
+               bytes = g_byte_array_sized_new (expected_length);
+       else
+               bytes = g_byte_array_new ();
+
+       buffer = g_malloc (16384);
+
+       while (success = g_input_stream_read_all (input_stream, buffer, 16384, &nread, cancellable, error),
+              success && nread > 0) {
+               g_byte_array_append (bytes, buffer, nread);
+       }
+
+       g_free (buffer);
+       g_object_unref (input_stream);
+
+       if (!success) {
+               g_byte_array_free (bytes, TRUE);
+               bytes = NULL;
+       }
+
+       return bytes;
+}
diff --git a/src/libedataserver/e-soup-session.h b/src/libedataserver/e-soup-session.h
new file mode 100644
index 0000000..09784e8
--- /dev/null
+++ b/src/libedataserver/e-soup-session.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOUP_SESSION_H
+#define E_SOUP_SESSION_H
+
+#include <glib.h>
+#include <libsoup/soup.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOUP_SESSION \
+       (e_soup_session_get_type ())
+#define E_SOUP_SESSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_SOUP_SESSION, ESoupSession))
+#define E_SOUP_SESSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_SOUP_SESSION, ESoupSessionClass))
+#define E_IS_SOUP_SESSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_SOUP_SESSION))
+#define E_IS_SOUP_SESSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_SOUP_SESSION))
+#define E_SOUP_SESSION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_SOUP_SESSION, ESoupSessionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESoupSession ESoupSession;
+typedef struct _ESoupSessionClass ESoupSessionClass;
+typedef struct _ESoupSessionPrivate ESoupSessionPrivate;
+
+/**
+ * ESoupSession:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.26
+ **/
+struct _ESoupSession {
+       /*< private >*/
+       SoupSession parent;
+       ESoupSessionPrivate *priv;
+};
+
+struct _ESoupSessionClass {
+       SoupSessionClass parent_class;
+
+       /* Padding for future expansion */
+       gpointer reserved[10];
+};
+
+GType          e_soup_session_get_type                 (void) G_GNUC_CONST;
+
+ESoupSession * e_soup_session_new                      (ESource *source);
+void           e_soup_session_setup_logging            (ESoupSession *session,
+                                                        const gchar *logging_level);
+ESource *      e_soup_session_get_source               (ESoupSession *session);
+void           e_soup_session_set_credentials          (ESoupSession *session,
+                                                        const ENamedParameters *credentials);
+ENamedParameters *
+               e_soup_session_dup_credentials          (ESoupSession *session);
+gboolean       e_soup_session_get_ssl_error_details    (ESoupSession *session,
+                                                        gchar **out_certificate_pem,
+                                                        GTlsCertificateFlags *out_certificate_errors);
+GInputStream * e_soup_session_send_request_sync        (ESoupSession *session,
+                                                        SoupRequest *request,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+GByteArray *   e_soup_session_send_request_simple_sync (ESoupSession *session,
+                                                        SoupRequest *request,
+                                                        GCancellable *cancellable,
+                                                        GError **error);
+
+G_END_DECLS
+
+#endif /* E_SOUP_SESSION_H */
diff --git a/src/libedataserver/e-webdav-session.c b/src/libedataserver/e-webdav-session.c
new file mode 100644
index 0000000..246c36a
--- /dev/null
+++ b/src/libedataserver/e-webdav-session.c
@@ -0,0 +1,74 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ * SECTION: e-webdav-session
+ * @include: libedataserver/libedataserver.h
+ * @short_description: A WebDAV, CalDAV and CardDAV session
+ *
+ * The #EWebDAVSession is a class to work with WebDAV (RFC 4918),
+ * CalDAV (RFC 4791) or CardDAV (RFC 6352) servers, providing API
+ * for common requests/responses, on top of an #ESoupSession.
+ **/
+
+#include "evolution-data-server-config.h"
+
+#include "e-source-webdav.h"
+
+#include "e-webdav-session.h"
+
+struct _EWebDAVSessionPrivate {
+       gboolean dummy;
+};
+
+G_DEFINE_TYPE (EWebDAVSession, e_webdav_session, E_TYPE_SOUP_SESSION)
+
+static void
+e_webdav_session_class_init (EWebDAVSessionClass *klass)
+{
+       g_type_class_add_private (klass, sizeof (EWebDAVSessionPrivate));
+}
+
+static void
+e_webdav_session_init (EWebDAVSession *webdav)
+{
+       webdav->priv = G_TYPE_INSTANCE_GET_PRIVATE (webdav, E_TYPE_WEBDAV_SESSION, EWebDAVSessionPrivate);
+}
+
+/**
+ * e_webdav_session_new:
+ * @source: an #ESource
+ *
+ * Creates a new #EWebDAVSession associated with given @source. It's
+ * a user's error to try to create the #EWebDAVSession for a source
+ * which doesn't have #ESourceWebdav extension properly defined.
+ *
+ * Returns: (transfer full): a new #EWebDAVSession; free it with g_object_unref(),
+ *    when no longer needed.
+ *
+ * Since: 3.26
+ **/
+EWebDAVSession *
+e_webdav_session_new (ESource *source)
+{
+       g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+       g_return_val_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND), NULL);
+
+       return g_object_new (E_TYPE_WEBDAV_SESSION,
+               "source", source,
+               NULL);
+}
diff --git a/src/libedataserver/e-webdav-session.h b/src/libedataserver/e-webdav-session.h
new file mode 100644
index 0000000..280ab72
--- /dev/null
+++ b/src/libedataserver/e-webdav-session.h
@@ -0,0 +1,83 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library 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.
+ *
+ * This library 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 this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_WEBDAV_SESSION_H
+#define E_WEBDAV_SESSION_H
+
+#include <glib.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-soup-session.h>
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEBDAV_SESSION \
+       (e_webdav_session_get_type ())
+#define E_WEBDAV_SESSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_CAST \
+       ((obj), E_TYPE_WEBDAV_SESSION, EWebDAVSession))
+#define E_WEBDAV_SESSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_CAST \
+       ((cls), E_TYPE_WEBDAV_SESSION, EWebDAVSessionClass))
+#define E_IS_WEBDAV_SESSION(obj) \
+       (G_TYPE_CHECK_INSTANCE_TYPE \
+       ((obj), E_TYPE_WEBDAV_SESSION))
+#define E_IS_WEBDAV_SESSION_CLASS(cls) \
+       (G_TYPE_CHECK_CLASS_TYPE \
+       ((cls), E_TYPE_WEBDAV_SESSION))
+#define E_WEBDAV_SESSION_GET_CLASS(obj) \
+       (G_TYPE_INSTANCE_GET_CLASS \
+       ((obj), E_TYPE_WEBDAV_SESSION, EWebDAVSessionClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebDAVSession EWebDAVSession;
+typedef struct _EWebDAVSessionClass EWebDAVSessionClass;
+typedef struct _EWebDAVSessionPrivate EWebDAVSessionPrivate;
+
+/**
+ * EWebDAVSession:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.26
+ **/
+struct _EWebDAVSession {
+       /*< private >*/
+       ESoupSession parent;
+       EWebDAVSessionPrivate *priv;
+};
+
+struct _EWebDAVSessionClass {
+       ESoupSessionClass parent_class;
+
+       /* Padding for future expansion */
+       gpointer reserved[10];
+};
+
+GType          e_webdav_session_get_type               (void) G_GNUC_CONST;
+
+EWebDAVSession *e_webdav_session_new                   (ESource *source);
+
+G_END_DECLS
+
+#endif /* E_WEBDAV_SESSION_H */
diff --git a/src/libedataserver/libedataserver.h b/src/libedataserver/libedataserver.h
index 47b6304..437cb02 100644
--- a/src/libedataserver/libedataserver.h
+++ b/src/libedataserver/libedataserver.h
@@ -43,6 +43,7 @@
 #include <libedataserver/e-secret-store.h>
 #include <libedataserver/e-sexp.h>
 #include <libedataserver/e-soup-auth-bearer.h>
+#include <libedataserver/e-soup-session.h>
 #include <libedataserver/e-soup-ssl-trust.h>
 #include <libedataserver/e-source-address-book.h>
 #include <libedataserver/e-source-alarms.h>
@@ -91,6 +92,7 @@
 #include <libedataserver/e-uid.h>
 #include <libedataserver/e-url.h>
 #include <libedataserver/e-webdav-discover.h>
+#include <libedataserver/e-webdav-session.h>
 #include <libedataserver/e-xml-hash-utils.h>
 #include <libedataserver/e-xml-utils.h>
 #include <libedataserver/eds-version.h>


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