[evolution-data-server] Bug #690177 - Use trust-prompt for certificate verification in WebDAV backends



commit 705af70540ee7b87b2d66228a0cd6c50c2538210
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jan 9 23:03:37 2013 +0100

    Bug #690177 - Use trust-prompt for certificate verification in WebDAV backends

 .../backends/webdav/e-book-backend-webdav.c        |  108 +++-
 calendar/backends/caldav/e-cal-backend-caldav.c    |  193 ++++---
 calendar/backends/http/e-cal-backend-http.c        |   56 ++-
 libebackend/e-backend.c                            |  216 +++++++-
 libebackend/e-backend.h                            |   19 +
 libedataserver/e-source-webdav.c                   |  599 +++++++++++++++++---
 libedataserver/e-source-webdav.h                   |   33 +-
 modules/trust-prompt/module-trust-prompt.c         |    6 +-
 modules/trust-prompt/trust-prompt-gtk.c            |   23 +-
 modules/trust-prompt/trust-prompt.h                |    1 +
 po/POTFILES.in                                     |    1 +
 services/evolution-user-prompter/prompt-user-gtk.c |    8 +-
 12 files changed, 1041 insertions(+), 222 deletions(-)
---
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c b/addressbook/backends/webdav/e-book-backend-webdav.c
index cb0a403..ef3c2bb 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -141,9 +141,52 @@ get_closure (EDataBookView *book_view)
 	return g_object_get_data (G_OBJECT (book_view), WEBDAV_CLOSURE_NAME);
 }
 
