[evolution-data-server] Bug #690177 - Use trust-prompt for certificate verification in WebDAV backends
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug #690177 - Use trust-prompt for certificate verification in WebDAV backends
- Date: Wed, 9 Jan 2013 22:04:56 +0000 (UTC)
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]