+static guint
+send_and_handle_ssl (EBookBackendWebdav *webdav,
+		     SoupMessage *message,
+		     GCancellable *cancellable)
+{
+	guint status_code;
+
+	status_code = soup_session_send_message (webdav->priv->session, message);
+	if (status_code == SOUP_STATUS_SSL_FAILED) {
+		ESource *source;
+		ESourceWebdav *extension;
+		ESourceRegistry *registry;
+		EBackend *backend;
+		ETrustPromptResponse response;
+		ENamedParameters *parameters;
+
+		backend = E_BACKEND (webdav);
+		source = e_backend_get_source (backend);
+		registry = e_book_backend_get_registry (E_BOOK_BACKEND (backend));
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+
+		parameters = e_named_parameters_new ();
+
+		response = e_source_webdav_prepare_ssl_trust_prompt (extension, message, registry, parameters);
+		if (response == E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+			response = e_backend_trust_prompt_sync (backend, parameters, cancellable, NULL);
+			if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
+				e_source_webdav_store_ssl_trust_prompt (extension, message, response);
+		}
+
+		e_named_parameters_free (parameters);
+
+		if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
+		    response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) {
+			g_object_set (webdav->priv->session, SOUP_SESSION_SSL_STRICT, FALSE, NULL);
+			status_code = soup_session_send_message (webdav->priv->session, message);
+		}
+	}
+
+	return status_code;
+}
+
 static EContact *
 download_contact (EBookBackendWebdav *webdav,
-                  const gchar *uri)
+                  const gchar *uri,
+		  GCancellable *cancellable)
 {
 	SoupMessage *message;
 	const gchar  *etag;
@@ -154,7 +197,7 @@ download_contact (EBookBackendWebdav *webdav,
 	soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
 	soup_message_headers_append (message->request_headers, "Connection", "close");
 
-	status = soup_session_send_message (webdav->priv->session, message);
+	status = send_and_handle_ssl (webdav, message, cancellable);
 	if (status != 200) {
 		g_warning ("Couldn't load '%s' (http status %d)", uri, status);
 		g_object_unref (message);
@@ -194,7 +237,8 @@ download_contact (EBookBackendWebdav *webdav,
 static guint
 upload_contact (EBookBackendWebdav *webdav,
                 EContact *contact,
-                gchar **reason)
+                gchar **reason,
+		GCancellable *cancellable)
 {
 	ESource     *source;
 	ESourceWebdav *webdav_extension;
@@ -250,7 +294,7 @@ upload_contact (EBookBackendWebdav *webdav,
 		message, "text/vcard", SOUP_MEMORY_TEMPORARY,
 		request, strlen (request));
 
-	status   = soup_session_send_message (webdav->priv->session, message);
+	status   = send_and_handle_ssl (webdav, message, cancellable);
 	new_etag = soup_message_headers_get_list (message->response_headers, "ETag");
 
 	redir_uri = soup_message_headers_get_list (message->response_headers, "Location");
@@ -349,7 +393,7 @@ e_book_backend_webdav_create_contacts (EBookBackend *backend,
 	/* kill revision field (might have been set by some other backend) */
 	e_contact_set (contact, E_CONTACT_REV, NULL);
 
-	status = upload_contact (webdav, contact, &status_reason);
+	status = upload_contact (webdav, contact, &status_reason, cancellable);
 	if (status != 201 && status != 204) {
 		g_object_unref (contact);
 		if (status == 401 || status == 407) {
@@ -377,7 +421,7 @@ e_book_backend_webdav_create_contacts (EBookBackend *backend,
 
 		g_warning ("Server didn't return etag for new address resource");
 		new_uid = e_contact_get_const (contact, E_CONTACT_UID);
-		new_contact = download_contact (webdav, new_uid);
+		new_contact = download_contact (webdav, new_uid, cancellable);
 		g_object_unref (contact);
 
 		if (new_contact == NULL) {
@@ -403,7 +447,8 @@ e_book_backend_webdav_create_contacts (EBookBackend *backend,
 
 static guint
 delete_contact (EBookBackendWebdav *webdav,
-                const gchar *uri)
+                const gchar *uri,
+		GCancellable *cancellable)
 {
 	SoupMessage *message;
 	guint        status;
@@ -412,7 +457,7 @@ delete_contact (EBookBackendWebdav *webdav,
 	soup_message_headers_append (message->request_headers, "User-Agent", USERAGENT);
 	soup_message_headers_append (message->request_headers, "Connection", "close");
 
-	status = soup_session_send_message (webdav->priv->session, message);
+	status = send_and_handle_ssl (webdav, message, cancellable);
 	g_object_unref (message);
 
 	return status;
@@ -449,7 +494,7 @@ e_book_backend_webdav_remove_contacts (EBookBackend *backend,
 		return;
 	}
 
-	status = delete_contact (webdav, uid);
+	status = delete_contact (webdav, uid, cancellable);
 	if (status != 204) {
 		if (status == 401 || status == 407) {
 			e_data_book_respond_remove_contacts (
@@ -512,7 +557,7 @@ e_book_backend_webdav_modify_contacts (EBookBackend *backend,
 
 	/* modify contact */
 	contact = e_contact_new_from_vcard (vcard);
-	status = upload_contact (webdav, contact, &status_reason);
+	status = upload_contact (webdav, contact, &status_reason, cancellable);
 	if (status != 201 && status != 204) {
 		g_object_unref (contact);
 		if (status == 401 || status == 407) {
@@ -555,7 +600,7 @@ e_book_backend_webdav_modify_contacts (EBookBackend *backend,
 		EContact *new_contact;
 
 		g_warning ("Server didn't return etag for modified address resource");
-		new_contact = download_contact (webdav, uid);
+		new_contact = download_contact (webdav, uid, cancellable);
 		if (new_contact != NULL) {
 			contact = new_contact;
 		}
@@ -586,7 +631,7 @@ e_book_backend_webdav_get_contact (EBookBackend *backend,
 		contact = e_book_backend_cache_get_contact (priv->cache, uid);
 		g_mutex_unlock (&priv->cache_lock);
 	} else {
-		contact = download_contact (webdav, uid);
+		contact = download_contact (webdav, uid, cancellable);
 		/* update cache as we possibly have changes */
 		if (contact != NULL) {
 			g_mutex_lock (&priv->cache_lock);
@@ -743,7 +788,8 @@ parse_propfind_response (xmlTextReaderPtr reader)
 }
 
 static SoupMessage *
-send_propfind (EBookBackendWebdav *webdav)
+send_propfind (EBookBackendWebdav *webdav,
+	       GCancellable *cancellable)
 {
 	SoupMessage               *message;
 	EBookBackendWebdavPrivate *priv    = webdav->priv;
@@ -759,7 +805,7 @@ send_propfind (EBookBackendWebdav *webdav)
 		message, "text/xml", SOUP_MEMORY_TEMPORARY,
 		(gchar *) request, strlen (request));
 
-	soup_session_send_message (priv->session, message);
+	send_and_handle_ssl (webdav, message, cancellable);
 
 	return message;
 }
@@ -835,7 +881,8 @@ xp_object_get_status (xmlXPathObjectPtr result)
 
 static gboolean
 check_addressbook_changed (EBookBackendWebdav *webdav,
-                           gchar **new_ctag)
+                           gchar **new_ctag,
+			   GCancellable *cancellable)
 {
 	gboolean res = TRUE;
 	const gchar *request = "<?xml version=\"1.0\" encoding=\"utf-8\"?><propfind xmlns=\"DAV:\"><prop><getctag/></prop></propfind>";
@@ -861,7 +908,7 @@ check_addressbook_changed (EBookBackendWebdav *webdav,
 	soup_message_headers_append (message->request_headers, "Connection", "close");
 	soup_message_headers_append (message->request_headers, "Depth", "0");
 	soup_message_set_request (message, "text/xml", SOUP_MEMORY_TEMPORARY, (gchar *) request, strlen (request));
-	soup_session_send_message (priv->session, message);
+	send_and_handle_ssl (webdav, message, cancellable);
 
 	if (message->status_code == 207 && message->response_body) {
 		xmlDocPtr xml;
@@ -916,7 +963,8 @@ check_addressbook_changed (EBookBackendWebdav *webdav,
 static GError *
 download_contacts (EBookBackendWebdav *webdav,
                    EFlag *running,
-                  EDataBookView *book_view)
+		   EDataBookView *book_view,
+		   GCancellable *cancellable)
 {
 	EBookBackendWebdavPrivate *priv = webdav->priv;
 	EBookBackend		  *book_backend;
@@ -932,7 +980,7 @@ download_contacts (EBookBackendWebdav *webdav,
 
 	g_mutex_lock (&priv->update_lock);
 
-	if (!check_addressbook_changed (webdav, &new_ctag)) {
+	if (!check_addressbook_changed (webdav, &new_ctag, cancellable)) {
 		g_free (new_ctag);
 		g_mutex_unlock (&priv->update_lock);
 		return EDB_ERROR (SUCCESS);
@@ -945,7 +993,7 @@ download_contacts (EBookBackendWebdav *webdav,
 				_("Loading Addressbook summary..."));
 	}
 
-	message = send_propfind (webdav);
+	message = send_propfind (webdav, cancellable);
 	status  = message->status_code;
 
 	if (status == 401 || status == 407) {
@@ -1047,7 +1095,7 @@ download_contacts (EBookBackendWebdav *webdav,
 		/* download contact if it is not cached or its ETag changed */
 		if (contact == NULL || etag == NULL ||
 		    strcmp (e_contact_get_const (contact, E_CONTACT_REV), etag) != 0) {
-			contact = download_contact (webdav, complete_uri);
+			contact = download_contact (webdav, complete_uri, cancellable);
 			if (contact != NULL) {
 				g_mutex_lock (&priv->cache_lock);
 				if (e_book_backend_cache_remove_contact (priv->cache, complete_uri))
@@ -1103,7 +1151,7 @@ book_view_thread (gpointer data)
 	 * it's stopped */
 	g_object_ref (book_view);
 
-	error = download_contacts (webdav, closure->running, book_view);
+	error = download_contacts (webdav, closure->running, book_view, NULL);
 
 	g_object_unref (book_view);
 
@@ -1191,7 +1239,7 @@ e_book_backend_webdav_get_contact_list (EBookBackend *backend,
 
 	if (e_backend_get_online (E_BACKEND (backend))) {
 		/* make sure the cache is up to date */
-		GError *error = download_contacts (webdav, NULL, NULL);
+		GError *error = download_contacts (webdav, NULL, NULL, cancellable);
 
 		if (error) {
 			e_data_book_respond_get_contact_list (book, opid, error, NULL);
@@ -1235,7 +1283,7 @@ e_book_backend_webdav_get_contact_list_uids (EBookBackend *backend,
 
 	if (e_backend_get_online (E_BACKEND (backend))) {
 		/* make sure the cache is up to date */
-		GError *error = download_contacts (webdav, NULL, NULL);
+		GError *error = download_contacts (webdav, NULL, NULL, cancellable);
 
 		if (error) {
 			e_data_book_respond_get_contact_list_uids (book, opid, error, NULL);
@@ -1372,13 +1420,13 @@ e_book_backend_webdav_open (EBookBackend *backend,
 	g_mutex_unlock (&priv->cache_lock);
 
 	session = soup_session_sync_new ();
-	g_object_set (session, SOUP_SESSION_TIMEOUT, 90, NULL);
+	g_object_set (session,
+		SOUP_SESSION_TIMEOUT, 90,
+		SOUP_SESSION_SSL_STRICT, TRUE,
+		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+		NULL);
 
-	g_object_bind_property (
-		webdav_extension, "ignore-invalid-cert",
-		session, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
-		G_BINDING_SYNC_CREATE |
-		G_BINDING_INVERT_BOOLEAN);
+	e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
 
 	g_signal_connect (
 		session, "authenticate",
@@ -1514,7 +1562,7 @@ book_backend_webdav_try_password_sync (ESourceAuthenticator *authenticator,
 	webdav->priv->password = g_strdup (password->str);
 
 	/* Send a PROPFIND to test whether user/password is correct. */
-	message = send_propfind (webdav);
+	message = send_propfind (webdav, cancellable);
 
 	switch (message->status_code) {
 		case SOUP_STATUS_OK:
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index f7f7635..ece46ee 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -536,10 +536,6 @@ status_code_to_result (SoupMessage *message,
                        GError **perror)
 {
 	ECalBackendCalDAVPrivate *priv;
-	ESource *source;
-	ESourceWebdav *extension;
-	const gchar *extension_name;
-	gboolean ignore_invalid_cert;
 
 	g_return_val_if_fail (cbdav != NULL, FALSE);
 	g_return_val_if_fail (message != NULL, FALSE);
@@ -550,11 +546,8 @@ status_code_to_result (SoupMessage *message,
 		return TRUE;
 	}
 
-	source = e_backend_get_source (E_BACKEND (cbdav));
-
-	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-	extension = e_source_get_extension (source, extension_name);
-	ignore_invalid_cert = e_source_webdav_get_ignore_invalid_cert (extension);
+	if (perror && *perror)
+		return FALSE;
 
 	switch (message->status_code) {
 	case SOUP_STATUS_CANT_CONNECT:
@@ -590,22 +583,12 @@ status_code_to_result (SoupMessage *message,
 		break;
 
 	case SOUP_STATUS_SSL_FAILED:
-		if (ignore_invalid_cert) {
-			g_propagate_error (
-				perror,
-				e_data_cal_create_error_fmt ( OtherError,
-				_("Failed to connect to a server using SSL: %s"),
-				message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
-				(soup_status_get_phrase (message->status_code) ? soup_status_get_phrase (message->status_code) : _("Unknown error"))));
-		} else {
-			g_propagate_error (
-				perror, EDC_ERROR_EX (OtherError,
-				_("Failed to connect to a server using SSL. "
-				"One possible reason is an invalid certificate being used by the server. "
-				"If this is expected, like self-signed certificate being used on the server, "
-				"then disable certificate validity tests by selecting 'Ignore invalid SSL certificate' option "
-				"in Properties")));
-		}
+		g_propagate_error (
+			perror,
+			e_data_cal_create_error_fmt ( OtherError,
+			_("Failed to connect to a server using SSL: %s"),
+			message->reason_phrase && *message->reason_phrase ? message->reason_phrase :
+			(soup_status_get_phrase (message->status_code) ? soup_status_get_phrase (message->status_code) : _("Unknown error"))));
 		break;
 
 	default:
@@ -1040,21 +1023,55 @@ redirect_handler (SoupMessage *msg,
 }
 
 static void
-send_and_handle_redirection (SoupSession *soup_session,
+send_and_handle_redirection (ECalBackendCalDAV *cbdav,
                              SoupMessage *msg,
-                             gchar **new_location)
+                             gchar **new_location,
+			     GCancellable *cancellable,
+			     GError **error)
 {
 	gchar *old_uri = NULL;
 
+	g_return_if_fail (E_IS_CAL_BACKEND_CALDAV (cbdav));
 	g_return_if_fail (msg != NULL);
 
 	if (new_location)
 		old_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
 
 	soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
-	soup_message_add_header_handler (msg, "got_body", "Location", G_CALLBACK (redirect_handler), soup_session);
+	soup_message_add_header_handler (msg, "got_body", "Location", G_CALLBACK (redirect_handler), cbdav->priv->session);
 	soup_message_headers_append (msg->request_headers, "Connection", "close");
-	soup_session_send_message (soup_session, msg);
+	soup_session_send_message (cbdav->priv->session, msg);
+
+	if (msg->status_code == SOUP_STATUS_SSL_FAILED) {
+		ESource *source;
+		ESourceWebdav *extension;
+		ESourceRegistry *registry;
+		EBackend *backend;
+		ETrustPromptResponse response;
+		ENamedParameters *parameters;
+
+		backend = E_BACKEND (cbdav);
+		source = e_backend_get_source (backend);
+		registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+
+		parameters = e_named_parameters_new ();
+
+		response = e_source_webdav_prepare_ssl_trust_prompt (extension, msg, registry, parameters);
+		if (response == E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+			response = e_backend_trust_prompt_sync (backend, parameters, cancellable, error);
+			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 (cbdav->priv->session, SOUP_SESSION_SSL_STRICT, FALSE, NULL);
+			soup_session_send_message (cbdav->priv->session, msg);
+		}
+	}
 
 	if (new_location) {
 		gchar *new_loc = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
@@ -1088,19 +1105,20 @@ caldav_generate_uri (ECalBackendCalDAV *cbdav,
 static gboolean
 caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
                              gboolean *server_unreachable,
+			     GCancellable *cancellable,
                              GError **perror)
 {
-	SoupMessage               *message;
-	const gchar                *header;
-	gboolean                   calendar_access;
-	gboolean                   put_allowed;
-	gboolean                   delete_allowed;
+	SoupMessage *message;
+	const gchar *header;
+	gboolean calendar_access;
+	gboolean put_allowed;
+	gboolean delete_allowed;
+	ESource *source;
+	ESourceWebdav *webdav_extension;
 
 	g_return_val_if_fail (cbdav != NULL, FALSE);
 	g_return_val_if_fail (server_unreachable != NULL, FALSE);
 
-	/* FIXME: setup text_uri */
-
 	message = soup_message_new (SOUP_METHOD_OPTIONS, cbdav->priv->uri);
 	if (message == NULL) {
 		g_propagate_error (perror, EDC_ERROR (NoSuchCal));
@@ -1110,7 +1128,14 @@ caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
 		message->request_headers,
 		"User-Agent", "Evolution/" VERSION);
 
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	/* re-check server's certificate trust, if needed */
+	g_object_set (cbdav->priv->session, SOUP_SESSION_SSL_STRICT, TRUE, NULL);
+
+	source = e_backend_get_source (E_BACKEND (cbdav));
+	webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+	e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
+
+	send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
 
 	if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
 		switch (message->status_code) {
@@ -1265,7 +1290,7 @@ check_calendar_changed_on_server (ECalBackendCalDAV *cbdav)
 		buf_content, buf_size);
 
 	/* Send the request now */
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, NULL, NULL);
 
 	/* Clean up the memory */
 	xmlOutputBufferClose (buf);
@@ -1418,7 +1443,7 @@ caldav_server_list_objects (ECalBackendCalDAV *cbdav,
 		buf_content, buf_size);
 
 	/* Send the request now */
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, NULL, NULL);
 
 	/* Clean up the memory */
 	xmlOutputBufferClose (buf);
@@ -1475,7 +1500,7 @@ caldav_server_download_attachment (ECalBackendCalDAV *cbdav,
 	}
 
 	soup_message_headers_append (message->request_headers, "User-Agent", "Evolution/" VERSION);
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, NULL, NULL);
 
 	if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
 		status_code_to_result (message, cbdav, FALSE, error);
@@ -1498,6 +1523,7 @@ caldav_server_download_attachment (ECalBackendCalDAV *cbdav,
 static gboolean
 caldav_server_get_object (ECalBackendCalDAV *cbdav,
                           CalDAVObject *object,
+			  GCancellable *cancellable,
                           GError **perror)
 {
 	SoupMessage              *message;
@@ -1518,7 +1544,7 @@ caldav_server_get_object (ECalBackendCalDAV *cbdav,
 		message->request_headers,
 		"User-Agent", "Evolution/" VERSION);
 
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
 
 	if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
 		status_code_to_result (message, cbdav, FALSE, perror);
@@ -1564,6 +1590,7 @@ static void
 caldav_post_freebusy (ECalBackendCalDAV *cbdav,
                       const gchar *url,
                       gchar **post_fb,
+		      GCancellable *cancellable,
                       GError **error)
 {
 	SoupMessage *message;
@@ -1586,7 +1613,7 @@ caldav_post_freebusy (ECalBackendCalDAV *cbdav,
 		SOUP_MEMORY_COPY,
 		*post_fb, strlen (*post_fb));
 
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
 
 	if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
 		status_code_to_result (message, cbdav, FALSE, error);
@@ -1645,6 +1672,7 @@ static gboolean
 caldav_server_put_object (ECalBackendCalDAV *cbdav,
                           CalDAVObject *object,
                           icalcomponent *icalcomp,
+			  GCancellable *cancellable,
                           GError **perror)
 {
 	SoupMessage              *message;
@@ -1687,7 +1715,7 @@ caldav_server_put_object (ECalBackendCalDAV *cbdav,
 		strlen (object->cdata));
 
 	uri = NULL;
-	send_and_handle_redirection (cbdav->priv->session, message, &uri);
+	send_and_handle_redirection (cbdav, message, &uri, cancellable, perror);
 
 	if (uri) {
 		gchar *file = strrchr (uri, '/');
@@ -1734,7 +1762,7 @@ caldav_server_put_object (ECalBackendCalDAV *cbdav,
 			}
 		}
 
-		if (!caldav_server_get_object (cbdav, object, &local_error)) {
+		if (!caldav_server_get_object (cbdav, object, cancellable, &local_error)) {
 			if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
 				gchar *file;
 
@@ -1748,7 +1776,7 @@ caldav_server_put_object (ECalBackendCalDAV *cbdav,
 					g_free (object->href);
 					object->href = file;
 
-					if (!caldav_server_get_object (cbdav, object, &local_error)) {
+					if (!caldav_server_get_object (cbdav, object, cancellable, &local_error)) {
 						if (g_error_matches (local_error, E_DATA_CAL_ERROR, ObjectNotFound)) {
 							g_clear_error (&local_error);
 
@@ -1792,6 +1820,7 @@ caldav_server_put_object (ECalBackendCalDAV *cbdav,
 static void
 caldav_server_delete_object (ECalBackendCalDAV *cbdav,
                              CalDAVObject *object,
+			     GCancellable *cancellable,
                              GError **perror)
 {
 	SoupMessage              *message;
@@ -1817,7 +1846,7 @@ caldav_server_delete_object (ECalBackendCalDAV *cbdav,
 			"If-Match", object->etag);
 	}
 
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
 
 	status_code_to_result (message, cbdav, FALSE, perror);
 
@@ -1828,7 +1857,9 @@ caldav_server_delete_object (ECalBackendCalDAV *cbdav,
 }
 
 static gboolean
-caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav)
+caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav,
+				    GCancellable *cancellable,
+				    GError **error)
 {
 	SoupMessage *message;
 	xmlOutputBufferPtr buf;
@@ -1870,7 +1901,7 @@ caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav)
 		buf_content, buf_size);
 
 	/* Send the request now */
-	send_and_handle_redirection (cbdav->priv->session, message, NULL);
+	send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
 
 	/* Clean up the memory */
 	xmlOutputBufferClose (buf);
@@ -1921,7 +1952,7 @@ caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav)
 			buf_content, buf_size);
 
 		/* Send the request now */
-		send_and_handle_redirection (cbdav->priv->session, message, NULL);
+		send_and_handle_redirection (cbdav, message, NULL, cancellable, error);
 
 		if (message->status_code == 207 && parse_propfind_response (message, XPATH_SCHEDULE_OUTBOX_URL_STATUS, XPATH_SCHEDULE_OUTBOX_URL, &cbdav->priv->schedule_outbox_url)) {
 			if (!*cbdav->priv->schedule_outbox_url) {
@@ -2365,7 +2396,7 @@ caldav_synch_slave_loop (gpointer data)
 			GError *local_error = NULL;
 			gboolean online;
 
-			if (caldav_server_open_calendar (cbdav, &server_unreachable, &local_error)) {
+			if (caldav_server_open_calendar (cbdav, &server_unreachable, NULL, &local_error)) {
 				cbdav->priv->opened = TRUE;
 				update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
 				g_cond_signal (&cbdav->priv->cond);
@@ -2715,6 +2746,7 @@ initialize_backend (ECalBackendCalDAV *cbdav,
 
 static gboolean
 open_calendar (ECalBackendCalDAV *cbdav,
+	       GCancellable *cancellable,
                GError **error)
 {
 	gboolean server_unreachable = FALSE;
@@ -2727,7 +2759,7 @@ open_calendar (ECalBackendCalDAV *cbdav,
 	proxy_settings_changed (cbdav->priv->proxy, cbdav->priv);
 
 	success = caldav_server_open_calendar (
-		cbdav, &server_unreachable, &local_error);
+		cbdav, &server_unreachable, cancellable, &local_error);
 
 	if (success) {
 		update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
@@ -2790,7 +2822,7 @@ caldav_do_open (ECalBackendSync *backend,
 	if (online) {
 		GError *local_error = NULL;
 
-		opened = open_calendar (cbdav, &local_error);
+		opened = open_calendar (cbdav, cancellable, &local_error);
 
 		if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired) || g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
 			g_clear_error (&local_error);
@@ -3683,6 +3715,7 @@ do_create_objects (ECalBackendCalDAV *cbdav,
                    const GSList *in_calobjs,
                    GSList **uids,
                    GSList **new_components,
+		   GCancellable *cancellable,
                    GError **perror)
 {
 	ECalComponent            *comp;
@@ -3755,7 +3788,7 @@ do_create_objects (ECalBackendCalDAV *cbdav,
 		object.etag  = NULL;
 		object.cdata = pack_cobj (cbdav, icalcomp);
 
-		did_put = caldav_server_put_object (cbdav, &object, icalcomp, perror);
+		did_put = caldav_server_put_object (cbdav, &object, icalcomp, cancellable, perror);
 
 		caldav_object_free (&object, FALSE);
 	} else {
@@ -3781,6 +3814,7 @@ do_modify_objects (ECalBackendCalDAV *cbdav,
                    CalObjModType mod,
                    GSList **old_components,
                    GSList **new_components,
+		   GCancellable *cancellable,
                    GError **error)
 {
 	ECalComponent            *comp;
@@ -3920,7 +3954,7 @@ do_modify_objects (ECalBackendCalDAV *cbdav,
 		object.etag  = etag;
 		object.cdata = pack_cobj (cbdav, cache_comp);
 
-		did_put = caldav_server_put_object (cbdav, &object, cache_comp, error);
+		did_put = caldav_server_put_object (cbdav, &object, cache_comp, cancellable, error);
 
 		caldav_object_free (&object, FALSE);
 		href = NULL;
@@ -3951,6 +3985,7 @@ do_remove_objects (ECalBackendCalDAV *cbdav,
                    CalObjModType mod,
                    GSList **old_components,
                    GSList **new_components,
+		   GCancellable *cancellable,
                    GError **perror)
 {
 	icalcomponent            *cache_comp;
@@ -4033,9 +4068,9 @@ do_remove_objects (ECalBackendCalDAV *cbdav,
 		if (mod == CALOBJ_MOD_THIS && rid && *rid) {
 			caldav_object.cdata = pack_cobj (cbdav, cache_comp);
 
-			caldav_server_put_object (cbdav, &caldav_object, cache_comp, perror);
+			caldav_server_put_object (cbdav, &caldav_object, cache_comp, cancellable, perror);
 		} else
-			caldav_server_delete_object (cbdav, &caldav_object, perror);
+			caldav_server_delete_object (cbdav, &caldav_object, cancellable, perror);
 
 		caldav_object_free (&caldav_object, FALSE);
 		href = NULL;
@@ -4130,6 +4165,7 @@ process_object (ECalBackendCalDAV *cbdav,
                 ECalComponent *ecomp,
                 gboolean online,
                 icalproperty_method method,
+		GCancellable *cancellable,
                 GError **error)
 {
 	ESourceRegistry *registry;
@@ -4171,7 +4207,7 @@ process_object (ECalBackendCalDAV *cbdav,
 
 				new_obj_strs.data = new_obj_str;
 				do_modify_objects (cbdav, &new_obj_strs, mod,
-						  &old_components, &new_components, &err);
+						  &old_components, &new_components, cancellable, &err);
 				if (!err && new_components && new_components->data) {
 					if (!old_components || !old_components->data) {
 						e_cal_backend_notify_component_created (backend, new_components->data);
@@ -4187,7 +4223,7 @@ process_object (ECalBackendCalDAV *cbdav,
 				GSList ids = {0,};
 
 				ids.data = id;
-				do_remove_objects (cbdav, &ids, mod, &old_components, &new_components, &err);
+				do_remove_objects (cbdav, &ids, mod, &old_components, &new_components, cancellable, &err);
 				if (!err && old_components && old_components->data) {
 					if (new_components && new_components->data) {
 						e_cal_backend_notify_component_modified (backend, old_components->data, new_components->data);
@@ -4205,7 +4241,7 @@ process_object (ECalBackendCalDAV *cbdav,
 
 			new_objs.data = new_obj_str;
 
-			do_create_objects (cbdav, &new_objs, NULL, &new_components, &err);
+			do_create_objects (cbdav, &new_objs, NULL, &new_components, cancellable, &err);
 
 			if (!err) {
 				if (new_components && new_components->data)
@@ -4221,7 +4257,7 @@ process_object (ECalBackendCalDAV *cbdav,
 			GSList ids = {0,};
 
 			ids.data = id;
-			do_remove_objects (cbdav, &ids, CALOBJ_MOD_THIS, &old_components, &new_components, &err);
+			do_remove_objects (cbdav, &ids, CALOBJ_MOD_THIS, &old_components, &new_components, cancellable, &err);
 			if (!err && old_components && old_components->data) {
 				if (new_components && new_components->data) {
 					e_cal_backend_notify_component_modified (backend, old_components->data, new_components->data);
@@ -4307,7 +4343,7 @@ do_receive_objects (ECalBackendSync *backend,
 			method = tmethod;
 		}
 
-		process_object (cbdav, ecomp, online, method, &err);
+		process_object (cbdav, ecomp, online, method, cancellable, &err);
 		g_object_unref (ecomp);
 	}
 
@@ -4363,6 +4399,7 @@ caldav_busy_stub (
                   in_calobjs,
                   uids,
                   new_components,
+		  cancellable,
                   perror))
 
 caldav_busy_stub (
@@ -4381,6 +4418,7 @@ caldav_busy_stub (
                   mod,
                   old_components,
                   new_components,
+		  cancellable,
                   perror))
 
 caldav_busy_stub (
@@ -4399,6 +4437,7 @@ caldav_busy_stub (
                   mod,
                   old_components,
                   new_components,
+		  cancellable,
                   perror))
 
 caldav_busy_stub (
@@ -4634,10 +4673,11 @@ caldav_get_free_busy (ECalBackendSync *backend,
 	}
 
 	if (!cbdav->priv->schedule_outbox_url) {
-		caldav_receive_schedule_outbox_url (cbdav);
+		caldav_receive_schedule_outbox_url (cbdav, cancellable, error);
 		if (!cbdav->priv->schedule_outbox_url) {
 			cbdav->priv->calendar_schedule = FALSE;
-			g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Schedule outbox url not found")));
+			if (error && !*error)
+				g_propagate_error (error, EDC_ERROR_EX (OtherError, _("Schedule outbox url not found")));
 			return;
 		}
 	}
@@ -4718,7 +4758,7 @@ caldav_get_free_busy (ECalBackendSync *backend,
 
 	e_return_data_cal_error_if_fail (str != NULL, OtherError);
 
-	caldav_post_freebusy (cbdav, cbdav->priv->schedule_outbox_url, &str, &err);
+	caldav_post_freebusy (cbdav, cbdav->priv->schedule_outbox_url, &str, cancellable, &err);
 
 	if (!err) {
 		/* parse returned xml */
@@ -4890,7 +4930,7 @@ caldav_try_password_sync (ESourceAuthenticator *authenticator,
 	g_free (cbdav->priv->password);
 	cbdav->priv->password = g_strdup (password->str);
 
-	open_calendar (cbdav, &local_error);
+	open_calendar (cbdav, cancellable, &local_error);
 
 	if (local_error == NULL) {
 		result = E_SOURCE_AUTHENTICATION_ACCEPTED;
@@ -4996,23 +5036,6 @@ e_cal_backend_caldav_finalize (GObject *object)
 static void
 cal_backend_caldav_constructed (GObject *object)
 {
-	ESource *source;
-	ESourceWebdav *extension;
-	ECalBackendCalDAV *cbdav;
-	const gchar *extension_name;
-
-	cbdav = E_CAL_BACKEND_CALDAV (object);
-
-	source = e_backend_get_source (E_BACKEND (cbdav));
-	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-	extension = e_source_get_extension (source, extension_name);
-
-	g_object_bind_property (
-		extension, "ignore-invalid-cert",
-		cbdav->priv->session, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
-		G_BINDING_SYNC_CREATE |
-		G_BINDING_INVERT_BOOLEAN);
-
 	/* Chain up to parent's constructed() method. */
 	G_OBJECT_CLASS (e_cal_backend_caldav_parent_class)->
 		constructed (object);
@@ -5023,7 +5046,11 @@ e_cal_backend_caldav_init (ECalBackendCalDAV *cbdav)
 {
 	cbdav->priv = E_CAL_BACKEND_CALDAV_GET_PRIVATE (cbdav);
 	cbdav->priv->session = soup_session_sync_new ();
-	g_object_set (cbdav->priv->session, SOUP_SESSION_TIMEOUT, 90, NULL);
+	g_object_set (cbdav->priv->session,
+		SOUP_SESSION_TIMEOUT, 90,
+		SOUP_SESSION_SSL_STRICT, TRUE,
+		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+		NULL);
 
 	cbdav->priv->proxy = e_proxy_new ();
 	e_proxy_setup_proxy (cbdav->priv->proxy);
diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c
index c2db383..dad22eb 100644
--- a/calendar/backends/http/e-cal-backend-http.c
+++ b/calendar/backends/http/e-cal-backend-http.c
@@ -160,31 +160,22 @@ e_cal_backend_http_finalize (GObject *object)
 static void
 e_cal_backend_http_constructed (GObject *object)
 {
-	ESource *source;
 	ECalBackendHttp *backend;
-	ESourceWebdav *extension;
 	SoupSession *soup_session;
-	const gchar *extension_name;
 
 	/* Chain up to parent's constructed() method. */
 	G_OBJECT_CLASS (e_cal_backend_http_parent_class)->constructed (object);
 
 	soup_session = soup_session_sync_new ();
-	g_object_set (soup_session, SOUP_SESSION_TIMEOUT, 90, NULL);
+	g_object_set (soup_session,
+		SOUP_SESSION_TIMEOUT, 90,
+		SOUP_SESSION_SSL_STRICT, TRUE,
+		SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE, TRUE,
+		NULL);
 
 	backend = E_CAL_BACKEND_HTTP (object);
 	backend->priv->soup_session = soup_session;
 
-	source = e_backend_get_source (E_BACKEND (backend));
-	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
-	extension = e_source_get_extension (source, extension_name);
-
-	g_object_bind_property (
-		extension, "ignore-invalid-cert",
-		soup_session, SOUP_SESSION_SSL_USE_SYSTEM_CA_FILE,
-		G_BINDING_SYNC_CREATE |
-		G_BINDING_INVERT_BOOLEAN);
-
 	g_signal_connect (
 		backend->priv->soup_session, "authenticate",
 		G_CALLBACK (soup_authenticate), backend);
@@ -493,6 +484,36 @@ cal_backend_http_load (ECalBackendHttp *backend,
 	}
 
 	status_code = soup_session_send_message (soup_session, soup_message);
+	if (status_code == SOUP_STATUS_SSL_FAILED) {
+		ESource *source;
+		ESourceWebdav *extension;
+		ESourceRegistry *registry;
+		EBackend *ebackend;
+		ETrustPromptResponse response;
+		ENamedParameters *parameters;
+
+		ebackend = E_BACKEND (backend);
+		source = e_backend_get_source (ebackend);
+		registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
+		extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+
+		parameters = e_named_parameters_new ();
+
+		response = e_source_webdav_prepare_ssl_trust_prompt (extension, soup_message, registry, parameters);
+		if (response == E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+			response = e_backend_trust_prompt_sync (ebackend, parameters, cancellable, NULL);
+			if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
+				e_source_webdav_store_ssl_trust_prompt (extension, soup_message, response);
+		}
+
+		e_named_parameters_free (parameters);
+
+		if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
+		    response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY) {
+			g_object_set (soup_session, SOUP_SESSION_SSL_STRICT, FALSE, NULL);
+			status_code = soup_session_send_message (soup_session, soup_message);
+		}
+	}
 
 	if (G_IS_CANCELLABLE (cancellable))
 		g_cancellable_disconnect (cancellable, cancel_id);
@@ -841,6 +862,7 @@ e_cal_backend_http_open (ECalBackendSync *backend,
 	ESource *source;
 	ESourceRegistry *registry;
 	ESourceAuthentication *auth_extension;
+	ESourceWebdav *webdav_extension;
 	const gchar *extension_name;
 	const gchar *cache_dir;
 	gboolean auth_required;
@@ -867,6 +889,12 @@ e_cal_backend_http_open (ECalBackendSync *backend,
 	auth_extension = e_source_get_extension (source, extension_name);
 	auth_required = e_source_authentication_required (auth_extension);
 
+	extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
+	webdav_extension = e_source_get_extension (source, extension_name);
+
+	g_object_set (cbhttp->priv->soup_session, SOUP_SESSION_SSL_STRICT, TRUE, NULL);
+	e_source_webdav_unset_temporary_ssl_trust (webdav_extension);
+
 	if (priv->source_changed_id == 0) {
 		priv->source_changed_id = g_signal_connect (
 			source, "changed",
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
index d24ba37..494100f 100644
--- a/libebackend/e-backend.c
+++ b/libebackend/e-backend.c
@@ -31,13 +31,16 @@
  * All #EBackend instances are created by an #EBackendFactory.
  **/
 
-#include "e-backend.h"
-
 #include <config.h>
 #include <glib/gi18n-lib.h>
 
 #include <gio/gio.h>
 
+#include <libedataserver/libedataserver.h>
+
+#include "e-backend.h"
+#include "e-user-prompter.h"
+
 #define E_BACKEND_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_BACKEND, EBackendPrivate))
@@ -46,6 +49,7 @@ typedef struct _AsyncContext AsyncContext;
 
 struct _EBackendPrivate {
 	ESource *source;
+	EUserPrompter *prompter;
 	gboolean online;
 };
 
@@ -56,7 +60,8 @@ struct _AsyncContext {
 enum {
 	PROP_0,
 	PROP_ONLINE,
-	PROP_SOURCE
+	PROP_SOURCE,
+	PROP_USER_PROMPTER
 };
 
 G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)
@@ -121,6 +126,12 @@ backend_get_property (GObject *object,
 				value, e_backend_get_source (
 				E_BACKEND (object)));
 			return;
+
+		case PROP_USER_PROMPTER:
+			g_value_set_object (
+				value, e_backend_get_user_prompter (
+				E_BACKEND (object)));
+			return;
 	}
 
 	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -138,6 +149,11 @@ backend_dispose (GObject *object)
 		priv->source = NULL;
 	}
 
+	if (priv->prompter) {
+		g_object_unref (priv->prompter);
+		priv->prompter = NULL;
+	}
+
 	/* Chain up to parent's dispose() method. */
 	G_OBJECT_CLASS (e_backend_parent_class)->dispose (object);
 }
@@ -280,12 +296,24 @@ e_backend_class_init (EBackendClass *class)
 			G_PARAM_READWRITE |
 			G_PARAM_CONSTRUCT_ONLY |
 			G_PARAM_STATIC_STRINGS));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_USER_PROMPTER,
+		g_param_spec_object (
+			"user-prompter",
+			"User Prompter",
+			"User prompter instance",
+			E_TYPE_USER_PROMPTER,
+			G_PARAM_READABLE |
+			G_PARAM_STATIC_STRINGS));
 }
 
 static void
 e_backend_init (EBackend *backend)
 {
 	backend->priv = E_BACKEND_GET_PRIVATE (backend);
+	backend->priv->prompter = e_user_prompter_new ();
 }
 
 /**
@@ -461,3 +489,185 @@ e_backend_authenticate_finish (EBackend *backend,
 	return class->authenticate_finish (backend, result, error);
 }
 
+/**
+ * e_backend_get_user_prompter:
+ * @backend: an #EBackend
+ *
+ * Gets an instance of #EUserPrompter, associated with this @backend.
+ * The instance is owned by the @backend.
+ *
+ * Returns: (transfer-none): an #EUserPrompter instance
+ *
+ * Since: 3.8
+ **/
+EUserPrompter *
+e_backend_get_user_prompter (EBackend *backend)
+{
+	g_return_val_if_fail (E_IS_BACKEND (backend), NULL);
+
+	return backend->priv->prompter;
+}
+
+/**
+ * e_backend_trust_prompt_sync:
+ * @backend: an #EBackend
+ * @parameters: an #ENamedParameters with values for the trust prompt
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Asks a user a trust prompt with given @parameters, and returns what
+ * user responded. This blocks until the response is delivered.
+ *
+ * Returns: an #ETrustPromptResponse what user responded
+ *
+ * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
+ *    it's on error or if user closes the trust prompt dialog with other
+ *    than the offered buttons. Usual behaviour in such case is to treat
+ *    it as a temporary reject.
+ *
+ * Since: 3.8
+ **/
+ETrustPromptResponse
+e_backend_trust_prompt_sync (EBackend *backend,
+			     const ENamedParameters *parameters,
+			     GCancellable *cancellable,
+			     GError **error)
+{
+	EUserPrompter *prompter;
+	gint response;
+
+	g_return_val_if_fail (E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+	g_return_val_if_fail (parameters != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	prompter = e_backend_get_user_prompter (backend);
+	g_return_val_if_fail (prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	/* Just a workaround for libsoup bug about not providing connection certificate
+	   on failed requests. This is fixed in libsoup since 2.41.3, but let's have this
+	   for now. I wrote it here to avoid code duplication, and once the libsoup 2.42.0
+	   will be out all this code, together with other SOUP_CHECK_VERSION usages also
+	   in evolution, will be removed.
+	*/
+	if (!e_named_parameters_get (parameters, "cert")) {
+		GSList *button_captions = NULL;
+		const gchar *markup;
+		gchar *tmp = NULL;
+
+		button_captions = g_slist_append (button_captions, _("_Reject"));
+		button_captions = g_slist_append (button_captions, _("Accept _Temporarily"));
+		button_captions = g_slist_append (button_captions, _("_Accept Permanently"));
+
+		markup = e_named_parameters_get (parameters, "markup");
+		if (!markup) {
+			gchar *bhost;
+
+			bhost = g_strconcat ("<b>", e_named_parameters_get (parameters, "host"), "</b>", NULL);
+			tmp = g_strdup_printf (_("SSL certificate for '%s' is not trusted. Do you wish to accept it?"), bhost);
+			g_free (bhost);
+
+			markup = tmp;
+		}
+
+		response = e_user_prompter_prompt_sync (prompter, "question", _("Certificate trust..."),
+			markup, NULL, TRUE, button_captions, cancellable, NULL);
+
+		if (response == 1)
+			response = 2;
+		else if (response == 2)
+			response = 1;
+
+		g_slist_free (button_captions);
+		g_free (tmp);
+	} else {
+		response = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters, NULL, cancellable, error);
+	}
+
+	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;
+}
+
+/**
+ * e_backend_trust_prompt:
+ * @backend: an #EBackend
+ * @parameters: an #ENamedParameters with values for the trust prompt
+ * @cancellable: (allow-none): optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Initiates a user trust prompt with given @parameters.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_backend_trust_prompt_finish() to get the result of the operation.
+ *
+ * Since: 3.8
+ **/
+void
+e_backend_trust_prompt (EBackend *backend,
+			const ENamedParameters *parameters,
+			GCancellable *cancellable,
+			GAsyncReadyCallback callback,
+			gpointer user_data)
+{
+	EUserPrompter *prompter;
+
+	g_return_if_fail (E_IS_BACKEND (backend));
+	g_return_if_fail (parameters != NULL);
+
+	prompter = e_backend_get_user_prompter (backend);
+	g_return_if_fail (prompter != NULL);
+
+	e_user_prompter_extension_prompt (prompter, "ETrustPrompt::trust-prompt", parameters, cancellable, callback, user_data);
+}
+
+/**
+ * e_backend_trust_prompt_finish:
+ * @backend: an #EBackend
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_backend_trust_prompt().  If
+ * an error occurred, the function will set @error and return %E_TRUST_PROMPT_RESPONSE_UNKNOWN.
+ *
+ * Returns: an #ETrustPromptResponse what user responded
+ *
+ * Note: The function can return also %E_TRUST_PROMPT_RESPONSE_UNKNOWN,
+ *    it's on error or if user closes the trust prompt dialog with other
+ *    than the offered buttons. Usual behaviour in such case is to treat
+ *    it as a temporary reject.
+ *
+ * Since: 3.8
+ **/
+ETrustPromptResponse
+e_backend_trust_prompt_finish (EBackend *backend,
+			       GAsyncResult *result,
+			       GError **error)
+{
+	EUserPrompter *prompter;
+	gint response;
+
+	g_return_val_if_fail (E_IS_BACKEND (backend), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	prompter = e_backend_get_user_prompter (backend);
+	g_return_val_if_fail (prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+	response = e_user_prompter_extension_prompt_finish (prompter, result, NULL, error);
+
+	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;
+}
diff --git a/libebackend/e-backend.h b/libebackend/e-backend.h
index 30b697e..65b8e8b 100644
--- a/libebackend/e-backend.h
+++ b/libebackend/e-backend.h
@@ -46,6 +46,9 @@
 
 G_BEGIN_DECLS
 
+/* forward declaration */
+struct _EUserPrompter;
+
 typedef struct _EBackend EBackend;
 typedef struct _EBackendClass EBackendClass;
 typedef struct _EBackendPrivate EBackendPrivate;
@@ -100,6 +103,22 @@ void		e_backend_authenticate		(EBackend *backend,
 gboolean	e_backend_authenticate_finish	(EBackend *backend,
 						 GAsyncResult *result,
 						 GError **error);
+struct _EUserPrompter *
+		e_backend_get_user_prompter	(EBackend *backend);
+ETrustPromptResponse
+		e_backend_trust_prompt_sync	(EBackend *backend,
+						 const ENamedParameters *parameters,
+						 GCancellable *cancellable,
+						 GError **error);
+void		e_backend_trust_prompt		(EBackend *backend,
+						 const ENamedParameters *parameters,
+						 GCancellable *cancellable,
+						 GAsyncReadyCallback callback,
+						 gpointer user_data);
+ETrustPromptResponse
+		e_backend_trust_prompt_finish	(EBackend *backend,
+						 GAsyncResult *result,
+						 GError **error);
 
 G_END_DECLS
 
diff --git a/libedataserver/e-source-webdav.c b/libedataserver/e-source-webdav.c
index e0ae6ba..0bbbf8f 100644
--- a/libedataserver/e-source-webdav.c
+++ b/libedataserver/e-source-webdav.c
@@ -44,12 +44,21 @@
  * ]|
  **/
 
-#include "e-source-webdav.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib/gi18n-lib.h>
 
 #include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source-address-book.h>
 #include <libedataserver/e-source-authentication.h>
+#include <libedataserver/e-source-calendar.h>
+#include <libedataserver/e-source-registry.h>
 #include <libedataserver/e-source-security.h>
 
+#include "e-source-webdav.h"
+
 #define E_SOURCE_WEBDAV_GET_PRIVATE(obj) \
 	(G_TYPE_INSTANCE_GET_PRIVATE \
 	((obj), E_TYPE_SOURCE_WEBDAV, ESourceWebdavPrivate))
@@ -60,9 +69,9 @@ struct _ESourceWebdavPrivate {
 	gchar *email_address;
 	gchar *resource_path;
 	gchar *resource_query;
+	gchar *ssl_trust;
 	gboolean avoid_ifmatch;
 	gboolean calendar_auto_schedule;
-	gboolean ignore_invalid_cert;
 	SoupURI *soup_uri;
 };
 
@@ -72,7 +81,7 @@ enum {
 	PROP_CALENDAR_AUTO_SCHEDULE,
 	PROP_DISPLAY_NAME,
 	PROP_EMAIL_ADDRESS,
-	PROP_IGNORE_INVALID_CERT,
+	PROP_SSL_TRUST,
 	PROP_RESOURCE_PATH,
 	PROP_RESOURCE_QUERY,
 	PROP_SOUP_URI
@@ -256,12 +265,6 @@ source_webdav_set_property (GObject *object,
 				g_value_get_string (value));
 			return;
 
-		case PROP_IGNORE_INVALID_CERT:
-			e_source_webdav_set_ignore_invalid_cert (
-				E_SOURCE_WEBDAV (object),
-				g_value_get_boolean (value));
-			return;
-
 		case PROP_RESOURCE_PATH:
 			e_source_webdav_set_resource_path (
 				E_SOURCE_WEBDAV (object),
@@ -274,6 +277,12 @@ source_webdav_set_property (GObject *object,
 				g_value_get_string (value));
 			return;
 
+		case PROP_SSL_TRUST:
+			e_source_webdav_set_ssl_trust (
+				E_SOURCE_WEBDAV (object),
+				g_value_get_string (value));
+			return;
+
 		case PROP_SOUP_URI:
 			e_source_webdav_set_soup_uri (
 				E_SOURCE_WEBDAV (object),
@@ -319,13 +328,6 @@ source_webdav_get_property (GObject *object,
 				E_SOURCE_WEBDAV (object)));
 			return;
 
-		case PROP_IGNORE_INVALID_CERT:
-			g_value_set_boolean (
-				value,
-				e_source_webdav_get_ignore_invalid_cert (
-				E_SOURCE_WEBDAV (object)));
-			return;
-
 		case PROP_RESOURCE_PATH:
 			g_value_take_string (
 				value,
@@ -340,6 +342,13 @@ source_webdav_get_property (GObject *object,
 				E_SOURCE_WEBDAV (object)));
 			return;
 
+		case PROP_SSL_TRUST:
+			g_value_take_string (
+				value,
+				e_source_webdav_dup_ssl_trust (
+				E_SOURCE_WEBDAV (object)));
+			return;
+
 		case PROP_SOUP_URI:
 			g_value_take_boxed (
 				value,
@@ -364,6 +373,7 @@ source_webdav_finalize (GObject *object)
 	g_free (priv->email_address);
 	g_free (priv->resource_path);
 	g_free (priv->resource_query);
+	g_free (priv->ssl_trust);
 
 	soup_uri_free (priv->soup_uri);
 
@@ -500,18 +510,6 @@ e_source_webdav_class_init (ESourceWebdavClass *class)
 
 	g_object_class_install_property (
 		object_class,
-		PROP_IGNORE_INVALID_CERT,
-		g_param_spec_boolean (
-			"ignore-invalid-cert",
-			"Ignore Invalid Cert",
-			"Ignore invalid SSL certificates",
-			FALSE,
-			G_PARAM_READWRITE |
-			G_PARAM_CONSTRUCT |
-			E_SOURCE_PARAM_SETTING));
-
-	g_object_class_install_property (
-		object_class,
 		PROP_RESOURCE_PATH,
 		g_param_spec_string (
 			"resource-path",
@@ -536,6 +534,18 @@ e_source_webdav_class_init (ESourceWebdavClass *class)
 
 	g_object_class_install_property (
 		object_class,
+		PROP_SSL_TRUST,
+		g_param_spec_string (
+			"ssl-trust",
+			"SSL Trust",
+			"SSL certificate trust setting, for invalid server certificates",
+			NULL,
+			G_PARAM_READWRITE |
+			G_PARAM_CONSTRUCT |
+			E_SOURCE_PARAM_SETTING));
+
+	g_object_class_install_property (
+		object_class,
 		PROP_SOUP_URI,
 		g_param_spec_boxed (
 			"soup-uri",
@@ -832,53 +842,6 @@ e_source_webdav_set_email_address (ESourceWebdav *extension,
 }
 
 /**
- * e_source_webdav_get_ignore_invalid_cert:
- * @extension: an #ESourceWebdav
- *
- * Returns %TRUE if invalid SSL certificates should be ignored.
- *
- * This option allows SSL certificates to be accepted even if they have
- * signed by an unrecognized Certificate Authority.
- *
- * Returns: whether invalid SSL certificates should be ignored
- *
- * Since: 3.6
- **/
-gboolean
-e_source_webdav_get_ignore_invalid_cert (ESourceWebdav *extension)
-{
-	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
-
-	return extension->priv->ignore_invalid_cert;
-}
-
-/**
- * e_source_webdav_set_ignore_invalid_cert:
- * @extension: an #ESourceWebdav
- * @ignore_invalid_cert: whether invalid SSL certificates should be ignored
- *
- * Sets whether invalid SSL certificates should be ignored.
- *
- * This option allows SSL certificates to be accepted even if they have
- * signed by an unrecognized Certificate Authority.
- *
- * Since: 3.6
- **/
-void
-e_source_webdav_set_ignore_invalid_cert (ESourceWebdav *extension,
-                                         gboolean ignore_invalid_cert)
-{
-	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
-
-	if (extension->priv->ignore_invalid_cert == ignore_invalid_cert)
-		return;
-
-	extension->priv->ignore_invalid_cert = ignore_invalid_cert;
-
-	g_object_notify (G_OBJECT (extension), "ignore-invalid-cert");
-}
-
-/**
  * e_source_webdav_get_resource_path:
  * @extension: an #ESourceWebdav
  *
@@ -1057,6 +1020,93 @@ e_source_webdav_set_resource_query (ESourceWebdav *extension,
 }
 
 /**
+ * e_source_webdav_get_ssl_trust:
+ * @extension: an #ESourceWebdav
+ *
+ * Returns an SSL certificate trust for the @extension.
+ * The value encodes three parameters, divided by a pipe '|',
+ * the first is users preference, can be one of "reject", "accept",
+ * "temporary-reject" and "temporary-accept". The second is a host
+ * name for which the trust was set. Finally the last is a SHA1
+ * hash of the certificate. This is not meant to be changed by a caller,
+ * it is supposed to be manipulated with e_source_webdav_prepare_ssl_trust_prompt()
+ * and e_source_webdav_store_ssl_trust_prompt().
+ *
+ * Returns: an SSL certificate trust for the @extension
+ *
+ * Since: 3.8
+ **/
+const gchar *
+e_source_webdav_get_ssl_trust (ESourceWebdav *extension)
+{
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	return extension->priv->ssl_trust;
+}
+
+/**
+ * e_source_webdav_dup_ssl_trust:
+ * @extension: an #ESourceWebdav
+ *
+ * Thread-safe variation of e_source_webdav_get_ssl_trust().
+ * Use this function when accessing @extension from multiple threads.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: the newly-allocated copy of #ESourceWebdav:ssl-trust
+ *
+ * Since: 3.8
+ **/
+gchar *
+e_source_webdav_dup_ssl_trust (ESourceWebdav *extension)
+{
+	const gchar *protected;
+	gchar *duplicate;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), NULL);
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	protected = e_source_webdav_get_ssl_trust (extension);
+	duplicate = g_strdup (protected);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	return duplicate;
+}
+
+/**
+ * e_source_webdav_set_ssl_trust:
+ * @extension: an #ESourceWebdav
+ * @ssl_trust: (allow-none): the ssl_trust to store, or %NULL to unset
+ *
+ * Sets the SSL certificate trust. See e_source_webdav_get_ssl_trust()
+ * for more infomation about its content and how to use it.
+ *
+ * Since: 3.8
+ **/
+void
+e_source_webdav_set_ssl_trust (ESourceWebdav *extension,
+			       const gchar *ssl_trust)
+{
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	g_mutex_lock (&extension->priv->property_lock);
+
+	if (g_strcmp0 (extension->priv->ssl_trust, ssl_trust) == 0) {
+		g_mutex_unlock (&extension->priv->property_lock);
+		return;
+	}
+
+	g_free (extension->priv->ssl_trust);
+	extension->priv->ssl_trust = g_strdup (ssl_trust);
+
+	g_mutex_unlock (&extension->priv->property_lock);
+
+	g_object_notify (G_OBJECT (extension), "ssl-trust");
+}
+
+/**
  * e_source_webdav_dup_soup_uri:
  * @extension: an #ESourceWebdav
  *
@@ -1123,3 +1173,402 @@ e_source_webdav_set_soup_uri (ESourceWebdav *extension,
 	g_object_thaw_notify (G_OBJECT (extension));
 }
 
+static gboolean
+decode_ssl_trust (ESourceWebdav *extension,
+		  ETrustPromptResponse *response,
+		  gchar **host,
+		  gchar **hash)
+{
+	gchar *ssl_trust, **strv;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
+
+	ssl_trust = e_source_webdav_dup_ssl_trust (extension);
+	if (!ssl_trust || !*ssl_trust) {
+		g_free (ssl_trust);
+		return FALSE;
+	}
+
+	strv = g_strsplit (ssl_trust, "|", 3);
+	if (!strv || !strv[0] || !strv[1] || !strv[2] || strv[3]) {
+		g_free (ssl_trust);
+		g_strfreev (strv);
+		return FALSE;
+	}
+
+	if (response) {
+		const gchar *resp = strv[0];
+
+		if (g_strcmp0 (resp, "reject") == 0)
+			*response = E_TRUST_PROMPT_RESPONSE_REJECT;
+		else if (g_strcmp0 (resp, "accept") == 0)
+			*response = E_TRUST_PROMPT_RESPONSE_ACCEPT;
+		else if (g_strcmp0 (resp, "temporary-reject") == 0)
+			*response = E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
+		else if (g_strcmp0 (resp, "temporary-accept") == 0)
+			*response = E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
+		else
+			*response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+	}
+
+	if (host)
+		*host = g_strdup (strv[1]);
+
+	if (hash)
+		*hash = g_strdup (strv[2]);
+
+	g_free (ssl_trust);
+	g_strfreev (strv);
+
+	return TRUE;
+}
+
+static gboolean
+encode_ssl_trust (ESourceWebdav *extension,
+		  ETrustPromptResponse response,
+		  const gchar *host,
+		  const gchar *hash)
+{
+	gchar *ssl_trust;
+	const gchar *resp;
+	gboolean changed;
+
+	g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), FALSE);
+
+	if (response == E_TRUST_PROMPT_RESPONSE_REJECT)
+		resp = "reject";
+	else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT)
+		resp = "accept";
+	else if (response == E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY)
+		resp = "temporary-reject";
+	else if (response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY)
+		resp = "temporary-accept";
+	else
+		resp = "temporary-reject";
+
+	if (host && *host && hash && *hash) {
+		ssl_trust = g_strconcat (resp, "|", host, "|", hash, NULL);
+	} else {
+		ssl_trust = NULL;
+	}
+
+	changed = g_strcmp0 (extension->priv->ssl_trust, ssl_trust) != 0;
+
+	if (changed)
+		e_source_webdav_set_ssl_trust (extension, ssl_trust);
+
+	g_free (ssl_trust);
+
+	return changed;
+}
+
+/**
+ * e_source_webdav_prepare_ssl_trust_prompt:
+ * @extension: an #ESourceWebdav
+ * @message: a #SoupMessage with #SOUP_STATUS_SSL_FAILED status code
+ * @registry: an #ESourceRegistry, to use for parent lookups
+ * @parameters: an #ENamedParameters to be populated
+ *
+ * Checks @messages<!-- -->'s certificate against currently stored trust
+ * response and either returns what to do immediately, or returns
+ * #E_TRUST_PROMPT_RESPONSE_UNKNOWN and populates @parameters with necessary
+ * values for a trust prompt.
+ *
+ * Returns: What to do with SSL connection, where #E_TRUST_PROMPT_RESPONSE_UNKNOWN
+ *   means 'ask a user, with populated parameters'.
+ *
+ * Note: The #E_TRUST_PROMPT_RESPONSE_REJECT is returned on any errors, like
+ *  the @message not being with the #SOUP_STATUS_SSL_FAILED status code,
+ *  no certificate being stored in the @message and so on.
+ *
+ * Since: 3.8
+ **/
+ETrustPromptResponse
+e_source_webdav_prepare_ssl_trust_prompt (ESourceWebdav *extension,
+					  SoupMessage *message,
+					  ESourceRegistry *registry,
+					  ENamedParameters *parameters)
+{
+	ETrustPromptResponse response;
+	ESource *source;
+	GTlsCertificate *cert = NULL;
+	GTlsCertificateFlags cert_errors = 0;
+	GByteArray *bytes = NULL;
+	SoupURI *soup_uri;
+	const gchar *host;
+	gchar *base64, *old_host = NULL, *old_hash = NULL, *cert_errs_str, *markup = NULL;
+	gint issuer_count;
+
+	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);
+
+	if (message->status_code != SOUP_STATUS_SSL_FAILED)
+		return E_TRUST_PROMPT_RESPONSE_REJECT;
+
+	if (!soup_message_get_https_status (message, &cert, &cert_errors) || !cert) {
+		/* before libsoup 2.41.3 the certificate was not set on failed requests,
+		   thus workaround this and do a simple prompt later
+		*/
+		#ifdef SOUP_CHECK_VERSION
+		#if SOUP_CHECK_VERSION(2, 41, 3)
+		return E_TRUST_PROMPT_RESPONSE_REJECT;
+		#endif
+		#endif
+	}
+
+	soup_uri = soup_message_get_uri (message);
+	if (!soup_uri || !soup_uri_get_host (soup_uri))
+		return E_TRUST_PROMPT_RESPONSE_REJECT;
+
+	#ifdef SOUP_CHECK_VERSION
+	#if SOUP_CHECK_VERSION(2, 41, 3)
+	g_return_val_if_fail (cert != NULL, E_TRUST_PROMPT_RESPONSE_REJECT);
+	g_object_get (cert, "certificate", &bytes, NULL);
+	#else
+	bytes = g_byte_array_new ();
+	g_byte_array_append (bytes, (guint8 *) "1", 1);
+	#endif
+	#else
+	bytes = g_byte_array_new ();
+	g_byte_array_append (bytes, (guint8 *) "1", 1);
+	#endif
+
+	if (!bytes)
+		return E_TRUST_PROMPT_RESPONSE_REJECT;
+
+	host = soup_uri_get_host (soup_uri);
+
+	if (decode_ssl_trust (extension, &response, &old_host, &old_hash)) {
+		gchar *hash = g_compute_checksum_for_data (G_CHECKSUM_SHA1, bytes->data, bytes->len);
+
+		if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN &&
+		    g_strcmp0 (old_host, host) == 0 &&
+		    g_strcmp0 (old_hash, hash) == 0) {
+			g_free (old_host);
+			g_free (old_hash);
+			g_free (hash);
+
+			return response;
+		}
+
+		g_free (old_host);
+		g_free (old_hash);
+		g_free (hash);
+	}
+
+	source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
+	if (source) {
+		gchar *bhost = g_strconcat ("<b>", host, "</b>", NULL);
+		gchar *bname = NULL;
+
+		if (e_source_get_parent (source)) {
+			ESource *parent = NULL;
+
+			parent = e_source_registry_ref_source (registry, e_source_get_parent (source));
+
+			if (parent) {
+				bname = g_strconcat ("<b>", e_source_get_display_name (parent), ": ",
+					e_source_get_display_name (source), "</b>", NULL);
+				g_object_unref (parent);
+			}
+		}
+
+		if (!bname)
+			bname = g_strconcat ("<b>", e_source_get_display_name (source), "</b>", NULL);
+
+		if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+			/* Translators: The first %s is replaced with a host name, like "www.example.com";
+			   the second %s is replaced with actual source name, like "On The Web: My Work"
+			*/
+			markup = g_strdup_printf (_("SSL certificate for host '%s', used by address book '%s', is not trusted. Do you wish to accept it?"),
+				bhost, bname);
+		} else if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+			/* Translators: The first %s is replaced with a host name, like "www.example.com";
+			   the second %s is replaced with actual source name, like "On The Web: My Work"
+			*/
+			markup = g_strdup_printf (_("SSL certificate for host '%s', used by calendar '%s', is not trusted. Do you wish to accept it?"),
+				bhost, bname);
+		} else if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+			/* Translators: The first %s is replaced with a host name, like "www.example.com";
+			   the second %s is replaced with actual source name, like "On The Web: My Work"
+			*/
+			markup = g_strdup_printf (_("SSL certificate for host '%s', used by memo list '%s', is not trusted. Do you wish to accept it?"),
+				bhost, bname);
+		} else if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+			/* Translators: The first %s is replaced with a host name, like "www.example.com";
+			   the second %s is replaced with actual source name, like "On The Web: My Work"
+			*/
+			markup = g_strdup_printf (_("SSL certificate for host '%s', used by task list '%s', is not trusted. Do you wish to accept it?"),
+				bhost, bname);
+		}
+
+		g_object_unref (source);
+		g_free (bname);
+		g_free (bhost);
+	}
+
+	base64 = g_base64_encode (bytes->data, bytes->len);
+	cert_errs_str = g_strdup_printf ("%x", cert_errors);
+
+	e_named_parameters_set (parameters, "host", host);
+	e_named_parameters_set (parameters, "markup", markup);
+	e_named_parameters_set (parameters, "certificate", base64);
+	e_named_parameters_set (parameters, "certificate-errors", cert_errs_str);
+
+	g_byte_array_unref (bytes);
+	g_free (cert_errs_str);
+	g_free (markup);
+
+	issuer_count = 0;
+	while (cert) {
+		GTlsCertificate *issuer = NULL;
+		g_object_get (cert, "issuer", &issuer, NULL);
+
+		cert = issuer;
+
+		if (cert) {
+			bytes = NULL;
+			g_object_get (cert, "certificate", &bytes, NULL);
+
+			if (bytes) {
+				base64 = g_base64_encode (bytes->data, bytes->len);
+				if (issuer_count == 0) {
+					e_named_parameters_set (parameters, "issuer", base64);
+				} else {
+					gchar *name;
+
+					name = g_strdup_printf ("issuer-%d", issuer_count);
+					e_named_parameters_set (parameters, name, base64);
+					g_free (name);
+				}
+
+				g_free (base64);
+				g_byte_array_unref (bytes);
+			} else {
+				break;
+			}
+		}
+
+		issuer_count++;
+	}
+
+	return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+}
+
+static void
+webdav_extension_changes_written_cb (GObject *source_object,
+				     GAsyncResult *result,
+				     gpointer user_data)
+{
+	GError *error = NULL;
+
+	e_source_write_finish (E_SOURCE (source_object), result, &error);
+
+	if (error) {
+		g_message ("%s: Failed with error: %s", G_STRFUNC, error->message);
+		g_clear_error (&error);
+	}
+}
+
+/**
+ * e_source_webdav_store_ssl_trust_prompt:
+ * @extension: an #ESourceWebdav
+ * @message: a #SoupMessage with #SOUP_STATUS_SSL_FAILED status code
+ * @response: user's response from a trust prompt
+ *
+ * Stores user's response from a trust prompt, thus it is re-used the next
+ * time it'll be needed. An #E_TRUST_PROMPT_RESPONSE_UNKNOWN is treated as
+ * a temporary reject, which means the user will be asked again.
+ *
+ * Since: 3.8
+ **/
+void
+e_source_webdav_store_ssl_trust_prompt (ESourceWebdav *extension,
+					SoupMessage *message,
+					ETrustPromptResponse response)
+{
+	GTlsCertificate *cert = NULL;
+	GByteArray *bytes = NULL;
+	SoupURI *soup_uri;
+	const gchar *host;
+	gchar *hash;
+	gboolean changed;
+
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+	g_return_if_fail (SOUP_IS_MESSAGE (message));
+
+	if (message->status_code != SOUP_STATUS_SSL_FAILED)
+		return;
+
+	if (!soup_message_get_https_status (message, &cert, NULL) || !cert) {
+		/* before libsoup 2.41.3 the certificate was not set on failed requests,
+		   thus workaround this and store only simple value
+		*/
+		#ifdef SOUP_CHECK_VERSION
+		#if SOUP_CHECK_VERSION(2, 41, 3)
+		return;
+		#endif
+		#endif
+	}
+
+	soup_uri = soup_message_get_uri (message);
+	if (!soup_uri || !soup_uri_get_host (soup_uri))
+		return;
+
+	#ifdef SOUP_CHECK_VERSION
+	#if SOUP_CHECK_VERSION(2, 41, 3)
+	g_return_if_fail (cert != NULL);
+	g_object_get (cert, "certificate", &bytes, NULL);
+	#else
+	bytes = g_byte_array_new ();
+	g_byte_array_append (bytes, (guint8 *) "1", 1);
+	#endif
+	#else
+	bytes = g_byte_array_new ();
+	g_byte_array_append (bytes, (guint8 *) "1", 1);
+	#endif
+
+	if (!bytes)
+		return;
+
+	host = soup_uri_get_host (soup_uri);
+	hash = g_compute_checksum_for_data (G_CHECKSUM_SHA1, bytes->data, bytes->len);
+
+	changed = encode_ssl_trust (extension, response, host, hash);
+
+	g_byte_array_unref (bytes);
+	g_free (hash);
+
+	if (changed) {
+		ESource *source;
+
+		source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
+		e_source_write (source, NULL, webdav_extension_changes_written_cb, NULL);
+		g_object_unref (source);
+	}
+}
+
+/**
+ * e_source_webdav_unset_temporary_ssl_trust:
+ * @extension: an #ESourceWebdav
+ *
+ * Unsets temporary trust set on this @extension, but keeps
+ * it as is for other values.
+ *
+ * Since: 3.8
+ **/
+void
+e_source_webdav_unset_temporary_ssl_trust (ESourceWebdav *extension)
+{
+	ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+
+	g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+
+	if (!decode_ssl_trust (extension, &response, NULL, NULL) ||
+	    response == E_TRUST_PROMPT_RESPONSE_UNKNOWN ||
+	    response == E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY ||
+	    response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY)
+		e_source_webdav_set_ssl_trust (extension, NULL);
+}
diff --git a/libedataserver/e-source-webdav.h b/libedataserver/e-source-webdav.h
index 797d238..7209160 100644
--- a/libedataserver/e-source-webdav.h
+++ b/libedataserver/e-source-webdav.h
@@ -57,6 +57,10 @@
 
 G_BEGIN_DECLS
 
+/* forward declaration */
+struct _ENamedParameters;
+struct _ESourceRegistry;
+
 typedef struct _ESourceWebdav ESourceWebdav;
 typedef struct _ESourceWebdavClass ESourceWebdavClass;
 typedef struct _ESourceWebdavPrivate ESourceWebdavPrivate;
@@ -78,6 +82,14 @@ struct _ESourceWebdavClass {
 	ESourceExtensionClass parent_class;
 };
 
+typedef enum {
+	E_TRUST_PROMPT_RESPONSE_UNKNOWN			= -1,
+	E_TRUST_PROMPT_RESPONSE_REJECT			=  0,
+	E_TRUST_PROMPT_RESPONSE_ACCEPT			=  1,
+	E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY	=  2,
+	E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY	=  3
+} ETrustPromptResponse;
+
 GType		e_source_webdav_get_type	(void) G_GNUC_CONST;
 gboolean	e_source_webdav_get_avoid_ifmatch
 						(ESourceWebdav *extension);
@@ -103,11 +115,6 @@ gchar *		e_source_webdav_dup_email_address
 void		e_source_webdav_set_email_address
 						(ESourceWebdav *extension,
 						 const gchar *email_address);
-gboolean	e_source_webdav_get_ignore_invalid_cert
-						(ESourceWebdav *extension);
-void		e_source_webdav_set_ignore_invalid_cert
-						(ESourceWebdav *extension,
-						 gboolean ignore_invalid_cert);
 const gchar *	e_source_webdav_get_resource_path
 						(ESourceWebdav *extension);
 gchar *		e_source_webdav_dup_resource_path
@@ -122,9 +129,25 @@ gchar *		e_source_webdav_dup_resource_query
 void		e_source_webdav_set_resource_query
 						(ESourceWebdav *extension,
 						 const gchar *resource_query);
+const gchar *	e_source_webdav_get_ssl_trust	(ESourceWebdav *extension);
+gchar *		e_source_webdav_dup_ssl_trust	(ESourceWebdav *extension);
+void		e_source_webdav_set_ssl_trust	(ESourceWebdav *extension,
+						 const gchar *ssl_trust);
 SoupURI *	e_source_webdav_dup_soup_uri	(ESourceWebdav *extension);
 void		e_source_webdav_set_soup_uri	(ESourceWebdav *extension,
 						 SoupURI *soup_uri);
+ETrustPromptResponse
+		e_source_webdav_prepare_ssl_trust_prompt
+						(ESourceWebdav *extension,
+						 SoupMessage *message,
+						 struct _ESourceRegistry *registry,
+						 struct _ENamedParameters *parameters);
+void		e_source_webdav_store_ssl_trust_prompt
+						(ESourceWebdav *extension,
+						 SoupMessage *message,
+						 ETrustPromptResponse response);
+void		e_source_webdav_unset_temporary_ssl_trust
+						(ESourceWebdav *extension);
 
 G_END_DECLS
 
diff --git a/modules/trust-prompt/module-trust-prompt.c b/modules/trust-prompt/module-trust-prompt.c
index d8db7d8..7e2f363 100644
--- a/modules/trust-prompt/module-trust-prompt.c
+++ b/modules/trust-prompt/module-trust-prompt.c
@@ -140,6 +140,7 @@ e_module_unload (GTypeModule *type_module)
 /* ETrustPrompt::trust-prompt
    The dialog expects these parameters:
       "host" - host from which the certificate is received
+      "markup" - markup for the trust prompt, if not set, then "SSL certificate for '<b>host</b>' is not trusted. Do you wish to accept it?" is used
       "certificate" - a base64-encoded DER certificate, for which ask on trust
       "certificate-errors" - a hexa-decimal integer (as string) corresponding to GTlsCertificateFlags
 
@@ -287,7 +288,7 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 				gint prompt_id,
 				const ENamedParameters *parameters)
 {
-	const gchar *host, *base64_cert, *cert_errs_str;
+	const gchar *host, *markup, *base64_cert, *cert_errs_str;
 	gchar *fingerprint, *reason;
 	gint64 cert_errs;
 	CERTCertDBHandle *certdb;
@@ -301,6 +302,7 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 	g_return_val_if_fail (parameters != NULL, FALSE);
 
 	host = e_named_parameters_get (parameters, "host");
+	markup = e_named_parameters_get (parameters, "markup");
 	base64_cert = e_named_parameters_get (parameters, "certificate");
 	cert_errs_str = e_named_parameters_get (parameters, "certificate-errors");
 
@@ -323,7 +325,7 @@ trust_prompt_show_trust_prompt (EUserPrompterServerExtension *extension,
 	reason = cert_errors_to_reason (cert_errs);
 	fingerprint = cert_fingerprint (cert);
 
-	success = trust_prompt_show (extension, prompt_id, host, cert, fingerprint, reason, issuers);
+	success = trust_prompt_show (extension, prompt_id, host, markup, cert, fingerprint, reason, issuers);
 
 	trust_prompt_free_certificate (cert);
 	g_slist_free_full (issuers, trust_prompt_free_certificate);
diff --git a/modules/trust-prompt/trust-prompt-gtk.c b/modules/trust-prompt/trust-prompt-gtk.c
index 2c94f11..d05194d 100644
--- a/modules/trust-prompt/trust-prompt-gtk.c
+++ b/modules/trust-prompt/trust-prompt-gtk.c
@@ -136,6 +136,7 @@ gboolean
 trust_prompt_show (EUserPrompterServerExtension *extension,
 		   gint prompt_id,
 		   const gchar *host,
+		   const gchar *markup,
 		   const CERTCertificate *pcert,
 		   const gchar *cert_fingerprint,
 		   const gchar *reason,
@@ -143,7 +144,7 @@ trust_prompt_show (EUserPrompterServerExtension *extension,
 {
 	GtkWidget *dialog, *widget;
 	GtkGrid *grid;
-	gchar *tmp, *info, *issuer, *subject;
+	gchar *tmp, *issuer, *subject, *head;
 	GSList *issuers, *iter;
 	CERTCertificate *cert;
 	gint row = 0;
@@ -189,13 +190,23 @@ trust_prompt_show (EUserPrompterServerExtension *extension,
 		NULL);
 	gtk_grid_attach (grid, widget, 0, row, 1, 3);
 
-	info = g_strconcat ("<b>", host, "</b>", NULL);
-	tmp = g_strdup_printf (_("SSL Certificate for '%s' is not trusted. Do you wish to accept it?\n\n"
-				    "Detailed information about the certificate:"), info);
-	g_free (info);
+	tmp = NULL;
+	if (!markup || !*markup) {
+		gchar *bhost;
+
+		bhost = g_strconcat ("<b>", host, "</b>", NULL);
+		tmp = g_strdup_printf (_("SSL certificate for '%s' is not trusted. Do you wish to accept it?"), bhost);
+		g_free (bhost);
+
+		markup = tmp;
+	}
+
+	head = g_strdup_printf ("%s\n\n%s", markup, _("Detailed information about the certificate:"));
+
 	widget = gtk_label_new (NULL);
-	gtk_label_set_markup (GTK_LABEL (widget), tmp);
+	gtk_label_set_markup (GTK_LABEL (widget), head);
 	gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+	g_free (head);
 	g_free (tmp);
 
 	gtk_grid_attach (grid, widget, 1, row, 2, 1);
diff --git a/modules/trust-prompt/trust-prompt.h b/modules/trust-prompt/trust-prompt.h
index 9966ad6..cbcae58 100644
--- a/modules/trust-prompt/trust-prompt.h
+++ b/modules/trust-prompt/trust-prompt.h
@@ -36,6 +36,7 @@ gboolean
 trust_prompt_show (EUserPrompterServerExtension *extension,
 		   gint prompt_id,
 		   const gchar *host,
+		   const gchar *markup,
 		   const CERTCertificate *pcert,
 		   const gchar *cert_fingerprint,
 		   const gchar *reason,
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 938c944..2d5a2c4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -201,6 +201,7 @@ libedataserver/e-source-authenticator.c
 libedataserver/e-source.c
 libedataserver/e-source-mail-signature.c
 libedataserver/e-source-registry.c
+libedataserver/e-source-webdav.c
 libedataserver/e-time-utils.c
 libedataserver/org.gnome.evolution.shell.network-config.gschema.xml.in
 modules/gnome-online-accounts/goaewsclient.c
diff --git a/services/evolution-user-prompter/prompt-user-gtk.c b/services/evolution-user-prompter/prompt-user-gtk.c
index c493410..257a73b 100644
--- a/services/evolution-user-prompter/prompt-user-gtk.c
+++ b/services/evolution-user-prompter/prompt-user-gtk.c
@@ -80,10 +80,10 @@ prompt_user_show (EUserPrompterServer *server,
 			ntype = GTK_MESSAGE_ERROR;
 	}
 
-	if (use_markup)
-		message = gtk_message_dialog_new_with_markup (NULL, 0, ntype, GTK_BUTTONS_NONE,
-			"%s", primary_text ? primary_text : "");
-	else
+	if (use_markup) {
+		message = gtk_message_dialog_new_with_markup (NULL, 0, ntype, GTK_BUTTONS_NONE, "%s", "");
+		gtk_message_dialog_set_markup (GTK_MESSAGE_DIALOG (message), primary_text ? primary_text : "");
+	} else
 		message = gtk_message_dialog_new (NULL, 0, ntype, GTK_BUTTONS_NONE,
 			"%s", primary_text ? primary_text : "");
 



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