[evolution-data-server] Move authentication of backends back to the client
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Move authentication of backends back to the client
- Date: Mon, 2 Feb 2015 13:46:43 +0000 (UTC)
commit 884fb8d872787d9c9e8132d4cfca47f275d9da3e
Author: Milan Crha <mcrha redhat com>
Date: Mon Feb 2 14:44:05 2015 +0100
Move authentication of backends back to the client
Since this change the client is responsible to provide credentials
to use to authenticate backends (through ESource-s, to be more precise),
unless the credentials are already saved.
Makefile.am | 5 +
addressbook/backends/file/e-book-backend-file.c | 3 +
.../backends/google/e-book-backend-google.c | 163 ++-
addressbook/backends/ldap/e-book-backend-ldap.c | 120 +-
.../backends/webdav/e-book-backend-webdav.c | 180 ++-
addressbook/libebook/e-book-client.c | 101 ++-
addressbook/libebook/e-book-client.h | 4 +
addressbook/libedata-book/e-book-backend.c | 19 -
addressbook/libedata-book/e-data-book.c | 67 +-
calendar/backends/caldav/e-cal-backend-caldav.c | 253 ++-
.../backends/contacts/e-cal-backend-contacts.c | 6 +-
calendar/backends/file/e-cal-backend-file.c | 4 +
calendar/backends/gtasks/e-cal-backend-gtasks.c | 8 +-
calendar/backends/http/e-cal-backend-http.c | 214 ++-
calendar/libecal/e-cal-client.c | 93 +
calendar/libecal/e-cal-client.h | 2 +
calendar/libedata-cal/e-cal-backend.c | 19 -
calendar/libedata-cal/e-data-cal.c | 66 +-
configure.ac | 36 +-
examples/cursor/cursor-data.c | 2 +-
libebackend/Makefile.am | 12 +-
libebackend/e-authentication-mediator.c | 1290 --------------
libebackend/e-authentication-mediator.h | 115 --
libebackend/e-authentication-session.c | 1794 --------------------
libebackend/e-authentication-session.h | 232 ---
libebackend/e-backend-factory.c | 3 +-
libebackend/e-backend-factory.h | 2 +-
libebackend/e-backend.c | 554 +++++--
libebackend/e-backend.h | 49 +-
libebackend/e-collection-backend.c | 111 +-
libebackend/e-collection-backend.h | 3 +
libebackend/e-data-factory.c | 3 +-
libebackend/e-dbus-server.c | 2 -
.../e-server-side-source-credentials-provider.c | 92 +
.../e-server-side-source-credentials-provider.h | 79 +
libebackend/e-server-side-source.c | 571 +++++--
libebackend/e-server-side-source.h | 12 -
libebackend/e-soup-ssl-trust.c | 104 +-
libebackend/e-soup-ssl-trust.h | 4 +-
libebackend/e-source-registry-server.c | 756 +--------
libebackend/e-source-registry-server.h | 24 +-
libebackend/e-subprocess-factory.c | 4 +-
libebackend/libebackend.h | 5 -
libedataserver/Makefile.am | 15 +-
libedataserver/e-client.c | 290 ++++
libedataserver/e-client.h | 30 +
libedataserver/e-data-server-util.c | 99 ++-
libedataserver/e-data-server-util.h | 23 +
{libebackend => libedataserver}/e-extensible.c | 15 +-
{libebackend => libedataserver}/e-extensible.h | 4 +-
{libebackend => libedataserver}/e-extension.c | 8 +-
{libebackend => libedataserver}/e-extension.h | 6 +-
{libebackend => libedataserver}/e-module.c | 55 +-
{libebackend => libedataserver}/e-module.h | 21 +-
libedataserver/e-source-authentication.c | 121 ++-
libedataserver/e-source-authentication.h | 7 +
libedataserver/e-source-authenticator.c | 528 ------
libedataserver/e-source-authenticator.h | 115 --
.../e-source-credentials-provider-impl-password.c | 144 ++
.../e-source-credentials-provider-impl-password.h | 75 +
.../e-source-credentials-provider-impl.c | 319 ++++
.../e-source-credentials-provider-impl.h | 128 ++
libedataserver/e-source-credentials-provider.c | 1030 +++++++++++
libedataserver/e-source-credentials-provider.h | 150 ++
libedataserver/e-source-enums.h | 72 +-
libedataserver/e-source-registry.c | 674 ++------
libedataserver/e-source-registry.h | 23 +-
libedataserver/e-source-webdav.c | 385 +----
libedataserver/e-source-webdav.h | 30 +-
libedataserver/e-source.c | 1003 +++++++++++-
libedataserver/e-source.h | 129 ++-
libedataserver/libedataserver-private.h | 11 +-
libedataserver/libedataserver.h | 6 +
libedataserver/libedataserver.pc.in | 3 +
libedataserverui/Makefile.am | 77 +
.../e-credentials-prompter-impl-password.c | 561 ++++++
.../e-credentials-prompter-impl-password.h | 78 +
libedataserverui/e-credentials-prompter-impl.c | 232 +++
libedataserverui/e-credentials-prompter-impl.h | 115 ++
libedataserverui/e-credentials-prompter.c | 1763 +++++++++++++++++++
libedataserverui/e-credentials-prompter.h | 189 ++
libedataserverui/e-trust-prompt.c | 672 ++++++++
libedataserverui/e-trust-prompt.h | 59 +
libedataserverui/libedataserverui.h | 30 +
libedataserverui/libedataserverui.pc.in | 18 +
modules/gnome-online-accounts/Makefile.am | 36 +-
.../gnome-online-accounts/e-goa-password-based.c | 193 +--
.../gnome-online-accounts/e-goa-password-based.h | 6 +-
.../gnome-online-accounts/module-credentials-goa.c | 37 +
.../module-gnome-online-accounts.c | 9 -
modules/owncloud-backend/module-owncloud-backend.c | 53 +-
modules/owncloud-backend/owncloud-utils.c | 192 +--
modules/owncloud-backend/owncloud-utils.h | 7 +-
modules/ubuntu-online-accounts/Makefile.am | 38 +-
.../e-signon-session-password.c | 375 ++---
.../e-signon-session-password.h | 6 +-
.../module-credentials-uoa.c | 37 +
.../module-ubuntu-online-accounts.c | 7 -
po/POTFILES.in | 8 +-
.../org.gnome.evolution.dataserver.AddressBook.xml | 4 +
.../org.gnome.evolution.dataserver.Calendar.xml | 4 +
private/org.gnome.evolution.dataserver.Source.xml | 99 +-
...rg.gnome.evolution.dataserver.SourceManager.xml | 34 -
tests/book-migration/setup-migration-test.c | 2 +-
tests/libebook/client/test-book-client-self.c | 2 +-
.../client/test-book-client-view-operations.c | 8 +-
tests/test-server-utils/e-test-server-utils.c | 12 +-
107 files changed, 10348 insertions(+), 7320 deletions(-)
---
diff --git a/Makefile.am b/Makefile.am
index 6f219eb..26495b7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -9,12 +9,17 @@ DISTCHECK_CONFIGURE_FLAGS = \
--disable-examples \
$(NULL)
+if HAVE_GTK
+EDSUI_SUBDIR=libedataserverui
+endif
+
SUBDIRS = \
camel \
data \
private \
libedataserver \
libebackend \
+ $(EDSUI_SUBDIR) \
addressbook \
calendar \
modules \
diff --git a/addressbook/backends/file/e-book-backend-file.c b/addressbook/backends/file/e-book-backend-file.c
index 8f8f085..b0dbe79 100644
--- a/addressbook/backends/file/e-book-backend-file.c
+++ b/addressbook/backends/file/e-book-backend-file.c
@@ -1140,6 +1140,9 @@ book_backend_file_open_sync (EBookBackend *backend,
source = e_backend_get_source (E_BACKEND (backend));
+ /* Local source is always connected. */
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
g_type_ensure (E_TYPE_SOURCE_REVISION_GUARDS);
guards = e_source_get_extension (source, E_SOURCE_EXTENSION_REVISION_GUARDS);
diff --git a/addressbook/backends/google/e-book-backend-google.c
b/addressbook/backends/google/e-book-backend-google.c
index 19d1ac7..7f4d29b 100644
--- a/addressbook/backends/google/e-book-backend-google.c
+++ b/addressbook/backends/google/e-book-backend-google.c
@@ -44,17 +44,7 @@
#define GDATA_CHECK_VERSION(major,minor,micro) 0
#endif
-/* Forward Declarations */
-static void e_book_backend_google_source_authenticator_init
- (ESourceAuthenticatorInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
- EBookBackendGoogle,
- e_book_backend_google,
- E_TYPE_BOOK_BACKEND,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- e_book_backend_google_source_authenticator_init))
+G_DEFINE_TYPE (EBookBackendGoogle, e_book_backend_google, E_TYPE_BOOK_BACKEND)
struct _EBookBackendGooglePrivate {
EBookBackendCache *cache;
@@ -1209,9 +1199,9 @@ fallback_set_proxy_uri (EBookBackend *backend)
#endif
static gboolean
-request_authorization (EBookBackend *backend,
- GCancellable *cancellable,
- GError **error)
+connect_without_password (EBookBackend *backend,
+ GCancellable *cancellable,
+ GError **error)
{
EBookBackendGooglePrivate *priv;
@@ -1277,10 +1267,7 @@ request_authorization (EBookBackend *backend,
return TRUE;
/* Otherwise it's up to us to obtain a login secret. */
- return e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, error);
+ return FALSE;
}
typedef enum {
@@ -1421,6 +1408,7 @@ e_book_backend_google_notify_online_cb (EBookBackend *backend,
GParamSpec *pspec)
{
EBookBackendGooglePrivate *priv;
+ ESource *source;
gboolean is_online;
priv = E_BOOK_BACKEND_GOOGLE_GET_PRIVATE (backend);
@@ -1428,11 +1416,22 @@ e_book_backend_google_notify_online_cb (EBookBackend *backend,
g_debug (G_STRFUNC);
is_online = e_backend_get_online (E_BACKEND (backend));
+ source = e_backend_get_source (E_BACKEND (backend));
if (is_online && e_book_backend_is_opened (backend)) {
- request_authorization (backend, NULL, NULL);
- if (backend_is_authorized (backend))
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
+ if (connect_without_password (backend, NULL, NULL)) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
e_book_backend_set_writable (backend, TRUE);
+ cache_refresh_if_needed (backend);
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ e_backend_schedule_credentials_required (E_BACKEND (backend),
E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ NULL, 0, NULL, NULL, G_STRFUNC);
+ }
} else {
/* Going offline, so cancel all running operations */
google_cancel_all_operations (backend);
@@ -1443,6 +1442,9 @@ e_book_backend_google_notify_online_cb (EBookBackend *backend,
* as writeable again once the user's authenticated again. */
e_book_backend_set_writable (backend, FALSE);
+ if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_DISCONNECTED)
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
/* We can free our service. */
g_clear_object (&priv->service);
}
@@ -1711,19 +1713,52 @@ book_backend_google_open_sync (EBookBackend *backend,
e_book_backend_set_writable (backend, FALSE);
if (is_online) {
- success = request_authorization (backend, cancellable, error);
+ ESource *source = e_backend_get_source (E_BACKEND (backend));
+
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
+ success = connect_without_password (backend, cancellable, error);
if (success) {
+ GError *local_error = NULL;
+
/* Refresh the authorizer. This may block. */
success = gdata_authorizer_refresh_authorization (
- priv->authorizer, cancellable, error);
+ priv->authorizer, cancellable, &local_error);
+
+ if (success) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+ } else {
+ GError *local_error2 = NULL;
+
+ e_source_set_connection_status (source,
E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ if (local_error && !e_backend_credentials_required_sync (E_BACKEND (backend),
E_SOURCE_CREDENTIALS_REASON_ERROR,
+ NULL, 0, local_error, cancellable, &local_error2)) {
+ g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
+ }
+
+ g_clear_error (&local_error2);
+
+ if (local_error)
+ g_propagate_error (error, local_error);
+ }
+ } else {
+ GError *local_error = NULL;
+
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ if (!e_backend_credentials_required_sync (E_BACKEND (backend),
E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ NULL, 0, NULL, cancellable, &local_error)) {
+ g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error ? local_error->message : "Unknown error");
+ }
+
+ g_clear_error (&local_error);
}
}
- if (!is_online || backend_is_authorized (backend)) {
- if (is_online) {
- e_book_backend_set_writable (backend, TRUE);
- cache_refresh_if_needed (backend);
- }
+ if (is_online && backend_is_authorized (backend)) {
+ e_book_backend_set_writable (backend, TRUE);
+ cache_refresh_if_needed (backend);
}
return success;
@@ -2281,16 +2316,18 @@ book_backend_google_refresh_sync (EBookBackend *backend,
}
static ESourceAuthenticationResult
-book_backend_google_try_password_sync (ESourceAuthenticator *authenticator,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
+book_backend_google_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
EBookBackendGooglePrivate *priv;
ESourceAuthentication *auth_extension;
ESourceAuthenticationResult result;
ESource *source;
- const gchar *extension_name;
+ const gchar *username;
gchar *user;
GError *local_error = NULL;
@@ -2298,24 +2335,34 @@ book_backend_google_try_password_sync (ESourceAuthenticator *authenticator,
/* We should not have gotten here if we're offline. */
g_return_val_if_fail (
- e_backend_get_online (E_BACKEND (authenticator)),
+ e_backend_get_online (E_BACKEND (backend)),
E_SOURCE_AUTHENTICATION_ERROR);
/* Nor should we have gotten here if we're already authorized. */
g_return_val_if_fail (
- !backend_is_authorized (E_BOOK_BACKEND (authenticator)),
+ !backend_is_authorized (E_BOOK_BACKEND (backend)),
E_SOURCE_AUTHENTICATION_ERROR);
- priv = E_BOOK_BACKEND_GOOGLE (authenticator)->priv;
+ priv = E_BOOK_BACKEND_GOOGLE (backend)->priv;
- source = e_backend_get_source (E_BACKEND (authenticator));
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
+ source = e_backend_get_source (backend);
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
user = e_source_authentication_dup_user (auth_extension);
- gdata_client_login_authorizer_authenticate (
- GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
- user, password->str, cancellable, &local_error);
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (!username || !*username)
+ username = user;
+
+ if (gdata_client_login_authorizer_authenticate (GDATA_CLIENT_LOGIN_AUTHORIZER (priv->authorizer),
+ user, e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD), cancellable,
&local_error)) {
+ EBookBackend *book_backend = E_BOOK_BACKEND (backend);
+
+ if (gdata_authorizer_refresh_authorization (priv->authorizer, cancellable, &local_error) &&
+ backend_is_authorized (book_backend)) {
+ e_book_backend_set_writable (book_backend, TRUE);
+ cache_refresh_if_needed (book_backend);
+ }
+ }
g_free (user);
@@ -2341,7 +2388,8 @@ static void
e_book_backend_google_class_init (EBookBackendGoogleClass *class)
{
GObjectClass *object_class;
- EBookBackendClass *backend_class;
+ EBackendClass *backend_class;
+ EBookBackendClass *book_backend_class;
g_type_class_add_private (class, sizeof (EBookBackendGooglePrivate));
@@ -2349,23 +2397,20 @@ e_book_backend_google_class_init (EBookBackendGoogleClass *class)
object_class->dispose = book_backend_google_dispose;
object_class->finalize = book_backend_google_finalize;
- backend_class = E_BOOK_BACKEND_CLASS (class);
- backend_class->get_backend_property = book_backend_google_get_backend_property;
- backend_class->open_sync = book_backend_google_open_sync;
- backend_class->create_contacts_sync = book_backend_google_create_contacts_sync;
- backend_class->modify_contacts_sync = book_backend_google_modify_contacts_sync;
- backend_class->remove_contacts_sync = book_backend_google_remove_contacts_sync;
- backend_class->get_contact_sync = book_backend_google_get_contact_sync;
- backend_class->get_contact_list_sync = book_backend_google_get_contact_list_sync;
- backend_class->start_view = book_backend_google_start_view;
- backend_class->stop_view = book_backend_google_stop_view;
- backend_class->refresh_sync = book_backend_google_refresh_sync;
-}
-
-static void
-e_book_backend_google_source_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = book_backend_google_try_password_sync;
+ backend_class = E_BACKEND_CLASS (class);
+ backend_class->authenticate_sync = book_backend_google_authenticate_sync;
+
+ book_backend_class = E_BOOK_BACKEND_CLASS (class);
+ book_backend_class->get_backend_property = book_backend_google_get_backend_property;
+ book_backend_class->open_sync = book_backend_google_open_sync;
+ book_backend_class->create_contacts_sync = book_backend_google_create_contacts_sync;
+ book_backend_class->modify_contacts_sync = book_backend_google_modify_contacts_sync;
+ book_backend_class->remove_contacts_sync = book_backend_google_remove_contacts_sync;
+ book_backend_class->get_contact_sync = book_backend_google_get_contact_sync;
+ book_backend_class->get_contact_list_sync = book_backend_google_get_contact_list_sync;
+ book_backend_class->start_view = book_backend_google_start_view;
+ book_backend_class->stop_view = book_backend_google_stop_view;
+ book_backend_class->refresh_sync = book_backend_google_refresh_sync;
}
static void
diff --git a/addressbook/backends/ldap/e-book-backend-ldap.c b/addressbook/backends/ldap/e-book-backend-ldap.c
index 52ce591..559ebbf 100644
--- a/addressbook/backends/ldap/e-book-backend-ldap.c
+++ b/addressbook/backends/ldap/e-book-backend-ldap.c
@@ -117,17 +117,7 @@ typedef struct LDAPOp LDAPOp;
"Incorrect msg type %d passed to %s", \
_msg_type, G_STRFUNC))
-/* Forward Declarations */
-static void e_book_backend_ldap_source_authenticator_init
- (ESourceAuthenticatorInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
- EBookBackendLDAP,
- e_book_backend_ldap,
- E_TYPE_BOOK_BACKEND,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- e_book_backend_ldap_source_authenticator_init))
+G_DEFINE_TYPE (EBookBackendLDAP, e_book_backend_ldap, E_TYPE_BOOK_BACKEND)
struct _EBookBackendLDAPPrivate {
gboolean connected;
@@ -4940,6 +4930,8 @@ book_backend_ldap_open (EBookBackend *backend,
e_book_backend_set_writable (backend, TRUE);
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
auth_required = e_source_authentication_required (auth_extension);
if (!auth_required)
@@ -4953,11 +4945,16 @@ book_backend_ldap_open (EBookBackend *backend,
auth_required = TRUE;
}
- if (auth_required && error == NULL)
- e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, &error);
+ if (auth_required && error == NULL) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ e_backend_credentials_required_sync (E_BACKEND (backend),
E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ NULL, 0, NULL, cancellable, &error);
+ } else if (!auth_required && !error) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ }
if (error != NULL && enable_debug)
printf ("%s ... failed to connect to server \n", G_STRFUNC);
@@ -5545,10 +5542,12 @@ book_backend_ldap_get_contact_list_uids (EBookBackend *backend,
}
static ESourceAuthenticationResult
-book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
+book_backend_ldap_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
ESourceAuthenticationResult result;
EBookBackendLDAP *bl;
@@ -5556,23 +5555,27 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
ESource *source;
gint ldap_error;
gchar *dn = NULL;
- const gchar *extension_name;
+ const gchar *username;
gchar *method;
- gchar *user;
+ gchar *auth_user;
- bl = E_BOOK_BACKEND_LDAP (authenticator);
- source = e_backend_get_source (E_BACKEND (authenticator));
+ bl = E_BOOK_BACKEND_LDAP (backend);
+ source = e_backend_get_source (backend);
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
/* We should not have gotten here if we're offline. */
g_return_val_if_fail (
- e_backend_get_online (E_BACKEND (authenticator)),
+ e_backend_get_online (backend),
E_SOURCE_AUTHENTICATION_ERROR);
method = e_source_authentication_dup_method (auth_extension);
- user = e_source_authentication_dup_user (auth_extension);
+ auth_user = e_source_authentication_dup_user (auth_extension);
+
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (!username || !*username) {
+ username = auth_user;
+ }
if (!method)
method = g_strdup ("none");
@@ -5581,7 +5584,7 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
if (bl->priv->ldap && !strcmp (method, "ldap/simple-email")) {
LDAPMessage *res, *e;
- gchar *query = g_strdup_printf ("(mail=%s)", user);
+ gchar *query = g_strdup_printf ("(mail=%s)", username);
gchar *entry_dn;
g_rec_mutex_lock (&eds_ldap_handler_lock);
@@ -5606,7 +5609,11 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
error, G_IO_ERROR,
G_IO_ERROR_INVALID_DATA,
_("Failed to get the DN "
- "for user '%s'"), user);
+ "for user '%s'"), username);
+
+ g_free (method);
+ g_free (auth_user);
+
return E_SOURCE_AUTHENTICATION_ERROR;
}
@@ -5620,14 +5627,14 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
ldap_msgfree (res);
} else if (!g_strcmp0 (method, "ldap/simple-binddn")) {
- dn = g_strdup (user);
+ dn = g_strdup (username);
}
g_free (bl->priv->auth_dn);
g_free (bl->priv->auth_secret);
bl->priv->auth_dn = dn;
- bl->priv->auth_secret = g_strdup (password->str);
+ bl->priv->auth_secret = g_strdup (e_named_parameters_get (credentials,
E_SOURCE_CREDENTIAL_PASSWORD));
/* now authenticate against the DN we were either supplied or queried for */
if (enable_debug)
@@ -5642,6 +5649,9 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
e_book_backend_ldap_connect (bl, &local_error);
+ g_free (method);
+ g_free (auth_user);
+
if (local_error == NULL) {
return E_SOURCE_AUTHENTICATION_ACCEPTED;
@@ -5679,7 +5689,7 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
}
#ifdef ENABLE_SASL_BINDS
else if (!g_ascii_strncasecmp (method, SASL_PREFIX, strlen (SASL_PREFIX))) {
- g_print ("sasl bind (mech = %s) as %s", method + strlen (SASL_PREFIX), user);
+ g_print ("sasl bind (mech = %s) as %s", method + strlen (SASL_PREFIX), username);
g_rec_mutex_lock (&eds_ldap_handler_lock);
if (!bl->priv->connected || !bl->priv->ldap) {
@@ -5689,6 +5699,9 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
e_book_backend_ldap_connect (bl, &local_error);
+ g_free (method);
+ g_free (auth_user);
+
if (local_error == NULL) {
return E_SOURCE_AUTHENTICATION_ACCEPTED;
@@ -5723,8 +5736,7 @@ book_backend_ldap_try_password_sync (ESourceAuthenticator *authenticator,
exit:
switch (ldap_error) {
case LDAP_SUCCESS:
- e_book_backend_set_writable (
- E_BOOK_BACKEND (authenticator), TRUE);
+ e_book_backend_set_writable (E_BOOK_BACKEND (backend), TRUE);
/* force a requery on the root dse since some ldap
* servers are set up such that they don't report
@@ -5765,7 +5777,7 @@ exit:
}
g_free (method);
- g_free (user);
+ g_free (auth_user);
return result;
}
@@ -5774,7 +5786,8 @@ static void
e_book_backend_ldap_class_init (EBookBackendLDAPClass *class)
{
GObjectClass *object_class;
- EBookBackendClass *backend_class;
+ EBackendClass *backend_class;
+ EBookBackendClass *book_backend_class;
g_type_class_add_private (class, sizeof (EBookBackendLDAPPrivate));
@@ -5786,30 +5799,27 @@ e_book_backend_ldap_class_init (EBookBackendLDAPClass *class)
object_class = G_OBJECT_CLASS (class);
object_class->finalize = book_backend_ldap_finalize;
- backend_class = E_BOOK_BACKEND_CLASS (class);
- backend_class->get_backend_property = book_backend_ldap_get_backend_property;
- backend_class->open = book_backend_ldap_open;
- backend_class->create_contacts = book_backend_ldap_create_contacts;
- backend_class->modify_contacts = book_backend_ldap_modify_contacts;
- backend_class->remove_contacts = book_backend_ldap_remove_contacts;
- backend_class->get_contact = book_backend_ldap_get_contact;
- backend_class->get_contact_list = book_backend_ldap_get_contact_list;
- backend_class->get_contact_list_uids = book_backend_ldap_get_contact_list_uids;
- backend_class->start_view = book_backend_ldap_start_view;
- backend_class->stop_view = book_backend_ldap_stop_view;
- backend_class->refresh_sync = book_backend_ldap_refresh_sync;
+ backend_class = E_BACKEND_CLASS (class);
+ backend_class->authenticate_sync = book_backend_ldap_authenticate_sync;
+
+ book_backend_class = E_BOOK_BACKEND_CLASS (class);
+ book_backend_class->get_backend_property = book_backend_ldap_get_backend_property;
+ book_backend_class->open = book_backend_ldap_open;
+ book_backend_class->create_contacts = book_backend_ldap_create_contacts;
+ book_backend_class->modify_contacts = book_backend_ldap_modify_contacts;
+ book_backend_class->remove_contacts = book_backend_ldap_remove_contacts;
+ book_backend_class->get_contact = book_backend_ldap_get_contact;
+ book_backend_class->get_contact_list = book_backend_ldap_get_contact_list;
+ book_backend_class->get_contact_list_uids = book_backend_ldap_get_contact_list_uids;
+ book_backend_class->start_view = book_backend_ldap_start_view;
+ book_backend_class->stop_view = book_backend_ldap_stop_view;
+ book_backend_class->refresh_sync = book_backend_ldap_refresh_sync;
/* Register our ESource extension. */
E_TYPE_SOURCE_LDAP;
}
static void
-e_book_backend_ldap_source_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = book_backend_ldap_try_password_sync;
-}
-
-static void
e_book_backend_ldap_init (EBookBackendLDAP *backend)
{
backend->priv = E_BOOK_BACKEND_LDAP_GET_PRIVATE (backend);
diff --git a/addressbook/backends/webdav/e-book-backend-webdav.c
b/addressbook/backends/webdav/e-book-backend-webdav.c
index 4402a9e..5f130a5 100644
--- a/addressbook/backends/webdav/e-book-backend-webdav.c
+++ b/addressbook/backends/webdav/e-book-backend-webdav.c
@@ -51,17 +51,7 @@
#define WEBDAV_CONTACT_ETAG "X-EVOLUTION-WEBDAV-ETAG"
#define WEBDAV_CONTACT_HREF "X-EVOLUTION-WEBDAV-HREF"
-/* Forward Declarations */
-static void e_book_backend_webdav_source_authenticator_init
- (ESourceAuthenticatorInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
- EBookBackendWebdav,
- e_book_backend_webdav,
- E_TYPE_BOOK_BACKEND,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- e_book_backend_webdav_source_authenticator_init))
+G_DEFINE_TYPE (EBookBackendWebdav, e_book_backend_webdav, E_TYPE_BOOK_BACKEND)
struct _EBookBackendWebdavPrivate {
gboolean marked_for_offline;
@@ -230,10 +220,7 @@ send_and_handle_ssl (EBookBackendWebdav *webdav,
{
guint status_code;
- e_soup_ssl_trust_connect (
- message, e_backend_get_source (E_BACKEND (webdav)),
- e_book_backend_get_registry (E_BOOK_BACKEND (webdav)),
- cancellable);
+ e_soup_ssl_trust_connect (message, e_backend_get_source (E_BACKEND (webdav)));
status_code = soup_session_send_message (webdav->priv->session, message);
@@ -1133,7 +1120,7 @@ soup_authenticate (SoupSession *session,
if (retrying)
return;
- if (!priv->username || !*priv->username)
+ if (!priv->username || !*priv->username || !priv->password)
soup_message_set_status (message, SOUP_STATUS_FORBIDDEN);
else
soup_auth_authenticate (auth, priv->username, priv->password);
@@ -1221,6 +1208,8 @@ book_backend_webdav_get_backend_property (EBookBackend *backend,
static gboolean
book_backend_webdav_test_can_connect (EBookBackendWebdav *webdav,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
GCancellable *cancellable,
GError **error)
{
@@ -1257,11 +1246,32 @@ book_backend_webdav_test_can_connect (EBookBackendWebdav *webdav,
e_client_error_to_string (E_CLIENT_ERROR_AUTHENTICATION_REQUIRED));
break;
+ case SOUP_STATUS_SSL_FAILED:
+ if (out_certificate_pem && out_certificate_errors) {
+ GTlsCertificate *certificate = NULL;
+
+ g_object_get (G_OBJECT (message),
+ "tls-certificate", &certificate,
+ "tls-errors", out_certificate_errors,
+ NULL);
+
+ if (certificate) {
+ g_object_get (certificate, "certificate-pem", out_certificate_pem,
NULL);
+ g_object_unref (certificate);
+ }
+ }
+
+ g_set_error_literal (
+ error, SOUP_HTTP_ERROR,
+ message->status_code,
+ message->reason_phrase);
+ break;
+
default:
- g_set_error (
+ g_set_error_literal (
error, SOUP_HTTP_ERROR,
message->status_code,
- "%s", message->reason_phrase);
+ message->reason_phrase);
break;
}
@@ -1370,13 +1380,55 @@ book_backend_webdav_open_sync (EBookBackend *backend,
e_backend_set_online (E_BACKEND (backend), TRUE);
e_book_backend_set_writable (backend, TRUE);
- if (e_source_authentication_required (auth_extension))
- success = e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
+ if (e_source_authentication_required (auth_extension)) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ success = e_backend_credentials_required_sync (E_BACKEND (backend),
+ E_SOURCE_CREDENTIALS_REASON_REQUIRED, NULL, 0, NULL,
cancellable, error);
- else
- success = book_backend_webdav_test_can_connect (webdav, cancellable, error);
+ } else {
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
+ GError *local_error = NULL;
+
+ success = book_backend_webdav_test_can_connect (webdav, &certificate_pem,
&certificate_errors, cancellable, &local_error);
+ if (!success && !g_cancellable_is_cancelled (cancellable)) {
+ ESourceCredentialsReason reason;
+ GError *local_error2 = NULL;
+
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ e_source_set_connection_status (source,
E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+ } else if (g_error_matches (local_error, E_CLIENT_ERROR,
E_CLIENT_ERROR_AUTHENTICATION_FAILED) ||
+ g_error_matches (local_error, E_CLIENT_ERROR,
E_CLIENT_ERROR_AUTHENTICATION_REQUIRED)) {
+ reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
+ } else {
+ reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
+ }
+
+ if (!e_backend_credentials_required_sync (E_BACKEND (backend), reason,
certificate_pem, certificate_errors,
+ local_error, cancellable, &local_error2)) {
+ g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
+ }
+
+ if (!local_error2 && g_error_matches (local_error, SOUP_HTTP_ERROR,
SOUP_STATUS_SSL_FAILED)) {
+ /* These cerificate errors are treated through the authentication */
+ g_clear_error (&local_error);
+ } else {
+ g_propagate_error (error, local_error);
+ local_error = NULL;
+ }
+
+ g_clear_error (&local_error2);
+ }
+
+ g_free (certificate_pem);
+
+ if (local_error)
+ g_propagate_error (error, local_error);
+ }
soup_uri_free (suri);
@@ -1764,31 +1816,49 @@ book_backend_webdav_get_contact_list_sync (EBookBackend *backend,
}
static ESourceAuthenticationResult
-book_backend_webdav_try_password_sync (ESourceAuthenticator *authenticator,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
+book_backend_webdav_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
- EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (authenticator);
+ EBookBackendWebdav *webdav = E_BOOK_BACKEND_WEBDAV (backend);
ESourceAuthentication *auth_extension;
ESourceAuthenticationResult result;
ESource *source;
- const gchar *extension_name;
+ const gchar *username;
GError *local_error = NULL;
- source = e_backend_get_source (E_BACKEND (authenticator));
- extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
- auth_extension = e_source_get_extension (source, extension_name);
+ source = e_backend_get_source (backend);
+ auth_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ g_free (webdav->priv->username);
+ webdav->priv->username = NULL;
+
+ g_free (webdav->priv->password);
+ webdav->priv->password = g_strdup (e_named_parameters_get (credentials,
E_SOURCE_CREDENTIAL_PASSWORD));
- webdav->priv->username =
- e_source_authentication_dup_user (auth_extension);
- webdav->priv->password = g_strdup (password->str);
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (username && *username) {
+ webdav->priv->username = g_strdup (username);
+ } else {
+ webdav->priv->username = e_source_authentication_dup_user (auth_extension);
+ }
- if (book_backend_webdav_test_can_connect (webdav, cancellable, &local_error)) {
+ if (book_backend_webdav_test_can_connect (webdav, out_certificate_pem, out_certificate_errors,
cancellable, &local_error)) {
result = E_SOURCE_AUTHENTICATION_ACCEPTED;
- } else if (g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED)) {
- result = E_SOURCE_AUTHENTICATION_REJECTED;
+ } else if (g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_FAILED) ||
+ g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED)) {
+ if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ||
+ g_error_matches (local_error, E_CLIENT_ERROR, E_CLIENT_ERROR_AUTHENTICATION_REQUIRED))
+ result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
g_clear_error (&local_error);
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+ g_propagate_error (error, local_error);
} else {
result = E_SOURCE_AUTHENTICATION_ERROR;
g_propagate_error (error, local_error);
@@ -1801,7 +1871,8 @@ static void
e_book_backend_webdav_class_init (EBookBackendWebdavClass *class)
{
GObjectClass *object_class;
- EBookBackendClass *backend_class;
+ EBackendClass *backend_class;
+ EBookBackendClass *book_backend_class;
g_type_class_add_private (class, sizeof (EBookBackendWebdavPrivate));
@@ -1809,22 +1880,19 @@ e_book_backend_webdav_class_init (EBookBackendWebdavClass *class)
object_class->dispose = book_backend_webdav_dispose;
object_class->finalize = book_backend_webdav_finalize;
- backend_class = E_BOOK_BACKEND_CLASS (class);
- backend_class->get_backend_property = book_backend_webdav_get_backend_property;
- backend_class->open_sync = book_backend_webdav_open_sync;
- backend_class->create_contacts_sync = book_backend_webdav_create_contacts_sync;
- backend_class->modify_contacts_sync = book_backend_webdav_modify_contacts_sync;
- backend_class->remove_contacts_sync = book_backend_webdav_remove_contacts_sync;
- backend_class->get_contact_sync = book_backend_webdav_get_contact_sync;
- backend_class->get_contact_list_sync = book_backend_webdav_get_contact_list_sync;
- backend_class->start_view = e_book_backend_webdav_start_view;
- backend_class->stop_view = e_book_backend_webdav_stop_view;
-}
-
-static void
-e_book_backend_webdav_source_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = book_backend_webdav_try_password_sync;
+ backend_class = E_BACKEND_CLASS (class);
+ backend_class->authenticate_sync = book_backend_webdav_authenticate_sync;
+
+ book_backend_class = E_BOOK_BACKEND_CLASS (class);
+ book_backend_class->get_backend_property = book_backend_webdav_get_backend_property;
+ book_backend_class->open_sync = book_backend_webdav_open_sync;
+ book_backend_class->create_contacts_sync = book_backend_webdav_create_contacts_sync;
+ book_backend_class->modify_contacts_sync = book_backend_webdav_modify_contacts_sync;
+ book_backend_class->remove_contacts_sync = book_backend_webdav_remove_contacts_sync;
+ book_backend_class->get_contact_sync = book_backend_webdav_get_contact_sync;
+ book_backend_class->get_contact_list_sync = book_backend_webdav_get_contact_list_sync;
+ book_backend_class->start_view = e_book_backend_webdav_start_view;
+ book_backend_class->stop_view = e_book_backend_webdav_stop_view;
}
static void
diff --git a/addressbook/libebook/e-book-client.c b/addressbook/libebook/e-book-client.c
index ebd1a2c..609b0a2 100644
--- a/addressbook/libebook/e-book-client.c
+++ b/addressbook/libebook/e-book-client.c
@@ -96,6 +96,7 @@ struct _SignalClosure {
struct _ConnectClosure {
ESource *source;
GCancellable *cancellable;
+ guint32 wait_for_connected_seconds;
};
struct _RunInThreadClosure {
@@ -1001,6 +1002,34 @@ book_client_refresh_sync (EClient *client,
return TRUE;
}
+static gboolean
+book_client_retrieve_properties_sync (EClient *client,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBookClient *book_client;
+ gchar **properties = NULL;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_BOOK_CLIENT (client), FALSE);
+
+ book_client = E_BOOK_CLIENT (client);
+
+ e_dbus_address_book_call_retrieve_properties_sync (
+ book_client->priv->dbus_proxy, &properties, cancellable, &local_error);
+
+ book_client_process_properties (book_client, properties);
+ g_strfreev (properties);
+
+ if (local_error != NULL) {
+ g_dbus_error_strip_remote_error (local_error);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
book_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
GObject *source_object,
@@ -1246,6 +1275,7 @@ e_book_client_class_init (EBookClientClass *class)
client_class->set_backend_property_sync = book_client_set_backend_property_sync;
client_class->open_sync = book_client_open_sync;
client_class->refresh_sync = book_client_refresh_sync;
+ client_class->retrieve_properties_sync = book_client_retrieve_properties_sync;
/**
* EBookClient:locale:
@@ -1293,6 +1323,7 @@ e_book_client_init (EBookClient *client)
/**
* e_book_client_connect_sync:
* @source: an #ESource
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
@@ -1302,6 +1333,15 @@ e_book_client_init (EBookClient *client)
* Unlike with e_book_client_new(), there is no need to call
* e_client_open_sync() after obtaining the #EBookClient.
*
+ * The @wait_for_connected_seconds argument had been added since 3.14,
+ * to let the caller decide how long to wait for the backend to fully
+ * connect to its (possibly remote) data store. This is required due
+ * to a change in the authentication process, which is fully asynchronous
+ * and done on the client side, while not every client is supposed to
+ * response to authentication requests. In case the backend will not connect
+ * within the set interval, then it is opened in an offline mode. A special
+ * value -1 can be used to not wait for the connected state at all.
+ *
* For error handling convenience, any error message returned by this
* function will have a descriptive prefix that includes the display
* name of @source.
@@ -1312,6 +1352,7 @@ e_book_client_init (EBookClient *client)
**/
EClient *
e_book_client_connect_sync (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error)
{
@@ -1336,6 +1377,12 @@ e_book_client_connect_sync (ESource *source,
g_strfreev (properties);
}
+ if (!local_error && wait_for_connected_seconds != (guint32) -1) {
+ /* These errors are ignored, the book is left opened in an offline mode. */
+ e_client_wait_for_connected_sync (E_CLIENT (client),
+ wait_for_connected_seconds, cancellable, NULL);
+ }
+
if (local_error != NULL) {
g_dbus_error_strip_remote_error (local_error);
g_propagate_error (error, local_error);
@@ -1349,6 +1396,23 @@ e_book_client_connect_sync (ESource *source,
return E_CLIENT (client);
}
+static void
+book_client_connect_wait_for_connected_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ /* These errors are ignored, the book is left opened in an offline mode. */
+ e_client_wait_for_connected_finish (E_CLIENT (source_object), result, NULL);
+
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
/* Helper for e_book_client_connect() */
static void
book_client_connect_open_cb (GObject *source_object,
@@ -1368,6 +1432,24 @@ book_client_connect_open_cb (GObject *source_object,
client_object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
if (client_object) {
book_client_process_properties (E_BOOK_CLIENT (client_object), properties);
+
+ if (!local_error) {
+ ConnectClosure *closure;
+
+ closure = g_simple_async_result_get_op_res_gpointer (simple);
+ if (closure->wait_for_connected_seconds != (guint32) -1) {
+ e_client_wait_for_connected (E_CLIENT (client_object),
+ closure->wait_for_connected_seconds,
+ closure->cancellable,
+ book_client_connect_wait_for_connected_cb, g_object_ref (simple));
+
+ g_clear_object (&client_object);
+ g_object_unref (simple);
+ g_strfreev (properties);
+ return;
+ }
+ }
+
g_clear_object (&client_object);
}
@@ -1427,6 +1509,7 @@ exit:
/**
* e_book_client_connect:
* @source: an #ESource
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @callback: (scope async): a #GAsyncReadyCallback to call when the request
* is satisfied
@@ -1434,6 +1517,15 @@ exit:
*
* Asynchronously creates a new #EBookClient for @source.
*
+ * The @wait_for_connected_seconds argument had been added since 3.14,
+ * to let the caller decide how long to wait for the backend to fully
+ * connect to its (possibly remote) data store. This is required due
+ * to a change in the authentication process, which is fully asynchronous
+ * and done on the client side, while not every client is supposed to
+ * response to authentication requests. In case the backend will not connect
+ * within the set interval, then it is opened in an offline mode. A special
+ * value -1 can be used to not wait for the connected state at all.
+ *
* Unlike with e_book_client_new(), there is no need to call e_client_open()
* after obtaining the #EBookClient.
*
@@ -1444,6 +1536,7 @@ exit:
**/
void
e_book_client_connect (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -1462,6 +1555,7 @@ e_book_client_connect (ESource *source,
closure = g_slice_new0 (ConnectClosure);
closure->source = g_object_ref (source);
+ closure->wait_for_connected_seconds = wait_for_connected_seconds;
if (G_IS_CANCELLABLE (cancellable))
closure->cancellable = g_object_ref (cancellable);
@@ -1625,6 +1719,7 @@ connect_direct (EBookClient *client,
* e_book_client_connect_direct_sync:
* @registry: an #ESourceRegistry
* @source: an #ESource
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
@@ -1638,12 +1733,13 @@ connect_direct (EBookClient *client,
EClient *
e_book_client_connect_direct_sync (ESourceRegistry *registry,
ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error)
{
EClient *client;
- client = e_book_client_connect_sync (source, cancellable, error);
+ client = e_book_client_connect_sync (source, wait_for_connected_seconds, cancellable, error);
if (!client)
return NULL;
@@ -1702,6 +1798,7 @@ exit:
/**
* e_book_client_connect_direct:
* @source: an #ESource
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @callback: (scope async): a #GAsyncReadyCallback to call when the request
* is satisfied
@@ -1717,6 +1814,7 @@ exit:
**/
void
e_book_client_connect_direct (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -1734,6 +1832,7 @@ e_book_client_connect_direct (ESource *source,
* time and block other clients from receiving signals. */
closure = g_slice_new0 (ConnectClosure);
closure->source = g_object_ref (source);
+ closure->wait_for_connected_seconds = wait_for_connected_seconds;
if (G_IS_CANCELLABLE (cancellable))
closure->cancellable = g_object_ref (cancellable);
diff --git a/addressbook/libebook/e-book-client.h b/addressbook/libebook/e-book-client.h
index feb3df8..3a557f9 100644
--- a/addressbook/libebook/e-book-client.h
+++ b/addressbook/libebook/e-book-client.h
@@ -102,9 +102,11 @@ struct _EBookClientClass {
GType e_book_client_get_type (void) G_GNUC_CONST;
EClient * e_book_client_connect_sync (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error);
void e_book_client_connect (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
@@ -113,9 +115,11 @@ EClient * e_book_client_connect_finish (GAsyncResult *result,
EClient * e_book_client_connect_direct_sync
(ESourceRegistry *registry,
ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error);
void e_book_client_connect_direct (ESource *source,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/addressbook/libedata-book/e-book-backend.c b/addressbook/libedata-book/e-book-backend.c
index acc6e67..51cc2f4 100644
--- a/addressbook/libedata-book/e-book-backend.c
+++ b/addressbook/libedata-book/e-book-backend.c
@@ -568,24 +568,6 @@ book_backend_constructed (GObject *object)
}
}
-static gboolean
-book_backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error)
-{
- EBookBackend *book_backend;
- ESourceRegistry *registry;
- ESource *source;
-
- book_backend = E_BOOK_BACKEND (backend);
- registry = e_book_backend_get_registry (book_backend);
- source = e_backend_get_source (backend);
-
- return e_source_registry_authenticate_sync (
- registry, source, auth, cancellable, error);
-}
-
static void
book_backend_prepare_shutdown (EBackend *backend)
{
@@ -721,7 +703,6 @@ e_book_backend_class_init (EBookBackendClass *class)
object_class->constructed = book_backend_constructed;
backend_class = E_BACKEND_CLASS (class);
- backend_class->authenticate_sync = book_backend_authenticate_sync;
backend_class->prepare_shutdown = book_backend_prepare_shutdown;
class->get_backend_property = book_backend_get_backend_property;
diff --git a/addressbook/libedata-book/e-data-book.c b/addressbook/libedata-book/e-data-book.c
index 4d66ec7..165fcda 100644
--- a/addressbook/libedata-book/e-data-book.c
+++ b/addressbook/libedata-book/e-data-book.c
@@ -545,24 +545,20 @@ e_data_book_string_slist_to_comma_string (const GSList *strings)
return res;
}
-static void
-data_book_complete_open_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
+static GPtrArray *
+data_book_encode_properties (EDBusAddressBook *dbus_interface)
{
- AsyncContext *async_context = user_data;
- GError *error = NULL;
+ GPtrArray *properties_array;
- e_book_backend_open_finish (
- E_BOOK_BACKEND (source_object), result, &error);
+ g_warn_if_fail (E_DBUS_IS_ADDRESS_BOOK (dbus_interface));
- if (error == NULL) {
- GPtrArray *properties_array;
+ properties_array = g_ptr_array_new_with_free_func (g_free);
+
+ if (dbus_interface) {
GParamSpec **properties;
guint ii, n_properties = 0;
- properties_array = g_ptr_array_new_with_free_func (g_free);
- properties = g_object_class_list_properties (G_OBJECT_GET_CLASS
(async_context->dbus_interface), &n_properties);
+ properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (dbus_interface),
&n_properties);
for (ii = 0; ii < n_properties; ii++) {
gboolean can_process =
@@ -581,7 +577,7 @@ data_book_complete_open_cb (GObject *source_object,
GVariant *stored = NULL;
g_value_init (&value, properties[ii]->value_type);
- g_object_get_property ((GObject *) async_context->dbus_interface,
properties[ii]->name, &value);
+ g_object_get_property ((GObject *) dbus_interface, properties[ii]->name,
&value);
#define WORKOUT(gvl, gvr) \
if (g_type_is_a (properties[ii]->value_type, G_TYPE_ ## gvl)) \
@@ -611,8 +607,47 @@ data_book_complete_open_cb (GObject *source_object,
}
g_free (properties);
+ }
+
+ g_ptr_array_add (properties_array, NULL);
+
+ return properties_array;
+}
+
+static gboolean
+data_book_handle_retrieve_properties_cb (EDBusAddressBook *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ EDataBook *data_book)
+{
+ GPtrArray *properties_array;
+
+ properties_array = data_book_encode_properties (dbus_interface);
+
+ e_dbus_address_book_complete_retrieve_properties (
+ dbus_interface,
+ invocation,
+ (const gchar * const *) properties_array->pdata);
- g_ptr_array_add (properties_array, NULL);
+ g_ptr_array_free (properties_array, TRUE);
+
+ return TRUE;
+}
+
+static void
+data_book_complete_open_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ AsyncContext *async_context = user_data;
+ GError *error = NULL;
+
+ e_book_backend_open_finish (
+ E_BOOK_BACKEND (source_object), result, &error);
+
+ if (error == NULL) {
+ GPtrArray *properties_array;
+
+ properties_array = data_book_encode_properties (async_context->dbus_interface);
e_dbus_address_book_complete_open (
async_context->dbus_interface,
@@ -2066,6 +2101,10 @@ e_data_book_init (EDataBook *data_book)
(GDestroyNotify) g_ptr_array_unref);
g_signal_connect (
+ dbus_interface, "handle-retrieve-properties",
+ G_CALLBACK (data_book_handle_retrieve_properties_cb),
+ data_book);
+ g_signal_connect (
dbus_interface, "handle-open",
G_CALLBACK (data_book_handle_open_cb),
data_book);
diff --git a/calendar/backends/caldav/e-cal-backend-caldav.c b/calendar/backends/caldav/e-cal-backend-caldav.c
index a4425a5..192260b 100644
--- a/calendar/backends/caldav/e-cal-backend-caldav.c
+++ b/calendar/backends/caldav/e-cal-backend-caldav.c
@@ -95,9 +95,9 @@ struct _ECalBackendCalDAVPrivate {
gchar *uri;
/* Authentication info */
+ gchar *username; /* not NULL only as override */
gchar *password;
gboolean auth_required;
- gboolean force_ask_password;
/* support for 'getctag' extension */
gboolean ctag_supported;
@@ -122,7 +122,7 @@ struct _ECalBackendCalDAVPrivate {
/* If we fail to obtain an OAuth2 access token,
* soup_authenticate_bearer() stashes an error
- * here to be claimed in caldav_authenticate().
+ * here to be claimed in caldav_credentials_required_sync().
* This lets us propagate a more useful error
* message than a generic 401 description. */
GError *bearer_auth_error;
@@ -132,8 +132,6 @@ struct _ECalBackendCalDAVPrivate {
/* Forward Declarations */
static void e_caldav_backend_initable_init
(GInitableIface *interface);
-static void caldav_source_authenticator_init
- (ESourceAuthenticatorInterface *iface);
G_DEFINE_TYPE_WITH_CODE (
ECalBackendCalDAV,
@@ -141,10 +139,7 @@ G_DEFINE_TYPE_WITH_CODE (
E_TYPE_CAL_BACKEND_SYNC,
G_IMPLEMENT_INTERFACE (
G_TYPE_INITABLE,
- e_caldav_backend_initable_init)
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- caldav_source_authenticator_init))
+ e_caldav_backend_initable_init))
/* ************************************************************************* */
/* Debugging */
@@ -155,7 +150,13 @@ G_DEFINE_TYPE_WITH_CODE (
#define DEBUG_SERVER_ITEMS "items"
#define DEBUG_ATTACHMENTS "attachments"
-static gboolean open_calendar_wrapper (ECalBackendCalDAV *cbdav, GCancellable *cancellable, GError **error,
gboolean can_call_authenticate, gboolean *know_unreachable);
+static gboolean open_calendar_wrapper (ECalBackendCalDAV *cbdav,
+ GCancellable *cancellable,
+ GError **error,
+ gboolean first_attempt,
+ gboolean *know_unreachable,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors);
static void convert_to_inline_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
static void convert_to_url_attachment (ECalBackendCalDAV *cbdav, icalcomponent *icalcomp);
@@ -609,6 +610,10 @@ status_code_to_result (SoupMessage *message,
_("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"))));
+ if (is_opening && perror && *perror) {
+ (*perror)->domain = SOUP_HTTP_ERROR;
+ (*perror)->code = SOUP_STATUS_SSL_FAILED;
+ }
break;
default:
@@ -997,7 +1002,7 @@ soup_authenticate_bearer (SoupSession *session,
E_SOUP_AUTH_BEARER (auth),
access_token, expires_in_seconds);
- /* Stash the error to be picked up by caldav_authenticate().
+ /* Stash the error to be picked up by caldav_credentials_required_sync().
* There's no way to explicitly propagate a GError directly
* through libsoup, so we have to work around it. */
if (local_error != NULL) {
@@ -1036,24 +1041,29 @@ soup_authenticate (SoupSession *session,
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
auth_extension = e_source_get_extension (source, extension_name);
- if (retrying || cbdav->priv->force_ask_password) {
- cbdav->priv->force_ask_password = TRUE;
+ if (retrying)
return;
- }
if (E_IS_SOUP_AUTH_BEARER (auth)) {
soup_authenticate_bearer (session, msg, auth, cbdav);
/* do not send same password twice, but keep it for later use */
- } else if (cbdav->priv->password != NULL) {
- gchar *user;
+ } else {
+ gchar *auth_user;
+ const gchar *username;
- user = e_source_authentication_dup_user (auth_extension);
- if (!user || !*user)
+ auth_user = e_source_authentication_dup_user (auth_extension);
+
+ username = cbdav->priv->username;
+ if (!username || !*username)
+ username = auth_user;
+
+ if (!username || !*username || !cbdav->priv->password)
soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
else
- soup_auth_authenticate (auth, user, cbdav->priv->password);
- g_free (user);
+ soup_auth_authenticate (auth, username, cbdav->priv->password);
+
+ g_free (auth_user);
}
}
@@ -1110,10 +1120,7 @@ send_and_handle_redirection (ECalBackendCalDAV *cbdav,
if (new_location)
old_uri = soup_uri_to_string (soup_message_get_uri (msg), FALSE);
- e_soup_ssl_trust_connect (
- msg, e_backend_get_source (E_BACKEND (cbdav)),
- e_cal_backend_get_registry (E_CAL_BACKEND (cbdav)),
- cancellable);
+ e_soup_ssl_trust_connect (msg, e_backend_get_source (E_BACKEND (cbdav)));
soup_message_set_flags (msg, SOUP_MESSAGE_NO_REDIRECT);
soup_message_add_header_handler (msg, "got_body", "Location", G_CALLBACK (redirect_handler),
cbdav->priv->session);
@@ -1152,6 +1159,8 @@ caldav_generate_uri (ECalBackendCalDAV *cbdav,
static gboolean
caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
gboolean *server_unreachable,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
GCancellable *cancellable,
GError **perror)
{
@@ -1161,7 +1170,6 @@ caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
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);
@@ -1171,22 +1179,39 @@ caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
g_propagate_error (perror, EDC_ERROR (NoSuchCal));
return FALSE;
}
+
soup_message_headers_append (
message->request_headers,
"User-Agent", "Evolution/" VERSION);
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);
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
send_and_handle_redirection (cbdav, message, NULL, cancellable, perror);
if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
switch (message->status_code) {
case SOUP_STATUS_CANT_CONNECT:
case SOUP_STATUS_CANT_CONNECT_PROXY:
*server_unreachable = TRUE;
break;
+ case SOUP_STATUS_SSL_FAILED:
+ if (out_certificate_pem && out_certificate_errors) {
+ GTlsCertificate *certificate = NULL;
+
+ g_object_get (G_OBJECT (message),
+ "tls-certificate", &certificate,
+ "tls-errors", out_certificate_errors,
+ NULL);
+
+ if (certificate) {
+ g_object_get (certificate, "certificate-pem", out_certificate_pem,
NULL);
+ g_object_unref (certificate);
+ }
+ }
+ break;
}
status_code_to_result (message, cbdav, TRUE, perror);
@@ -1218,12 +1243,14 @@ caldav_server_open_calendar (ECalBackendCalDAV *cbdav,
g_object_unref (message);
if (calendar_access) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
e_cal_backend_set_writable (
E_CAL_BACKEND (cbdav),
put_allowed && delete_allowed);
return TRUE;
}
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
g_propagate_error (perror, EDC_ERROR (PermissionDenied));
return FALSE;
}
@@ -1248,10 +1275,11 @@ caldav_unref_in_thread (ECalBackendCalDAV *cbdav)
}
static gboolean
-caldav_authenticate (ECalBackendCalDAV *cbdav,
- gboolean ref_cbdav,
- GCancellable *cancellable,
- GError **error)
+caldav_credentials_required_sync (ECalBackendCalDAV *cbdav,
+ gboolean ref_cbdav,
+ gboolean first_attempt,
+ GCancellable *cancellable,
+ GError **error)
{
gboolean success = TRUE;
@@ -1271,10 +1299,10 @@ caldav_authenticate (ECalBackendCalDAV *cbdav,
g_mutex_unlock (&cbdav->priv->bearer_auth_error_lock);
if (success) {
- success = e_backend_authenticate_sync (
- E_BACKEND (cbdav),
- E_SOURCE_AUTHENTICATOR (cbdav),
- cancellable, error);
+ success = e_backend_credentials_required_sync (E_BACKEND (cbdav),
+ (first_attempt || !cbdav->priv->password) ? E_SOURCE_CREDENTIALS_REASON_REQUIRED :
+ E_SOURCE_CREDENTIALS_REASON_REJECTED,
+ NULL, 0, NULL, cancellable, error);
}
if (ref_cbdav)
@@ -1359,7 +1387,7 @@ check_calendar_changed_on_server (ECalBackendCalDAV *cbdav,
/* Check the result */
if (message->status_code == 401) {
- caldav_authenticate (cbdav, TRUE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
} else if (message->status_code != 207) {
/* does not support it, but report calendar changed to update cache */
cbdav->priv->ctag_supported = FALSE;
@@ -1521,7 +1549,7 @@ caldav_server_list_objects (ECalBackendCalDAV *cbdav,
E_CAL_BACKEND (cbdav), FALSE);
break;
case 401:
- caldav_authenticate (cbdav, TRUE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
break;
default:
g_warning ("Server did not response with 207, but with code %d (%s)",
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
@@ -1644,7 +1672,7 @@ caldav_server_query_for_uid (ECalBackendCalDAV *cbdav,
E_CAL_BACKEND (cbdav), FALSE);
break;
case 401:
- caldav_authenticate (cbdav, TRUE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, TRUE, FALSE, NULL, NULL);
break;
default:
g_warning ("Server did not response with 207, but with code %d (%s)",
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
@@ -1709,7 +1737,7 @@ caldav_server_download_attachment (ECalBackendCalDAV *cbdav,
status_code_to_result (message, cbdav, FALSE, error);
if (message->status_code == 401)
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
g_object_unref (message);
return FALSE;
@@ -1753,7 +1781,7 @@ caldav_server_get_object (ECalBackendCalDAV *cbdav,
status_code_to_result (message, cbdav, FALSE, perror);
if (message->status_code == 401)
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
else
g_warning ("Could not fetch object '%s' from server, status:%d (%s)", uri,
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
g_object_unref (message);
@@ -1816,7 +1844,7 @@ caldav_post_freebusy (ECalBackendCalDAV *cbdav,
if (!SOUP_STATUS_IS_SUCCESSFUL (message->status_code)) {
status_code_to_result (message, cbdav, FALSE, error);
if (message->status_code == 401)
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
else
g_warning ("Could not post free/busy request to '%s', status:%d (%s)", url,
message->status_code, soup_status_get_phrase (message->status_code) ? soup_status_get_phrase
(message->status_code) : "Unknown code");
@@ -2008,7 +2036,7 @@ caldav_server_put_object (ECalBackendCalDAV *cbdav,
g_propagate_error (perror, local_error);
}
} else if (message->status_code == 401) {
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
}
g_object_unref (message);
@@ -2050,7 +2078,7 @@ caldav_server_delete_object (ECalBackendCalDAV *cbdav,
status_code_to_result (message, cbdav, FALSE, perror);
if (message->status_code == 401)
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
g_object_unref (message);
}
@@ -2171,7 +2199,7 @@ caldav_receive_schedule_outbox_url (ECalBackendCalDAV *cbdav,
xmlOutputBufferClose (buf);
xmlFreeDoc (doc);
} else if (message->status_code == 401) {
- caldav_authenticate (cbdav, FALSE, NULL, NULL);
+ caldav_credentials_required_sync (cbdav, FALSE, FALSE, NULL, NULL);
}
if (message)
@@ -2536,7 +2564,7 @@ caldav_synch_slave_loop (gpointer data)
}
if (!cbdav->priv->opened) {
- if (open_calendar_wrapper (cbdav, NULL, NULL, TRUE, &know_unreachable)) {
+ if (open_calendar_wrapper (cbdav, NULL, NULL, TRUE, &know_unreachable, NULL, NULL)) {
cbdav->priv->opened = TRUE;
update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
g_cond_signal (&cbdav->priv->cond);
@@ -2877,27 +2905,33 @@ static gboolean
open_calendar_wrapper (ECalBackendCalDAV *cbdav,
GCancellable *cancellable,
GError **error,
- gboolean can_call_authenticate,
- gboolean *know_unreachable)
+ gboolean first_attempt,
+ gboolean *know_unreachable,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors)
{
gboolean server_unreachable = FALSE;
+ gboolean awaiting_credentials = FALSE;
gboolean success;
GError *local_error = NULL;
g_return_val_if_fail (cbdav != NULL, FALSE);
- success = caldav_server_open_calendar (cbdav, &server_unreachable, cancellable, &local_error);
+ success = caldav_server_open_calendar (cbdav, &server_unreachable, out_certificate_pem,
out_certificate_errors, cancellable, &local_error);
- if (can_call_authenticate && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
+ if (first_attempt && g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
g_clear_error (&local_error);
- success = caldav_authenticate (cbdav, FALSE, cancellable, &local_error);
+ awaiting_credentials = TRUE;
+ success = caldav_credentials_required_sync (cbdav, FALSE, first_attempt, cancellable,
&local_error);
}
if (success) {
- update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
- g_cond_signal (&cbdav->priv->cond);
-
cbdav->priv->is_google = is_google_uri (cbdav->priv->uri);
+
+ if (!awaiting_credentials) {
+ update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
+ g_cond_signal (&cbdav->priv->cond);
+ }
} else if (server_unreachable) {
cbdav->priv->opened = FALSE;
e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
@@ -2930,13 +2964,19 @@ caldav_do_open (ECalBackendSync *backend,
gboolean only_if_exists,
GError **perror)
{
- ECalBackendCalDAV *cbdav;
+ ECalBackendCalDAV *cbdav;
+ ESourceWebdav *webdav_extension;
+ ESource *source;
gboolean online;
cbdav = E_CAL_BACKEND_CALDAV (backend);
g_mutex_lock (&cbdav->priv->busy_lock);
+ 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);
+
/* let it decide the 'getctag' extension availability again */
cbdav->priv->ctag_supported = TRUE;
@@ -2958,7 +2998,39 @@ caldav_do_open (ECalBackendSync *backend,
cbdav->priv->is_google = FALSE;
if (online) {
- open_calendar_wrapper (cbdav, cancellable, perror, TRUE, NULL);
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
+ GError *local_error = NULL;
+
+ if (!open_calendar_wrapper (cbdav, cancellable, &local_error, TRUE, NULL, &certificate_pem,
&certificate_errors) &&
+ !g_cancellable_is_cancelled (cancellable)) {
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
+ GError *local_error2 = NULL;
+
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ }
+
+ if (!e_backend_credentials_required_sync (E_BACKEND (backend), reason,
certificate_pem, certificate_errors,
+ local_error, cancellable, &local_error2)) {
+ g_warning ("%s: Failed to call credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
+ }
+
+ if (!local_error2 && (
+ g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED) ||
+ g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired) ||
+ g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed))) {
+ /* These errors are treated through the authentication */
+ g_clear_error (&local_error);
+ } else {
+ g_propagate_error (perror, local_error);
+ local_error = NULL;
+ }
+ g_clear_error (&local_error2);
+ }
+
+ g_clear_error (&local_error);
+ g_free (certificate_pem);
} else {
e_cal_backend_set_writable (E_CAL_BACKEND (cbdav), FALSE);
}
@@ -5249,46 +5321,59 @@ caldav_source_changed_cb (ESource *source,
}
static ESourceAuthenticationResult
-caldav_try_password_sync (ESourceAuthenticator *authenticator,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
+caldav_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
ECalBackendCalDAV *cbdav;
ESourceAuthenticationResult result;
+ const gchar *username;
GError *local_error = NULL;
- cbdav = E_CAL_BACKEND_CALDAV (authenticator);
+ cbdav = E_CAL_BACKEND_CALDAV (backend);
- /* Busy lock is already acquired by caldav_do_open(). */
+ g_mutex_lock (&cbdav->priv->busy_lock);
- if (cbdav->priv->force_ask_password) {
- cbdav->priv->force_ask_password = FALSE;
- return E_SOURCE_AUTHENTICATION_REJECTED;
- }
+ g_free (cbdav->priv->username);
+ cbdav->priv->username = NULL;
g_free (cbdav->priv->password);
- cbdav->priv->password = g_strdup (password->str);
+ cbdav->priv->password = g_strdup (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD));
- open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL);
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (username && *username) {
+ cbdav->priv->username = g_strdup (username);
+ }
+
+ open_calendar_wrapper (cbdav, cancellable, &local_error, FALSE, NULL, out_certificate_pem,
out_certificate_errors);
if (local_error == NULL) {
result = E_SOURCE_AUTHENTICATION_ACCEPTED;
- } else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed)) {
- result = E_SOURCE_AUTHENTICATION_REJECTED;
+
+ update_slave_cmd (cbdav->priv, SLAVE_SHOULD_WORK);
+ g_cond_signal (&cbdav->priv->cond);
+ } else if (g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationFailed) ||
+ g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired)) {
+ if (!e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) ||
+ g_error_matches (local_error, E_DATA_CAL_ERROR, AuthenticationRequired))
+ result = E_SOURCE_AUTHENTICATION_REQUIRED;
+ else
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
g_clear_error (&local_error);
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+ g_propagate_error (error, local_error);
} else {
result = E_SOURCE_AUTHENTICATION_ERROR;
g_propagate_error (error, local_error);
}
- return result;
-}
+ g_mutex_unlock (&cbdav->priv->busy_lock);
-static void
-caldav_source_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = caldav_try_password_sync;
+ return result;
}
/* ************************************************************************* */
@@ -5320,6 +5405,7 @@ e_cal_backend_caldav_finalize (GObject *object)
g_cond_clear (&priv->slave_gone_cond);
g_free (priv->uri);
+ g_free (priv->username);
g_free (priv->password);
g_free (priv->schedule_outbox_url);
@@ -5456,12 +5542,14 @@ static void
e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
{
GObjectClass *object_class;
- ECalBackendClass *backend_class;
+ EBackendClass *backend_class;
+ ECalBackendClass *cal_backend_class;
ECalBackendSyncClass *sync_class;
- object_class = (GObjectClass *) class;
- backend_class = (ECalBackendClass *) class;
- sync_class = (ECalBackendSyncClass *) class;
+ object_class = G_OBJECT_CLASS (class);
+ backend_class = E_BACKEND_CLASS (class);
+ cal_backend_class = E_CAL_BACKEND_CLASS (class);
+ sync_class = E_CAL_BACKEND_SYNC_CLASS (class);
caldav_debug_init ();
@@ -5471,8 +5559,11 @@ e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
object_class->dispose = e_cal_backend_caldav_dispose;
object_class->finalize = e_cal_backend_caldav_finalize;
- backend_class->get_backend_property = caldav_get_backend_property;
- backend_class->shutdown = caldav_shutdown;
+ backend_class->authenticate_sync = caldav_authenticate_sync;
+
+ cal_backend_class->get_backend_property = caldav_get_backend_property;
+ cal_backend_class->shutdown = caldav_shutdown;
+ cal_backend_class->start_view = caldav_start_view;
sync_class->open_sync = caldav_do_open;
sync_class->refresh_sync = caldav_refresh;
@@ -5487,8 +5578,6 @@ e_cal_backend_caldav_class_init (ECalBackendCalDAVClass *class)
sync_class->get_object_list_sync = caldav_get_object_list;
sync_class->add_timezone_sync = caldav_add_timezone;
sync_class->get_free_busy_sync = caldav_get_free_busy;
-
- backend_class->start_view = caldav_start_view;
}
static void
diff --git a/calendar/backends/contacts/e-cal-backend-contacts.c
b/calendar/backends/contacts/e-cal-backend-contacts.c
index 6080478..2ab4b6b 100644
--- a/calendar/backends/contacts/e-cal-backend-contacts.c
+++ b/calendar/backends/contacts/e-cal-backend-contacts.c
@@ -144,7 +144,7 @@ create_book_record (ECalBackendContacts *cbc,
br->cbc = g_object_ref (cbc);
e_book_client_connect (
- source, NULL, book_client_connected_cb, br);
+ source, 30, NULL, book_client_connected_cb, br);
}
static BookRecord *
@@ -1146,6 +1146,10 @@ e_cal_backend_contacts_open (ECalBackendSync *backend,
if (priv->addressbook_loaded)
return;
+ /* Local source is always connected. */
+ e_source_set_connection_status (e_backend_get_source (E_BACKEND (backend)),
+ E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
priv->addressbook_loaded = TRUE;
e_cal_backend_set_writable (E_CAL_BACKEND (backend), FALSE);
e_backend_set_online (E_BACKEND (backend), TRUE);
diff --git a/calendar/backends/file/e-cal-backend-file.c b/calendar/backends/file/e-cal-backend-file.c
index e777379..e0b16a2 100644
--- a/calendar/backends/file/e-cal-backend-file.c
+++ b/calendar/backends/file/e-cal-backend-file.c
@@ -1381,6 +1381,10 @@ e_cal_backend_file_open (ECalBackendSync *backend,
priv = cbfile->priv;
g_rec_mutex_lock (&priv->idle_save_rmutex);
+ /* Local source is always connected. */
+ e_source_set_connection_status (e_backend_get_source (E_BACKEND (backend)),
+ E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
/* Claim a succesful open if we are already open */
if (priv->path && priv->comp_uid_hash) {
/* Success */
diff --git a/calendar/backends/gtasks/e-cal-backend-gtasks.c b/calendar/backends/gtasks/e-cal-backend-gtasks.c
index 2e668e4..63e4e6d 100644
--- a/calendar/backends/gtasks/e-cal-backend-gtasks.c
+++ b/calendar/backends/gtasks/e-cal-backend-gtasks.c
@@ -678,11 +678,9 @@ ecb_gtasks_request_authorization (ECalBackend *backend,
if (!GDATA_IS_CLIENT_LOGIN_AUTHORIZER (gtasks->priv->authorizer))
return TRUE;
- /* Otherwise it's up to us to obtain a login secret. */
- return e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, error);
+ /* Otherwise it's up to us to obtain a login secret, but
+ there is currently no way to do it, thus simply fail. */
+ return FALSE;
}
static gchar *
diff --git a/calendar/backends/http/e-cal-backend-http.c b/calendar/backends/http/e-cal-backend-http.c
index c13fe19..38599d7 100644
--- a/calendar/backends/http/e-cal-backend-http.c
+++ b/calendar/backends/http/e-cal-backend-http.c
@@ -34,17 +34,7 @@
#define EDC_ERROR(_code) e_data_cal_create_error (_code, NULL)
#define EDC_ERROR_EX(_code, _msg) e_data_cal_create_error (_code, _msg)
-/* Forward Declarations */
-static void e_cal_backend_http_source_authenticator_init
- (ESourceAuthenticatorInterface *iface);
-
-G_DEFINE_TYPE_WITH_CODE (
- ECalBackendHttp,
- e_cal_backend_http,
- E_TYPE_CAL_BACKEND_SYNC,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- e_cal_backend_http_source_authenticator_init))
+G_DEFINE_TYPE (ECalBackendHttp, e_cal_backend_http, E_TYPE_CAL_BACKEND_SYNC)
/* Private part of the ECalBackendHttp structure */
struct _ECalBackendHttpPrivate {
@@ -67,6 +57,7 @@ struct _ECalBackendHttpPrivate {
gboolean opened;
gboolean requires_auth;
+ gchar *username;
gchar *password;
};
@@ -89,7 +80,8 @@ soup_authenticate (SoupSession *session,
ESourceAuthentication *auth_extension;
ESource *source;
const gchar *extension_name;
- gchar *user;
+ const gchar *username;
+ gchar *auth_user;
if (retrying)
return;
@@ -100,14 +92,18 @@ soup_authenticate (SoupSession *session,
extension_name = E_SOURCE_EXTENSION_AUTHENTICATION;
auth_extension = e_source_get_extension (source, extension_name);
- user = e_source_authentication_dup_user (auth_extension);
+ auth_user = e_source_authentication_dup_user (auth_extension);
- if (!user || !*user)
+ username = cbhttp->priv->username;
+ if (!username || !*username)
+ username = auth_user;
+
+ if (!username || !*username || !cbhttp->priv->password)
soup_message_set_status (msg, SOUP_STATUS_FORBIDDEN);
- else if (cbhttp->priv->password != NULL)
- soup_auth_authenticate (auth, user, cbhttp->priv->password);
+ else
+ soup_auth_authenticate (auth, username, cbhttp->priv->password);
- g_free (user);
+ g_free (auth_user);
}
/* Dispose handler for the file backend */
@@ -158,6 +154,7 @@ e_cal_backend_http_finalize (GObject *object)
}
g_free (priv->uri);
+ g_free (priv->username);
g_free (priv->password);
/* Chain up to parent's finalize() method. */
@@ -453,10 +450,35 @@ cal_backend_http_cancelled (GCancellable *cancellable,
SOUP_STATUS_CANCELLED);
}
+static void
+cal_backend_http_extract_ssl_failed_data (SoupMessage *msg,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors)
+{
+ GTlsCertificate *certificate = NULL;
+
+ g_return_if_fail (SOUP_IS_MESSAGE (msg));
+
+ if (!out_certificate_pem || !out_certificate_errors)
+ return;
+
+ g_object_get (G_OBJECT (msg),
+ "tls-certificate", &certificate,
+ "tls-errors", out_certificate_errors,
+ NULL);
+
+ if (certificate) {
+ g_object_get (certificate, "certificate-pem", out_certificate_pem, NULL);
+ g_object_unref (certificate);
+ }
+}
+
static gboolean
cal_backend_http_load (ECalBackendHttp *backend,
- GCancellable *cancellable,
const gchar *uri,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
GError **error)
{
ECalBackendHttpPrivate *priv = backend->priv;
@@ -469,6 +491,7 @@ cal_backend_http_load (ECalBackendHttp *backend,
SoupURI *uri_parsed;
GHashTable *old_cache;
GSList *comps_in_cache;
+ ESource *source;
guint status_code;
gulong cancel_id = 0;
@@ -500,10 +523,11 @@ cal_backend_http_load (ECalBackendHttp *backend,
&cancel_data, (GDestroyNotify) NULL);
}
- e_soup_ssl_trust_connect (
- soup_message, e_backend_get_source (E_BACKEND (backend)),
- e_cal_backend_get_registry (E_CAL_BACKEND (backend)),
- cancellable);
+ source = e_backend_get_source (E_BACKEND (backend));
+
+ e_soup_ssl_trust_connect (soup_message, source);
+
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
status_code = soup_session_send_message (soup_session, soup_message);
@@ -511,6 +535,8 @@ cal_backend_http_load (ECalBackendHttp *backend,
g_cancellable_disconnect (cancellable, cancel_id);
if (status_code == SOUP_STATUS_NOT_MODIFIED) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
/* attempts with ETag can result in 304 status code */
g_object_unref (soup_message);
priv->opened = TRUE;
@@ -545,7 +571,7 @@ cal_backend_http_load (ECalBackendHttp *backend,
redirected_uri =
webcal_to_http_method (newuri, FALSE);
success = cal_backend_http_load (
- backend, cancellable, redirected_uri, error);
+ backend, redirected_uri, out_certificate_pem, out_certificate_errors,
cancellable, error);
g_free (redirected_uri);
} else {
@@ -556,6 +582,15 @@ cal_backend_http_load (ECalBackendHttp *backend,
success = FALSE;
}
+ if (success) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+ } else if (status_code == SOUP_STATUS_SSL_FAILED) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+ cal_backend_http_extract_ssl_failed_data (soup_message, out_certificate_pem,
out_certificate_errors);
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ }
+
g_object_unref (soup_message);
return success;
}
@@ -571,11 +606,21 @@ cal_backend_http_load (ECalBackendHttp *backend,
g_set_error (
error, SOUP_HTTP_ERROR, status_code,
"%s", soup_message->reason_phrase);
+
+ if (status_code == SOUP_STATUS_SSL_FAILED) {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+ cal_backend_http_extract_ssl_failed_data (soup_message, out_certificate_pem,
out_certificate_errors);
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ }
+
g_object_unref (soup_message);
empty_cache (backend);
return FALSE;
}
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+
if (priv->store) {
const gchar *etag;
@@ -739,6 +784,8 @@ begin_retrieval_cb (GIOSchedulerJob *job,
ECalBackendHttp *backend)
{
const gchar *uri;
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
GError *error = NULL;
if (!e_backend_get_online (E_BACKEND (backend)))
@@ -752,19 +799,33 @@ begin_retrieval_cb (GIOSchedulerJob *job,
backend->priv->is_loading = TRUE;
uri = cal_backend_http_ensure_uri (backend);
- cal_backend_http_load (backend, cancellable, uri, &error);
+ cal_backend_http_load (backend, uri, &certificate_pem, &certificate_errors, cancellable, &error);
+
+ if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
+ g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ GError *local_error = NULL;
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
+
+ if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ }
+
+ e_backend_credentials_required_sync (E_BACKEND (backend),
+ reason, certificate_pem, certificate_errors, error, cancellable, &local_error);
- if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
g_clear_error (&error);
- e_backend_authenticate_sync (
- E_BACKEND (backend),
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, &error);
+ error = local_error;
} else if (g_error_matches (error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) {
+ GError *local_error = NULL;
+
+ e_backend_credentials_required_sync (E_BACKEND (backend),
E_SOURCE_CREDENTIALS_REASON_REJECTED,
+ certificate_pem, certificate_errors, error, cancellable, &local_error);
+
g_clear_error (&error);
- error = EDC_ERROR (AuthenticationRequired);
+ error = local_error;
}
+ g_free (certificate_pem);
backend->priv->is_loading = FALSE;
/* Ignore cancellations. */
@@ -848,7 +909,6 @@ e_cal_backend_http_open (ECalBackendSync *backend,
ECalBackendHttp *cbhttp;
ECalBackendHttpPrivate *priv;
ESource *source;
- ESourceRegistry *registry;
ESourceWebdav *webdav_extension;
const gchar *extension_name;
const gchar *cache_dir;
@@ -866,8 +926,6 @@ e_cal_backend_http_open (ECalBackendSync *backend,
source = e_backend_get_source (E_BACKEND (backend));
cache_dir = e_cal_backend_get_cache_dir (E_CAL_BACKEND (backend));
- registry = e_cal_backend_get_registry (E_CAL_BACKEND (backend));
-
extension_name = E_SOURCE_EXTENSION_WEBDAV_BACKEND;
webdav_extension = e_source_get_extension (source, extension_name);
@@ -902,25 +960,39 @@ e_cal_backend_http_open (ECalBackendSync *backend,
e_cal_backend_set_writable (E_CAL_BACKEND (backend), FALSE);
if (e_backend_get_online (E_BACKEND (backend))) {
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
const gchar *uri;
uri = cal_backend_http_ensure_uri (cbhttp);
- opened = cal_backend_http_load (
- cbhttp, cancellable,
- uri, &local_error);
+ opened = cal_backend_http_load (cbhttp, uri, &certificate_pem,
+ &certificate_errors, cancellable, &local_error);
+
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED) ||
+ g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ GError *local_error2 = NULL;
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
- if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ }
+
+ e_backend_credentials_required_sync (E_BACKEND (cbhttp), reason, certificate_pem,
+ certificate_errors, local_error, cancellable, &local_error2);
g_clear_error (&local_error);
- opened = e_source_registry_authenticate_sync (
- registry, source,
- E_SOURCE_AUTHENTICATOR (backend),
- cancellable, &local_error);
+ local_error = local_error2;
} else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_FORBIDDEN)) {
+ GError *local_error2 = NULL;
+
+ e_backend_credentials_required_sync (E_BACKEND (cbhttp),
E_SOURCE_CREDENTIALS_REASON_REJECTED,
+ certificate_pem, certificate_errors, local_error, cancellable, &local_error2);
+
g_clear_error (&local_error);
- local_error = EDC_ERROR (AuthenticationRequired);
+ local_error = local_error2;
}
+ g_free (certificate_pem);
if (local_error != NULL)
g_propagate_error (perror, g_error_copy (local_error));
@@ -1407,29 +1479,42 @@ e_cal_backend_http_send_objects (ECalBackendSync *backend,
}
static ESourceAuthenticationResult
-cal_backend_http_try_password_sync (ESourceAuthenticator *authenticator,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
+e_cal_backend_http_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
- ECalBackendHttp *backend;
+ ECalBackendHttp *cbhttp;
ESourceAuthenticationResult result;
- const gchar *uri;
+ const gchar *uri, *username;
GError *local_error = NULL;
- backend = E_CAL_BACKEND_HTTP (authenticator);
+ cbhttp = E_CAL_BACKEND_HTTP (backend);
+
+ g_free (cbhttp->priv->username);
+ cbhttp->priv->username = NULL;
- g_free (backend->priv->password);
- backend->priv->password = g_strdup (password->str);
+ g_free (cbhttp->priv->password);
+ cbhttp->priv->password = g_strdup (e_named_parameters_get (credentials,
E_SOURCE_CREDENTIAL_PASSWORD));
- uri = cal_backend_http_ensure_uri (backend);
- cal_backend_http_load (backend, cancellable, uri, &local_error);
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (username && *username) {
+ cbhttp->priv->username = g_strdup (username);
+ }
+
+ uri = cal_backend_http_ensure_uri (cbhttp);
+ cal_backend_http_load (cbhttp, uri, out_certificate_pem, out_certificate_errors, cancellable,
&local_error);
if (local_error == NULL) {
result = E_SOURCE_AUTHENTICATION_ACCEPTED;
} else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
result = E_SOURCE_AUTHENTICATION_REJECTED;
g_clear_error (&local_error);
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+ g_propagate_error (error, local_error);
} else {
result = E_SOURCE_AUTHENTICATION_ERROR;
g_propagate_error (error, local_error);
@@ -1454,23 +1539,27 @@ static void
e_cal_backend_http_class_init (ECalBackendHttpClass *class)
{
GObjectClass *object_class;
- ECalBackendClass *backend_class;
+ EBackendClass *backend_class;
+ ECalBackendClass *cal_backend_class;
ECalBackendSyncClass *sync_class;
g_type_class_add_private (class, sizeof (ECalBackendHttpPrivate));
object_class = (GObjectClass *) class;
- backend_class = (ECalBackendClass *) class;
+ backend_class = E_BACKEND_CLASS (class);
+ cal_backend_class = (ECalBackendClass *) class;
sync_class = (ECalBackendSyncClass *) class;
object_class->dispose = e_cal_backend_http_dispose;
object_class->finalize = e_cal_backend_http_finalize;
object_class->constructed = e_cal_backend_http_constructed;
- /* Execute one method at a time. */
- backend_class->use_serial_dispatch_queue = TRUE;
+ backend_class->authenticate_sync = e_cal_backend_http_authenticate_sync;
- backend_class->get_backend_property = e_cal_backend_http_get_backend_property;
+ /* Execute one method at a time. */
+ cal_backend_class->use_serial_dispatch_queue = TRUE;
+ cal_backend_class->get_backend_property = e_cal_backend_http_get_backend_property;
+ cal_backend_class->start_view = e_cal_backend_http_start_view;
sync_class->open_sync = e_cal_backend_http_open;
sync_class->refresh_sync = e_cal_backend_http_refresh;
@@ -1483,13 +1572,4 @@ e_cal_backend_http_class_init (ECalBackendHttpClass *class)
sync_class->get_object_list_sync = e_cal_backend_http_get_object_list;
sync_class->add_timezone_sync = e_cal_backend_http_add_timezone;
sync_class->get_free_busy_sync = e_cal_backend_http_get_free_busy;
-
- backend_class->start_view = e_cal_backend_http_start_view;
}
-
-static void
-e_cal_backend_http_source_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = cal_backend_http_try_password_sync;
-}
-
diff --git a/calendar/libecal/e-cal-client.c b/calendar/libecal/e-cal-client.c
index f139d61..09d115b 100644
--- a/calendar/libecal/e-cal-client.c
+++ b/calendar/libecal/e-cal-client.c
@@ -104,6 +104,7 @@ struct _SignalClosure {
struct _ConnectClosure {
ESource *source;
GCancellable *cancellable;
+ guint32 wait_for_connected_seconds;
};
struct _RunInThreadClosure {
@@ -1132,6 +1133,33 @@ cal_client_refresh_sync (EClient *client,
return TRUE;
}
+static gboolean
+cal_client_retrieve_properties_sync (EClient *client,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ECalClient *cal_client;
+ gchar **properties = NULL;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_CAL_CLIENT (client), FALSE);
+
+ cal_client = E_CAL_CLIENT (client);
+
+ e_dbus_calendar_call_retrieve_properties_sync (cal_client->priv->dbus_proxy, &properties,
cancellable, &local_error);
+
+ cal_client_process_properties (cal_client, properties);
+ g_strfreev (properties);
+
+ if (local_error != NULL) {
+ g_dbus_error_strip_remote_error (local_error);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
static void
cal_client_init_in_dbus_thread (GSimpleAsyncResult *simple,
GObject *source_object,
@@ -1534,6 +1562,7 @@ e_cal_client_class_init (ECalClientClass *class)
client_class->set_backend_property_sync = cal_client_set_backend_property_sync;
client_class->open_sync = cal_client_open_sync;
client_class->refresh_sync = cal_client_refresh_sync;
+ client_class->retrieve_properties_sync = cal_client_retrieve_properties_sync;
g_object_class_install_property (
object_class,
@@ -1612,12 +1641,22 @@ e_cal_client_init (ECalClient *client)
* e_cal_client_connect_sync:
* @source: an #ESource
* @source_type: source type of the calendar
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Creates a new #ECalClient for @source and @source_type. If an error
* occurs, the function will set @error and return %FALSE.
*
+ * The @wait_for_connected_seconds argument had been added since 3.14,
+ * to let the caller decide how long to wait for the backend to fully
+ * connect to its (possibly remote) data store. This is required due
+ * to a change in the authentication process, which is fully asynchronous
+ * and done on the client side, while not every client is supposed to
+ * response to authentication requests. In case the backend will not connect
+ * within the set interval, then it is opened in an offline mode. A special
+ * value -1 can be used to not wait for the connected state at all.
+ *
* Unlike with e_cal_client_new(), there is no need to call
* e_client_open_sync() after obtaining the #ECalClient.
*
@@ -1632,6 +1671,7 @@ e_cal_client_init (ECalClient *client)
EClient *
e_cal_client_connect_sync (ESource *source,
ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error)
{
@@ -1661,6 +1701,12 @@ e_cal_client_connect_sync (ESource *source,
g_strfreev (properties);
}
+ if (!local_error && wait_for_connected_seconds != (guint32) -1) {
+ /* These errors are ignored, the book is left opened in an offline mode. */
+ e_client_wait_for_connected_sync (E_CLIENT (client),
+ wait_for_connected_seconds, cancellable, NULL);
+ }
+
if (local_error != NULL) {
g_dbus_error_strip_remote_error (local_error);
g_propagate_error (error, local_error);
@@ -1674,6 +1720,23 @@ e_cal_client_connect_sync (ESource *source,
return E_CLIENT (client);
}
+static void
+cal_client_connect_wait_for_connected_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+
+ simple = G_SIMPLE_ASYNC_RESULT (user_data);
+
+ /* These errors are ignored, the book is left opened in an offline mode. */
+ e_client_wait_for_connected_finish (E_CLIENT (source_object), result, NULL);
+
+ g_simple_async_result_complete (simple);
+
+ g_object_unref (simple);
+}
+
/* Helper for e_cal_client_connect() */
static void
cal_client_connect_open_cb (GObject *source_object,
@@ -1693,6 +1756,24 @@ cal_client_connect_open_cb (GObject *source_object,
client_object = g_async_result_get_source_object (G_ASYNC_RESULT (simple));
if (client_object) {
cal_client_process_properties (E_CAL_CLIENT (client_object), properties);
+
+ if (!local_error) {
+ ConnectClosure *closure;
+
+ closure = g_simple_async_result_get_op_res_gpointer (simple);
+ if (closure->wait_for_connected_seconds != (guint32) -1) {
+ e_client_wait_for_connected (E_CLIENT (client_object),
+ closure->wait_for_connected_seconds,
+ closure->cancellable,
+ cal_client_connect_wait_for_connected_cb, g_object_ref (simple));
+
+ g_clear_object (&client_object);
+ g_object_unref (simple);
+ g_strfreev (properties);
+ return;
+ }
+ }
+
g_clear_object (&client_object);
}
@@ -1753,6 +1834,7 @@ exit:
* e_cal_client_connect:
* @source: an #ESource
* @source_type: source tpe of the calendar
+ * @wait_for_connected_seconds: timeout, in seconds, to wait for the backend to be fully connected
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
* @callback: (scope async): a #GAsyncReadyCallback to call when the request
* is satisfied
@@ -1760,6 +1842,15 @@ exit:
*
* Asynchronously creates a new #ECalClient for @source and @source_type.
*
+ * The @wait_for_connected_seconds argument had been added since 3.14,
+ * to let the caller decide how long to wait for the backend to fully
+ * connect to its (possibly remote) data store. This is required due
+ * to a change in the authentication process, which is fully asynchronous
+ * and done on the client side, while not every client is supposed to
+ * response to authentication requests. In case the backend will not connect
+ * within the set interval, then it is opened in an offline mode. A special
+ * value -1 can be used to not wait for the connected state at all.
+ *
* Unlike with e_cal_client_new(), there is no need to call e_client_open()
* after obtaining the #ECalClient.
*
@@ -1771,6 +1862,7 @@ exit:
void
e_cal_client_connect (ESource *source,
ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
@@ -1793,6 +1885,7 @@ e_cal_client_connect (ESource *source,
closure = g_slice_new0 (ConnectClosure);
closure->source = g_object_ref (source);
+ closure->wait_for_connected_seconds = wait_for_connected_seconds;
if (G_IS_CANCELLABLE (cancellable))
closure->cancellable = g_object_ref (cancellable);
diff --git a/calendar/libecal/e-cal-client.h b/calendar/libecal/e-cal-client.h
index a0c2c42..25b4319 100644
--- a/calendar/libecal/e-cal-client.h
+++ b/calendar/libecal/e-cal-client.h
@@ -137,10 +137,12 @@ const gchar * e_cal_client_error_to_string (ECalClientError code);
GType e_cal_client_get_type (void) G_GNUC_CONST;
EClient * e_cal_client_connect_sync (ESource *source,
ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GError **error);
void e_cal_client_connect (ESource *source,
ECalClientSourceType source_type,
+ guint32 wait_for_connected_seconds,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
diff --git a/calendar/libedata-cal/e-cal-backend.c b/calendar/libedata-cal/e-cal-backend.c
index 5302151..6179986 100644
--- a/calendar/libedata-cal/e-cal-backend.c
+++ b/calendar/libedata-cal/e-cal-backend.c
@@ -706,24 +706,6 @@ cal_backend_constructed (GObject *object)
}
}
-static gboolean
-cal_backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error)
-{
- ECalBackend *cal_backend;
- ESourceRegistry *registry;
- ESource *source;
-
- cal_backend = E_CAL_BACKEND (backend);
- registry = e_cal_backend_get_registry (cal_backend);
- source = e_backend_get_source (backend);
-
- return e_source_registry_authenticate_sync (
- registry, source, auth, cancellable, error);
-}
-
static void
cal_backend_prepare_shutdown (EBackend *backend)
{
@@ -929,7 +911,6 @@ e_cal_backend_class_init (ECalBackendClass *class)
object_class->constructed = cal_backend_constructed;
backend_class = E_BACKEND_CLASS (class);
- backend_class->authenticate_sync = cal_backend_authenticate_sync;
backend_class->prepare_shutdown = cal_backend_prepare_shutdown;
class->get_backend_property = cal_backend_get_backend_property;
diff --git a/calendar/libedata-cal/e-data-cal.c b/calendar/libedata-cal/e-data-cal.c
index ee2b9dd..e291065 100644
--- a/calendar/libedata-cal/e-data-cal.c
+++ b/calendar/libedata-cal/e-data-cal.c
@@ -497,24 +497,20 @@ e_data_cal_create_error_fmt (EDataCalCallStatus status,
return error;
}
-static void
-data_cal_complete_open_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
+static GPtrArray *
+data_cal_encode_properties (EDBusCalendar *dbus_interface)
{
- AsyncContext *async_context = user_data;
- GError *error = NULL;
+ GPtrArray *properties_array;
- e_cal_backend_open_finish (
- E_CAL_BACKEND (source_object), result, &error);
+ g_warn_if_fail (E_DBUS_IS_CALENDAR (dbus_interface));
- if (error == NULL) {
- GPtrArray *properties_array;
+ properties_array = g_ptr_array_new_with_free_func (g_free);
+
+ if (dbus_interface) {
GParamSpec **properties;
guint ii, n_properties = 0;
- properties_array = g_ptr_array_new_with_free_func (g_free);
- properties = g_object_class_list_properties (G_OBJECT_GET_CLASS
(async_context->dbus_interface), &n_properties);
+ properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (dbus_interface),
&n_properties);
for (ii = 0; ii < n_properties; ii++) {
gboolean can_process =
@@ -533,7 +529,7 @@ data_cal_complete_open_cb (GObject *source_object,
GVariant *stored = NULL;
g_value_init (&value, properties[ii]->value_type);
- g_object_get_property ((GObject *) async_context->dbus_interface,
properties[ii]->name, &value);
+ g_object_get_property ((GObject *) dbus_interface, properties[ii]->name,
&value);
#define WORKOUT(gvl, gvr) \
if (g_type_is_a (properties[ii]->value_type, G_TYPE_ ## gvl)) \
@@ -563,8 +559,47 @@ data_cal_complete_open_cb (GObject *source_object,
}
g_free (properties);
+ }
+
+ g_ptr_array_add (properties_array, NULL);
+
+ return properties_array;
+}
+
+static gboolean
+data_cal_handle_retrieve_properties_cb (EDBusCalendar *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ EDataCal *data_cal)
+{
+ GPtrArray *properties_array;
+
+ properties_array = data_cal_encode_properties (dbus_interface);
+
+ e_dbus_calendar_complete_retrieve_properties (
+ dbus_interface,
+ invocation,
+ (const gchar * const *) properties_array->pdata);
- g_ptr_array_add (properties_array, NULL);
+ g_ptr_array_free (properties_array, TRUE);
+
+ return TRUE;
+}
+
+static void
+data_cal_complete_open_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ AsyncContext *async_context = user_data;
+ GError *error = NULL;
+
+ e_cal_backend_open_finish (
+ E_CAL_BACKEND (source_object), result, &error);
+
+ if (error == NULL) {
+ GPtrArray *properties_array;
+
+ properties_array = data_cal_encode_properties (async_context->dbus_interface);
e_dbus_calendar_complete_open (
async_context->dbus_interface,
@@ -2644,6 +2679,9 @@ e_data_cal_init (EDataCal *data_cal)
(GDestroyNotify) g_ptr_array_unref);
g_signal_connect (
+ dbus_interface, "handle-retrieve-properties",
+ G_CALLBACK (data_cal_handle_retrieve_properties_cb), data_cal);
+ g_signal_connect (
dbus_interface, "handle-open",
G_CALLBACK (data_cal_handle_open_cb), data_cal);
g_signal_connect (
diff --git a/configure.ac b/configure.ac
index 761fb1e..e25d929 100644
--- a/configure.ac
+++ b/configure.ac
@@ -39,8 +39,8 @@ m4_define([glib_minimum_version], [2.40])
m4_define([glib_encoded_version], [GLIB_VERSION_2_40])
dnl Keep these two definitions in agreement.
-m4_define([gdk_minimum_version], [3.2])
-m4_define([gdk_encoded_version], [GDK_VERSION_3_2])
+m4_define([gdk_minimum_version], [3.6])
+m4_define([gdk_encoded_version], [GDK_VERSION_3_6])
dnl Keep these two definitions in agreement.
m4_define([soup_minimum_version], [2.42])
@@ -73,9 +73,9 @@ GLIB_GSETTINGS
dnl ******************************
dnl D-Bus versioning
dnl ******************************
-ADDRESS_BOOK_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.AddressBook8"
-CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar6"
-SOURCES_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Sources3"
+ADDRESS_BOOK_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.AddressBook9"
+CALENDAR_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Calendar7"
+SOURCES_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.Sources4"
USER_PROMPTER_DBUS_SERVICE_NAME="org.gnome.evolution.dataserver.UserPrompter0"
AC_DEFINE_UNQUOTED(
@@ -106,27 +106,31 @@ AC_SUBST(USER_PROMPTER_DBUS_SERVICE_NAME)
dnl ******************************
dnl Libtool versioning
dnl ******************************
-LIBEDATASERVER_CURRENT=19
+LIBEDATASERVER_CURRENT=20
LIBEDATASERVER_REVISION=0
LIBEDATASERVER_AGE=0
-LIBECAL_CURRENT=17
+LIBEDATASERVERUI_CURRENT=1
+LIBEDATASERVERUI_REVISION=0
+LIBEDATASERVERUI_AGE=0
+
+LIBECAL_CURRENT=18
LIBECAL_REVISION=0
LIBECAL_AGE=0
-LIBEDATACAL_CURRENT=26
+LIBEDATACAL_CURRENT=27
LIBEDATACAL_REVISION=0
LIBEDATACAL_AGE=0
-LIBEDATABOOK_CURRENT=24
+LIBEDATABOOK_CURRENT=25
LIBEDATABOOK_REVISION=0
LIBEDATABOOK_AGE=0
-LIBEBOOK_CURRENT=18
+LIBEBOOK_CURRENT=19
LIBEBOOK_REVISION=1
LIBEBOOK_AGE=3
-LIBEBOOK_CONTACTS_CURRENT=0
+LIBEBOOK_CONTACTS_CURRENT=1
LIBEBOOK_CONTACTS_REVISION=0
LIBEBOOK_CONTACTS_AGE=0
@@ -134,7 +138,7 @@ LIBCAMEL_CURRENT=51
LIBCAMEL_REVISION=0
LIBCAMEL_AGE=0
-LIBEBACKEND_CURRENT=9
+LIBEBACKEND_CURRENT=10
LIBEBACKEND_REVISION=0
LIBEBACKEND_AGE=0
@@ -144,6 +148,9 @@ AC_SUBST(EDS_MICRO_VERSION)
AC_SUBST(LIBEDATASERVER_CURRENT)
AC_SUBST(LIBEDATASERVER_REVISION)
AC_SUBST(LIBEDATASERVER_AGE)
+AC_SUBST(LIBEDATASERVERUI_CURRENT)
+AC_SUBST(LIBEDATASERVERUI_REVISION)
+AC_SUBST(LIBEDATASERVERUI_AGE)
AC_SUBST(LIBECAL_CURRENT)
AC_SUBST(LIBECAL_REVISION)
AC_SUBST(LIBECAL_AGE)
@@ -1652,6 +1659,9 @@ AC_SUBST(imagesdir)
moduledir='${privlibdir}'/registry-modules
AC_SUBST(moduledir)
+credentialmoduledir='${privlibdir}'/credential-modules
+AC_SUBST(credentialmoduledir)
+
ebook_backenddir='${privlibdir}'/addressbook-backends
AC_SUBST(ebook_backenddir)
@@ -1798,6 +1808,8 @@ libebackend/libebackend.pc
libedataserver/Makefile
libedataserver/eds-version.h
libedataserver/libedataserver.pc
+libedataserverui/Makefile
+libedataserverui/libedataserverui.pc
modules/Makefile
modules/cache-reaper/Makefile
modules/gnome-online-accounts/Makefile
diff --git a/examples/cursor/cursor-data.c b/examples/cursor/cursor-data.c
index 6d217d7..c7fd19b 100644
--- a/examples/cursor/cursor-data.c
+++ b/examples/cursor/cursor-data.c
@@ -43,7 +43,7 @@ cursor_data_source_added (ESourceRegistry *registry,
return;
/* Open the address book */
- address_book = (EBookClient *) e_book_client_connect_sync (source, NULL, &error);
+ address_book = (EBookClient *) e_book_client_connect_sync (source, 30, NULL, &error);
if (!address_book)
g_error ("Unable to create the test book: %s", error->message);
diff --git a/libebackend/Makefile.am b/libebackend/Makefile.am
index 4cef9b0..dd4633f 100644
--- a/libebackend/Makefile.am
+++ b/libebackend/Makefile.am
@@ -55,20 +55,16 @@ libebackend_1_2_la_CPPFLAGS = \
libebackend_1_2_la_SOURCES = \
$(BUILT_SOURCES) \
- e-authentication-mediator.c \
- e-authentication-session.c \
e-backend.c \
e-backend-factory.c \
e-collection-backend.c \
e-collection-backend-factory.c \
e-data-factory.c \
e-dbus-server.c \
- e-extensible.c \
- e-extension.c \
e-oauth2-support.c \
e-offline-listener.c \
- e-module.c \
e-server-side-source.c \
+ e-server-side-source-credentials-provider.c \
e-soup-auth-bearer.c \
e-soup-ssl-trust.c \
e-source-registry-server.c \
@@ -102,8 +98,6 @@ libebackendincludedir = $(privincludedir)/libebackend
libebackendinclude_HEADERS = \
libebackend.h \
- e-authentication-mediator.h \
- e-authentication-session.h \
e-backend.h \
e-backend-enums.h \
e-backend-enumtypes.h \
@@ -112,12 +106,10 @@ libebackendinclude_HEADERS = \
e-collection-backend-factory.h \
e-data-factory.h \
e-dbus-server.h \
- e-extensible.h \
- e-extension.h \
e-oauth2-support.h \
e-offline-listener.h \
- e-module.h \
e-server-side-source.h \
+ e-server-side-source-credentials-provider.h \
e-soup-auth-bearer.h \
e-soup-ssl-trust.h \
e-source-registry-server.h \
diff --git a/libebackend/e-backend-factory.c b/libebackend/e-backend-factory.c
index ec95d26..6786656 100644
--- a/libebackend/e-backend-factory.c
+++ b/libebackend/e-backend-factory.c
@@ -37,8 +37,9 @@
#include <config.h>
+#include <libedataserver/libedataserver.h>
+
#include <libebackend/e-data-factory.h>
-#include "e-module.h"
G_DEFINE_ABSTRACT_TYPE (EBackendFactory, e_backend_factory, E_TYPE_EXTENSION)
diff --git a/libebackend/e-backend-factory.h b/libebackend/e-backend-factory.h
index 6fa61ca..6619694 100644
--- a/libebackend/e-backend-factory.h
+++ b/libebackend/e-backend-factory.h
@@ -22,8 +22,8 @@
#ifndef E_BACKEND_FACTORY_H
#define E_BACKEND_FACTORY_H
+#include <libedataserver/libedataserver.h>
#include <libebackend/e-backend.h>
-#include <libebackend/e-extension.h>
/* Standard GObject macros */
#define E_TYPE_BACKEND_FACTORY \
diff --git a/libebackend/e-backend.c b/libebackend/e-backend.c
index a020c4d..4398e76 100644
--- a/libebackend/e-backend.c
+++ b/libebackend/e-backend.c
@@ -50,8 +50,6 @@
#define G_IS_RESOLVER_ERROR(error, code) \
(g_error_matches ((error), G_RESOLVER_ERROR, (code)))
-typedef struct _AsyncContext AsyncContext;
-
struct _EBackendPrivate {
GMutex property_lock;
ESource *source;
@@ -68,10 +66,9 @@ struct _EBackendPrivate {
GMutex network_monitor_cancellable_lock;
GCancellable *network_monitor_cancellable;
-};
-struct _AsyncContext {
- ESourceAuthenticator *auth;
+ GMutex authenticate_cancellable_lock;
+ GCancellable *authenticate_cancellable;
};
enum {
@@ -86,15 +83,6 @@ enum {
G_DEFINE_ABSTRACT_TYPE (EBackend, e_backend, G_TYPE_OBJECT)
static void
-async_context_free (AsyncContext *async_context)
-{
- if (async_context->auth != NULL)
- g_object_unref (async_context->auth);
-
- g_slice_free (AsyncContext, async_context);
-}
-
-static void
backend_network_monitor_can_reach_cb (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
@@ -232,6 +220,173 @@ backend_network_changed_cb (GNetworkMonitor *network_monitor,
backend_update_online_state (backend);
}
+static ESourceAuthenticationResult
+e_backend_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EBackendClass *class;
+
+ g_return_val_if_fail (E_IS_BACKEND (backend), E_SOURCE_AUTHENTICATION_ERROR);
+ g_return_val_if_fail (credentials != NULL, E_SOURCE_AUTHENTICATION_ERROR);
+
+ class = E_BACKEND_GET_CLASS (backend);
+ g_return_val_if_fail (class->authenticate_sync != NULL, E_SOURCE_AUTHENTICATION_ERROR);
+
+ return class->authenticate_sync (backend, credentials, out_certificate_pem, out_certificate_errors,
cancellable, error);
+}
+
+typedef struct _AuthenticateThreadData {
+ EBackend *backend;
+ GCancellable *cancellable;
+ ENamedParameters *credentials;
+} AuthenticateThreadData;
+
+static AuthenticateThreadData *
+authenticate_thread_data_new (EBackend *backend,
+ GCancellable *cancellable,
+ const ENamedParameters *credentials)
+{
+ AuthenticateThreadData *data;
+
+ data = g_new0 (AuthenticateThreadData, 1);
+ data->backend = g_object_ref (backend);
+ data->cancellable = g_object_ref (cancellable);
+ data->credentials = credentials ? e_named_parameters_new_clone (credentials) : e_named_parameters_new
();
+
+ return data;
+}
+
+static void
+authenticate_thread_data_free (AuthenticateThreadData *data)
+{
+ if (data) {
+ g_clear_object (&data->backend);
+ g_clear_object (&data->cancellable);
+ e_named_parameters_free (data->credentials);
+ g_free (data);
+ }
+}
+
+static gpointer
+backend_source_authenticate_thread (gpointer user_data)
+{
+ ESourceAuthenticationResult auth_result;
+ AuthenticateThreadData *thread_data = user_data;
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
+ GError *local_error = NULL;
+ ESource *source;
+
+ g_return_val_if_fail (thread_data != NULL, NULL);
+
+ source = e_backend_get_source (thread_data->backend);
+
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTING);
+
+ /* Update the SSL trust transparently. */
+ if (e_named_parameters_get (thread_data->credentials, E_SOURCE_CREDENTIAL_SSL_TRUST) &&
+ e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND)) {
+ ESourceWebdav *webdav_extension;
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ e_source_webdav_set_ssl_trust (webdav_extension,
+ e_named_parameters_get (thread_data->credentials, E_SOURCE_CREDENTIAL_SSL_TRUST));
+ }
+
+ auth_result = e_backend_authenticate_sync (thread_data->backend, thread_data->credentials,
+ &certificate_pem, &certificate_errors, thread_data->cancellable, &local_error);
+
+ if (!g_cancellable_is_cancelled (thread_data->cancellable)) {
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
+
+ switch (auth_result) {
+ case E_SOURCE_AUTHENTICATION_ERROR:
+ reason = E_SOURCE_CREDENTIALS_REASON_ERROR;
+ break;
+ case E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
+ reason = E_SOURCE_CREDENTIALS_REASON_SSL_FAILED;
+ break;
+ case E_SOURCE_AUTHENTICATION_ACCEPTED:
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_CONNECTED);
+ break;
+ case E_SOURCE_AUTHENTICATION_REQUIRED:
+ reason = E_SOURCE_CREDENTIALS_REASON_REQUIRED;
+ break;
+ case E_SOURCE_AUTHENTICATION_REJECTED:
+ reason = E_SOURCE_CREDENTIALS_REASON_REJECTED;
+ break;
+ }
+
+ if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
+ const gchar *username = e_named_parameters_get (thread_data->credentials,
E_SOURCE_CREDENTIAL_USERNAME);
+ gboolean call_write = FALSE;
+
+ if (username && *username && e_source_has_extension (source,
E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *extension_authentication = e_source_get_extension
(source, E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ if (g_strcmp0 (username, e_source_authentication_get_user
(extension_authentication)) != 0) {
+ e_source_authentication_set_user (extension_authentication, username);
+ call_write = TRUE;
+ }
+ }
+
+ if (username && *username && e_source_has_extension (source,
E_SOURCE_EXTENSION_COLLECTION)) {
+ ESourceCollection *extension_collection = e_source_get_extension (source,
E_SOURCE_EXTENSION_COLLECTION);
+
+ if (g_strcmp0 (username, e_source_collection_get_identity
(extension_collection)) != 0) {
+ e_source_collection_set_identity (extension_collection, username);
+ call_write = TRUE;
+ }
+ }
+
+ if (call_write) {
+ GError *local_error2 = NULL;
+
+ if (!e_source_write_sync (source, thread_data->cancellable, &local_error2)) {
+ g_warning ("%s: Failed to store changed user name: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
+ }
+
+ g_clear_error (&local_error2);
+ }
+ } else {
+ GError *local_error2 = NULL;
+
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ if (!e_source_invoke_credentials_required_sync (source, reason, certificate_pem,
certificate_errors,
+ local_error, thread_data->cancellable, &local_error2)) {
+ g_warning ("%s: Failed to invoke credentials required: %s", G_STRFUNC,
local_error2 ? local_error2->message : "Unknown error");
+ }
+
+ g_clear_error (&local_error2);
+ }
+ } else {
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+ }
+
+ g_free (certificate_pem);
+ g_clear_error (&local_error);
+
+ authenticate_thread_data_free (thread_data);
+
+ return NULL;
+}
+
+static void
+backend_source_authenticate_cb (ESource *source,
+ const ENamedParameters *credentials,
+ EBackend *backend)
+{
+ g_return_if_fail (E_IS_BACKEND (backend));
+ g_return_if_fail (credentials != NULL);
+
+ e_backend_schedule_authenticate (backend, credentials);
+}
+
static void
backend_set_source (EBackend *backend,
ESource *source)
@@ -240,6 +395,8 @@ backend_set_source (EBackend *backend,
g_return_if_fail (backend->priv->source == NULL);
backend->priv->source = g_object_ref (source);
+
+ g_signal_connect (backend->priv->source, "authenticate", G_CALLBACK (backend_source_authenticate_cb),
backend);
}
static void
@@ -337,6 +494,17 @@ backend_dispose (GObject *object)
priv->update_online_state = NULL;
}
+ if (priv->source) {
+ g_signal_handlers_disconnect_by_func (priv->source, backend_source_authenticate_cb, object);
+ }
+
+ g_mutex_lock (&priv->authenticate_cancellable_lock);
+ if (priv->authenticate_cancellable) {
+ g_cancellable_cancel (priv->authenticate_cancellable);
+ g_clear_object (&priv->authenticate_cancellable);
+ }
+ g_mutex_unlock (&priv->authenticate_cancellable_lock);
+
g_clear_object (&priv->source);
g_clear_object (&priv->prompter);
g_clear_object (&priv->connectable);
@@ -357,6 +525,7 @@ backend_finalize (GObject *object)
g_mutex_clear (&priv->property_lock);
g_mutex_clear (&priv->update_online_state_lock);
g_mutex_clear (&priv->network_monitor_cancellable_lock);
+ g_mutex_clear (&priv->authenticate_cancellable_lock);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_backend_parent_class)->finalize (object);
@@ -390,86 +559,6 @@ backend_constructed (GObject *object)
}
}
-static void
-backend_authenticate_thread (GSimpleAsyncResult *simple,
- GObject *object,
- GCancellable *cancellable)
-{
- AsyncContext *async_context;
- GError *error = NULL;
-
- async_context = g_simple_async_result_get_op_res_gpointer (simple);
-
- e_backend_authenticate_sync (
- E_BACKEND (object),
- async_context->auth,
- cancellable, &error);
-
- if (error != NULL)
- g_simple_async_result_take_error (simple, error);
-}
-
-static gboolean
-backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error)
-{
- g_set_error (
- error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
- _("%s does not support authentication"),
- G_OBJECT_TYPE_NAME (backend));
-
- return FALSE;
-}
-
-static void
-backend_authenticate (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *simple;
- AsyncContext *async_context;
-
- async_context = g_slice_new0 (AsyncContext);
- async_context->auth = g_object_ref (auth);
-
- simple = g_simple_async_result_new (
- G_OBJECT (backend), callback,
- user_data, backend_authenticate);
-
- g_simple_async_result_set_check_cancellable (simple, cancellable);
-
- g_simple_async_result_set_op_res_gpointer (
- simple, async_context, (GDestroyNotify) async_context_free);
-
- g_simple_async_result_run_in_thread (
- simple, backend_authenticate_thread,
- G_PRIORITY_DEFAULT, cancellable);
-
- g_object_unref (simple);
-}
-
-static gboolean
-backend_authenticate_finish (EBackend *backend,
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (
- g_simple_async_result_is_valid (
- result, G_OBJECT (backend),
- backend_authenticate), FALSE);
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
-
- /* Assume success unless a GError is set. */
- return !g_simple_async_result_propagate_error (simple, error);
-}
-
static gboolean
backend_get_destination_address (EBackend *backend,
gchar **host,
@@ -499,9 +588,6 @@ e_backend_class_init (EBackendClass *class)
object_class->finalize = backend_finalize;
object_class->constructed = backend_constructed;
- class->authenticate_sync = backend_authenticate_sync;
- class->authenticate = backend_authenticate;
- class->authenticate_finish = backend_authenticate_finish;
class->get_destination_address = backend_get_destination_address;
class->prepare_shutdown = backend_prepare_shutdown;
@@ -577,6 +663,9 @@ e_backend_init (EBackend *backend)
g_mutex_init (&backend->priv->property_lock);
g_mutex_init (&backend->priv->update_online_state_lock);
g_mutex_init (&backend->priv->network_monitor_cancellable_lock);
+ g_mutex_init (&backend->priv->authenticate_cancellable_lock);
+
+ backend->priv->authenticate_cancellable = NULL;
/* Configure network monitoring. */
@@ -646,6 +735,9 @@ e_backend_set_online (EBackend *backend,
g_mutex_unlock (&backend->priv->network_monitor_cancellable_lock);
g_object_notify (G_OBJECT (backend), "online");
+
+ if (!backend->priv->online && backend->priv->source)
+ e_source_set_connection_status (backend->priv->source,
E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
}
/**
@@ -763,112 +855,256 @@ e_backend_ref_main_context (EBackend *backend)
}
/**
- * e_backend_authenticate_sync:
+ * e_backend_credentials_required_sync:
* @backend: an #EBackend
- * @auth: an #ESourceAuthenticator
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
- * Convenience function providing a consistent authentication interface
- * for backends running in either the registry service itself or a client
- * process communicating with the registry service over D-Bus.
+ * Synchronously lets the clients know that the backned requires credentials to be
+ * properly opened. It's a proxy function for e_source_invoke_credentials_required_sync(),
+ * where can be found more information about actual parameters meaning.
*
- * Authenticates @backend's #EBackend:source, using @auth to handle
- * authentication attempts. The @backend and @auth arguments may be one
- * and the same if @backend implements the #ESourceAuthenticator interface.
- * The operation loops until authentication is successful or the user aborts
- * further authentication attempts. If an error occurs, the function will
- * set @error and return %FALSE.
+ * The provided credentials are received through EBackend::authenticate_sync()
+ * method asynchronously.
*
- * Returns: %TRUE on success, %FALSE on failure
+ * If an error occurs, the function sets @error and returns %FALSE.
*
- * Since: 3.6
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
**/
gboolean
-e_backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error)
+e_backend_credentials_required_sync (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ GError **error)
{
- EBackendClass *class;
+ ESource *source;
g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
- g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE);
- class = E_BACKEND_GET_CLASS (backend);
- g_return_val_if_fail (class->authenticate_sync != NULL, FALSE);
+ source = e_backend_get_source (backend);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
- return class->authenticate_sync (backend, auth, cancellable, error);
+ return e_source_invoke_credentials_required_sync (source,
+ reason, certificate_pem, certificate_errors, op_error, cancellable, error);
+}
+
+typedef struct _CredentialsRequiredData {
+ ESourceCredentialsReason reason;
+ gchar *certificate_pem;
+ GTlsCertificateFlags certificate_errors;
+ GError *op_error;
+} CredentialsRequiredData;
+
+static void
+credentials_required_data_free (gpointer ptr)
+{
+ CredentialsRequiredData *data = ptr;
+
+ if (data) {
+ g_free (data->certificate_pem);
+ g_clear_error (&data->op_error);
+ g_free (data);
+ }
+}
+
+static void
+backend_credentials_required_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ CredentialsRequiredData *data = task_data;
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_backend_credentials_required_sync (
+ E_BACKEND (source_object), data->reason, data->certificate_pem,
+ data->certificate_errors, data->op_error,
+ cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
}
/**
- * e_backend_authenticate:
+ * e_backend_credentials_required:
* @backend: an #EBackend
- * @auth: an #ESourceAuthenticator
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
* @cancellable: optional #GCancellable object, or %NULL
- * @callback: a #GAsyncReadyCallback to call when the request is satisfied
- * @user_data: data to pass to the callback function
- *
- * Convenience function providing a consistent authentication interface
- * for backends running in either the registry service itself or a client
- * process communicating with the registry service over D-Bus.
+ * @error: return location for a #GError, or %NULL
*
- * Asynchronously authenticates @backend's #EBackend:source, using @auth
- * to handle authentication attempts. The @backend and @auth arguments may
- * be one and the same if @backend implements the #ESourceAuthenticator
- * interface. The operation loops until authentication is succesful or the
- * user aborts further authentication attempts.
+ * Asynchronously calls the e_backend_credentials_required_sync() on the @backend,
+ * to inform clients that credentials are required.
*
- * When the operation is finished, @callback will be called. You can then
- * call e_backend_authenticate_finish() to get the result of the operation.
+ * When the operation is finished, @callback will be called. You can then
+ * call e_backend_credentials_required_finish() to get the result of the operation.
*
- * Since: 3.6
+ * Since: 3.14
**/
void
-e_backend_authenticate (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+e_backend_credentials_required (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
- EBackendClass *class;
+ CredentialsRequiredData *data;
+ GTask *task;
g_return_if_fail (E_IS_BACKEND (backend));
- g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
- class = E_BACKEND_GET_CLASS (backend);
- g_return_if_fail (class->authenticate != NULL);
+ data = g_new0 (CredentialsRequiredData, 1);
+ data->reason = reason;
+ data->certificate_pem = g_strdup (certificate_pem);
+ data->certificate_errors = certificate_errors;
+ data->op_error = op_error ? g_error_copy (op_error) : NULL;
+
+ task = g_task_new (backend, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_backend_credentials_required);
+ g_task_set_task_data (task, data, credentials_required_data_free);
- class->authenticate (backend, auth, cancellable, callback, user_data);
+ g_task_run_in_thread (task, backend_credentials_required_thread);
+
+ g_object_unref (task);
}
/**
- * e_backend_authenticate_finish:
+ * e_backend_credentials_required_finish:
* @backend: an #EBackend
* @result: a #GAsyncResult
* @error: return location for a #GError, or %NULL
*
- * Finishes the operation started with e_backend_authenticate(). If
- * an error occurred, the function will set @error and return %FALSE.
+ * Finishes the operation started with e_backend_credentials_required().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
*
- * Returns: %TRUE on success, %FALSE on failure
+ * Returns: %TRUE on success, %FALSE on error
*
- * Since: 3.6
+ * Since: 3.14
**/
gboolean
-e_backend_authenticate_finish (EBackend *backend,
- GAsyncResult *result,
- GError **error)
+e_backend_credentials_required_finish (EBackend *backend,
+ GAsyncResult *result,
+ GError **error)
{
- EBackendClass *class;
-
g_return_val_if_fail (E_IS_BACKEND (backend), FALSE);
- g_return_val_if_fail (G_IS_ASYNC_RESULT (result), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, backend), FALSE);
- class = E_BACKEND_GET_CLASS (backend);
- g_return_val_if_fail (class->authenticate_finish != NULL, FALSE);
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_backend_credentials_required), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+backend_scheduled_credentials_required_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+ gchar *who_calls = user_data;
+
+ g_return_if_fail (E_IS_BACKEND (source_object));
+
+ if (!e_backend_credentials_required_finish (E_BACKEND (source_object), result, &error) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("%s: Failed to invoke credentials required: %s", who_calls ? who_calls : G_STRFUNC,
+ error ? error->message : "Unknown error");
+ }
+
+ g_clear_error (&error);
+ g_free (who_calls);
+}
+
+/**
+ * e_backend_schedule_credentials_required:
+ * @backend: an #EBackend
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @who_calls: (allow none): an identification who calls this
+ *
+ * Asynchronously invokes e_backend_credentials_required(), but installs its
+ * own callback which only prints a runtime warning on the console when
+ * the call fails. The @who_calls is a prefix of the console message.
+ * This is useful when the caller just wants to start the operation
+ * without having actual place where to show the operation result.
+ *
+ * Since: 3.14
+ **/
+void
+e_backend_schedule_credentials_required (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ const gchar *who_calls)
+{
+ g_return_if_fail (E_IS_BACKEND (backend));
+
+ e_backend_credentials_required (backend, reason, certificate_pem, certificate_errors,
+ op_error, cancellable, backend_scheduled_credentials_required_done_cb, g_strdup (who_calls));
+}
+
+/**
+ * e_backend_schedule_authenticate:
+ * @backend: an #EBackend
+ * @credentials: (allow none): a credentials to use to authenticate, or %NULL
+ *
+ * Schedules a new authenticate session, cancelling any previously run.
+ * This is usually done automatically, when an 'authenticate' signal is
+ * received for the associated #ESource. With %NULL @credentials an attempt
+ * without it is run.
+ *
+ * Since: 3.14
+ **/
+void
+e_backend_schedule_authenticate (EBackend *backend,
+ const ENamedParameters *credentials)
+{
+ GCancellable *cancellable;
+ AuthenticateThreadData *thread_data;
+
+ g_return_if_fail (E_IS_BACKEND (backend));
+
+ g_mutex_lock (&backend->priv->authenticate_cancellable_lock);
+ if (backend->priv->authenticate_cancellable) {
+ g_cancellable_cancel (backend->priv->authenticate_cancellable);
+ g_clear_object (&backend->priv->authenticate_cancellable);
+ }
+
+ backend->priv->authenticate_cancellable = g_cancellable_new ();
+ cancellable = g_object_ref (backend->priv->authenticate_cancellable);
+
+ g_mutex_unlock (&backend->priv->authenticate_cancellable_lock);
+
+ thread_data = authenticate_thread_data_new (backend, cancellable, credentials);
+
+ g_thread_unref (g_thread_new (NULL, backend_source_authenticate_thread, thread_data));
- return class->authenticate_finish (backend, result, error);
+ g_clear_object (&cancellable);
}
/**
diff --git a/libebackend/e-backend.h b/libebackend/e-backend.h
index 4859fb1..bed359a 100644
--- a/libebackend/e-backend.h
+++ b/libebackend/e-backend.h
@@ -83,25 +83,20 @@ struct _EBackendClass {
/*< public >*/
/* Methods */
- gboolean (*authenticate_sync) (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error);
- void (*authenticate) (EBackend *backend,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
- gboolean (*authenticate_finish) (EBackend *backend,
- GAsyncResult *result,
- GError **error);
-
gboolean (*get_destination_address)
(EBackend *backend,
gchar **host,
guint16 *port);
void (*prepare_shutdown) (EBackend *backend);
+ ESourceAuthenticationResult
+ (*authenticate_sync) (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error);
+
/*< private >*/
gpointer reserved[11];
};
@@ -116,18 +111,36 @@ GSocketConnectable *
void e_backend_set_connectable (EBackend *backend,
GSocketConnectable *connectable);
GMainContext * e_backend_ref_main_context (EBackend *backend);
-gboolean e_backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *auth,
+gboolean e_backend_credentials_required_sync
+ (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
GCancellable *cancellable,
GError **error);
-void e_backend_authenticate (EBackend *backend,
- ESourceAuthenticator *auth,
+void e_backend_credentials_required (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-gboolean e_backend_authenticate_finish (EBackend *backend,
+gboolean e_backend_credentials_required_finish
+ (EBackend *backend,
GAsyncResult *result,
GError **error);
+void e_backend_schedule_credentials_required
+ (EBackend *backend,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ const gchar *who_calls);
+void e_backend_schedule_authenticate (EBackend *backend,
+ const ENamedParameters *credentials);
struct _EUserPrompter *
e_backend_get_user_prompter (EBackend *backend);
ETrustPromptResponse
diff --git a/libebackend/e-collection-backend.c b/libebackend/e-collection-backend.c
index 1fdb2f1..0134460 100644
--- a/libebackend/e-collection-backend.c
+++ b/libebackend/e-collection-backend.c
@@ -787,36 +787,6 @@ collection_backend_constructed (GObject *object)
(GDestroyNotify) g_object_unref);
}
-static gboolean
-collection_backend_authenticate_sync (EBackend *backend,
- ESourceAuthenticator *authenticator,
- GCancellable *cancellable,
- GError **error)
-{
- ECollectionBackend *collection_backend;
- ESourceRegistryServer *server;
- EAuthenticationSession *session;
- ESource *source;
- const gchar *source_uid;
- gboolean success;
-
- source = e_backend_get_source (backend);
- source_uid = e_source_get_uid (source);
-
- collection_backend = E_COLLECTION_BACKEND (backend);
- server = e_collection_backend_ref_server (collection_backend);
- session = e_source_registry_server_new_auth_session (
- server, authenticator, source_uid);
-
- success = e_source_registry_server_authenticate_sync (
- server, session, cancellable, error);
-
- g_object_unref (session);
- g_object_unref (server);
-
- return success;
-}
-
static void
collection_backend_populate (ECollectionBackend *backend)
{
@@ -875,12 +845,6 @@ collection_backend_child_added (ECollectionBackend *backend,
e_server_side_source_set_removable (
E_SERVER_SIDE_SOURCE (child_source), FALSE);
- /* Collection children inherit the authentication session type. */
- g_object_bind_property (
- collection_source, "auth-session-type",
- child_source, "auth-session-type",
- G_BINDING_SYNC_CREATE);
-
/* Collection children inherit OAuth 2.0 support if available. */
g_object_bind_property (
collection_source, "oauth2-support",
@@ -1017,7 +981,6 @@ static void
e_collection_backend_class_init (ECollectionBackendClass *class)
{
GObjectClass *object_class;
- EBackendClass *backend_class;
g_type_class_add_private (class, sizeof (ECollectionBackendPrivate));
@@ -1028,9 +991,6 @@ e_collection_backend_class_init (ECollectionBackendClass *class)
object_class->finalize = collection_backend_finalize;
object_class->constructed = collection_backend_constructed;
- backend_class = E_BACKEND_CLASS (class);
- backend_class->authenticate_sync = collection_backend_authenticate_sync;
-
class->populate = collection_backend_populate;
class->dup_resource_id = collection_backend_dup_resource_id;
class->child_added = collection_backend_child_added;
@@ -1726,3 +1686,74 @@ e_collection_backend_delete_resource_finish (ECollectionBackend *backend,
return class->delete_resource_finish (backend, result, error);
}
+static void
+collection_backend_child_authenticate_done_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ESource *source;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source_object));
+
+ source = E_SOURCE (source_object);
+
+ if (!e_source_invoke_authenticate_finish (source, result, &error) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("%s: Failed to invoke authenticate for '%s': %s", G_STRFUNC,
+ e_source_get_uid (source), error ? error->message : "Unknown error");
+ }
+
+ g_clear_error (&error);
+}
+
+/**
+ * e_collection_backend_authenticate_children:
+ * @backend: an #ECollectionBackend
+ * @credentials: credentials to authenticate with
+ *
+ * Authenticates all enabled children sources with the given @crendetials.
+ * This is usually called when the collection source successfully used
+ * the @credentials to connect to the (possibly) remote data store, to
+ * open the childern too. Already connected child sources are skipped.
+ *
+ * Since: 3.14
+ **/
+void
+e_collection_backend_authenticate_children (ECollectionBackend *backend,
+ const ENamedParameters *credentials)
+{
+ ESource *master_source, *child, *cred_source;
+ ESourceRegistryServer *registry_server;
+ ESourceCredentialsProvider *credentials_provider;
+ GList *sources, *link;
+
+ g_return_if_fail (E_IS_COLLECTION_BACKEND (backend));
+
+ master_source = e_backend_get_source (E_BACKEND (backend));
+ g_return_if_fail (master_source != NULL);
+
+ registry_server = e_collection_backend_ref_server (backend);
+ g_return_if_fail (registry_server != NULL);
+
+ credentials_provider = e_source_registry_server_ref_credentials_provider (registry_server);
+ sources = e_source_registry_server_list_sources (registry_server, NULL);
+ for (link = sources; link; link = g_list_next (link)) {
+ child = link->data;
+
+ if (child && !e_source_equal (child, master_source) && e_source_get_enabled (child) && (
+ e_source_get_connection_status (child) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS
||
+ e_source_get_connection_status (child) == E_SOURCE_CONNECTION_STATUS_DISCONNECTED)) {
+ cred_source = e_source_credentials_provider_ref_credentials_source
(credentials_provider, child);
+
+ if (cred_source && e_source_equal (cred_source, master_source)) {
+ e_source_invoke_authenticate (child, credentials, NULL,
collection_backend_child_authenticate_done_cb, NULL);
+ }
+
+ g_clear_object (&cred_source);
+ }
+ }
+
+ g_clear_object (&credentials_provider);
+ g_clear_object (®istry_server);
+}
diff --git a/libebackend/e-collection-backend.h b/libebackend/e-collection-backend.h
index 90bc5bd..adb44bb 100644
--- a/libebackend/e-collection-backend.h
+++ b/libebackend/e-collection-backend.h
@@ -160,6 +160,9 @@ gboolean e_collection_backend_delete_resource_finish
(ECollectionBackend *backend,
GAsyncResult *result,
GError **error);
+void e_collection_backend_authenticate_children
+ (ECollectionBackend *backend,
+ const ENamedParameters *credentials);
G_END_DECLS
diff --git a/libebackend/e-data-factory.c b/libebackend/e-data-factory.c
index 0ef200f..e21d741 100644
--- a/libebackend/e-data-factory.c
+++ b/libebackend/e-data-factory.c
@@ -26,7 +26,8 @@
#include <config.h>
#include <glib/gi18n-lib.h>
-#include <libebackend/e-extensible.h>
+#include <libedataserver/libedataserver.h>
+
#include <libebackend/e-backend-factory.h>
#include <libebackend/e-dbus-server.h>
diff --git a/libebackend/e-dbus-server.c b/libebackend/e-dbus-server.c
index 511bf9e..ea5bdc4 100644
--- a/libebackend/e-dbus-server.c
+++ b/libebackend/e-dbus-server.c
@@ -31,8 +31,6 @@
#include <libedataserver/libedataserver.h>
-#include <libebackend/e-module.h>
-#include <libebackend/e-extensible.h>
#include <libebackend/e-backend-enumtypes.h>
#define E_DBUS_SERVER_GET_PRIVATE(obj) \
diff --git a/libebackend/e-server-side-source-credentials-provider.c
b/libebackend/e-server-side-source-credentials-provider.c
new file mode 100644
index 0000000..ce287e7
--- /dev/null
+++ b/libebackend/e-server-side-source-credentials-provider.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-server-side-source-credentials-provider.h"
+
+struct _EServerSideSourceCredentialsProviderPrivate {
+ gboolean dummy;
+};
+
+G_DEFINE_TYPE (EServerSideSourceCredentialsProvider, e_server_side_source_credentials_provider,
E_TYPE_SOURCE_CREDENTIALS_PROVIDER)
+
+static ESource *
+server_side_source_credentials_provider_ref_source (ESourceCredentialsProvider *provider,
+ const gchar *uid)
+{
+ ESource *source;
+ GObject *registry;
+
+ g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+ g_return_val_if_fail (uid, NULL);
+
+ registry = e_source_credentials_provider_ref_registry (provider);
+ if (registry) {
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (registry), NULL);
+
+ source = e_source_registry_server_ref_source (E_SOURCE_REGISTRY_SERVER (registry), uid);
+ }
+
+ g_clear_object (®istry);
+
+ return source;
+}
+
+static void
+e_server_side_source_credentials_provider_class_init (EServerSideSourceCredentialsProviderClass *class)
+{
+ ESourceCredentialsProviderClass *provider_class;
+
+ g_type_class_add_private (class, sizeof (EServerSideSourceCredentialsProviderPrivate));
+
+ provider_class = E_SOURCE_CREDENTIALS_PROVIDER_CLASS (class);
+ provider_class->ref_source = server_side_source_credentials_provider_ref_source;
+}
+
+static void
+e_server_side_source_credentials_provider_init (EServerSideSourceCredentialsProvider *provider)
+{
+ provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider,
E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER, EServerSideSourceCredentialsProviderPrivate);
+}
+
+/**
+ * e_server_side_source_credentials_provider_new:
+ * @registry: an #ESourceRegistryServer
+ *
+ * Creates a new #EServerSideSourceCredentialsProvider, which is meant to abstract
+ * credential management for #ESource<!-- -->-s.
+ *
+ * Returns: (transfer full): a new #EServerSideSourceCredentialsProvider
+ *
+ * Since: 3.14
+ **/
+ESourceCredentialsProvider *
+e_server_side_source_credentials_provider_new (ESourceRegistryServer *registry)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (registry), NULL);
+
+ return g_object_new (E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER,
+ "registry", registry,
+ NULL);
+}
diff --git a/libebackend/e-server-side-source-credentials-provider.h
b/libebackend/e-server-side-source-credentials-provider.h
new file mode 100644
index 0000000..e830447
--- /dev/null
+++ b/libebackend/e-server-side-source-credentials-provider.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
+#error "Only <libebackend/libebackend.h> should be included directly."
+#endif
+
+#ifndef E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_H
+#define E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libedataserver/libedataserver.h>
+#include <libebackend/e-source-registry-server.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER \
+ (e_server_side_source_credentials_provider_get_type ())
+#define E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER, EServerSideSourceCredentialsProvider))
+#define E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER, EServerSideSourceCredentialsProviderClass))
+#define E_IS_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER))
+#define E_IS_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER))
+#define E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER, EServerSideSourceCredentialsProviderClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EServerSideSourceCredentialsProvider EServerSideSourceCredentialsProvider;
+typedef struct _EServerSideSourceCredentialsProviderClass EServerSideSourceCredentialsProviderClass;
+typedef struct _EServerSideSourceCredentialsProviderPrivate EServerSideSourceCredentialsProviderPrivate;
+
+/**
+ * EServerSideSourceCredentialsProvider:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.14
+ **/
+struct _EServerSideSourceCredentialsProvider {
+ ESourceCredentialsProvider parent;
+ EServerSideSourceCredentialsProviderPrivate *priv;
+};
+
+struct _EServerSideSourceCredentialsProviderClass {
+ ESourceCredentialsProviderClass parent_class;
+};
+
+GType e_server_side_source_credentials_provider_get_type (void) G_GNUC_CONST;
+ESourceCredentialsProvider *
+ e_server_side_source_credentials_provider_new (ESourceRegistryServer *registry);
+G_END_DECLS
+
+#endif /* E_SERVER_SIDE_SOURCE_CREDENTIALS_PROVIDER_H */
diff --git a/libebackend/e-server-side-source.c b/libebackend/e-server-side-source.c
index 91f646c..8e3213e 100644
--- a/libebackend/e-server-side-source.c
+++ b/libebackend/e-server-side-source.c
@@ -27,6 +27,7 @@
#include "e-server-side-source.h"
#include <config.h>
+#include <stdio.h>
#include <glib/gi18n-lib.h>
/* Private D-Bus classes. */
@@ -52,9 +53,18 @@ struct _EServerSideSourcePrivate {
/* For comparison. */
gchar *file_contents;
- gboolean allow_auth_prompt;
- GType auth_session_type;
gchar *write_directory;
+
+ GMutex last_values_lock;
+ gchar *last_reason;
+ gchar *last_certificate_pem;
+ gchar *last_certificate_errors;
+ gchar *last_dbus_error_name;
+ gchar *last_dbus_error_message;
+ ENamedParameters *last_credentials;
+
+ GMutex pending_credentials_lookup_lock;
+ GCancellable *pending_credentials_lookup;
};
struct _AsyncContext {
@@ -66,8 +76,6 @@ struct _AsyncContext {
enum {
PROP_0,
- PROP_ALLOW_AUTH_PROMPT,
- PROP_AUTH_SESSION_TYPE,
PROP_EXPORTED,
PROP_FILE,
PROP_OAUTH2_SUPPORT,
@@ -192,14 +200,304 @@ server_side_source_traverse_cb (GNode *node,
return FALSE;
}
+static ESourceCredentialsReason
+server_side_source_credentials_reason_from_text (const gchar *arg_reason)
+{
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
+
+ if (arg_reason && *arg_reason) {
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
+ enum_value = g_enum_get_value_by_nick (enum_class, arg_reason);
+
+ if (enum_value) {
+ reason = enum_value->value;
+ } else {
+ g_warning ("%s: Unknown reason enum: '%s'", G_STRFUNC, arg_reason);
+ }
+
+ g_type_class_unref (enum_class);
+ }
+
+ return reason;
+}
+
+typedef struct _ReinvokeCredentialsRequiredData {
+ EServerSideSource *source;
+ gchar *arg_reason;
+ gchar *arg_certificate_pem;
+ gchar *arg_certificate_errors;
+ gchar *arg_dbus_error_name;
+ gchar *arg_dbus_error_message;
+} ReinvokeCredentialsRequiredData;
+
+static void
+reinvoke_credentials_required_data_free (gpointer ptr)
+{
+ ReinvokeCredentialsRequiredData *data = ptr;
+
+ if (data) {
+ g_clear_object (&data->source);
+ g_free (data->arg_reason);
+ g_free (data->arg_certificate_pem);
+ g_free (data->arg_certificate_errors);
+ g_free (data->arg_dbus_error_name);
+ g_free (data->arg_dbus_error_message);
+ g_free (data);
+ }
+}
+
+static void server_side_source_credentials_lookup_cb (GObject *source_object, GAsyncResult *result, gpointer
user_data);
+
static gboolean
-server_side_source_allow_auth_prompt_cb (EDBusSource *dbus_interface,
- GDBusMethodInvocation *invocation,
- EServerSideSource *source)
+server_side_source_invoke_credentials_required_cb (EDBusSource *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *arg_reason,
+ const gchar *arg_certificate_pem,
+ const gchar *arg_certificate_errors,
+ const gchar *arg_dbus_error_name,
+ const gchar *arg_dbus_error_message,
+ EServerSideSource *source)
{
- e_server_side_source_set_allow_auth_prompt (source, TRUE);
+ gboolean skip_emit = FALSE;
+
+ if (invocation)
+ e_dbus_source_complete_invoke_credentials_required (dbus_interface, invocation);
- e_dbus_source_complete_allow_auth_prompt (dbus_interface, invocation);
+ g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
+ if (source->priv->pending_credentials_lookup) {
+ g_cancellable_cancel (source->priv->pending_credentials_lookup);
+ g_clear_object (&source->priv->pending_credentials_lookup);
+ }
+ g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
+
+ g_mutex_lock (&source->priv->last_values_lock);
+
+ g_free (source->priv->last_reason);
+ g_free (source->priv->last_certificate_pem);
+ g_free (source->priv->last_certificate_errors);
+ g_free (source->priv->last_dbus_error_name);
+ g_free (source->priv->last_dbus_error_message);
+ source->priv->last_reason = g_strdup (arg_reason);
+ source->priv->last_certificate_pem = g_strdup (arg_certificate_pem);
+ source->priv->last_certificate_errors = g_strdup (arg_certificate_errors);
+ source->priv->last_dbus_error_name = g_strdup (arg_dbus_error_name);
+ source->priv->last_dbus_error_message = g_strdup (arg_dbus_error_message);
+
+ g_mutex_unlock (&source->priv->last_values_lock);
+
+ /* Do not bother clients, when the password is stored. */
+ if (server_side_source_credentials_reason_from_text (arg_reason) ==
E_SOURCE_CREDENTIALS_REASON_REQUIRED) {
+ ESourceRegistryServer *server;
+ ESourceCredentialsProvider *credentials_provider;
+
+ server = e_server_side_source_get_server (source);
+ credentials_provider = server ? e_source_registry_server_ref_credentials_provider (server) :
NULL;
+
+ if (credentials_provider) {
+ ReinvokeCredentialsRequiredData *data;
+ GCancellable *cancellable;
+
+ g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
+ if (source->priv->pending_credentials_lookup) {
+ g_cancellable_cancel (source->priv->pending_credentials_lookup);
+ g_clear_object (&source->priv->pending_credentials_lookup);
+ }
+ cancellable = g_cancellable_new ();
+ source->priv->pending_credentials_lookup = g_object_ref (cancellable);
+ g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
+
+ data = g_new0 (ReinvokeCredentialsRequiredData, 1);
+ data->source = g_object_ref (source);
+ data->arg_reason = g_strdup (arg_reason);
+ data->arg_certificate_pem = g_strdup (arg_certificate_pem);
+ data->arg_certificate_errors = g_strdup (arg_certificate_errors);
+ data->arg_dbus_error_name = g_strdup (arg_dbus_error_name);
+ data->arg_dbus_error_message = g_strdup (arg_dbus_error_message);
+
+ skip_emit = TRUE;
+
+ e_source_credentials_provider_lookup (credentials_provider, E_SOURCE (source),
+ cancellable, server_side_source_credentials_lookup_cb, data);
+
+ g_object_unref (cancellable);
+ }
+
+ g_clear_object (&credentials_provider);
+ }
+
+ if (!skip_emit) {
+ e_dbus_source_emit_credentials_required (dbus_interface, arg_reason, arg_certificate_pem,
arg_certificate_errors, arg_dbus_error_name, arg_dbus_error_message);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+server_side_source_invoke_authenticate_cb (EDBusSource *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ const gchar * const *arg_credentials,
+ EServerSideSource *source)
+{
+ gchar **last_credentials_strv = NULL;
+
+ g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), TRUE);
+
+ g_mutex_lock (&source->priv->pending_credentials_lookup_lock);
+ if (source->priv->pending_credentials_lookup) {
+ g_cancellable_cancel (source->priv->pending_credentials_lookup);
+ g_clear_object (&source->priv->pending_credentials_lookup);
+ }
+ g_mutex_unlock (&source->priv->pending_credentials_lookup_lock);
+
+ g_mutex_lock (&source->priv->last_values_lock);
+
+ /* Empty credentials are used to use the last credentials instead */
+ if (source->priv->last_credentials && arg_credentials && !arg_credentials[0]) {
+ last_credentials_strv = e_named_parameters_to_strv (source->priv->last_credentials);
+ arg_credentials = (const gchar * const *) last_credentials_strv;
+ } else if (arg_credentials && arg_credentials[0]) {
+ e_named_parameters_free (source->priv->last_credentials);
+ source->priv->last_credentials = e_named_parameters_new_strv (arg_credentials);
+ }
+
+ g_free (source->priv->last_reason);
+ g_free (source->priv->last_certificate_pem);
+ g_free (source->priv->last_certificate_errors);
+ g_free (source->priv->last_dbus_error_name);
+ g_free (source->priv->last_dbus_error_message);
+ source->priv->last_reason = NULL;
+ source->priv->last_certificate_pem = NULL;
+ source->priv->last_certificate_errors = NULL;
+ source->priv->last_dbus_error_name = NULL;
+ source->priv->last_dbus_error_message = NULL;
+
+ g_mutex_unlock (&source->priv->last_values_lock);
+
+ if (invocation)
+ e_dbus_source_complete_invoke_authenticate (dbus_interface, invocation);
+
+ e_dbus_source_emit_authenticate (dbus_interface, arg_credentials);
+
+ g_strfreev (last_credentials_strv);
+
+ return TRUE;
+}
+
+static void
+server_side_source_credentials_lookup_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GDBusObject *dbus_object;
+ EDBusSource *dbus_source;
+ ReinvokeCredentialsRequiredData *data = user_data;
+ ENamedParameters *credentials = NULL;
+ gboolean success;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (source_object));
+ g_return_if_fail (data != NULL);
+
+ success = e_source_credentials_provider_lookup_finish (E_SOURCE_CREDENTIALS_PROVIDER (source_object),
result, &credentials, &error);
+
+ dbus_object = e_source_ref_dbus_object (E_SOURCE (data->source));
+ if (!dbus_object) {
+ e_named_parameters_free (credentials);
+ g_warn_if_fail (dbus_object != NULL);
+ reinvoke_credentials_required_data_free (data);
+ return;
+ }
+
+ dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
+ if (!dbus_source) {
+ e_named_parameters_free (credentials);
+ g_warn_if_fail (dbus_source != NULL);
+ g_object_unref (dbus_object);
+ reinvoke_credentials_required_data_free (data);
+ return;
+ }
+
+ if (success && credentials) {
+ gchar **arg_credentials;
+
+ arg_credentials = e_named_parameters_to_strv (credentials);
+
+ server_side_source_invoke_authenticate_cb (dbus_source, NULL,
+ (const gchar * const *) arg_credentials, data->source);
+
+ g_strfreev (arg_credentials);
+ } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ if (error) {
+ printf ("%s: Failed to lookup password: %s\n", G_STRFUNC, error ? error->message :
"Unknown error");
+ fflush (stdout);
+ }
+
+ if (server_side_source_credentials_reason_from_text (data->arg_reason) ==
E_SOURCE_CREDENTIALS_REASON_REQUIRED &&
+ error && !e_source_credentials_provider_can_prompt (E_SOURCE_CREDENTIALS_PROVIDER
(source_object), E_SOURCE (data->source))) {
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ gchar *dbus_error_name;
+
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
+ enum_value = g_enum_get_value (enum_class, E_SOURCE_CREDENTIALS_REASON_ERROR);
+
+ g_return_if_fail (enum_value != NULL);
+
+ dbus_error_name = g_dbus_error_encode_gerror (error);
+
+ /* Use error reason when the source cannot be prompted for credentials */
+ e_dbus_source_emit_credentials_required (dbus_source, enum_value->value_nick,
+ data->arg_certificate_pem, data->arg_certificate_errors, dbus_error_name,
error->message);
+
+ g_type_class_unref (enum_class);
+ g_free (dbus_error_name);
+ } else {
+ /* Reinvoke for clients only, if not cancelled */
+ const gchar *arg_dbus_error_name, *arg_dbus_error_message;
+ gchar *dbus_error_name = NULL;
+
+ arg_dbus_error_name = data->arg_dbus_error_name;
+ arg_dbus_error_message = data->arg_dbus_error_message;
+
+ if (!arg_dbus_error_name || !*arg_dbus_error_name) {
+ dbus_error_name = g_dbus_error_encode_gerror (error);
+ arg_dbus_error_name = dbus_error_name;
+ arg_dbus_error_message = error->message;
+ }
+
+ e_dbus_source_emit_credentials_required (dbus_source, data->arg_reason,
+ data->arg_certificate_pem, data->arg_certificate_errors,
+ arg_dbus_error_name, arg_dbus_error_message);
+
+ g_free (dbus_error_name);
+ }
+ }
+
+ e_named_parameters_free (credentials);
+ reinvoke_credentials_required_data_free (data);
+ g_object_unref (dbus_source);
+ g_object_unref (dbus_object);
+ g_clear_error (&error);
+}
+
+static gboolean
+server_side_source_get_last_credentials_required_arguments_cb (EDBusSource *dbus_interface,
+ GDBusMethodInvocation *invocation,
+ EServerSideSource *source)
+{
+ g_mutex_lock (&source->priv->last_values_lock);
+
+ e_dbus_source_complete_get_last_credentials_required_arguments (dbus_interface, invocation,
+ source->priv->last_reason ? source->priv->last_reason : "",
+ source->priv->last_certificate_pem ? source->priv->last_certificate_pem : "",
+ source->priv->last_certificate_errors ? source->priv->last_certificate_errors : "",
+ source->priv->last_dbus_error_name ? source->priv->last_dbus_error_name : "",
+ source->priv->last_dbus_error_message ? source->priv->last_dbus_error_message : "");
+
+ g_mutex_unlock (&source->priv->last_values_lock);
return TRUE;
}
@@ -520,18 +818,6 @@ server_side_source_set_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_ALLOW_AUTH_PROMPT:
- e_server_side_source_set_allow_auth_prompt (
- E_SERVER_SIDE_SOURCE (object),
- g_value_get_boolean (value));
- return;
-
- case PROP_AUTH_SESSION_TYPE:
- e_server_side_source_set_auth_session_type (
- E_SERVER_SIDE_SOURCE (object),
- g_value_get_gtype (value));
- return;
-
case PROP_FILE:
server_side_source_set_file (
E_SERVER_SIDE_SOURCE (object),
@@ -591,20 +877,6 @@ server_side_source_get_property (GObject *object,
GParamSpec *pspec)
{
switch (property_id) {
- case PROP_ALLOW_AUTH_PROMPT:
- g_value_set_boolean (
- value,
- e_server_side_source_get_allow_auth_prompt (
- E_SERVER_SIDE_SOURCE (object)));
- return;
-
- case PROP_AUTH_SESSION_TYPE:
- g_value_set_gtype (
- value,
- e_server_side_source_get_auth_session_type (
- E_SERVER_SIDE_SOURCE (object)));
- return;
-
case PROP_EXPORTED:
g_value_set_boolean (
value,
@@ -679,6 +951,31 @@ server_side_source_dispose (GObject *object)
priv = E_SERVER_SIDE_SOURCE_GET_PRIVATE (object);
+ g_mutex_lock (&priv->last_values_lock);
+
+ g_free (priv->last_reason);
+ g_free (priv->last_certificate_pem);
+ g_free (priv->last_certificate_errors);
+ g_free (priv->last_dbus_error_name);
+ g_free (priv->last_dbus_error_message);
+ priv->last_reason = NULL;
+ priv->last_certificate_pem = NULL;
+ priv->last_certificate_errors = NULL;
+ priv->last_dbus_error_name = NULL;
+ priv->last_dbus_error_message = NULL;
+
+ e_named_parameters_free (priv->last_credentials);
+ priv->last_credentials = NULL;
+
+ g_mutex_unlock (&priv->last_values_lock);
+
+ g_mutex_lock (&priv->pending_credentials_lookup_lock);
+ if (priv->pending_credentials_lookup) {
+ g_cancellable_cancel (priv->pending_credentials_lookup);
+ g_clear_object (&priv->pending_credentials_lookup);
+ }
+ g_mutex_unlock (&priv->pending_credentials_lookup_lock);
+
if (priv->server != NULL) {
g_object_remove_weak_pointer (
G_OBJECT (priv->server), &priv->server);
@@ -709,6 +1006,8 @@ server_side_source_finalize (GObject *object)
g_free (priv->write_directory);
g_weak_ref_clear (&priv->oauth2_support);
+ g_mutex_clear (&priv->last_values_lock);
+ g_mutex_clear (&priv->pending_credentials_lookup_lock);
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_server_side_source_parent_class)->finalize (object);
@@ -1125,6 +1424,41 @@ server_side_source_get_oauth2_access_token_sync (ESource *source,
}
static gboolean
+server_side_source_invoke_credentials_required_impl (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar *arg_reason,
+ const gchar *arg_certificate_pem,
+ const gchar *arg_certificate_errors,
+ const gchar *arg_dbus_error_name,
+ const gchar *arg_dbus_error_message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
+
+ return server_side_source_invoke_credentials_required_cb (dbus_source, NULL,
+ arg_reason ? arg_reason : "",
+ arg_certificate_pem ? arg_certificate_pem : "",
+ arg_certificate_errors ? arg_certificate_errors : "",
+ arg_dbus_error_name ? arg_dbus_error_name : "",
+ arg_dbus_error_message ? arg_dbus_error_message : "",
+ E_SERVER_SIDE_SOURCE (source));
+}
+
+static gboolean
+server_side_source_invoke_authenticate_impl (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar * const *arg_credentials,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
+
+ return server_side_source_invoke_authenticate_cb (dbus_source, NULL,
+ arg_credentials, E_SERVER_SIDE_SOURCE (source));
+}
+
+static gboolean
server_side_source_initable_init (GInitable *initable,
GCancellable *cancellable,
GError **error)
@@ -1150,8 +1484,14 @@ server_side_source_initable_init (GInitable *initable,
g_object_unref (dbus_object);
g_signal_connect (
- dbus_source, "handle-allow-auth-prompt",
- G_CALLBACK (server_side_source_allow_auth_prompt_cb), source);
+ dbus_source, "handle-invoke-credentials-required",
+ G_CALLBACK (server_side_source_invoke_credentials_required_cb), source);
+ g_signal_connect (
+ dbus_source, "handle-invoke-authenticate",
+ G_CALLBACK (server_side_source_invoke_authenticate_cb), source);
+ g_signal_connect (
+ dbus_source, "handle-get-last-credentials-required-arguments",
+ G_CALLBACK (server_side_source_get_last_credentials_required_arguments_cb), source);
g_object_unref (dbus_source);
@@ -1184,37 +1524,11 @@ e_server_side_source_class_init (EServerSideSourceClass *class)
source_class->write_sync = server_side_source_write_sync;
source_class->write = server_side_source_write;
source_class->write_finish = server_side_source_write_finish;
- source_class->remote_create_sync =
- server_side_source_remote_create_sync;
- source_class->remote_delete_sync =
- server_side_source_remote_delete_sync;
- source_class->get_oauth2_access_token_sync =
- server_side_source_get_oauth2_access_token_sync;
-
- g_object_class_install_property (
- object_class,
- PROP_ALLOW_AUTH_PROMPT,
- g_param_spec_boolean (
- "allow-auth-prompt",
- "Allow Auth Prompt",
- "Whether authentication sessions may "
- "interrupt the user for a password",
- TRUE,
- G_PARAM_READWRITE |
- G_PARAM_CONSTRUCT |
- G_PARAM_STATIC_STRINGS));
-
- g_object_class_install_property (
- object_class,
- PROP_AUTH_SESSION_TYPE,
- g_param_spec_gtype (
- "auth-session-type",
- "Auth Session Type",
- "The type of authentication session "
- "to instantiate for this source",
- E_TYPE_AUTHENTICATION_SESSION,
- G_PARAM_READWRITE |
- G_PARAM_STATIC_STRINGS));
+ source_class->remote_create_sync = server_side_source_remote_create_sync;
+ source_class->remote_delete_sync = server_side_source_remote_delete_sync;
+ source_class->get_oauth2_access_token_sync = server_side_source_get_oauth2_access_token_sync;
+ source_class->invoke_credentials_required_impl = server_side_source_invoke_credentials_required_impl;
+ source_class->invoke_authenticate_impl = server_side_source_invoke_authenticate_impl;
g_object_class_install_property (
object_class,
@@ -1350,9 +1664,9 @@ e_server_side_source_init (EServerSideSource *source)
user_dir = e_server_side_source_get_user_dir ();
source->priv->write_directory = g_strdup (user_dir);
- source->priv->auth_session_type = E_TYPE_AUTHENTICATION_SESSION;
-
g_weak_ref_init (&source->priv->oauth2_support, NULL);
+ g_mutex_init (&source->priv->last_values_lock);
+ g_mutex_init (&source->priv->pending_credentials_lookup_lock);
}
/**
@@ -1710,122 +2024,6 @@ e_server_side_source_get_server (EServerSideSource *source)
}
/**
- * e_server_side_source_get_allow_auth_prompt:
- * @source: an #EServerSideSource
- *
- * Returns whether an authentication prompt is allowed to be shown
- * for @source. #EAuthenticationSession will honor this setting by
- * dismissing the session if it can't find a valid stored password.
- *
- * See e_server_side_source_set_allow_auth_prompt() for further
- * discussion.
- *
- * Returns: whether auth prompts are allowed for @source
- *
- * Since: 3.6
- **/
-gboolean
-e_server_side_source_get_allow_auth_prompt (EServerSideSource *source)
-{
- g_return_val_if_fail (E_IS_SERVER_SIDE_SOURCE (source), FALSE);
-
- return source->priv->allow_auth_prompt;
-}
-
-/**
- * e_server_side_source_set_allow_auth_prompt:
- * @source: an #EServerSideSource
- * @allow_auth_prompt: whether auth prompts are allowed for @source
- *
- * Sets whether an authentication prompt is allowed to be shown for @source.
- * #EAuthenticationSession will honor this setting by dismissing the session
- * if it can't find a valid stored password.
- *
- * If the user declines to provide a password for @source when prompted
- * by an #EAuthenticationSession, the #ESourceRegistryServer will set this
- * property to %FALSE to suppress any further prompting, which would likely
- * annoy the user. However when an #ESourceRegistry instance is created by
- * a client application, the first thing it does is reset this property to
- * %TRUE for all registered data sources. So suppressing authentication
- * prompts is only ever temporary.
- *
- * Since: 3.6
- **/
-void
-e_server_side_source_set_allow_auth_prompt (EServerSideSource *source,
- gboolean allow_auth_prompt)
-{
- g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
-
- if (source->priv->allow_auth_prompt == allow_auth_prompt)
- return;
-
- source->priv->allow_auth_prompt = allow_auth_prompt;
-
- g_object_notify (G_OBJECT (source), "allow-auth-prompt");
-}
-
-/**
- * e_server_side_source_get_auth_session_type:
- * @source: an #EServerSideSource
- *
- * Returns the type of authentication session to use for @source,
- * which will get passed to e_source_registry_server_authenticate().
- *
- * This defaults to the #GType for #EAuthenticationSession.
- *
- * Returns: the type of authentication session to use for @source
- *
- * Since: 3.8
- **/
-GType
-e_server_side_source_get_auth_session_type (EServerSideSource *source)
-{
- g_return_val_if_fail (
- E_IS_SERVER_SIDE_SOURCE (source),
- E_TYPE_AUTHENTICATION_SESSION);
-
- return source->priv->auth_session_type;
-}
-
-/**
- * e_server_side_source_set_auth_session_type:
- * @source: an #EServerSideSource
- * @auth_session_type: the type of authentication session to use for @source
- *
- * Sets the type of authentication session to use for @source, which will
- * get passed to e_source_registry_server_authenticate().
- *
- * @auth_session_type must be derived from #EAuthenticationSession, or else
- * the function will reject the #GType with a runtime warning.
- *
- * This defaults to the #GType for #EAuthenticationSession.
- *
- * Since: 3.8
- **/
-void
-e_server_side_source_set_auth_session_type (EServerSideSource *source,
- GType auth_session_type)
-{
- g_return_if_fail (E_IS_SERVER_SIDE_SOURCE (source));
-
- if (!g_type_is_a (auth_session_type, E_TYPE_AUTHENTICATION_SESSION)) {
- g_warning (
- "Invalid auth session type '%s' for source '%s'",
- g_type_name (auth_session_type),
- e_source_get_display_name (E_SOURCE (source)));
- return;
- }
-
- if (auth_session_type == source->priv->auth_session_type)
- return;
-
- source->priv->auth_session_type = auth_session_type;
-
- g_object_notify (G_OBJECT (source), "auth-session-type");
-}
-
-/**
* e_server_side_source_get_exported:
* @source: an #EServerSideSource
*
@@ -2205,4 +2403,3 @@ e_server_side_source_set_oauth2_support (EServerSideSource *source,
g_object_notify (G_OBJECT (source), "oauth2-support");
}
-
diff --git a/libebackend/e-server-side-source.h b/libebackend/e-server-side-source.h
index cd7ac31..c5fca9d 100644
--- a/libebackend/e-server-side-source.h
+++ b/libebackend/e-server-side-source.h
@@ -22,8 +22,6 @@
#ifndef E_SERVER_SIDE_SOURCE_H
#define E_SERVER_SIDE_SOURCE_H
-#include <libedataserver/libedataserver.h>
-
#include <libebackend/e-oauth2-support.h>
#include <libebackend/e-source-registry-server.h>
@@ -91,16 +89,6 @@ GFile * e_server_side_source_get_file (EServerSideSource *source);
GNode * e_server_side_source_get_node (EServerSideSource *source);
ESourceRegistryServer *
e_server_side_source_get_server (EServerSideSource *source);
-gboolean e_server_side_source_get_allow_auth_prompt
- (EServerSideSource *source);
-void e_server_side_source_set_allow_auth_prompt
- (EServerSideSource *source,
- gboolean allow_auth_prompt);
-GType e_server_side_source_get_auth_session_type
- (EServerSideSource *source);
-void e_server_side_source_set_auth_session_type
- (EServerSideSource *source,
- GType auth_session_type);
gboolean e_server_side_source_get_exported
(EServerSideSource *source);
const gchar * e_server_side_source_get_write_directory
diff --git a/libebackend/e-soup-ssl-trust.c b/libebackend/e-soup-ssl-trust.c
index d0f10a6..2fa3258 100644
--- a/libebackend/e-soup-ssl-trust.c
+++ b/libebackend/e-soup-ssl-trust.c
@@ -34,41 +34,10 @@
typedef struct _ESoupSslTrustData {
SoupMessage *soup_message; /* weak */
ESource *source;
- ESourceRegistry *registry;
- GCancellable *cancellable;
GClosure *accept_certificate_closure;
} ESoupSslTrustData;
-static ETrustPromptResponse
-trust_prompt_sync (const ENamedParameters *parameters,
- GCancellable *cancellable,
- GError **error)
-{
- EUserPrompter *prompter;
- gint response;
-
- g_return_val_if_fail (parameters != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
-
- prompter = e_user_prompter_new ();
- g_return_val_if_fail (prompter != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
-
- response = e_user_prompter_extension_prompt_sync (prompter, "ETrustPrompt::trust-prompt", parameters,
NULL, cancellable, error);
-
- g_object_unref (prompter);
-
- if (response == 0)
- return E_TRUST_PROMPT_RESPONSE_REJECT;
- if (response == 1)
- return E_TRUST_PROMPT_RESPONSE_ACCEPT;
- if (response == 2)
- return E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
- if (response == -1)
- return E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
-
- return E_TRUST_PROMPT_RESPONSE_UNKNOWN;
-}
-
static gboolean
e_soup_ssl_trust_accept_certificate_cb (GTlsConnection *conn,
GTlsCertificate *peer_cert,
@@ -77,22 +46,38 @@ e_soup_ssl_trust_accept_certificate_cb (GTlsConnection *conn,
{
ESoupSslTrustData *handler = user_data;
ETrustPromptResponse response;
- ENamedParameters *parameters;
-
- parameters = e_named_parameters_new ();
+ SoupURI *soup_uri;
+ const gchar *host;
+ gchar *auth_host = NULL;
+
+ soup_uri = soup_message_get_uri (handler->soup_message);
+ if (!soup_uri || !soup_uri_get_host (soup_uri))
+ return FALSE;
+
+ host = soup_uri_get_host (soup_uri);
+
+ if (e_source_has_extension (handler->source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *extension_authentication;
+
+ extension_authentication = e_source_get_extension (handler->source,
E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_host = e_source_authentication_dup_host (extension_authentication);
+
+ if (auth_host && *auth_host) {
+ /* Use the 'host' from the Authentication extension, because
+ it's the one used when storing the trust prompt result.
+ The SoupMessage can be redirected, thus it would not ever match. */
+ host = auth_host;
+ } else {
+ g_free (auth_host);
+ auth_host = NULL;
+ }
+ }
- response = e_source_webdav_prepare_ssl_trust_prompt (
+ response = e_source_webdav_verify_ssl_trust (
e_source_get_extension (handler->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND),
- handler->soup_message, peer_cert, errors, handler->registry, parameters);
- if (response == E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
- response = trust_prompt_sync (parameters, handler->cancellable, NULL);
- if (response != E_TRUST_PROMPT_RESPONSE_UNKNOWN)
- e_source_webdav_store_ssl_trust_prompt (
- e_source_get_extension (handler->source, E_SOURCE_EXTENSION_WEBDAV_BACKEND),
- handler->soup_message, peer_cert, response);
- }
+ host, peer_cert, errors);
- e_named_parameters_free (parameters);
+ g_free (auth_host);
return (response == E_TRUST_PROMPT_RESPONSE_ACCEPT ||
response == E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY);
@@ -126,8 +111,6 @@ e_soup_ssl_trust_message_finalized_cb (gpointer data,
handler = data;
g_clear_object (&handler->source);
- g_clear_object (&handler->registry);
- g_clear_object (&handler->cancellable);
/* Synchronously disconnects the accept certificate handler from all
* GTlsConnections. */
@@ -141,31 +124,26 @@ e_soup_ssl_trust_message_finalized_cb (gpointer data,
* e_soup_ssl_trust_connect:
* @soup_message: a #SoupMessage about to be sent to the source
* @source: an #ESource that uses WebDAV
- * @registry: (allow-none): an #ESourceRegistry, to use for parent lookups
- * @cancellable: (allow-none): #GCancellable to cancel the trust prompt
*
- * Sets up automatic SSL certificate trust handling for @message using the trust
- * data stored in @source's WebDAV extension. If @message is about to be sent on
+ * Sets up automatic SSL certificate trust handling for @soup_message using the trust
+ * data stored in @source's WebDAV extension. If @soup_message is about to be sent on
* an SSL connection with an invalid certificate, the code checks if the WebDAV
- * extension already has a trust response for that certificate with
- * e_source_webdav_prepare_ssl_trust_prompt and if not, prompts the user with
- * the "ETrustPrompt::trust-prompt" extension dialog and
- * saves the result with e_source_webdav_store_ssl_trust_prompt.
+ * extension already has a trust response for that certificate and verifies it
+ * with e_source_webdav_verify_ssl_trust(). If the verification fails, then
+ * the @soup_message send also fails.
*
- * This works by connecting to the "network-event" signal on @message and
+ * This works by connecting to the "network-event" signal on @soup_message and
* connecting to the "accept-certificate" signal on each #GTlsConnection for
- * which @message reports a #G_SOCKET_CLIENT_TLS_HANDSHAKING event. These
- * handlers are torn down automatically when @message is disposed. This process
- * is not thread-safe; it is sufficient for safety if all use of @message's
- * session and the disposal of @message occur in the same thread.
+ * which @soup_message reports a #G_SOCKET_CLIENT_TLS_HANDSHAKING event. These
+ * handlers are torn down automatically when @soup_message is disposed. This process
+ * is not thread-safe; it is sufficient for safety if all use of @soup_message's
+ * session and the disposal of @soup_message occur in the same thread.
*
* Since: 3.14
**/
void
e_soup_ssl_trust_connect (SoupMessage *soup_message,
- ESource *source,
- ESourceRegistry *registry,
- GCancellable *cancellable)
+ ESource *source)
{
ESoupSslTrustData *handler;
@@ -176,8 +154,6 @@ e_soup_ssl_trust_connect (SoupMessage *soup_message,
handler->soup_message = soup_message;
g_object_weak_ref (G_OBJECT (soup_message), e_soup_ssl_trust_message_finalized_cb, handler);
handler->source = g_object_ref (source);
- handler->registry = registry ? g_object_ref (registry) : NULL;
- handler->cancellable = cancellable ? g_object_ref (cancellable) : NULL;
handler->accept_certificate_closure = g_cclosure_new (G_CALLBACK
(e_soup_ssl_trust_accept_certificate_cb), handler, NULL);
g_closure_ref (handler->accept_certificate_closure);
diff --git a/libebackend/e-soup-ssl-trust.h b/libebackend/e-soup-ssl-trust.h
index 846585d..7dbdf09 100644
--- a/libebackend/e-soup-ssl-trust.h
+++ b/libebackend/e-soup-ssl-trust.h
@@ -29,9 +29,7 @@
G_BEGIN_DECLS
void e_soup_ssl_trust_connect (SoupMessage *soup_message,
- ESource *source,
- ESourceRegistry *registry,
- GCancellable *cancellable);
+ ESource *source);
G_END_DECLS
diff --git a/libebackend/e-source-registry-server.c b/libebackend/e-source-registry-server.c
index 5ff0efe..741920b 100644
--- a/libebackend/e-source-registry-server.c
+++ b/libebackend/e-source-registry-server.c
@@ -23,9 +23,8 @@
* The #ESourceRegistryServer is the heart of the registry D-Bus service.
* Acting as a global singleton store for all #EServerSideSource instances,
* its responsibilities include loading data source content from key files,
- * exporting data sources to clients over D-Bus, handling authentication
- * and content change requests from clients, and saving content changes
- * back to key files.
+ * exporting data sources to clients over D-Bus, handling content change
+ * requests from clients, and saving content changes back to key files.
*
* It also hosts any number of built-in or 3rd party data source collection
* backends, which coordinate with #ESourceRegistryServer to automatically
@@ -42,9 +41,8 @@
#include <e-dbus-source.h>
#include <e-dbus-source-manager.h>
-#include <libebackend/e-authentication-mediator.h>
-#include <libebackend/e-authentication-session.h>
-#include <libebackend/e-server-side-source.h>
+#include "e-server-side-source.h"
+#include "e-server-side-source-credentials-provider.h"
#define E_SOURCE_REGISTRY_SERVER_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -54,8 +52,6 @@
* sources with a [Collection] extension. */
#define BACKEND_DATA_KEY "__e_collection_backend__"
-typedef struct _AuthRequest AuthRequest;
-
struct _ESourceRegistryServerPrivate {
GMainContext *main_context;
@@ -69,30 +65,7 @@ struct _ESourceRegistryServerPrivate {
GMutex sources_lock;
GMutex orphans_lock;
- /* In pseudo-Python notation:
- *
- * running_auths = { UID : AuthRequest }
- * waiting_auths = { UID : [ AuthRequest, ... ] }
- *
- * We process all authenticators for a given source UID at once.
- * The thought being after the first authenticator for a given UID
- * completes (the first being most likely to trigger a user prompt),
- * then any other authenticators for that same UID should complete
- * quickly, hopefully without having to reprompt. That is unless
- * the user decides not to cache the secret at all, in which case
- * he gets what he asked for: lots of annoying prompts.
- */
- GMutex auth_lock;
- GHashTable *running_auths;
- GHashTable *waiting_auths;
-};
-
-struct _AuthRequest {
- volatile gint ref_count;
- EAuthenticationSession *session;
- GSimpleAsyncResult *simple;
- ESource *source; /* may be NULL */
- GCancellable *cancellable;
+ ESourceCredentialsProvider *credentials_provider;
};
enum {
@@ -104,11 +77,6 @@ enum {
LAST_SIGNAL
};
-/* Forward Declarations */
-static void source_registry_server_maybe_start_auth_session
- (ESourceRegistryServer *server,
- const gchar *uid);
-
static guint signals[LAST_SIGNAL];
G_DEFINE_TYPE (
@@ -116,65 +84,6 @@ G_DEFINE_TYPE (
e_source_registry_server,
E_TYPE_DATA_FACTORY)
-static AuthRequest *
-auth_request_new (EAuthenticationSession *session,
- GSimpleAsyncResult *simple,
- GCancellable *cancellable)
-{
- ESourceRegistryServer *server;
- AuthRequest *request;
- const gchar *uid;
-
- server = e_authentication_session_get_server (session);
- uid = e_authentication_session_get_source_uid (session);
-
- request = g_slice_new0 (AuthRequest);
- request->ref_count = 1;
- request->session = g_object_ref (session);
- request->simple = g_object_ref (simple);
-
- /* This will return NULL if the authenticating data source
- * has not yet been submitted to the D-Bus registry server. */
- request->source = e_source_registry_server_ref_source (server, uid);
-
- if (G_IS_CANCELLABLE (cancellable))
- request->cancellable = g_object_ref (cancellable);
-
- return request;
-}
-
-static AuthRequest *
-auth_request_ref (AuthRequest *request)
-{
- g_return_val_if_fail (request != NULL, NULL);
- g_return_val_if_fail (request->ref_count > 0, NULL);
-
- g_atomic_int_inc (&request->ref_count);
-
- return request;
-}
-
-static void
-auth_request_unref (AuthRequest *request)
-{
- g_return_if_fail (request != NULL);
- g_return_if_fail (request->ref_count > 0);
-
- if (g_atomic_int_dec_and_test (&request->ref_count)) {
-
- g_object_unref (request->session);
- g_object_unref (request->simple);
-
- if (request->source != NULL)
- g_object_unref (request->source);
-
- if (request->cancellable != NULL)
- g_object_unref (request->cancellable);
-
- g_slice_free (AuthRequest, request);
- }
-}
-
/* GDestroyNotify callback for 'sources' values */
static void
unref_data_source (ESource *source)
@@ -184,17 +93,6 @@ unref_data_source (ESource *source)
g_object_unref (source);
}
-/* GDestroyNotify callback for 'waiting_auths' values */
-static void
-free_auth_queue (GQueue *queue)
-{
- /* XXX g_queue_clear_full() would be nice here. */
- while (!g_queue_is_empty (queue))
- g_object_unref (g_queue_pop_head (queue));
-
- g_queue_free (queue);
-}
-
static void
source_registry_server_sources_insert (ESourceRegistryServer *server,
ESource *source)
@@ -367,384 +265,6 @@ source_registry_server_orphans_steal (ESourceRegistryServer *server,
return array;
}
-static void
-source_request_server_auth_request_cancel_all (ESourceRegistryServer *server)
-{
- GHashTableIter iter;
- gpointer value;
-
- g_mutex_lock (&server->priv->auth_lock);
-
- g_hash_table_iter_init (&iter, server->priv->waiting_auths);
-
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- GQueue *queue = value;
- GList *list, *link;
-
- list = g_queue_peek_head_link (queue);
-
- for (link = list; link != NULL; link = g_list_next (link)) {
- AuthRequest *request = link->data;
- g_cancellable_cancel (request->cancellable);
- }
- }
-
- g_hash_table_iter_init (&iter, server->priv->running_auths);
-
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- AuthRequest *request = value;
- g_cancellable_cancel (request->cancellable);
- }
-
- g_mutex_unlock (&server->priv->auth_lock);
-}
-
-static void
-source_registry_server_auth_request_push (ESourceRegistryServer *server,
- const gchar *uid,
- AuthRequest *request)
-{
- GQueue *queue;
-
- g_return_if_fail (uid != NULL);
-
- g_mutex_lock (&server->priv->auth_lock);
-
- queue = g_hash_table_lookup (server->priv->waiting_auths, uid);
-
- if (queue == NULL) {
- queue = g_queue_new ();
- g_hash_table_insert (
- server->priv->waiting_auths,
- g_strdup (uid), queue);
- }
-
- g_queue_push_tail (queue, auth_request_ref (request));
-
- g_mutex_unlock (&server->priv->auth_lock);
-}
-
-static AuthRequest *
-source_registry_server_auth_request_next (ESourceRegistryServer *server,
- const gchar *uid)
-{
- AuthRequest *request = NULL;
-
- g_return_val_if_fail (uid != NULL, NULL);
-
- g_mutex_lock (&server->priv->auth_lock);
-
- /* If we're already busy processing an authentication request
- * for this UID, the next request will have to wait in line. */
- if (!g_hash_table_contains (server->priv->running_auths, uid)) {
- GQueue *queue;
-
- queue = g_hash_table_lookup (
- server->priv->waiting_auths, uid);
-
- if (queue != NULL)
- request = g_queue_pop_head (queue);
-
- if (request != NULL)
- g_hash_table_insert (
- server->priv->running_auths,
- g_strdup (uid),
- auth_request_ref (request));
- }
-
- g_mutex_unlock (&server->priv->auth_lock);
-
- return request;
-}
-
-static void
-source_registry_server_auth_request_done (ESourceRegistryServer *server,
- const gchar *uid)
-{
- g_return_if_fail (uid != NULL);
-
- g_mutex_lock (&server->priv->auth_lock);
-
- g_hash_table_remove (server->priv->running_auths, uid);
-
- g_mutex_unlock (&server->priv->auth_lock);
-}
-
-static void
-source_registry_server_auth_session_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- ESourceRegistryServer *server;
- EAuthenticationSession *session;
- EAuthenticationSessionResult auth_result;
- AuthRequest *request;
- const gchar *uid;
- GError *error = NULL;
-
- session = E_AUTHENTICATION_SESSION (source_object);
- request = (AuthRequest *) user_data;
-
- auth_result = e_authentication_session_execute_finish (
- session, result, &error);
-
- if (error != NULL) {
- ESourceAuthenticator *authenticator;
-
- g_warn_if_fail (auth_result == E_AUTHENTICATION_SESSION_ERROR);
- g_simple_async_result_take_error (request->simple, error);
-
- /* If the authenticator is an EAuthenticationMediator,
- * have it emit a "server-error" signal to the client. */
- authenticator =
- e_authentication_session_get_authenticator (session);
- if (E_IS_AUTHENTICATION_MEDIATOR (authenticator))
- e_authentication_mediator_server_error (
- E_AUTHENTICATION_MEDIATOR (authenticator),
- error);
- }
-
- /* Authentication dismissals require additional handling. */
- if (auth_result == E_AUTHENTICATION_SESSION_DISMISSED) {
- ESourceAuthenticator *authenticator;
-
- /* If the authenticator is an EAuthenticationMediator,
- * have it emit a "dismissed" signal to the client. */
- authenticator =
- e_authentication_session_get_authenticator (session);
- if (E_IS_AUTHENTICATION_MEDIATOR (authenticator))
- e_authentication_mediator_dismiss (
- E_AUTHENTICATION_MEDIATOR (authenticator));
-
- /* Prevent further user interruptions. */
- if (request->source != NULL)
- e_server_side_source_set_allow_auth_prompt (
- E_SERVER_SIDE_SOURCE (request->source), FALSE);
-
- /* e_source_registry_server_authenticate_finish() should
- * return an error since authentication did not complete. */
- g_simple_async_result_set_error (
- request->simple,
- G_IO_ERROR, G_IO_ERROR_CANCELLED,
- _("The user declined to authenticate"));
- }
-
- g_simple_async_result_complete_in_idle (request->simple);
-
- server = e_authentication_session_get_server (session);
- uid = e_authentication_session_get_source_uid (session);
-
- source_registry_server_auth_request_done (server, uid);
- source_registry_server_maybe_start_auth_session (server, uid);
-
- auth_request_unref (request);
-}
-
-static gboolean
-source_registry_server_start_auth_session_idle_cb (gpointer user_data)
-{
- AuthRequest *request = user_data;
-
- /* Execute the new active auth session. This signals it to
- * respond with a cached secret in the keyring if it can, or
- * else show an authentication prompt and wait for input. */
- e_authentication_session_execute (
- request->session,
- G_PRIORITY_DEFAULT,
- request->cancellable,
- source_registry_server_auth_session_cb,
- auth_request_ref (request));
-
- return FALSE;
-}
-
-static void
-source_registry_server_maybe_start_auth_session (ESourceRegistryServer *server,
- const gchar *uid)
-{
- AuthRequest *request;
-
- /* We own the returned reference, unless we get NULL. */
- request = source_registry_server_auth_request_next (server, uid);
-
- if (request != NULL) {
- GSource *idle_source;
-
- /* Process the next AuthRequest from an idle callback
- * on our own GMainContext, since the current thread-
- * default GMainContext may only be temporary. */
- idle_source = g_idle_source_new ();
- g_source_set_callback (
- idle_source,
- source_registry_server_start_auth_session_idle_cb,
- auth_request_ref (request),
- (GDestroyNotify) auth_request_unref);
- g_source_attach (idle_source, server->priv->main_context);
- g_source_unref (idle_source);
-
- auth_request_unref (request);
- }
-}
-
-static void
-source_registry_server_authenticate_done_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- GError *error = NULL;
-
- e_source_registry_server_authenticate_finish (
- E_SOURCE_REGISTRY_SERVER (source_object), result, &error);
-
- /* Ignore cancellations. */
- if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
- g_error_free (error);
-
- } else if (error != NULL) {
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
-}
-
-static void
-source_registry_server_wait_for_client_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
-{
- EAuthenticationMediator *mediator;
- EAuthenticationSession *session;
- GError *error = NULL;
-
- mediator = E_AUTHENTICATION_MEDIATOR (source_object);
- session = E_AUTHENTICATION_SESSION (user_data);
-
- e_authentication_mediator_wait_for_client_finish (
- mediator, result, &error);
-
- if (error == NULL) {
- ESourceRegistryServer *server;
-
- server = e_authentication_session_get_server (session);
-
- /* Client is ready and waiting to test passwords, so
- * execute the authentication session as soon as all
- * other authentication sessions for this same data
- * source are finished.
- *
- * XXX Note this asynchronous operation is not cancellable
- * but it does time out on its own after a few minutes. */
- e_source_registry_server_authenticate (
- server, session, NULL,
- source_registry_server_authenticate_done_cb, NULL);
-
- } else {
- /* Most likely the client went dark and the operation
- * timed out. Emit a dismissed signal anyway just in
- * case the client is still alive and listening. */
- e_authentication_mediator_dismiss (mediator);
- g_warning ("%s: %s", G_STRFUNC, error->message);
- g_error_free (error);
- }
-
- g_object_unref (session);
-}
-
-static gboolean
-source_registry_server_allow_auth_prompt_all_cb (EDBusSourceManager *dbus_interface,
- GDBusMethodInvocation *invocation,
- ESourceRegistryServer *server)
-{
- GList *list, *link;
-
- list = e_source_registry_server_list_sources (server, NULL);
-
- for (link = list; link != NULL; link = g_list_next (link))
- e_server_side_source_set_allow_auth_prompt (
- E_SERVER_SIDE_SOURCE (link->data), TRUE);
-
- g_list_free_full (list, (GDestroyNotify) g_object_unref);
-
- e_dbus_source_manager_complete_allow_auth_prompt_all (
- dbus_interface, invocation);
-
- return TRUE;
-}
-
-static gboolean
-source_registry_server_authenticate_cb (EDBusSourceManager *dbus_interface,
- GDBusMethodInvocation *invocation,
- const gchar *source_uid,
- const gchar *prompt_title,
- const gchar *prompt_message,
- const gchar *prompt_description,
- ESourceRegistryServer *server)
-{
- GDBusConnection *connection;
- EAuthenticationSession *session;
- ESourceAuthenticator *authenticator;
- const gchar *sender;
- gchar *auth_object_path;
- GError *error = NULL;
-
- /* Export the D-Bus interface to a unique object path. This
- * effectively starts a new authentication session with the
- * method caller. */
-
- connection = g_dbus_method_invocation_get_connection (invocation);
- sender = g_dbus_method_invocation_get_sender (invocation);
-
- auth_object_path = e_data_factory_construct_path (
- E_DATA_FACTORY (server));
-
- authenticator = e_authentication_mediator_new (
- connection, auth_object_path, sender, &error);
-
- if (error != NULL) {
- g_warn_if_fail (authenticator == NULL);
- g_dbus_method_invocation_take_error (invocation, error);
- g_free (auth_object_path);
- return TRUE;
- }
-
- g_return_val_if_fail (
- E_IS_SOURCE_AUTHENTICATOR (authenticator), FALSE);
-
- /* Create the authentication session. */
- session = e_source_registry_server_new_auth_session (
- server, authenticator, source_uid);
-
- /* Configure the authentication session. */
- g_object_set (
- session,
- "prompt-title", prompt_title,
- "prompt-message", prompt_message,
- "prompt-description", prompt_description,
- NULL);
-
- /* Before adding the authentication session to the server we
- * must handshake with the client requesting authentication.
- * We do this by returning the object path of the exported
- * Authenticator interface and then waiting for the client to
- * acknowledge by calling the Ready() method on the interface.
- * This indicates the client is ready to receive signals.
- *
- * XXX Note this asynchronous operation is not cancellable
- * but it does time out on its own after a few minutes. */
- e_authentication_mediator_wait_for_client (
- E_AUTHENTICATION_MEDIATOR (authenticator),
- NULL, source_registry_server_wait_for_client_cb,
- g_object_ref (session));
-
- e_dbus_source_manager_complete_authenticate (
- dbus_interface, invocation, auth_object_path);
-
- g_object_unref (authenticator);
- g_object_unref (session);
- g_free (auth_object_path);
-
- return TRUE;
-}
-
static gboolean
source_registry_server_create_source (ESourceRegistryServer *server,
const gchar *uid,
@@ -1036,6 +556,19 @@ source_registry_server_adopt_orphans (ESourceRegistryServer *server,
}
static void
+source_registry_server_constructed (GObject *object)
+{
+ ESourceRegistryServer *server;
+
+ server = E_SOURCE_REGISTRY_SERVER (object);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_registry_server_parent_class)->constructed (object);
+
+ server->priv->credentials_provider = e_server_side_source_credentials_provider_new (server);
+}
+
+static void
source_registry_server_dispose (GObject *object)
{
ESourceRegistryServerPrivate *priv;
@@ -1047,23 +580,14 @@ source_registry_server_dispose (GObject *object)
priv->main_context = NULL;
}
- if (priv->object_manager != NULL) {
- g_object_unref (priv->object_manager);
- priv->object_manager = NULL;
- }
-
- if (priv->source_manager != NULL) {
- g_object_unref (priv->source_manager);
- priv->source_manager = NULL;
- }
+ g_clear_object (&priv->object_manager);
+ g_clear_object (&priv->source_manager);
+ g_clear_object (&priv->credentials_provider);
g_hash_table_remove_all (priv->sources);
g_hash_table_remove_all (priv->orphans);
g_hash_table_remove_all (priv->monitors);
- g_hash_table_remove_all (priv->running_auths);
- g_hash_table_remove_all (priv->waiting_auths);
-
/* Chain up to parent's dispose() method. */
G_OBJECT_CLASS (e_source_registry_server_parent_class)->
dispose (object);
@@ -1083,10 +607,6 @@ source_registry_server_finalize (GObject *object)
g_mutex_clear (&priv->sources_lock);
g_mutex_clear (&priv->orphans_lock);
- g_mutex_clear (&priv->auth_lock);
- g_hash_table_destroy (priv->running_auths);
- g_hash_table_destroy (priv->waiting_auths);
-
/* Chain up to parent's finalize() method. */
G_OBJECT_CLASS (e_source_registry_server_parent_class)->
finalize (object);
@@ -1116,9 +636,6 @@ source_registry_server_quit_server (EDBusServer *server,
priv = E_SOURCE_REGISTRY_SERVER_GET_PRIVATE (server);
- source_request_server_auth_request_cancel_all (
- E_SOURCE_REGISTRY_SERVER (server));
-
/* This makes the object manager unexport all objects. */
g_dbus_object_manager_server_set_connection (
priv->object_manager, NULL);
@@ -1273,6 +790,7 @@ e_source_registry_server_class_init (ESourceRegistryServerClass *class)
g_type_class_add_private (class, sizeof (ESourceRegistryServerPrivate));
object_class = G_OBJECT_CLASS (class);
+ object_class->constructed = source_registry_server_constructed;
object_class->dispose = source_registry_server_dispose;
object_class->finalize = source_registry_server_finalize;
@@ -1400,8 +918,6 @@ e_source_registry_server_init (ESourceRegistryServer *server)
GHashTable *sources;
GHashTable *orphans;
GHashTable *monitors;
- GHashTable *running_auths;
- GHashTable *waiting_auths;
const gchar *object_path;
object_path = E_SOURCE_REGISTRY_SERVER_OBJECT_PATH;
@@ -1429,18 +945,6 @@ e_source_registry_server_init (ESourceRegistryServer *server)
(GDestroyNotify) g_object_unref,
(GDestroyNotify) g_object_unref);
- running_auths = g_hash_table_new_full (
- (GHashFunc) g_str_hash,
- (GEqualFunc) g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) auth_request_unref);
-
- waiting_auths = g_hash_table_new_full (
- (GHashFunc) g_str_hash,
- (GEqualFunc) g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) free_auth_queue);
-
server->priv = E_SOURCE_REGISTRY_SERVER_GET_PRIVATE (server);
server->priv->main_context = g_main_context_ref_thread_default ();
server->priv->object_manager = object_manager;
@@ -1450,19 +954,6 @@ e_source_registry_server_init (ESourceRegistryServer *server)
server->priv->monitors = monitors;
g_mutex_init (&server->priv->sources_lock);
g_mutex_init (&server->priv->orphans_lock);
- g_mutex_init (&server->priv->auth_lock);
- server->priv->waiting_auths = waiting_auths;
- server->priv->running_auths = running_auths;
-
- g_signal_connect (
- source_manager, "handle-allow-auth-prompt-all",
- G_CALLBACK (source_registry_server_allow_auth_prompt_all_cb),
- server);
-
- g_signal_connect (
- source_manager, "handle-authenticate",
- G_CALLBACK (source_registry_server_authenticate_cb),
- server);
g_signal_connect (
source_manager, "handle-create-sources",
@@ -1491,6 +982,25 @@ e_source_registry_server_new (void)
}
/**
+ * e_source_registry_server_ref_credentials_provider:
+ * @server: an #ESourceRegistryServer
+ *
+ * Returns a referenced #ESourceRegistryCredentialsProvider.
+ *
+ * Returns: A referenced #ESourceRegistryCredentialsProvider. Unref it with
+ * g_object_unref(), when no longer needed.
+ *
+ * Since: 3.14
+ **/
+ESourceCredentialsProvider *
+e_source_registry_server_ref_credentials_provider (ESourceRegistryServer *server)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
+
+ return g_object_ref (server->priv->credentials_provider);
+}
+
+/**
* e_source_registry_server_add_source:
* @server: an #ESourceRegistryServer
* @source: an #ESource
@@ -2320,187 +1830,3 @@ e_source_registry_server_ref_backend_factory (ESourceRegistryServer *server,
* We specify this in source_registry_server_class_init(). */
return E_COLLECTION_BACKEND_FACTORY (factory);
}
-
-/**
- * e_source_registry_server_new_auth_session:
- * @server: an #ESourceRegistryServer
- * @authenticator: an #ESourceAuthenticator
- * @source_uid: a data source identifier
- *
- * Convenience function instantiates an appropriate authentication
- * session type for @source_uid.
- *
- * If @server has an #EServerSideSource instance for @source_uid, then
- * its #EServerSideSource:auth-session-type is used to instantiate a new
- * authentication session. Otherwise a plain #EAuthenticationSession is
- * instantiated.
- *
- * Unreference the returned #EAuthenticationSession with g_object_unref()
- * when finished with it.
- *
- * Returns: a new #EAuthenticationSession for @source_uid
- *
- * Since: 3.8
- **/
-EAuthenticationSession *
-e_source_registry_server_new_auth_session (ESourceRegistryServer *server,
- ESourceAuthenticator *authenticator,
- const gchar *source_uid)
-{
- GType auth_session_type = E_TYPE_AUTHENTICATION_SESSION;
- ESource *source;
-
- g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), NULL);
- g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (authenticator), NULL);
- g_return_val_if_fail (source_uid != NULL, NULL);
-
- source = e_source_registry_server_ref_source (server, source_uid);
- if (source != NULL) {
- auth_session_type =
- e_server_side_source_get_auth_session_type (
- E_SERVER_SIDE_SOURCE (source));
- g_object_unref (source);
- }
-
- g_return_val_if_fail (
- g_type_is_a (
- auth_session_type,
- E_TYPE_AUTHENTICATION_SESSION), NULL);
-
- return g_object_new (
- auth_session_type,
- "server", server,
- "authenticator", authenticator,
- "source-uid", source_uid, NULL);
-}
-
-/**
- * e_source_registry_server_authenticate_sync:
- * @server: an #ESourceRegistryServer
- * @session: an #EAuthenticationSession
- * @cancellable: optional #GCancellable object, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Queues the @session behind any ongoing or pending authentication
- * sessions for the same data source, and eventually executes @session
- * (see e_authentication_session_execute_sync() for more details).
- *
- * This function blocks until @session is finished executing. For a
- * non-blocking variation see e_source_registry_server_authenticate().
- *
- * If an error occurs, the function sets @error and returns %FALSE.
- *
- * Returns: %TRUE on success, %FALSE on failure
- *
- * Since: 3.6
- **/
-gboolean
-e_source_registry_server_authenticate_sync (ESourceRegistryServer *server,
- EAuthenticationSession *session,
- GCancellable *cancellable,
- GError **error)
-{
- EAsyncClosure *closure;
- GAsyncResult *result;
- gboolean success;
-
- g_return_val_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server), FALSE);
- g_return_val_if_fail (E_IS_AUTHENTICATION_SESSION (session), FALSE);
-
- closure = e_async_closure_new ();
-
- e_source_registry_server_authenticate (
- server, session, cancellable,
- e_async_closure_callback, closure);
-
- result = e_async_closure_wait (closure);
-
- success = e_source_registry_server_authenticate_finish (
- server, result, error);
-
- e_async_closure_free (closure);
-
- return success;
-}
-
-/**
- * e_source_registry_server_authenticate:
- * @server: an #ESourceRegistryServer
- * @session: an #EAuthenticationSession
- * @cancellable: optional #GCancellable object, or %NULL
- * @callback: a #GAsyncReadyCallback to call when the request is satisfied
- * @user_data: data to pass to the callback function
- *
- * Queues the @session behind any ongoing or pending authentication
- * sessions for the same data source, and eventually executes @session
- * (see e_authentication_session_execute_sync() for more details).
- *
- * This function returns immediately after enqueuing @session. When
- * @session is finished executing, @callback will be called. You can
- * then call e_source_registry_server_authenticate_finish() to get the
- * result of the operation.
- *
- * Since: 3.6
- **/
-void
-e_source_registry_server_authenticate (ESourceRegistryServer *server,
- EAuthenticationSession *session,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *simple;
- AuthRequest *request;
- const gchar *uid;
-
- g_return_if_fail (E_IS_SOURCE_REGISTRY_SERVER (server));
- g_return_if_fail (E_IS_AUTHENTICATION_SESSION (session));
-
- uid = e_authentication_session_get_source_uid (session);
- g_return_if_fail (uid != NULL);
-
- simple = g_simple_async_result_new (
- G_OBJECT (server), callback, user_data,
- e_source_registry_server_authenticate);
-
- g_simple_async_result_set_check_cancellable (simple, cancellable);
-
- request = auth_request_new (session, simple, cancellable);
- source_registry_server_auth_request_push (server, uid, request);
- auth_request_unref (request);
-
- source_registry_server_maybe_start_auth_session (server, uid);
-
- g_object_unref (simple);
-}
-
-/**
- * e_source_registry_server_authenticate_finish:
- * @server: an #ESourceRegistryServer
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Finishes the operation started with e_source_registry_server_authenticate().
- * If an error occurred, the function will set @error and return %FALSE.
- *
- * Returns: %TRUE on success, %FALSE on failure
- *
- * Since: 3.6
- **/
-gboolean
-e_source_registry_server_authenticate_finish (ESourceRegistryServer *server,
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (
- g_simple_async_result_is_valid (
- result, G_OBJECT (server),
- e_source_registry_server_authenticate), FALSE);
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
-
- /* Assume success unless a GError is set. */
- return !g_simple_async_result_propagate_error (simple, error);
-}
diff --git a/libebackend/e-source-registry-server.h b/libebackend/e-source-registry-server.h
index 44d7ee6..d86b100 100644
--- a/libebackend/e-source-registry-server.h
+++ b/libebackend/e-source-registry-server.h
@@ -24,7 +24,6 @@
#include <libedataserver/libedataserver.h>
-#include <libebackend/e-authentication-session.h>
#include <libebackend/e-backend-enums.h>
#include <libebackend/e-data-factory.h>
#include <libebackend/e-collection-backend.h>
@@ -109,6 +108,9 @@ struct _ESourceRegistryServerClass {
GType e_source_registry_server_get_type
(void) G_GNUC_CONST;
EDBusServer * e_source_registry_server_new (void);
+ESourceCredentialsProvider *
+ e_source_registry_server_ref_credentials_provider
+ (ESourceRegistryServer *server);
void e_source_registry_server_add_source
(ESourceRegistryServer *server,
ESource *source);
@@ -153,26 +155,6 @@ ECollectionBackendFactory *
e_source_registry_server_ref_backend_factory
(ESourceRegistryServer *server,
ESource *source);
-EAuthenticationSession *
- e_source_registry_server_new_auth_session
- (ESourceRegistryServer *server,
- ESourceAuthenticator *authenticator,
- const gchar *source_uid);
-gboolean e_source_registry_server_authenticate_sync
- (ESourceRegistryServer *server,
- EAuthenticationSession *session,
- GCancellable *cancellable,
- GError **error);
-void e_source_registry_server_authenticate
- (ESourceRegistryServer *server,
- EAuthenticationSession *session,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean e_source_registry_server_authenticate_finish
- (ESourceRegistryServer *server,
- GAsyncResult *result,
- GError **error);
#ifndef EDS_DISABLE_DEPRECATED
gboolean e_source_registry_server_load_all
diff --git a/libebackend/e-subprocess-factory.c b/libebackend/e-subprocess-factory.c
index 1fbca51..2fa077e 100644
--- a/libebackend/e-subprocess-factory.c
+++ b/libebackend/e-subprocess-factory.c
@@ -28,9 +28,9 @@
#include <glib/gi18n-lib.h>
-#include <libebackend/e-extensible.h>
+#include <libedataserver/libedataserver.h>
+
#include <libebackend/e-backend-factory.h>
-#include <libebackend/e-module.h>
#include <e-dbus-subprocess-backend.h>
diff --git a/libebackend/libebackend.h b/libebackend/libebackend.h
index f4f6feb..c0fa01c 100644
--- a/libebackend/libebackend.h
+++ b/libebackend/libebackend.h
@@ -22,8 +22,6 @@
#include <libedataserver/libedataserver.h>
-#include <libebackend/e-authentication-mediator.h>
-#include <libebackend/e-authentication-session.h>
#include <libebackend/e-backend-enums.h>
#include <libebackend/e-backend-enumtypes.h>
#include <libebackend/e-backend-factory.h>
@@ -32,10 +30,7 @@
#include <libebackend/e-collection-backend.h>
#include <libebackend/e-data-factory.h>
#include <libebackend/e-dbus-server.h>
-#include <libebackend/e-extensible.h>
-#include <libebackend/e-extension.h>
#include <libebackend/e-file-cache.h>
-#include <libebackend/e-module.h>
#include <libebackend/e-oauth2-support.h>
#include <libebackend/e-offline-listener.h>
#include <libebackend/e-server-side-source.h>
diff --git a/libedataserver/Makefile.am b/libedataserver/Makefile.am
index c41474e..b3396dd 100644
--- a/libedataserver/Makefile.am
+++ b/libedataserver/Makefile.am
@@ -29,6 +29,7 @@ SHARED_COMPILER_FLAGS = \
-DE_DATA_SERVER_LOCALEDIR=\""$(localedir)"\" \
-DE_DATA_SERVER_EXTENSIONDIR=\"$(extensiondir)\" \
-DE_DATA_SERVER_IMAGESDIR=\"$(imagesdir)\" \
+ -DE_DATA_SERVER_CREDENTIALMODULEDIR=\"$(credentialmoduledir)\" \
-DE_DATA_SERVER_PRIVDATADIR=\"$(privdatadir)\" \
-DE_DATA_SERVER_UI_UIDIR=\""$(uidir)"\" \
$(E_DATA_SERVER_CFLAGS) \
@@ -52,6 +53,8 @@ libedataserver_1_2_la_SOURCES = \
e-client-private.h \
e-collator.c \
e-credentials.c \
+ e-extensible.c \
+ e-extension.c \
e-flag.c \
e-free-form-exp.c \
e-gdbus-templates.c \
@@ -59,6 +62,7 @@ libedataserver_1_2_la_SOURCES = \
e-list.c \
e-list-iterator.c \
e-memory.c \
+ e-module.c \
e-operation-pool.c \
e-proxy.c \
e-sexp.c \
@@ -67,12 +71,14 @@ libedataserver_1_2_la_SOURCES = \
e-source-address-book.c \
e-source-alarms.c \
e-source-authentication.c \
- e-source-authenticator.c \
e-source-autocomplete.c \
e-source-backend.c \
e-source-calendar.c \
e-source-camel.c \
e-source-collection.c \
+ e-source-credentials-provider.c \
+ e-source-credentials-provider-impl.c \
+ e-source-credentials-provider-impl-password.c \
e-source-goa.c \
e-source-mail-account.c \
e-source-mail-composition.c \
@@ -131,6 +137,8 @@ libedataserverinclude_HEADERS = \
e-client.h \
e-collator.h \
e-credentials.h \
+ e-extensible.h \
+ e-extension.h \
e-flag.h \
e-free-form-exp.h \
e-gdbus-templates.h \
@@ -138,6 +146,7 @@ libedataserverinclude_HEADERS = \
e-list.h \
e-list-iterator.h \
e-memory.h \
+ e-module.h \
e-operation-pool.h \
e-proxy.h \
e-sexp.h \
@@ -145,12 +154,14 @@ libedataserverinclude_HEADERS = \
e-source-address-book.h \
e-source-alarms.h \
e-source-authentication.h \
- e-source-authenticator.h \
e-source-autocomplete.h \
e-source-backend.h \
e-source-calendar.h \
e-source-camel.h \
e-source-collection.h \
+ e-source-credentials-provider.h \
+ e-source-credentials-provider-impl.h \
+ e-source-credentials-provider-impl-password.h \
e-source-enums.h \
e-source-enumtypes.h \
e-source-extension.h \
diff --git a/libedataserver/e-client.c b/libedataserver/e-client.c
index 901724b..d2ad89d 100644
--- a/libedataserver/e-client.c
+++ b/libedataserver/e-client.c
@@ -43,6 +43,8 @@
#include <libedataserver/e-data-server-util.h>
+#include "e-flag.h"
+
#include "e-client.h"
#include "e-client-private.h"
@@ -1844,6 +1846,294 @@ e_client_refresh_sync (EClient *client,
return class->refresh_sync (client, cancellable, error);
}
+static void
+client_wait_for_connected_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ guint32 timeout_seconds;
+ gboolean success;
+ GError *local_error = NULL;
+
+ timeout_seconds = GPOINTER_TO_UINT (task_data);
+ success = e_client_wait_for_connected_sync (E_CLIENT (source_object), timeout_seconds, cancellable,
&local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_client_wait_for_connected:
+ * @client: an #EClient
+ * @timeout_seconds: a timeout for the wait, in seconds
+ * @cancellable: (allow none): a #GCancellable; or %NULL
+ * @callback: callback to call when a result is ready
+ * @user_data: user data for the @callback
+ *
+ * Asynchronously waits until the @client is connected (according
+ * to @ESource::connection-status property), but not longer than @timeout_seconds.
+ *
+ * The call is finished by e_client_wait_for_connected_finish() from
+ * the @callback.
+ *
+ * Since: 3.14
+ **/
+void
+e_client_wait_for_connected (EClient *client,
+ guint32 timeout_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_if_fail (E_IS_CLIENT (client));
+
+ task = g_task_new (client, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_client_wait_for_connected);
+ g_task_set_task_data (task, GUINT_TO_POINTER (timeout_seconds), NULL);
+
+ g_task_run_in_thread (task, client_wait_for_connected_thread);
+
+ g_object_unref (task);
+}
+
+/**
+ * e_client_wait_for_connected_finish:
+ * @client: an #EClient
+ * @result: a #GAsyncResult
+ * @error: (out): (allow none): a #GError to set an error, or %NULL
+ *
+ * Finishes previous call of e_client_wait_for_connected().
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_client_wait_for_connected_finish (EClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, client), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_client_wait_for_connected), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+static void
+client_wait_for_connected_cancelled_cb (GCancellable *cancellable,
+ EFlag *flag)
+{
+ g_return_if_fail (flag != NULL);
+
+ e_flag_set (flag);
+}
+
+static void
+client_wait_for_connected_notify_cb (ESource *source,
+ GParamSpec *param,
+ EFlag *flag)
+{
+ g_return_if_fail (flag != NULL);
+
+ if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED)
+ e_flag_set (flag);
+}
+
+/**
+ * @client: an #EClient
+ * @timeout_seconds: a timeout for the wait, in seconds
+ * @cancellable: (allow none): a #GCancellable; or %NULL
+ * @callback: callback to call when a result is ready
+ * @error: (out): (allow none): a #GError to set an error, or %NULL
+ *
+ * Synchronously waits until the @client is connected (according
+ * to @ESource::connection-status property), but not longer than @timeout_seconds.
+ *
+ * Note: This also calls e_client_retrieve_properties_sync() on success, to have
+ * up-to-date property values on the client side, without a delay due
+ * to property change notifcations delivery through D-Bus.
+ *
+ * Returns: %TRUE if successful, %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_client_wait_for_connected_sync (EClient *client,
+ guint32 timeout_seconds,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESource *source;
+ EFlag *flag;
+ gulong cancellable_handler_id = 0, notify_handler_id;
+ gint64 end_time;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
+
+ end_time = g_get_monotonic_time () + timeout_seconds * G_TIME_SPAN_SECOND;
+ flag = e_flag_new ();
+
+ if (cancellable)
+ cancellable_handler_id = g_cancellable_connect (cancellable,
+ G_CALLBACK (client_wait_for_connected_cancelled_cb), flag, NULL);
+
+ source = e_client_get_source (client);
+
+ notify_handler_id = g_signal_connect (source, "notify::connection-status",
+ G_CALLBACK (client_wait_for_connected_notify_cb), flag);
+
+ while (success = e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED,
+ !success && !g_cancellable_is_cancelled (cancellable)) {
+ e_flag_clear (flag);
+
+ if (timeout_seconds > 0) {
+ if (g_get_monotonic_time () > end_time)
+ break;
+
+ e_flag_wait_until (flag, end_time);
+ } else {
+ e_flag_wait (flag);
+ }
+ }
+
+ if (cancellable_handler_id > 0 && cancellable)
+ g_cancellable_disconnect (cancellable, cancellable_handler_id);
+
+ g_signal_handler_disconnect (source, notify_handler_id);
+ e_flag_set (flag);
+ e_flag_free (flag);
+
+ success = e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_CONNECTED;
+
+ if (!success && !g_cancellable_set_error_if_cancelled (cancellable, error))
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT, _("Timeout was reached"));
+ else if (success)
+ success = e_client_retrieve_properties_sync (client, cancellable, error);
+
+ return success;
+}
+
+/**
+ * e_client_retrieve_properties_sync:
+ * @client: an #EClient
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Retrieves @client properties to match server-side values, without waiting
+ * for the D-Bus property change notifications delivery.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_client_retrieve_properties_sync (EClient *client,
+ GCancellable *cancellable,
+ GError **error)
+{
+ EClientClass *klass;
+
+ g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
+
+ klass = E_CLIENT_GET_CLASS (client);
+ g_return_val_if_fail (klass != NULL, FALSE);
+ g_return_val_if_fail (klass->retrieve_properties_sync != NULL, FALSE);
+
+ return klass->retrieve_properties_sync (client, cancellable, error);
+}
+
+static void
+client_retrieve_properties_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_client_retrieve_properties_sync (E_CLIENT (source_object), cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_client_retrieve_properties:
+ * @client: an #EClient
+ * @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
+ *
+ * Asynchronously retrieves @client properties to match server-side values,
+ * without waiting for the D-Bus property change notifications delivery.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_client_retrieve_properties_finish() to get the result of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_client_retrieve_properties (EClient *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_if_fail (E_IS_CLIENT (client));
+
+ task = g_task_new (client, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_client_retrieve_properties);
+
+ g_task_run_in_thread (task, client_retrieve_properties_thread);
+
+ g_object_unref (task);
+}
+
+/**
+ * e_client_retrieve_properties_finish:
+ * @client: an #EClient
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Finishes the operation started with e_client_retrieve_properties().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_client_retrieve_properties_finish (EClient *client,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_CLIENT (client), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, client), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_client_retrieve_properties), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
/**
* e_client_util_slist_to_strv:
* @strings: (element-type utf8): a #GSList of strings (const gchar *)
diff --git a/libedataserver/e-client.h b/libedataserver/e-client.h
index a808e0f..d421671 100644
--- a/libedataserver/e-client.h
+++ b/libedataserver/e-client.h
@@ -266,6 +266,10 @@ struct _EClientClass {
gboolean (*refresh_sync) (EClient *client,
GCancellable *cancellable,
GError **error);
+ gboolean (*retrieve_properties_sync)
+ (EClient *client,
+ GCancellable *cancellable,
+ GError **error);
void (*opened) (EClient *client,
const GError *error);
@@ -318,6 +322,20 @@ gboolean e_client_refresh_sync (EClient *client,
GCancellable *cancellable,
GError **error);
+void e_client_wait_for_connected (EClient *client,
+ guint32 timeout_seconds,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_client_wait_for_connected_finish
+ (EClient *client,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_client_wait_for_connected_sync(EClient *client,
+ guint32 timeout_seconds,
+ GCancellable *cancellable,
+ GError **error);
+
GSList * e_client_util_parse_comma_strings
(const gchar *strings);
@@ -407,6 +425,18 @@ gboolean e_client_remove_finish (EClient *client,
gboolean e_client_remove_sync (EClient *client,
GCancellable *cancellable,
GError **error);
+gboolean e_client_retrieve_properties_sync
+ (EClient *client,
+ GCancellable *cancellable,
+ GError **error);
+void e_client_retrieve_properties (EClient *client,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_client_retrieve_properties_finish
+ (EClient *client,
+ GAsyncResult *result,
+ GError **error);
gchar ** e_client_util_slist_to_strv (const GSList *strings);
GSList * e_client_util_strv_to_slist (const gchar * const *strv);
GSList * e_client_util_copy_string_slist (GSList *copy_to,
diff --git a/libedataserver/e-data-server-util.c b/libedataserver/e-data-server-util.c
index c0456e5..6d7ee7f 100644
--- a/libedataserver/e-data-server-util.c
+++ b/libedataserver/e-data-server-util.c
@@ -32,6 +32,8 @@
#include <glib-object.h>
+#include "e-source-enumtypes.h"
+
#include "e-data-server-util.h"
/**
@@ -1090,6 +1092,27 @@ e_util_free_nullable_object_slist (GSList *objects)
}
/**
+ * e_util_safe_free_string:
+ * @str: a string to free
+ *
+ * Calls g_free() on @string, but before it rewrites its content with zeros.
+ * This is suitable to free strings with passwords.
+ *
+ * Since: 3.14
+ **/
+void
+e_util_safe_free_string (gchar *str)
+{
+ if (!str)
+ return;
+
+ if (*str)
+ memset (str, 0, sizeof (gchar) * strlen (str));
+
+ g_free (str);
+}
+
+/**
* e_queue_transfer:
* @src_queue: a source #GQueue
* @dst_queue: a destination #GQueue
@@ -1650,6 +1673,7 @@ static const gchar *localedir;
static const gchar *extensiondir;
static const gchar *imagesdir;
static const gchar *ui_uidir;
+static const gchar *credentialmoduledir;
static HMODULE hmodule;
G_LOCK_DEFINE_STATIC (mutex);
@@ -1731,6 +1755,7 @@ setup (void)
extensiondir = replace_prefix (prefix, E_DATA_SERVER_EXTENSIONDIR);
imagesdir = replace_prefix (prefix, E_DATA_SERVER_IMAGESDIR);
ui_uidir = replace_prefix (prefix, E_DATA_SERVER_UI_UIDIR);
+ credentialmoduledir = replace_prefix (prefix, E_DATA_SERVER_CREDENTIALMODULEDIR);
G_UNLOCK (mutex);
}
@@ -1756,6 +1781,7 @@ e_util_get_##varbl (void) \
PRIVATE_GETTER (extensiondir)
PRIVATE_GETTER (imagesdir)
PRIVATE_GETTER (ui_uidir)
+PRIVATE_GETTER (credentialmoduledir);
PUBLIC_GETTER (prefix)
PUBLIC_GETTER (cp_prefix)
@@ -1904,7 +1930,7 @@ e_data_server_util_get_dbus_call_timeout (void)
ENamedParameters *
e_named_parameters_new (void)
{
- return (ENamedParameters *) g_ptr_array_new_with_free_func (g_free);
+ return (ENamedParameters *) g_ptr_array_new_with_free_func ((GDestroyNotify) e_util_safe_free_string);
}
/**
@@ -1940,6 +1966,34 @@ e_named_parameters_new_strv (const gchar * const *strv)
}
/**
+ * e_named_parameters_new_clone:
+ * @parameters: an #ENamedParameters to be used as a content of a newly
+ * created #ENamedParameters
+ *
+ * Creates a new instance of an #ENamedParameters, with initial content
+ * being taken from @parameters. This should be freed with e_named_parameters_free(),
+ * when no longer needed. Names are compared case insensitively.
+ *
+ * The structure is not thread safe, if the caller requires thread safety,
+ * then it should provide it on its own.
+ *
+ * Returns: newly allocated #ENamedParameters
+ *
+ * Since: 3.14
+ **/
+ENamedParameters *
+e_named_parameters_new_clone (const ENamedParameters *parameters)
+{
+ ENamedParameters *clone;
+
+ clone = e_named_parameters_new ();
+ if (parameters)
+ e_named_parameters_assign (clone, parameters);
+
+ return clone;
+}
+
+/**
* e_named_parameters_free:
* @parameters: an #ENamedParameters
*
@@ -2395,3 +2449,46 @@ e_source_registry_debug_print (const gchar *format,
g_string_free (str, TRUE);
}
+
+/**
+ * e_type_traverse:
+ * @parent_type: the root #GType to traverse from
+ * @func: the function to call for each visited #GType
+ * @user_data: user data to pass to the function
+ *
+ * Calls @func for all instantiable subtypes of @parent_type.
+ *
+ * This is often useful for extending functionality by way of #EModule.
+ * A module may register a subtype of @parent_type in its e_module_load()
+ * function. Then later on the application will call e_type_traverse()
+ * to instantiate all registered subtypes of @parent_type.
+ *
+ * Since: 3.4
+ **/
+void
+e_type_traverse (GType parent_type,
+ ETypeFunc func,
+ gpointer user_data)
+{
+ GType *children;
+ guint n_children, ii;
+
+ g_return_if_fail (func != NULL);
+
+ children = g_type_children (parent_type, &n_children);
+
+ for (ii = 0; ii < n_children; ii++) {
+ GType type = children[ii];
+
+ /* Recurse over the child's children. */
+ e_type_traverse (type, func, user_data);
+
+ /* Skip abstract types. */
+ if (G_TYPE_IS_ABSTRACT (type))
+ continue;
+
+ func (type, user_data);
+ }
+
+ g_free (children);
+}
diff --git a/libedataserver/e-data-server-util.h b/libedataserver/e-data-server-util.h
index 065d063..7cd34b3 100644
--- a/libedataserver/e-data-server-util.h
+++ b/libedataserver/e-data-server-util.h
@@ -29,6 +29,8 @@
#include <sys/types.h>
#include <gio/gio.h>
+#include <libedataserver/e-source-enums.h>
+
G_BEGIN_DECLS
struct tm;
@@ -77,6 +79,8 @@ gchar ** e_util_slist_to_strv (const GSList *strings);
GSList * e_util_strv_to_slist (const gchar * const *strv);
void e_util_free_nullable_object_slist
(GSList *objects);
+void e_util_safe_free_string (gchar *str);
+
void e_queue_transfer (GQueue *src_queue,
GQueue *dst_queue);
GWeakRef * e_weak_ref_new (gpointer object);
@@ -141,11 +145,15 @@ void e_util_win32_initialize (void);
struct _ENamedParameters;
typedef struct _ENamedParameters ENamedParameters;
+#define E_TYPE_NAMED_PARAMETERS (e_named_parameters_get_type ())
+
GType e_named_parameters_get_type (void) G_GNUC_CONST;
ENamedParameters *
e_named_parameters_new (void);
ENamedParameters *
e_named_parameters_new_strv (const gchar * const *strv);
+ENamedParameters *
+ e_named_parameters_new_clone (const ENamedParameters *parameters);
void e_named_parameters_free (ENamedParameters *parameters);
void e_named_parameters_clear (ENamedParameters *parameters);
void e_named_parameters_assign (ENamedParameters *parameters,
@@ -216,6 +224,21 @@ gboolean e_source_registry_debug_enabled (void);
void e_source_registry_debug_print (const gchar *format,
...) G_GNUC_PRINTF (1, 2);
+/**
+ * ETypeFunc:
+ * @type: a #GType
+ * @user_data: user data passed to e_type_traverse()
+ *
+ * Specifies the type of functions passed to e_type_traverse().
+ *
+ * Since: 3.4
+ **/
+typedef void (*ETypeFunc) (GType type,
+ gpointer user_data);
+void e_type_traverse (GType parent_type,
+ ETypeFunc func,
+ gpointer user_data);
+
G_END_DECLS
#endif /* E_DATA_SERVER_UTIL_H */
diff --git a/libebackend/e-extensible.c b/libedataserver/e-extensible.c
similarity index 96%
rename from libebackend/e-extensible.c
rename to libedataserver/e-extensible.c
index eec96c8..fb57705 100644
--- a/libebackend/e-extensible.c
+++ b/libedataserver/e-extensible.c
@@ -17,7 +17,7 @@
/**
* SECTION: e-extensible
- * @include: libebackend/libebackend.h
+ * @include: libedataserver/libedataserver.h
* @short_description: An interface for extending objects
*
* #EExtension objects can be tacked on to any #GObject instance that
@@ -29,7 +29,7 @@
*
* <informalexample>
* <programlisting>
- * #include <libebackend/libebackend.h>
+ * #include <libedataserver/libedataserver.h>
*
* G_DEFINE_TYPE_WITH_CODE (
* ECustomWidget, e_custom_widget, GTK_TYPE_WIDGET,
@@ -55,11 +55,14 @@
* </informalexample>
**/
-#include "e-extensible.h"
-
+#ifdef HAVE_CONFIG_H
#include <config.h>
-#include <libebackend/e-extension.h>
-#include <libebackend/e-module.h>
+#endif
+
+#include "e-extension.h"
+#include "e-data-server-util.h"
+
+#include "e-extensible.h"
#define IS_AN_EXTENSION_TYPE(type) \
(g_type_is_a ((type), E_TYPE_EXTENSION))
diff --git a/libebackend/e-extensible.h b/libedataserver/e-extensible.h
similarity index 91%
rename from libebackend/e-extensible.h
rename to libedataserver/e-extensible.h
index 3f358f2..383d89a 100644
--- a/libebackend/e-extensible.h
+++ b/libedataserver/e-extensible.h
@@ -15,8 +15,8 @@
*
*/
-#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
-#error "Only <libebackend/libebackend.h> should be included directly."
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
#endif
#ifndef E_EXTENSIBLE_H
diff --git a/libebackend/e-extension.c b/libedataserver/e-extension.c
similarity index 98%
rename from libebackend/e-extension.c
rename to libedataserver/e-extension.c
index 9358a30..020e4a9 100644
--- a/libebackend/e-extension.c
+++ b/libedataserver/e-extension.c
@@ -17,7 +17,7 @@
/**
* SECTION: e-extension
- * @include: libebackend/libebackend.h
+ * @include: libedataserver/libedataserver.h
* @short_description: An abstract base class for extensions
*
* #EExtension provides a way to extend the functionality of objects
@@ -38,9 +38,11 @@
* registered in the library module's e_module_load() function.
**/
-#include "e-extension.h"
-
+#ifdef HAVE_CONFIG_H
#include <config.h>
+#endif
+
+#include "e-extension.h"
#define E_EXTENSION_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
diff --git a/libebackend/e-extension.h b/libedataserver/e-extension.h
similarity index 90%
rename from libebackend/e-extension.h
rename to libedataserver/e-extension.h
index f271ded..d75900b 100644
--- a/libebackend/e-extension.h
+++ b/libedataserver/e-extension.h
@@ -15,14 +15,14 @@
*
*/
-#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
-#error "Only <libebackend/libebackend.h> should be included directly."
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
#endif
#ifndef E_EXTENSION_H
#define E_EXTENSION_H
-#include <libebackend/e-extensible.h>
+#include <libedataserver/e-extensible.h>
/* Standard GObject macros */
#define E_TYPE_EXTENSION \
diff --git a/libebackend/e-module.c b/libedataserver/e-module.c
similarity index 87%
rename from libebackend/e-module.c
rename to libedataserver/e-module.c
index c5c81f2..feb0c14 100644
--- a/libebackend/e-module.c
+++ b/libedataserver/e-module.c
@@ -17,14 +17,17 @@
/**
* SECTION: e-module
- * @include: libebackend/libebackend.h
+ * @include: libedataserver/libedataserver.h
* @short_description: A module loader
**/
-#include "e-module.h"
-
+#ifdef HAVE_CONFIG_H
#include <config.h>
-#include <glib/gi18n.h>
+#endif
+
+#include <glib.h>
+
+#include "e-module.h"
#define E_MODULE_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
@@ -341,47 +344,3 @@ e_module_load_file (const gchar *filename)
return module;
}
-
-/**
- * e_type_traverse:
- * @parent_type: the root #GType to traverse from
- * @func: the function to call for each visited #GType
- * @user_data: user data to pass to the function
- *
- * Calls @func for all instantiable subtypes of @parent_type.
- *
- * This is often useful for extending functionality by way of #EModule.
- * A module may register a subtype of @parent_type in its e_module_load()
- * function. Then later on the application will call e_type_traverse()
- * to instantiate all registered subtypes of @parent_type.
- *
- * Since: 3.4
- **/
-void
-e_type_traverse (GType parent_type,
- ETypeFunc func,
- gpointer user_data)
-{
- GType *children;
- guint n_children, ii;
-
- g_return_if_fail (func != NULL);
-
- children = g_type_children (parent_type, &n_children);
-
- for (ii = 0; ii < n_children; ii++) {
- GType type = children[ii];
-
- /* Recurse over the child's children. */
- e_type_traverse (type, func, user_data);
-
- /* Skip abstract types. */
- if (G_TYPE_IS_ABSTRACT (type))
- continue;
-
- func (type, user_data);
- }
-
- g_free (children);
-}
-
diff --git a/libebackend/e-module.h b/libedataserver/e-module.h
similarity index 78%
rename from libebackend/e-module.h
rename to libedataserver/e-module.h
index af6471e..d4495eb 100644
--- a/libebackend/e-module.h
+++ b/libedataserver/e-module.h
@@ -15,8 +15,8 @@
*
*/
-#if !defined (__LIBEBACKEND_H_INSIDE__) && !defined (LIBEBACKEND_COMPILATION)
-#error "Only <libebackend/libebackend.h> should be included directly."
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
#endif
#ifndef E_MODULE_H
@@ -73,23 +73,6 @@ const gchar * e_module_get_filename (EModule *module);
EModule * e_module_load_file (const gchar *filename);
GList * e_module_load_all_in_directory (const gchar *dirname);
-/* This is here for lack of a better place for it. */
-
-/**
- * ETypeFunc:
- * @type: a #GType
- * @user_data: user data passed to e_type_traverse()
- *
- * Specifies the type of functions passed to e_type_traverse().
- *
- * Since: 3.4
- **/
-typedef void (*ETypeFunc) (GType type,
- gpointer user_data);
-void e_type_traverse (GType parent_type,
- ETypeFunc func,
- gpointer user_data);
-
G_END_DECLS
#endif /* E_MODULE_H */
diff --git a/libedataserver/e-source-authentication.c b/libedataserver/e-source-authentication.c
index 1d4c422..b648a9d 100644
--- a/libedataserver/e-source-authentication.c
+++ b/libedataserver/e-source-authentication.c
@@ -50,6 +50,7 @@ struct _ESourceAuthenticationPrivate {
gchar *proxy_uid;
gboolean remember_password;
gchar *user;
+ gchar *credential_name;
/* GNetworkAddress caches data internally, so we maintain the
* instance to preserve the cache as opposed to just creating
@@ -65,7 +66,8 @@ enum {
PROP_PORT,
PROP_PROXY_UID,
PROP_REMEMBER_PASSWORD,
- PROP_USER
+ PROP_USER,
+ PROP_CREDENTIAL_NAME
};
G_DEFINE_TYPE (
@@ -135,6 +137,12 @@ source_authentication_set_property (GObject *object,
E_SOURCE_AUTHENTICATION (object),
g_value_get_string (value));
return;
+
+ case PROP_CREDENTIAL_NAME:
+ e_source_authentication_set_credential_name (
+ E_SOURCE_AUTHENTICATION (object),
+ g_value_get_string (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -195,6 +203,13 @@ source_authentication_get_property (GObject *object,
e_source_authentication_dup_user (
E_SOURCE_AUTHENTICATION (object)));
return;
+
+ case PROP_CREDENTIAL_NAME:
+ g_value_take_string (
+ value,
+ e_source_authentication_dup_credential_name (
+ E_SOURCE_AUTHENTICATION (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -338,6 +353,21 @@ e_source_authentication_class_init (ESourceAuthenticationClass *class)
G_PARAM_CONSTRUCT |
G_PARAM_STATIC_STRINGS |
E_SOURCE_PARAM_SETTING));
+
+ /* An empty string or NULL means to use E_SOURCE_CREDENTIAL_PASSWORD to pass
+ the stored "password" into the backend with e_source_invoke_authenticate()/_sync() */
+ g_object_class_install_property (
+ object_class,
+ PROP_CREDENTIAL_NAME,
+ g_param_spec_string (
+ "credential-name",
+ "Credential Name",
+ "What name to use for the authentication method in credentials for authentication",
+ NULL,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS |
+ E_SOURCE_PARAM_SETTING));
}
static void
@@ -844,3 +874,92 @@ e_source_authentication_set_user (ESourceAuthentication *extension,
g_object_notify (G_OBJECT (extension), "user");
}
+
+/**
+ * e_source_authentication_get_credential_name:
+ * @extension: an #ESourceAuthentication
+ *
+ * Returns the credential name used to pass the stored or gathered credential
+ * (like password) into the e_source_invoke_authenticate(). This is
+ * a counterpart of the authentication method. The %NULL means to use
+ * the default name, which is #E_SOURCE_CREDENTIAL_PASSWORD.
+ *
+ * Returns: the credential name to use for authentication, or %NULL
+ *
+ * Since: 3.14
+ **/
+const gchar *
+e_source_authentication_get_credential_name (ESourceAuthentication *extension)
+{
+ g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+ return extension->priv->credential_name;
+}
+
+/**
+ * e_source_authentication_dup_credential_name:
+ * @extension: an #ESourceAuthentication
+ *
+ * Thread-safe variation of e_source_authentication_get_credential_name().
+ * Use this function when accessing @extension from multiple threads.
+ *
+ * The returned string should be freed with g_free() when no longer needed.
+ *
+ * Returns: a newly-allocated copy of #ESourceAuthentication:credential-name
+ *
+ * Since: 3.14
+ **/
+gchar *
+e_source_authentication_dup_credential_name (ESourceAuthentication *extension)
+{
+ const gchar *protected;
+ gchar *duplicate;
+
+ g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATION (extension), NULL);
+
+ g_mutex_lock (&extension->priv->property_lock);
+
+ protected = e_source_authentication_get_credential_name (extension);
+ duplicate = g_strdup (protected);
+
+ g_mutex_unlock (&extension->priv->property_lock);
+
+ return duplicate;
+}
+
+/**
+ * e_source_authentication_set_credential_name:
+ * @extension: an #ESourceAuthentication
+ * @credential_name: (allow-none): a credential name, or %NULL
+ *
+ * Sets the credential name used to pass the stored or gathered credential
+ * (like password) into the e_source_invoke_authenticate(). This is
+ * a counterpart of the authentication method. The %NULL means to use
+ * the default name, which is #E_SOURCE_CREDENTIAL_PASSWORD.
+ *
+ * The internal copy of @credential_name is automatically stripped
+ * of leading and trailing whitespace. If the resulting string is
+ * empty, %NULL is set instead.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_authentication_set_credential_name (ESourceAuthentication *extension,
+ const gchar *credential_name)
+{
+ g_return_if_fail (E_IS_SOURCE_AUTHENTICATION (extension));
+
+ g_mutex_lock (&extension->priv->property_lock);
+
+ if (g_strcmp0 (extension->priv->credential_name, credential_name) == 0) {
+ g_mutex_unlock (&extension->priv->property_lock);
+ return;
+ }
+
+ g_free (extension->priv->credential_name);
+ extension->priv->credential_name = e_util_strdup_strip (credential_name);
+
+ g_mutex_unlock (&extension->priv->property_lock);
+
+ g_object_notify (G_OBJECT (extension), "credential-name");
+}
diff --git a/libedataserver/e-source-authentication.h b/libedataserver/e-source-authentication.h
index 122b63e..267f1b0 100644
--- a/libedataserver/e-source-authentication.h
+++ b/libedataserver/e-source-authentication.h
@@ -121,6 +121,13 @@ gchar * e_source_authentication_dup_user
void e_source_authentication_set_user
(ESourceAuthentication *extension,
const gchar *user);
+const gchar * e_source_authentication_get_credential_name
+ (ESourceAuthentication *extension);
+gchar * e_source_authentication_dup_credential_name
+ (ESourceAuthentication *extension);
+void e_source_authentication_set_credential_name
+ (ESourceAuthentication *extension,
+ const gchar *credential_name);
G_END_DECLS
diff --git a/libedataserver/e-source-credentials-provider-impl-password.c
b/libedataserver/e-source-credentials-provider-impl-password.c
new file mode 100644
index 0000000..25315bf
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-password.c
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-source-credentials-provider-impl-password.h"
+
+struct _ESourceCredentialsProviderImplPasswordPrivate {
+ gboolean dummy;
+};
+
+G_DEFINE_TYPE (ESourceCredentialsProviderImplPassword, e_source_credentials_provider_impl_password,
E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL)
+
+static gboolean
+e_source_credentials_provider_impl_password_can_process (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ /* It can process any source by default */
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_password_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_password_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_password_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ gchar *password = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ *out_credentials = NULL;
+
+ if (!e_source_lookup_password_sync (source, cancellable, &password, error))
+ return FALSE;
+
+ if (!password) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Password not found"));
+ return FALSE;
+ }
+
+ *out_credentials = e_named_parameters_new ();
+ e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, password);
+
+ e_util_safe_free_string (password);
+
+ return TRUE;
+}
+
+static gboolean
+e_source_credentials_provider_impl_password_store_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+ g_return_val_if_fail (e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD) != NULL,
FALSE);
+
+ return e_source_store_password_sync (source,
+ e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_PASSWORD),
+ permanently, cancellable, error);
+}
+
+static gboolean
+e_source_credentials_provider_impl_password_delete_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ return e_source_delete_password_sync (source, cancellable, error);
+}
+
+static void
+e_source_credentials_provider_impl_password_class_init (ESourceCredentialsProviderImplPasswordClass *klass)
+{
+ ESourceCredentialsProviderImplClass *impl_class;
+
+ g_type_class_add_private (klass, sizeof (ESourceCredentialsProviderImplPasswordPrivate));
+
+ impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (klass);
+ impl_class->can_process = e_source_credentials_provider_impl_password_can_process;
+ impl_class->can_store = e_source_credentials_provider_impl_password_can_store;
+ impl_class->can_prompt = e_source_credentials_provider_impl_password_can_prompt;
+ impl_class->lookup_sync = e_source_credentials_provider_impl_password_lookup_sync;
+ impl_class->store_sync = e_source_credentials_provider_impl_password_store_sync;
+ impl_class->delete_sync = e_source_credentials_provider_impl_password_delete_sync;
+}
+
+static void
+e_source_credentials_provider_impl_password_init (ESourceCredentialsProviderImplPassword *provider_impl)
+{
+ provider_impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider_impl,
+ E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD,
ESourceCredentialsProviderImplPasswordPrivate);
+}
diff --git a/libedataserver/e-source-credentials-provider-impl-password.h
b/libedataserver/e-source-credentials-provider-impl-password.h
new file mode 100644
index 0000000..64343e3
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl-password.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_H
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/e-source-credentials-provider-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD \
+ (e_source_credentials_provider_impl_password_get_type ())
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD, ESourceCredentialsProviderImplPassword))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD,
ESourceCredentialsProviderImplPasswordClass))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD,
ESourceCredentialsProviderImplPasswordClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCredentialsProviderImplPassword ESourceCredentialsProviderImplPassword;
+typedef struct _ESourceCredentialsProviderImplPasswordClass ESourceCredentialsProviderImplPasswordClass;
+typedef struct _ESourceCredentialsProviderImplPasswordPrivate ESourceCredentialsProviderImplPasswordPrivate;
+
+/**
+ * ESourceCredentialsProviderImplPassword:
+ *
+ * Password based credentials provider implementation.
+ *
+ * Since: 3.14
+ **/
+struct _ESourceCredentialsProviderImplPassword {
+ ESourceCredentialsProviderImpl parent;
+ ESourceCredentialsProviderImplPasswordPrivate *priv;
+};
+
+struct _ESourceCredentialsProviderImplPasswordClass {
+ ESourceCredentialsProviderImplClass parent_class;
+};
+
+GType e_source_credentials_provider_impl_password_get_type (void);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD_H */
diff --git a/libedataserver/e-source-credentials-provider-impl.c
b/libedataserver/e-source-credentials-provider-impl.c
new file mode 100644
index 0000000..1082208
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl.c
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include "e-source-credentials-provider.h"
+#include "e-source-credentials-provider-impl.h"
+
+struct _ESourceCredentialsProviderImplPrivate {
+ gboolean dummy;
+};
+
+G_DEFINE_ABSTRACT_TYPE (ESourceCredentialsProviderImpl, e_source_credentials_provider_impl, E_TYPE_EXTENSION)
+
+static gboolean
+source_credentials_provider_impl_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Credentials lookup is not
supported"));
+
+ return FALSE;
+}
+
+static gboolean
+source_credentials_provider_impl_store_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Credentials store is not
supported"));
+
+ return FALSE;
+}
+
+static gboolean
+source_credentials_provider_impl_delete_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, _("Credentials delete is not
supported"));
+
+ return FALSE;
+}
+
+static void
+e_source_credentials_provider_impl_constructed (GObject *object)
+{
+ ESourceCredentialsProviderImpl *provider_impl = E_SOURCE_CREDENTIALS_PROVIDER_IMPL (object);
+ ESourceCredentialsProvider *provider;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_credentials_provider_impl_parent_class)->constructed (object);
+
+ provider = E_SOURCE_CREDENTIALS_PROVIDER (e_extension_get_extensible (E_EXTENSION (provider_impl)));
+
+ e_source_credentials_provider_register_impl (provider, provider_impl);
+}
+
+static void
+e_source_credentials_provider_impl_class_init (ESourceCredentialsProviderImplClass *klass)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+ ESourceCredentialsProviderImplClass *provider_impl_class;
+
+ g_type_class_add_private (klass, sizeof (ESourceCredentialsProviderImplPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->constructed = e_source_credentials_provider_impl_constructed;
+
+ extension_class = E_EXTENSION_CLASS (klass);
+ extension_class->extensible_type = E_TYPE_SOURCE_CREDENTIALS_PROVIDER;
+
+ provider_impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (klass);
+ provider_impl_class->lookup_sync = source_credentials_provider_impl_lookup_sync;
+ provider_impl_class->store_sync = source_credentials_provider_impl_store_sync;
+ provider_impl_class->delete_sync = source_credentials_provider_impl_delete_sync;
+}
+
+static void
+e_source_credentials_provider_impl_init (ESourceCredentialsProviderImpl *provider_impl)
+{
+ provider_impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider_impl,
+ E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL, ESourceCredentialsProviderImplPrivate);
+}
+
+/**
+ * e_source_credentials_provider_impl_get_provider:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ *
+ * Returns an #ESourceCredentialsProvider with which the @provider_impl is associated.
+ *
+ * Returns: an #ESourceCredentialsProvider
+ *
+ * Since: 3.14
+ **/
+ESourceCredentialsProvider *
+e_source_credentials_provider_impl_get_provider (ESourceCredentialsProviderImpl *provider_impl)
+{
+ EExtensible *extensible;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), NULL);
+
+ extensible = e_extension_get_extensible (E_EXTENSION (provider_impl));
+ if (!extensible)
+ return NULL;
+
+ return E_SOURCE_CREDENTIALS_PROVIDER (extensible);
+}
+
+/**
+ * e_source_credentials_provider_impl_can_process:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ * @source: an #ESource
+ *
+ * Returns whether the @provider_impl can process credentials for the @source.
+ *
+ * Returns: Whether the @provider_impl can process credentials for the @source.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_can_process (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->can_process != NULL, FALSE);
+
+ return klass->can_process (provider_impl, source);
+}
+
+/**
+ * e_source_credentials_provider_impl_can_store:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ *
+ * Returns whether the @provider_impl can store credentials.
+ *
+ * Returns: Whether the @provider_impl can store credentials.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->can_store != NULL, FALSE);
+
+ return klass->can_store (provider_impl);
+}
+
+/**
+ * e_source_credentials_provider_impl_can_prompt:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ *
+ * Returns whether credential prompt can be done for the @provider_impl.
+ *
+ * Returns: Whether credential prompt can be done for the @provider_impl.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->can_prompt != NULL, FALSE);
+
+ return klass->can_prompt (provider_impl);
+}
+
+/**
+ * e_source_credentials_provider_impl_lookup_sync:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ * @source: an #ESource
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @out_credentials: an #ENamedParameters to be set with stored credentials
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Asks @provider_impl to lookup for stored credentials for @source.
+ * The @out_credentials is populated with them. If the result is not
+ * %NULL, then it should be freed with e_anmed_parameters_free() when
+ * no longer needed.
+ *
+ * Default implementation returns %FALSE and sets #G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->lookup_sync != NULL, FALSE);
+
+ return klass->lookup_sync (provider_impl, source, cancellable, out_credentials, error);
+}
+
+/**
+ * e_source_credentials_provider_impl_store_sync:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ * @source: an #ESource
+ * @credentials: an #ENamedParameters containing credentials to store
+ * @permanently: whether to store credentials permanently, or for the current session only
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Asks @provider_impl to store @credentials for @source.
+ *
+ * Default implementation returns %FALSE and sets #G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_store_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->store_sync != NULL, FALSE);
+
+ return klass->store_sync (provider_impl, source, credentials, permanently, cancellable, error);
+}
+
+/**
+ * e_source_credentials_provider_impl_delete_sync:
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ * @source: an #ESource
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Asks @provider_impl to delete any stored credentials for @source.
+ *
+ * Default implementation returns %FALSE and sets #G_IO_ERROR_NOT_SUPPORTED error.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_impl_delete_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceCredentialsProviderImplClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS (provider_impl);
+ g_return_val_if_fail (klass->delete_sync != NULL, FALSE);
+
+ return klass->delete_sync (provider_impl, source, cancellable, error);
+}
diff --git a/libedataserver/e-source-credentials-provider-impl.h
b/libedataserver/e-source-credentials-provider-impl.h
new file mode 100644
index 0000000..73c5e5e
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider-impl.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_CREDENTIALS_PROVIDER_IMPL_H
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/e-extension.h>
+#include <libedataserver/e-source.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL \
+ (e_source_credentials_provider_impl_get_type ())
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL, ESourceCredentialsProviderImpl))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL, ESourceCredentialsProviderImplClass))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL))
+#define E_SOURCE_CREDENTIALS_PROVIDER_IMPL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL, ESourceCredentialsProviderImplClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCredentialsProviderImpl ESourceCredentialsProviderImpl;
+typedef struct _ESourceCredentialsProviderImplClass ESourceCredentialsProviderImplClass;
+typedef struct _ESourceCredentialsProviderImplPrivate ESourceCredentialsProviderImplPrivate;
+
+struct _ESourceCredentialsProvider;
+
+/**
+ * ESourceCredentialsProviderImpl:
+ *
+ * Credentials provider implementation base structure. The descendants
+ * implement the virtual methods. The descendants are automatically
+ * registered into an #ESourceCredentialsProvider.
+ *
+ * Since: 3.14
+ **/
+struct _ESourceCredentialsProviderImpl {
+ EExtension parent;
+ ESourceCredentialsProviderImplPrivate *priv;
+};
+
+struct _ESourceCredentialsProviderImplClass {
+ EExtensionClass parent_class;
+
+ gboolean (*can_process) (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source);
+ gboolean (*can_store) (ESourceCredentialsProviderImpl *provider_impl);
+ gboolean (*can_prompt) (ESourceCredentialsProviderImpl *provider_impl);
+ gboolean (*lookup_sync) (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error);
+ gboolean (*store_sync) (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error);
+ gboolean (*delete_sync) (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error);
+};
+
+GType e_source_credentials_provider_impl_get_type (void);
+struct _ESourceCredentialsProvider *
+ e_source_credentials_provider_impl_get_provider
+ (ESourceCredentialsProviderImpl *provider_impl);
+gboolean e_source_credentials_provider_impl_can_process
+ (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source);
+gboolean e_source_credentials_provider_impl_can_store
+ (ESourceCredentialsProviderImpl *provider_impl);
+gboolean e_source_credentials_provider_impl_can_prompt
+ (ESourceCredentialsProviderImpl *provider_impl);
+gboolean e_source_credentials_provider_impl_lookup_sync
+ (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error);
+gboolean e_source_credentials_provider_impl_store_sync
+ (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error);
+gboolean e_source_credentials_provider_impl_delete_sync
+ (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_CREDENTIALS_PROVIDER_IMPL_H */
diff --git a/libedataserver/e-source-credentials-provider.c b/libedataserver/e-source-credentials-provider.c
new file mode 100644
index 0000000..d9b2271
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider.c
@@ -0,0 +1,1030 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include "e-data-server-util.h"
+#include "e-source.h"
+#include "e-source-authentication.h"
+#include "e-source-collection.h"
+#include "e-source-registry.h"
+#include "e-source-credentials-provider-impl.h"
+#include "e-module.h"
+
+#include "libedataserver-private.h"
+
+#include "e-source-credentials-provider.h"
+
+/* built-in source credentials provider implementations */
+#include "e-source-credentials-provider-impl-password.h"
+
+struct _ESourceCredentialsProviderPrivate {
+ GWeakRef registry; /* The property can hold both client and server-side registry */
+ GMutex providers_lock;
+ GSList *providers; /* ESourceCredentialsProviderImpl *impl */
+ ESourceCredentialsProviderImpl *impl_password;
+};
+
+enum {
+ PROP_0,
+ PROP_REGISTRY
+};
+
+G_DEFINE_TYPE_WITH_CODE (ESourceCredentialsProvider, e_source_credentials_provider, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+static ESource *
+source_credentials_provider_ref_source (ESourceCredentialsProvider *provider,
+ const gchar *uid)
+{
+ GObject *registry;
+ ESource *source = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+ g_return_val_if_fail (uid, NULL);
+
+ registry = e_source_credentials_provider_ref_registry (provider);
+ if (registry) {
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+ source = e_source_registry_ref_source (E_SOURCE_REGISTRY (registry), uid);
+ }
+
+ g_clear_object (®istry);
+
+ return source;
+}
+
+static void
+source_credentials_provider_set_registry (ESourceCredentialsProvider *provider,
+ GObject *registry)
+{
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider));
+ g_return_if_fail (G_IS_OBJECT (registry));
+ g_return_if_fail (g_weak_ref_get (&provider->priv->registry) == NULL);
+
+ g_weak_ref_set (&provider->priv->registry, registry);
+}
+
+static void
+source_credentials_provider_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REGISTRY:
+ source_credentials_provider_set_registry (
+ E_SOURCE_CREDENTIALS_PROVIDER (object),
+ g_value_get_object (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_credentials_provider_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REGISTRY:
+ g_value_take_object (value,
+ e_source_credentials_provider_ref_registry (
+ E_SOURCE_CREDENTIALS_PROVIDER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+source_credentials_provider_constructed (GObject *object)
+{
+ ESourceCredentialsProvider *provider = E_SOURCE_CREDENTIALS_PROVIDER (object);
+ GList *module_types;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_credentials_provider_parent_class)->constructed (object);
+
+ module_types = e_module_load_all_in_directory (E_DATA_SERVER_CREDENTIALMODULEDIR);
+ g_list_free_full (module_types, (GDestroyNotify) g_type_module_unuse);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+
+ g_mutex_lock (&provider->priv->providers_lock);
+
+ /* Safety check. */
+ g_warn_if_fail (provider->priv->impl_password != NULL);
+
+ g_mutex_unlock (&provider->priv->providers_lock);
+}
+
+static void
+source_credentials_provider_finalize (GObject *object)
+{
+ ESourceCredentialsProvider *provider = E_SOURCE_CREDENTIALS_PROVIDER (object);
+
+ g_mutex_lock (&provider->priv->providers_lock);
+ g_slist_free_full (provider->priv->providers, g_object_unref);
+ provider->priv->providers = NULL;
+ g_clear_object (&provider->priv->impl_password);
+ g_mutex_unlock (&provider->priv->providers_lock);
+
+ g_mutex_clear (&provider->priv->providers_lock);
+ g_weak_ref_clear (&provider->priv->registry);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_source_credentials_provider_parent_class)->finalize (object);
+}
+
+static void
+e_source_credentials_provider_class_init (ESourceCredentialsProviderClass *class)
+{
+ GObjectClass *object_class;
+ ESourceCredentialsProviderClass *provider_class;
+
+ g_type_class_add_private (class, sizeof (ESourceCredentialsProviderPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = source_credentials_provider_set_property;
+ object_class->get_property = source_credentials_provider_get_property;
+ object_class->constructed = source_credentials_provider_constructed;
+ object_class->finalize = source_credentials_provider_finalize;
+
+ provider_class = E_SOURCE_CREDENTIALS_PROVIDER_CLASS (class);
+ provider_class->ref_source = source_credentials_provider_ref_source;
+
+ /**
+ * ESourceCredentialsProvider:registry:
+ *
+ * The Source Registry object, which can be either #ESourceregistry or #ESourceRegistryServer.
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_object (
+ "registry",
+ "Registry",
+ "An ESourceRegistry",
+ G_TYPE_OBJECT,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /* Ensure built-in credential providers implementation types */
+ g_type_ensure (E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD);
+}
+
+static void
+e_source_credentials_provider_init (ESourceCredentialsProvider *provider)
+{
+ provider->priv = G_TYPE_INSTANCE_GET_PRIVATE (provider, E_TYPE_SOURCE_CREDENTIALS_PROVIDER,
ESourceCredentialsProviderPrivate);
+
+ g_weak_ref_init (&provider->priv->registry, NULL);
+ provider->priv->providers = NULL;
+ g_mutex_init (&provider->priv->providers_lock);
+}
+
+static ESourceCredentialsProviderImpl *
+source_credential_provider_ref_impl_for_source (ESourceCredentialsProvider *provider,
+ ESource *source,
+ ESource **out_cred_source)
+{
+ ESourceCredentialsProviderImpl *impl = NULL;
+ ESource *cred_source;
+ GSList *link;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ if (out_cred_source)
+ *out_cred_source = NULL;
+
+ cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
+ if (!cred_source || cred_source == source) {
+ g_clear_object (&cred_source);
+ } else {
+ source = cred_source;
+ if (out_cred_source)
+ *out_cred_source = g_object_ref (cred_source);
+ }
+
+ g_mutex_lock (&provider->priv->providers_lock);
+
+ for (link = provider->priv->providers; link; link = link->next) {
+ ESourceCredentialsProviderImpl *adept = link->data;
+
+ if (adept && e_source_credentials_provider_impl_can_process (adept, source)) {
+ impl = g_object_ref (adept);
+ break;
+ }
+ }
+
+ if (!impl && provider->priv->impl_password)
+ impl = g_object_ref (provider->priv->impl_password);
+
+ g_mutex_unlock (&provider->priv->providers_lock);
+
+ g_clear_object (&cred_source);
+
+ if (!impl && out_cred_source)
+ g_clear_object (out_cred_source);
+
+ return impl;
+}
+
+/**
+ * e_source_credentials_provider_new:
+ * @registry: an #ESourceRegistry
+ *
+ * Creates a new #ESourceCredentialsProvider, which is meant to abstract
+ * credential management for #ESource<!-- -->-s.
+ *
+ * Returns: (transfer full): a new #ESourceCredentialsProvider
+ *
+ * Since: 3.14
+ **/
+ESourceCredentialsProvider *
+e_source_credentials_provider_new (ESourceRegistry *registry)
+{
+ g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), NULL);
+
+ return g_object_new (E_TYPE_SOURCE_CREDENTIALS_PROVIDER,
+ "registry", registry,
+ NULL);
+}
+
+/**
+ * e_source_credentials_provider_ref_registry:
+ * @provider: an #ESourceCredentialsProvider
+ *
+ * Returns refenrenced registry associated with this @provider.
+ *
+ * Returns: Reference registry associated with this @provider. Unref it
+ * with g_object_unref() when no longer needed.
+ *
+ * Since: 3.14
+ **/
+GObject *
+e_source_credentials_provider_ref_registry (ESourceCredentialsProvider *provider)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+
+ return g_weak_ref_get (&provider->priv->registry);
+}
+
+/**
+ * e_source_credentials_provider_register_impl:
+ * @provider: an #ESourceCredentialsProvider
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ *
+ * Registers a credentials provider implementation and adds its own reference on
+ * the @provider_impl.
+ *
+ * Returns: %TRUE on success, %FALSE on failure, like when there is
+ * the @provider_impl already registered.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_register_impl (ESourceCredentialsProvider *provider,
+ ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL (provider_impl), FALSE);
+
+ g_mutex_lock (&provider->priv->providers_lock);
+ if (g_slist_find (provider->priv->providers, provider_impl)) {
+ g_mutex_unlock (&provider->priv->providers_lock);
+ return FALSE;
+ }
+
+ /* Deal with the built-in password provider differently, it's a fallback provider_impl */
+ if (E_IS_SOURCE_CREDENTIALS_PROVIDER_IMPL_PASSWORD (provider_impl)) {
+ if (provider_impl == provider->priv->impl_password) {
+ g_mutex_unlock (&provider->priv->providers_lock);
+ return FALSE;
+ }
+
+ g_clear_object (&provider->priv->impl_password);
+ provider->priv->impl_password = g_object_ref (provider_impl);
+ } else {
+ provider->priv->providers = g_slist_prepend (provider->priv->providers, g_object_ref
(provider_impl));
+ }
+
+ g_mutex_unlock (&provider->priv->providers_lock);
+
+ return TRUE;
+}
+
+/**
+ * e_source_credentials_provider_unregister_impl:
+ * @provider: an #ESourceCredentialsProvider
+ * @provider_impl: an #ESourceCredentialsProviderImpl
+ *
+ * Unregisters previously registered @provider_impl with
+ * e_source_credentials_provider_register_impl(). Function does nothing,
+ * when the @provider_impl is not registered.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_credentials_provider_unregister_impl (ESourceCredentialsProvider *provider,
+ ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider));
+
+ g_mutex_lock (&provider->priv->providers_lock);
+
+ provider->priv->providers = g_slist_remove (provider->priv->providers, provider_impl);
+
+ g_mutex_unlock (&provider->priv->providers_lock);
+}
+
+/**
+ * e_source_credentials_provider_ref_source:
+ * @provider: an #ESourceCredentialsProvider
+ * @uid: an #ESource UID
+ *
+ * Returns referenced #ESource with the given @uid, or %NULL, when it could not be found.
+ *
+ * Returns: (transfer full): Referenced #ESource with the given @uid, or %NULL, when it
+ * could not be found. Unref the returned #ESource with g_object_unref(), when no longer needed.
+ *
+ * Since: 3.14
+ **/
+ESource *
+e_source_credentials_provider_ref_source (ESourceCredentialsProvider *provider,
+ const gchar *uid)
+{
+ ESourceCredentialsProviderClass *klass;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ klass = E_SOURCE_CREDENTIALS_PROVIDER_GET_CLASS (provider);
+ g_return_val_if_fail (klass->ref_source != NULL, NULL);
+
+ return klass->ref_source (provider, uid);
+}
+
+/**
+ * e_source_credentials_provider_ref_credentials_source:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource
+ *
+ * Returns a referenced parent #ESource, which holds the credentials for
+ * the given @source. This is useful for collections, where the credentials
+ * are usually stored on the collection source, thus shared between child
+ * sources. When ther eis no such parent source, a %NULL is returned, which
+ * means the @source holds credentials for itself.
+ *
+ * Returns: referenced parent #ESource, which holds credentials, or %NULL. Unref
+ * the returned non-NULL #ESource with g_object_unref(), when no longer needed.
+ *
+ * Since: 3.14
+ **/
+ESource *
+e_source_credentials_provider_ref_credentials_source (ESourceCredentialsProvider *provider,
+ ESource *source)
+{
+ ESource *collection, *cred_source = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), NULL);
+ g_return_val_if_fail (E_IS_SOURCE (source), NULL);
+
+ collection = g_object_ref (source);
+
+ while (collection &&
+ !e_source_has_extension (collection, E_SOURCE_EXTENSION_COLLECTION)) {
+ ESource *parent;
+
+ if (!e_source_get_parent (collection)) {
+ break;
+ }
+
+ parent = e_source_credentials_provider_ref_source (provider, e_source_get_parent
(collection));
+
+ g_clear_object (&collection);
+ collection = parent;
+ }
+
+ if (collection && e_source_has_extension (collection, E_SOURCE_EXTENSION_COLLECTION)) {
+ gboolean can_use_collection = FALSE;
+
+ /* Use the found parent collection source for credentials store only if
+ the child source doesn't have any authentication information, or this
+ information is not filled, or if either the host name or the user name
+ are the same with the collection source.
+
+ This allows to create a collection of sources which has one source
+ (like message send) on a different server, thus this source uses
+ its own credentials.
+ */
+ if (!e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ can_use_collection = TRUE;
+ } else if (e_source_has_extension (collection, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *auth_source, *auth_collection;
+ gchar *host_source, *host_collection;
+
+ auth_source = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ auth_collection = e_source_get_extension (collection,
E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ host_source = e_source_authentication_dup_host (auth_source);
+ host_collection = e_source_authentication_dup_host (auth_collection);
+
+ if (host_source && host_collection && g_ascii_strcasecmp (host_source,
host_collection) == 0) {
+ gchar *username_source, *username_collection;
+
+ username_source = e_source_authentication_dup_user (auth_source);
+ username_collection = e_source_authentication_dup_user (auth_collection);
+
+ if (username_source && username_collection && g_ascii_strcasecmp
(username_source, username_collection) == 0) {
+ can_use_collection = TRUE;
+ } else {
+ can_use_collection = !username_source || !*username_source;
+ }
+
+ g_free (username_source);
+ g_free (username_collection);
+ } else {
+ /* Only one of them is filled, then use the collection; otherwise
+ both are filled and they do not match, thus do not use collection. */
+ can_use_collection = (host_collection && *host_collection && (!host_source ||
!*host_source)) ||
+ (host_source && *host_source && (!host_collection ||
!*host_collection));
+ }
+
+ g_free (host_source);
+ g_free (host_collection);
+ }
+
+ if (can_use_collection)
+ cred_source = g_object_ref (collection);
+ }
+
+ g_clear_object (&collection);
+
+ return cred_source;
+}
+
+/**
+ * e_source_credentials_provider_can_store:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource
+ *
+ * Returns whether the @source can store its credentials. When %FALSE is returned,
+ * an attempt to call e_source_credentials_provider_store() or
+ * e_source_credentials_provider_store_sync() will fail for this @source.
+ *
+ * Returns: %TRUE, when the credentials storing for @source is possible, %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_can_store (ESourceCredentialsProvider *provider,
+ ESource *source)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ gboolean res;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, NULL);
+
+ g_return_val_if_fail (provider_impl != NULL, FALSE);
+ res = e_source_credentials_provider_impl_can_store (provider_impl);
+
+ g_clear_object (&provider_impl);
+
+ return res;
+}
+
+/**
+ * e_source_credentials_provider_can_prompt:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource
+ *
+ * Returns whether a credentials prompt can be shown for the @source.
+ *
+ * Returns: %TRUE, when a credentials prompt can be shown for @source, %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_can_prompt (ESourceCredentialsProvider *provider,
+ ESource *source)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ gboolean res;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, NULL);
+ g_return_val_if_fail (provider_impl != NULL, FALSE);
+
+ res = e_source_credentials_provider_impl_can_prompt (provider_impl);
+
+ g_clear_object (&provider_impl);
+
+ return res;
+}
+
+typedef struct _AsyncContext {
+ ESource *source;
+ ENamedParameters *credentials;
+ gboolean permanently;
+} AsyncContext;
+
+static AsyncContext *
+async_context_new (ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently)
+{
+ AsyncContext *async_context;
+
+ async_context = g_new0 (AsyncContext, 1);
+ async_context->source = g_object_ref (source);
+ async_context->permanently = permanently;
+ if (credentials)
+ async_context->credentials = e_named_parameters_new_clone (credentials);
+
+ return async_context;
+}
+
+static void
+async_context_free (gpointer ptr)
+{
+ AsyncContext *async_context = ptr;
+
+ if (async_context) {
+ g_clear_object (&async_context->source);
+ e_named_parameters_free (async_context->credentials);
+ g_free (async_context);
+ }
+}
+
+/**
+ * e_source_credentials_provider_lookup_sync:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to lookup credentials for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @out_credentials: (out): return location for the credentials
+ * @error: return location for a #GError, or %NULL
+ *
+ * Looks up the credentials for @source.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_lookup_sync (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_val_if_fail (provider_impl != NULL, FALSE);
+
+ success = e_source_credentials_provider_impl_lookup_sync (provider_impl, cred_source ? cred_source :
source, cancellable, out_credentials, error);
+
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+
+ return success;
+}
+
+static void
+source_credentials_provider_lookup_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ESourceCredentialsProvider *provider = source_object;
+ AsyncContext *async_context = task_data;
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_source_credentials_provider_lookup_sync (provider,
+ async_context->source,
+ cancellable,
+ &async_context->credentials,
+ &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_source_credentials_provider_lookup:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to lookup credentials for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously looks up for credentials for @source.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_source_credentials_provider_lookup_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_credentials_provider_lookup (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ GTask *task;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_if_fail (provider_impl != NULL);
+
+ async_context = async_context_new (cred_source ? cred_source : source, NULL, FALSE);
+
+ task = g_task_new (provider, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_source_credentials_provider_lookup);
+ g_task_set_task_data (task, async_context, async_context_free);
+
+ g_task_run_in_thread (task, source_credentials_provider_lookup_thread);
+
+ g_object_unref (task);
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+}
+
+/**
+ * e_source_credentials_provider_lookup_finish:
+ * @provider: an #ESourceCredentialsProvider
+ * @result: a #GAsyncResult
+ * @out_credentials: (out): return location for the credentials
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_credentials_provider_lookup().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_lookup_finish (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ AsyncContext *async_context;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_source_credentials_provider_lookup), FALSE);
+
+ async_context = g_task_get_task_data (G_TASK (result));
+
+ if (!g_task_had_error (G_TASK (result))) {
+ *out_credentials = async_context->credentials;
+ async_context->credentials = NULL;
+ }
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * e_source_credentials_provider_store_sync:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to store credentials for
+ * @credentials: an #ENamedParameters with credentials to store
+ * @permanently: store permanently or just for the session
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Stores the @credentials for @source. Note the actual stored values
+ * can differ for each storage. In other words, not all named parameters
+ * are stored for each @source.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_store_sync (ESourceCredentialsProvider *provider,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (credentials != NULL, FALSE);
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_val_if_fail (provider_impl != NULL, FALSE);
+
+ success = e_source_credentials_provider_impl_store_sync (provider_impl, cred_source ? cred_source :
source, credentials, permanently, cancellable, error);
+
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+
+ return success;
+}
+
+static void
+source_credentials_provider_store_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ESourceCredentialsProvider *provider = source_object;
+ AsyncContext *async_context = task_data;
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_source_credentials_provider_store_sync (provider,
+ async_context->source,
+ async_context->credentials,
+ async_context->permanently,
+ cancellable,
+ &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_source_credentials_provider_store:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to lookup credentials for
+ * @credentials: an #ENamedParameters with credentials to store
+ * @permanently: store permanently or just for the session
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously stores the @credentials for @source. Note the actual stored
+ * values can differ for each storage. In other words, not all named parameters
+ * are stored for each @source.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_source_credentials_provider_store_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_credentials_provider_store (ESourceCredentialsProvider *provider,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ GTask *task;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_if_fail (provider_impl != NULL);
+
+ async_context = async_context_new (cred_source ? cred_source : source, credentials, permanently);
+
+ task = g_task_new (provider, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_source_credentials_provider_store);
+ g_task_set_task_data (task, async_context, async_context_free);
+
+ g_task_run_in_thread (task, source_credentials_provider_store_thread);
+
+ g_object_unref (task);
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+}
+
+/**
+ * e_source_credentials_provider_store_finish:
+ * @provider: an #ESourceCredentialsProvider
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_credentials_provider_store().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_store_finish (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_source_credentials_provider_store), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * e_source_credentials_provider_delete_sync:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to store credentials for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Deletes any previously stored credentials for @source.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_delete_sync (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_val_if_fail (provider_impl != NULL, FALSE);
+
+ success = e_source_credentials_provider_impl_delete_sync (provider_impl, cred_source ? cred_source :
source, cancellable, error);
+
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+
+ return success;
+}
+
+static void
+source_credentials_provider_delete_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ESourceCredentialsProvider *provider = source_object;
+ AsyncContext *async_context = task_data;
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_source_credentials_provider_delete_sync (provider,
+ async_context->source, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_source_credentials_provider_delete:
+ * @provider: an #ESourceCredentialsProvider
+ * @source: an #ESource, to lookup credentials for
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @callback: a #GAsyncReadyCallback to call when the request is satisfied
+ * @user_data: data to pass to the callback function
+ *
+ * Asynchronously deletes any previously stored credentials for @source.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_source_credentials_provider_delete_finish() to get the result
+ * of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_credentials_provider_delete (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ESourceCredentialsProviderImpl *provider_impl;
+ ESource *cred_source = NULL;
+ GTask *task;
+ AsyncContext *async_context;
+
+ g_return_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ provider_impl = source_credential_provider_ref_impl_for_source (provider, source, &cred_source);
+ g_return_if_fail (provider_impl != NULL);
+
+ async_context = async_context_new (cred_source ? cred_source : source, NULL, FALSE);
+
+ task = g_task_new (provider, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_source_credentials_provider_delete);
+ g_task_set_task_data (task, async_context, async_context_free);
+
+ g_task_run_in_thread (task, source_credentials_provider_delete_thread);
+
+ g_object_unref (task);
+ g_clear_object (&provider_impl);
+ g_clear_object (&cred_source);
+}
+
+/**
+ * e_source_credentials_provider_delete_finish:
+ * @provider: an #ESourceCredentialsProvider
+ * @result: a #GAsyncResult
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_credentials_provider_delete().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_credentials_provider_delete_finish (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_SOURCE_CREDENTIALS_PROVIDER (provider), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, provider), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_source_credentials_provider_delete), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
diff --git a/libedataserver/e-source-credentials-provider.h b/libedataserver/e-source-credentials-provider.h
new file mode 100644
index 0000000..337a55a
--- /dev/null
+++ b/libedataserver/e-source-credentials-provider.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVER_H_INSIDE__) && !defined (LIBEDATASERVER_COMPILATION)
+#error "Only <libedataserver/libedataserver.h> should be included directly."
+#endif
+
+#ifndef E_SOURCE_CREDENTIALS_PROVIDER_H
+#define E_SOURCE_CREDENTIALS_PROVIDER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source.h>
+#include <libedataserver/e-source-registry.h>
+#include <libedataserver/e-source-credentials-provider-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_SOURCE_CREDENTIALS_PROVIDER \
+ (e_source_credentials_provider_get_type ())
+#define E_SOURCE_CREDENTIALS_PROVIDER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER, ESourceCredentialsProvider))
+#define E_SOURCE_CREDENTIALS_PROVIDER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER, ESourceCredentialsProviderClass))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER))
+#define E_IS_SOURCE_CREDENTIALS_PROVIDER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_SOURCE_CREDENTIALS_PROVIDER))
+#define E_SOURCE_CREDENTIALS_PROVIDER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_SOURCE_CREDENTIALS_PROVIDER, ESourceCredentialsProviderClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ESourceCredentialsProvider ESourceCredentialsProvider;
+typedef struct _ESourceCredentialsProviderClass ESourceCredentialsProviderClass;
+typedef struct _ESourceCredentialsProviderPrivate ESourceCredentialsProviderPrivate;
+
+/**
+ * ESourceCredentialsProvider:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.14
+ **/
+struct _ESourceCredentialsProvider {
+ GObject parent;
+ ESourceCredentialsProviderPrivate *priv;
+};
+
+struct _ESourceCredentialsProviderClass {
+ GObjectClass parent_class;
+
+ ESource * (*ref_source) (ESourceCredentialsProvider *provider,
+ const gchar *uid);
+};
+
+GType e_source_credentials_provider_get_type (void) G_GNUC_CONST;
+ESourceCredentialsProvider *
+ e_source_credentials_provider_new (ESourceRegistry *registry);
+GObject * e_source_credentials_provider_ref_registry
+ (ESourceCredentialsProvider *provider);
+gboolean e_source_credentials_provider_register_impl
+ (ESourceCredentialsProvider *provider,
+ ESourceCredentialsProviderImpl *provider_impl);
+void e_source_credentials_provider_unregister_impl
+ (ESourceCredentialsProvider *provider,
+ ESourceCredentialsProviderImpl *provider_impl);
+ESource * e_source_credentials_provider_ref_source
+ (ESourceCredentialsProvider *provider,
+ const gchar *uid);
+ESource * e_source_credentials_provider_ref_credentials_source
+ (ESourceCredentialsProvider *provider,
+ ESource *source);
+gboolean e_source_credentials_provider_can_store (ESourceCredentialsProvider *provider,
+ ESource *source);
+gboolean e_source_credentials_provider_can_prompt(ESourceCredentialsProvider *provider,
+ ESource *source);
+gboolean e_source_credentials_provider_lookup_sync
+ (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error);
+void e_source_credentials_provider_lookup (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_source_credentials_provider_lookup_finish
+ (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ ENamedParameters **out_credentials,
+ GError **error);
+gboolean e_source_credentials_provider_store_sync(ESourceCredentialsProvider *provider,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GError **error);
+void e_source_credentials_provider_store (ESourceCredentialsProvider *provider,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean permanently,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_source_credentials_provider_store_finish
+ (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_source_credentials_provider_delete_sync
+ (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GError **error);
+void e_source_credentials_provider_delete (ESourceCredentialsProvider *provider,
+ ESource *source,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_source_credentials_provider_delete_finish
+ (ESourceCredentialsProvider *provider,
+ GAsyncResult *result,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* E_SOURCE_CREDENTIALS_PROVIDER_H */
diff --git a/libedataserver/e-source-enums.h b/libedataserver/e-source-enums.h
index dec37f0..e33ccc4 100644
--- a/libedataserver/e-source-enums.h
+++ b/libedataserver/e-source-enums.h
@@ -69,19 +69,25 @@ typedef enum {
* ESourceAuthenticationResult:
* @E_SOURCE_AUTHENTICATION_ERROR:
* An error occurred while authenticating.
+ * @E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED:
+ * An SSL certificate check failed. Since: 3.14.
* @E_SOURCE_AUTHENTICATION_ACCEPTED:
* Server requesting authentication accepted password.
* @E_SOURCE_AUTHENTICATION_REJECTED:
* Server requesting authentication rejected password.
+ * @E_SOURCE_AUTHENTICATION_REQUIRED:
+ * Server requesting authentication, but none was given.
*
- * Status codes used by the #ESourceAuthenticator interface.
+ * Status codes used by the #EBackend authentication wrapper.
*
* Since: 3.6
**/
typedef enum {
E_SOURCE_AUTHENTICATION_ERROR,
+ E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED,
E_SOURCE_AUTHENTICATION_ACCEPTED,
- E_SOURCE_AUTHENTICATION_REJECTED
+ E_SOURCE_AUTHENTICATION_REJECTED,
+ E_SOURCE_AUTHENTICATION_REQUIRED
} ESourceAuthenticationResult;
/**
@@ -99,4 +105,66 @@ typedef enum {
E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY = 3
} ETrustPromptResponse;
+/**
+ * ESourceConnectionStatus:
+ * @E_SOURCE_CONNECTION_STATUS_DISCONNECTED:
+ * The source is currently disconnected from its (possibly remote) data store.
+ * @E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS:
+ * The source asked for credentials with a 'credentials-required' signal and
+ * is currently awaiting for them.
+ * @E_SOURCE_CONNECTION_STATUS_SSL_FAILED:
+ * A user rejected SSL certificate trust for the connection.
+ * @E_SOURCE_CONNECTION_STATUS_CONNECTING:
+ * The source is currently connecting to its (possibly remote) data store.
+ * @E_SOURCE_CONNECTION_STATUS_CONNECTED:
+ * The source is currently connected to its (possibly remote) data store.
+ *
+ * Connection status codes used by the #ESource to indicate its connection state.
+ * This is used in combination with authentication of the ESource. For example,
+ * if there are multiple clients asking for a password and a user enters the password
+ * in one of them, then the status will change into 'connecting', which is a signal
+ * do close the password prompt in the other client, because the credentials had
+ * been already provided.
+ *
+ * Since: 3.14
+ **/
+typedef enum {
+ E_SOURCE_CONNECTION_STATUS_DISCONNECTED,
+ E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS,
+ E_SOURCE_CONNECTION_STATUS_SSL_FAILED,
+ E_SOURCE_CONNECTION_STATUS_CONNECTING,
+ E_SOURCE_CONNECTION_STATUS_CONNECTED
+} ESourceConnectionStatus;
+
+/**
+ * ESourceCredentialsReason:
+ * @E_SOURCE_CREDENTIALS_REASON_UNKNOWN:
+ * A return value when there was no 'credentials-required' signal emitted yet,
+ * or a pair 'authenticate' signal had been received. This value should not
+ * be used in the call of 'credentials-required'.
+ * @E_SOURCE_CREDENTIALS_REASON_REQUIRED:
+ * This is the first attempt to get credentials for the source. It's usually
+ * used right after the source is opened and the authentication continues with
+ * a stored credentials, if any.
+ * @E_SOURCE_CREDENTIALS_REASON_REJECTED:
+ * The previously used credentials had been rejected by the server. That
+ * usually means that the user should be asked to provide/correct the credentials.
+ * @E_SOURCE_CREDENTIALS_REASON_SSL_FAILED:
+ * A secured connection failed due to some server-side certificate issues.
+ * @E_SOURCE_CREDENTIALS_REASON_ERROR:
+ * The server returned an error. It is not possible to connect to it
+ * at the moment usually.
+ *
+ * An ESource's authentication reason, used by an ESource::CredentialsRequired method.
+ *
+ * Since: 3.14
+ **/
+typedef enum {
+ E_SOURCE_CREDENTIALS_REASON_UNKNOWN,
+ E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ E_SOURCE_CREDENTIALS_REASON_REJECTED,
+ E_SOURCE_CREDENTIALS_REASON_SSL_FAILED,
+ E_SOURCE_CREDENTIALS_REASON_ERROR
+} ESourceCredentialsReason;
+
#endif /* E_SOURCE_ENUMS_H */
diff --git a/libedataserver/e-source-registry.c b/libedataserver/e-source-registry.c
index 5b37596..9d08b4e 100644
--- a/libedataserver/e-source-registry.c
+++ b/libedataserver/e-source-registry.c
@@ -54,6 +54,7 @@
#include <libedataserver/e-data-server-util.h>
#include <libedataserver/e-source-collection.h>
+#include <libedataserver/e-source-enumtypes.h>
/* Needed for the defaults API. */
#include <libedataserver/e-source-address-book.h>
@@ -61,8 +62,6 @@
#include <libedataserver/e-source-mail-account.h>
#include <libedataserver/e-source-mail-identity.h>
-#include "e-dbus-authenticator.h"
-
#define E_SOURCE_REGISTRY_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_SOURCE_REGISTRY, ESourceRegistryPrivate))
@@ -87,10 +86,10 @@
#define E_SETTINGS_DEFAULT_TASK_LIST_KEY "default-task-list"
typedef struct _AsyncContext AsyncContext;
-typedef struct _AuthContext AuthContext;
typedef struct _CreateContext CreateContext;
typedef struct _SourceClosure SourceClosure;
typedef struct _ThreadClosure ThreadClosure;
+typedef struct _CredentialsRequiredClosure CredentialsRequiredClosure;
struct _ESourceRegistryPrivate {
GMainContext *main_context;
@@ -120,19 +119,6 @@ struct _ESourceRegistryPrivate {
struct _AsyncContext {
ESource *source;
GList *list_of_sources;
- ESourceAuthenticator *auth;
-};
-
-/* Used in e_source_registry_authenticate_sync() */
-struct _AuthContext {
- ESourceAuthenticator *auth;
- EDBusAuthenticator *dbus_auth;
- GCancellable *cancellable;
- GMainLoop *main_loop;
- ESourceAuthenticationResult auth_result;
- GcrSecretExchange *secret_exchange;
- gboolean authenticating;
- GError **error;
};
/* Used in e_source_registry_create_sources_sync() */
@@ -156,6 +142,15 @@ struct _ThreadClosure {
GError *error;
};
+struct _CredentialsRequiredClosure {
+ GWeakRef registry;
+ ESource *source;
+ ESourceCredentialsReason reason;
+ gchar *certificate_pem;
+ GTlsCertificateFlags certificate_errors;
+ GError *op_error;
+};
+
enum {
PROP_0,
PROP_DEFAULT_ADDRESS_BOOK,
@@ -172,6 +167,7 @@ enum {
SOURCE_REMOVED,
SOURCE_ENABLED,
SOURCE_DISABLED,
+ CREDENTIALS_REQUIRED,
LAST_SIGNAL
};
@@ -208,33 +204,9 @@ async_context_free (AsyncContext *async_context)
async_context->list_of_sources,
(GDestroyNotify) g_object_unref);
- if (async_context->auth != NULL)
- g_object_unref (async_context->auth);
-
g_slice_free (AsyncContext, async_context);
}
-static void
-auth_context_free (AuthContext *auth_context)
-{
- if (auth_context->auth != NULL)
- g_object_unref (auth_context->auth);
-
- if (auth_context->dbus_auth != NULL)
- g_object_unref (auth_context->dbus_auth);
-
- if (auth_context->cancellable != NULL)
- g_object_unref (auth_context->cancellable);
-
- if (auth_context->main_loop != NULL)
- g_main_loop_unref (auth_context->main_loop);
-
- if (auth_context->secret_exchange != NULL)
- g_object_unref (auth_context->secret_exchange);
-
- g_slice_free (AuthContext, auth_context);
-}
-
static CreateContext *
create_context_new (void)
{
@@ -292,6 +264,21 @@ thread_closure_free (ThreadClosure *closure)
g_slice_free (ThreadClosure, closure);
}
+static void
+credentials_required_closure_free (gpointer ptr)
+{
+ CredentialsRequiredClosure *closure = ptr;
+
+ if (closure) {
+ g_weak_ref_clear (&closure->registry);
+ g_object_unref (closure->source);
+ g_free (closure->certificate_pem);
+ g_clear_error (&closure->op_error);
+
+ g_slice_free (CredentialsRequiredClosure, closure);
+ }
+};
+
G_LOCK_DEFINE_STATIC (singleton_lock);
static GWeakRef singleton;
@@ -647,6 +634,55 @@ source_registry_source_notify_enabled_cb (ESource *source,
g_source_unref (idle_source);
}
+static gboolean
+source_registry_source_credentials_required_idle_cb (gpointer user_data)
+{
+ CredentialsRequiredClosure *closure = user_data;
+ ESourceRegistry *registry;
+
+ registry = g_weak_ref_get (&closure->registry);
+
+ if (registry != NULL) {
+ g_signal_emit (
+ registry,
+ signals[CREDENTIALS_REQUIRED], 0,
+ closure->source, closure->reason, closure->certificate_pem,
+ closure->certificate_errors, closure->op_error);
+
+ g_object_unref (registry);
+ }
+
+ return FALSE;
+}
+
+static void
+source_registry_source_credentials_required_cb (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ ESourceRegistry *registry)
+{
+ GSource *idle_source;
+ CredentialsRequiredClosure *closure;
+
+ closure = g_slice_new0 (CredentialsRequiredClosure);
+ g_weak_ref_init (&closure->registry, registry);
+ closure->source = g_object_ref (source);
+ closure->reason = reason;
+ closure->certificate_pem = g_strdup (certificate_pem);
+ closure->certificate_errors = certificate_errors;
+ closure->op_error = op_error ? g_error_copy (op_error) : NULL;
+
+ idle_source = g_idle_source_new ();
+ g_source_set_callback (
+ idle_source,
+ source_registry_source_credentials_required_idle_cb,
+ closure, credentials_required_closure_free);
+ g_source_attach (idle_source, registry->priv->main_context);
+ g_source_unref (idle_source);
+}
+
static ESource *
source_registry_new_source (ESourceRegistry *registry,
GDBusObject *dbus_object)
@@ -695,6 +731,10 @@ source_registry_unref_source (ESource *source)
source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
source_registry_source_notify_enabled_cb, NULL);
+ g_signal_handlers_disconnect_matched (
+ source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ source_registry_source_credentials_required_cb, NULL);
+
g_object_unref (source);
}
@@ -728,6 +768,11 @@ source_registry_add_source (ESourceRegistry *registry,
G_CALLBACK (source_registry_source_notify_enabled_cb),
registry);
+ g_signal_connect (
+ source, "credentials-required",
+ G_CALLBACK (source_registry_source_credentials_required_cb),
+ registry);
+
g_hash_table_insert (
registry->priv->sources,
g_strdup (uid), g_object_ref (source));
@@ -1388,14 +1433,6 @@ source_registry_initable_init (GInitable *initable,
goto exit;
}
- /* Allow authentication prompts for all exported data sources
- * when a new EDBusSourceManagerProxy is created. The thought
- * being, if you cancel an authentication prompt you will not
- * be bothered again until you start (or restart) a new E-D-S
- * client app. Failure here is non-fatal, ignore errors. */
- e_dbus_source_manager_call_allow_auth_prompt_all_sync (
- registry->priv->dbus_source_manager, cancellable, NULL);
-
exit:
registry->priv->initialized = TRUE;
g_mutex_unlock (®istry->priv->init_lock);
@@ -1606,6 +1643,36 @@ e_source_registry_class_init (ESourceRegistryClass *class)
NULL, NULL, NULL,
G_TYPE_NONE, 1,
E_TYPE_SOURCE);
+
+ /**
+ * ESourceRegistry::credentials-required:
+ * @registry: the #ESourceRegistry which emitted the signal
+ * @source: the #ESource that requires credentials
+ * @reason: an #ESourceCredentialsReason indicating why the credentials are requested
+ * @certificate_pem: PEM-encoded secure connection certificate for failed SSL checks
+ * @certificate_errors: what failed with the SSL certificate
+ * @op_error: a #GError with a description of the error, or %NULL
+ *
+ * The ::credentials-required signal is emitted when the @source
+ * requires credentials to connect to (possibly remote)
+ * data store. The credentials can be passed to the source using
+ * e_source_authenticate() function. The signal is emitted in
+ * the thread-default main context from the time the @registry was created.
+ *
+ * Note: This is just a proxy signal for the ESource::credentials-required signal.
+ **/
+ signals[CREDENTIALS_REQUIRED] = g_signal_new (
+ "credentials-required",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+ G_STRUCT_OFFSET (ESourceRegistryClass, credentials_required),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 5,
+ E_TYPE_SOURCE,
+ E_TYPE_SOURCE_CREDENTIALS_REASON,
+ G_TYPE_STRING,
+ G_TYPE_TLS_CERTIFICATE_FLAGS,
+ G_TYPE_ERROR);
}
static void
@@ -1779,517 +1846,6 @@ e_source_registry_new_finish (GAsyncResult *result,
return g_task_propagate_pointer (G_TASK (result), error);
}
-/* Helper for e_source_registry_authenticate() */
-static void
-source_registry_authenticate_thread (GSimpleAsyncResult *simple,
- GObject *object,
- GCancellable *cancellable)
-{
- AsyncContext *async_context;
- GError *local_error = NULL;
-
- async_context = g_simple_async_result_get_op_res_gpointer (simple);
-
- e_source_registry_authenticate_sync (
- E_SOURCE_REGISTRY (object),
- async_context->source,
- async_context->auth,
- cancellable, &local_error);
-
- if (local_error != NULL)
- g_simple_async_result_take_error (simple, local_error);
-}
-
-/* Helper for e_source_registry_authenticate_sync() */
-static gboolean
-source_registry_authenticate_respond_cb (AuthContext *auth_context)
-{
- ESourceAuthenticationResult auth_result;
- GError *non_fatal_error = NULL;
-
- g_return_val_if_fail (auth_context->authenticating, FALSE);
-
- auth_result = auth_context->auth_result;
-
- /* Allow the next authentication attempt to proceed. */
- auth_context->authenticating = FALSE;
-
- /* Send the server a status update based on the authentication
- * result. Note, we don't really care if the D-Bus message gets
- * through to the server at this point. If it doesn't, the auth
- * session will either time out on its own or the authentication
- * dialog will eventually be dismissed by the user. */
-
- /* If we were cancelled from our side, we have a bit of a dilemma.
- * We need to tell the server to cancel the authentication session,
- * but that involves making a synchronous D-Bus call, which we are
- * not supposed to do if we know we've been cancelled. But if we
- * don't tell the server, the authentication session will be left
- * to timeout on its own (which may take minutes), and meanwhile
- * all other authentication requests are blocked. So choose the
- * lesser evil and make the synchronous call but without passing
- * the already-cancelled GCancellable. */
- if (g_cancellable_is_cancelled (auth_context->cancellable)) {
- e_dbus_authenticator_call_cancel_sync (
- auth_context->dbus_auth,
- NULL, &non_fatal_error);
- g_main_loop_quit (auth_context->main_loop);
-
- /* If an error occurred while attempting to authenticate,
- * tell the server to cancel the authentication session. */
- } else if (auth_result == E_SOURCE_AUTHENTICATION_ERROR) {
- e_dbus_authenticator_call_cancel_sync (
- auth_context->dbus_auth,
- auth_context->cancellable,
- &non_fatal_error);
- g_main_loop_quit (auth_context->main_loop);
-
- /* If the password was accepted, let the server know so it
- * can close any authentication dialogs and save the user
- * provided password to the keyring. */
- } else if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
- e_dbus_authenticator_call_accepted_sync (
- auth_context->dbus_auth,
- auth_context->cancellable,
- &non_fatal_error);
- g_main_loop_quit (auth_context->main_loop);
-
- /* If the password was rejected, let the server know so it can
- * indicate failure and request a different password, and then
- * wait for the next "response" signal. */
- } else {
- e_dbus_authenticator_call_rejected_sync (
- auth_context->dbus_auth,
- auth_context->cancellable,
- &non_fatal_error);
- }
-
- /* Leave breadcrumbs if something went wrong,
- * but don't fail the whole operation over it. */
- if (non_fatal_error != NULL) {
- g_dbus_error_strip_remote_error (non_fatal_error);
- g_warning ("%s: %s", G_STRFUNC, non_fatal_error->message);
- g_error_free (non_fatal_error);
- }
-
- return FALSE;
-}
-
-/* Helper for e_source_registry_authenticate_sync() */
-static void
-source_registry_authenticate_authenticate_cb (EDBusAuthenticator *dbus_auth,
- const gchar *encrypted_secret,
- AuthContext *auth_context)
-{
- GSource *idle_source;
- GMainContext *main_context;
- GString *password;
- gboolean valid_secret;
-
- /* We should only get one secret at a time. */
- g_return_if_fail (!auth_context->authenticating);
-
- valid_secret = gcr_secret_exchange_receive (
- auth_context->secret_exchange, encrypted_secret);
- g_return_if_fail (valid_secret);
-
- auth_context->authenticating = TRUE;
-
- /* This avoids revealing the password in a stack trace. */
- password = g_string_new (
- gcr_secret_exchange_get_secret (
- auth_context->secret_exchange, NULL));
-
- /* Try authenticating with the given password. We have to
- * call this synchronously because some authenticators use
- * mutexes to serialize I/O operations and are not prepared
- * to make authentication attempts from a different thread.
- *
- * Unfortunately this means we won't notice server-side
- * dismissals while the main loop is blocked. We respond
- * to the server from a low-priority idle callback so that
- * any pending "dismissed" signals get handled first. */
-
- auth_context->auth_result =
- e_source_authenticator_try_password_sync (
- auth_context->auth, password,
- auth_context->cancellable,
- auth_context->error);
-
- idle_source = g_idle_source_new ();
- main_context = g_main_context_get_thread_default ();
- g_source_set_callback (
- idle_source, (GSourceFunc)
- source_registry_authenticate_respond_cb,
- auth_context, NULL);
- g_source_attach (idle_source, main_context);
- g_source_unref (idle_source);
-
- g_string_free (password, TRUE);
-}
-
-/* Helper for e_source_registry_authenticate_sync() */
-static void
-source_registry_authenticate_dismissed_cb (EDBusAuthenticator *dbus_auth,
- AuthContext *auth_context)
-{
- /* Be careful not to overwrite an existing error in case this
- * is called after e_source_authenticator_try_password_sync()
- * but prior to the idle callback. */
- if (auth_context->auth_result != E_SOURCE_AUTHENTICATION_ERROR) {
- /* XXX Use a separate error code for dismissals? */
- g_set_error_literal (
- auth_context->error,
- G_IO_ERROR, G_IO_ERROR_CANCELLED,
- _("The user declined to authenticate"));
- auth_context->auth_result = E_SOURCE_AUTHENTICATION_ERROR;
- }
-
- g_main_loop_quit (auth_context->main_loop);
-}
-
-/* Helper for e_source_registry_authenticate_sync() */
-static void
-source_registry_authenticate_server_error_cb (EDBusAuthenticator *dbus_auth,
- const gchar *name,
- const gchar *message,
- AuthContext *auth_context)
-{
- /* Be careful not to overwrite an existing error */
- if (auth_context->auth_result != E_SOURCE_AUTHENTICATION_ERROR) {
- GError *error;
-
- error = g_dbus_error_new_for_dbus_error (name, message);
- g_propagate_error (auth_context->error, error);
-
- auth_context->auth_result = E_SOURCE_AUTHENTICATION_ERROR;
- }
-
- g_main_loop_quit (auth_context->main_loop);
-}
-
-/* Helper for e_source_registry_authenticate_sync() */
-static gboolean
-source_registry_call_authenticate_for_source (ESourceRegistry *registry,
- ESourceAuthenticator *auth,
- ESource *source,
- gchar **out_object_path,
- GCancellable *cancellable,
- GError **error)
-{
- ESource *collection;
- const gchar *uid;
- gchar *prompt_title = NULL;
- gchar *prompt_message = NULL;
- gchar *prompt_description = NULL;
- GError *local_error = NULL;
-
- g_object_ref (source);
-
- /* If the source is a member of a collection, we want to store
- * the password under the UID of the "collection" source so it
- * will apply to the entire collection.
- *
- * XXX This assumes all sources in a collection share a single
- * password. If that turns out not to be true in all cases
- * we could maybe add a "SharedPassword: true/false" key to
- * [Collection] and apply it here.
- *
- * Addendum: Assumption proven wrong. GOA's generic IMAP/SMTP
- * provider uses a plain ECollectionBackend (backend
- * name "none") with separately stored passwords for
- * IMAP vs SMTP. Just handle this case directly for
- * now, but don't rule out the "SharedPassword" idea.
- */
- collection = e_source_registry_find_extension (
- registry, source, E_SOURCE_EXTENSION_COLLECTION);
- if (collection != NULL) {
- ESourceBackend *extension;
- gchar *backend_name;
-
- extension = e_source_get_extension (
- collection, E_SOURCE_EXTENSION_COLLECTION);
- backend_name = e_source_backend_dup_backend_name (extension);
-
- if (g_strcmp0 (backend_name, "none") != 0) {
- g_object_unref (source);
- source = g_object_ref (collection);
- }
-
- g_free (backend_name);
-
- g_object_unref (collection);
- }
-
- uid = e_source_get_uid (source);
-
- e_source_authenticator_get_prompt_strings (
- auth, source,
- &prompt_title,
- &prompt_message,
- &prompt_description);
-
- e_dbus_source_manager_call_authenticate_sync (
- registry->priv->dbus_source_manager, uid,
- prompt_title, prompt_message, prompt_description,
- out_object_path, cancellable, &local_error);
-
- g_free (prompt_title);
- g_free (prompt_message);
- g_free (prompt_description);
-
- g_object_unref (source);
-
- if (local_error != NULL) {
- g_dbus_error_strip_remote_error (local_error);
- g_propagate_error (error, local_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * e_source_registry_authenticate_sync:
- * @registry: an #ESourceRegistry
- * @source: an #ESource
- * @auth: an #ESourceAuthenticator
- * @cancellable: (allow-none): optional #GCancellable object, or %NULL
- * @error: return location for a #GError, or %NULL
- *
- * Authenticates @source, using @auth to handle the authentication
- * attempts. The operation loops until authentication is successful or
- * the user aborts further authentication attempts. If an error occurs,
- * the function will set @error and return %FALSE.
- *
- * Note that @source need not have a #GDBusObject, which means this
- * function can test authentication on a scratch #ESource.
- *
- * Only backend implementations and data source editors should call this
- * function. The intent is for basic client applications to not have to
- * deal with authentication at all.
- *
- * Returns: %TRUE on success, %FALSE on failure
- *
- * Since: 3.6
- **/
-gboolean
-e_source_registry_authenticate_sync (ESourceRegistry *registry,
- ESource *source,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error)
-{
- AuthContext *auth_context;
- GMainContext *main_context;
- EDBusAuthenticator *dbus_auth;
- gboolean without_password;
- gchar *encryption_key;
- gchar *object_path = NULL;
- GError *local_error = NULL;
-
- g_return_val_if_fail (E_IS_SOURCE_REGISTRY (registry), FALSE);
- g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
- g_return_val_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth), FALSE);
-
- /* This extracts authentication prompt details for the ESource
- * before initiating an authentication session with the server,
- * so split it out of the main algorithm for clarity's sake. */
- source_registry_call_authenticate_for_source (
- registry, auth, source, &object_path,
- cancellable, &local_error);
-
- if (local_error != NULL) {
- g_warn_if_fail (object_path == NULL);
- g_propagate_error (error, local_error);
- return FALSE;
- }
-
- g_return_val_if_fail (object_path != NULL, FALSE);
-
- main_context = g_main_context_new ();
- g_main_context_push_thread_default (main_context);
-
- dbus_auth = e_dbus_authenticator_proxy_new_for_bus_sync (
- G_BUS_TYPE_SESSION,
- G_DBUS_PROXY_FLAGS_NONE,
- SOURCES_DBUS_SERVICE_NAME,
- object_path, cancellable, &local_error);
-
- g_free (object_path);
-
- /* Sanity check. */
- g_return_val_if_fail (
- ((dbus_auth != NULL) && (local_error == NULL)) ||
- ((dbus_auth == NULL) && (local_error != NULL)), FALSE);
-
- if (local_error != NULL)
- goto exit;
-
- without_password = e_source_authenticator_get_without_password (auth);
- e_dbus_authenticator_set_without_password (dbus_auth, without_password);
-
- auth_context = g_slice_new0 (AuthContext);
- auth_context->auth = g_object_ref (auth);
- auth_context->dbus_auth = dbus_auth; /* takes ownership */
- auth_context->main_loop = g_main_loop_new (main_context, FALSE);
- auth_context->error = &local_error;
-
- /* This just needs to be something other than
- * E_SOURCE_AUTHENTICATION_ERROR so we don't trip
- * up source_registry_authenticate_dismissed_cb(). */
- auth_context->auth_result = E_SOURCE_AUTHENTICATION_REJECTED;
-
- if (G_IS_CANCELLABLE (cancellable))
- auth_context->cancellable = g_object_ref (cancellable);
-
- auth_context->secret_exchange =
- gcr_secret_exchange_new (GCR_SECRET_EXCHANGE_PROTOCOL_1);
-
- g_signal_connect (
- dbus_auth, "authenticate",
- G_CALLBACK (source_registry_authenticate_authenticate_cb),
- auth_context);
-
- g_signal_connect (
- dbus_auth, "dismissed",
- G_CALLBACK (source_registry_authenticate_dismissed_cb),
- auth_context);
-
- g_signal_connect (
- dbus_auth, "server-error",
- G_CALLBACK (source_registry_authenticate_server_error_cb),
- auth_context);
-
- encryption_key = gcr_secret_exchange_begin (
- auth_context->secret_exchange);
-
- /* Signal the D-Bus server that we're ready to begin the
- * authentication session. This must happen AFTER we've
- * connected to the response signal since the server may
- * already have a response ready and waiting for us. */
- e_dbus_authenticator_call_ready_sync (
- dbus_auth, encryption_key, cancellable, &local_error);
-
- g_free (encryption_key);
-
- if (local_error == NULL)
- g_main_loop_run (auth_context->main_loop);
-
- auth_context_free (auth_context);
-
-exit:
- g_main_context_pop_thread_default (main_context);
-
- /* Make sure the main_context doesn't have pending operations;
- * workarounds https://bugzilla.gnome.org/show_bug.cgi?id=690126 */
- while (g_main_context_pending (main_context))
- g_main_context_iteration (main_context, FALSE);
-
- g_main_context_unref (main_context);
-
- if (local_error != NULL) {
- g_dbus_error_strip_remote_error (local_error);
- g_propagate_error (error, local_error);
- return FALSE;
- }
-
- return TRUE;
-}
-
-/**
- * e_source_registry_authenticate:
- * @registry: an #ESourceRegistry
- * @source: an #ESource
- * @auth: an #ESourceAuthenticator
- * @cancellable: (allow-none): optional #GCancellable object, or %NULL
- * @callback: (scope async): a #GAsyncReadyCallback to call when the request
- * is satisfied
- * @user_data: (closure): data to pass to the callback function
- *
- * Asynchronously authenticates @source, using @auth to handle the
- * authentication attempts. The operation loops until authentication
- * is successful or the user aborts further authentication attempts.
- *
- * Note that @source need not have a #GDBusObject, which means this
- * function can test authentication on a scratch #ESource.
- *
- * When the operation is finished, @callback will be called. You can then
- * call e_source_registry_authenticate_finish() to get the result of the
- * operation.
- *
- * Only backend implementations and data source editors should call this
- * function. The intent is for basic client applications to not have to
- * deal with authentication at all.
- *
- * Since: 3.6
- **/
-void
-e_source_registry_authenticate (ESourceRegistry *registry,
- ESource *source,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
-{
- GSimpleAsyncResult *simple;
- AsyncContext *async_context;
-
- g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
- g_return_if_fail (E_IS_SOURCE (source));
- g_return_if_fail (E_IS_SOURCE_AUTHENTICATOR (auth));
-
- async_context = g_slice_new0 (AsyncContext);
- async_context->source = g_object_ref (source);
- async_context->auth = g_object_ref (auth);
-
- simple = g_simple_async_result_new (
- G_OBJECT (registry), callback, user_data,
- e_source_registry_authenticate);
-
- g_simple_async_result_set_check_cancellable (simple, cancellable);
-
- g_simple_async_result_set_op_res_gpointer (
- simple, async_context, (GDestroyNotify) async_context_free);
-
- g_simple_async_result_run_in_thread (
- simple, source_registry_authenticate_thread,
- G_PRIORITY_DEFAULT, cancellable);
-
- g_object_unref (simple);
-}
-
-/**
- * e_source_registry_authenticate_finish:
- * @registry: an #ESourceRegistry
- * @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
- *
- * Finishes the operation started with e_source_registry_authenticate().
- * If an error occurred, the function will set @error and return %FALSE.
- *
- * Returns: %TRUE on success, %FALSE on failure
- *
- * Since: 3.6
- **/
-gboolean
-e_source_registry_authenticate_finish (ESourceRegistry *registry,
- GAsyncResult *result,
- GError **error)
-{
- GSimpleAsyncResult *simple;
-
- g_return_val_if_fail (
- g_simple_async_result_is_valid (
- result, G_OBJECT (registry),
- e_source_registry_authenticate), FALSE);
-
- simple = G_SIMPLE_ASYNC_RESULT (result);
-
- /* Assume success unless a GError is set. */
- return !g_simple_async_result_propagate_error (simple, error);
-}
-
/* Helper for e_source_registry_commit_source() */
static void
source_registry_commit_source_thread (GSimpleAsyncResult *simple,
diff --git a/libedataserver/e-source-registry.h b/libedataserver/e-source-registry.h
index bf47684..b3aba33 100644
--- a/libedataserver/e-source-registry.h
+++ b/libedataserver/e-source-registry.h
@@ -23,7 +23,6 @@
#define E_SOURCE_REGISTRY_H
#include <libedataserver/e-source.h>
-#include <libedataserver/e-source-authenticator.h>
/* Standard GObject macros */
#define E_TYPE_SOURCE_REGISTRY \
@@ -77,6 +76,12 @@ struct _ESourceRegistryClass {
ESource *source);
void (*source_disabled) (ESourceRegistry *registry,
ESource *source);
+ void (*credentials_required) (ESourceRegistry *registry,
+ ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error);
};
GType e_source_registry_get_type (void) G_GNUC_CONST;
@@ -89,22 +94,6 @@ void e_source_registry_new (GCancellable *cancellable,
ESourceRegistry *
e_source_registry_new_finish (GAsyncResult *result,
GError **error);
-gboolean e_source_registry_authenticate_sync
- (ESourceRegistry *registry,
- ESource *source,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GError **error);
-void e_source_registry_authenticate (ESourceRegistry *registry,
- ESource *source,
- ESourceAuthenticator *auth,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data);
-gboolean e_source_registry_authenticate_finish
- (ESourceRegistry *registry,
- GAsyncResult *result,
- GError **error);
gboolean e_source_registry_commit_source_sync
(ESourceRegistry *registry,
ESource *source,
diff --git a/libedataserver/e-source-webdav.c b/libedataserver/e-source-webdav.c
index c30d353..efd7e55 100644
--- a/libedataserver/e-source-webdav.c
+++ b/libedataserver/e-source-webdav.c
@@ -71,7 +71,6 @@ struct _ESourceWebdavPrivate {
gchar *ssl_trust;
gboolean avoid_ifmatch;
gboolean calendar_auto_schedule;
- gboolean ignore_invalid_cert;
SoupURI *soup_uri;
};
@@ -81,7 +80,6 @@ enum {
PROP_CALENDAR_AUTO_SCHEDULE,
PROP_DISPLAY_NAME,
PROP_EMAIL_ADDRESS,
- PROP_IGNORE_INVALID_CERT,
PROP_RESOURCE_PATH,
PROP_RESOURCE_QUERY,
PROP_SOUP_URI,
@@ -292,12 +290,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),
@@ -361,13 +353,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,
@@ -550,18 +535,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",
@@ -895,57 +868,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
- *
- * Deprecated: 3.8: The trust prompt APIs replace this.
- **/
-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
- *
- * Deprecated: 3.8: The trust prompt APIs replace this.
- **/
-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
*
@@ -1133,8 +1055,8 @@ e_source_webdav_set_resource_query (ESourceWebdav *extension,
* "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().
+ * it is supposed to be manipulated with e_source_webdav_update_ssl_trust()
+ * and e_source_webdav_verify_ssl_trust().
*
* Returns: an SSL certificate trust for the @extension
*
@@ -1367,154 +1289,83 @@ encode_ssl_trust (ESourceWebdav *extension,
}
/**
- * e_source_webdav_prepare_ssl_trust_prompt:
+ * e_source_webdav_update_ssl_trust:
* @extension: an #ESourceWebdav
- * @message: a #SoupMessage
- * @cert: the invalid certificate of the connection over which @message is about
+ * @host: a host name to store the certificate for
+ * @cert: the invalid certificate of the connection over which @host is about
* to be sent
- * @cert_errors: the error flags for @cert
- * @registry: (allow-none): an #ESourceRegistry, to use for parent lookups
- * @parameters: an #ENamedParameters to be populated
- *
- * Checks @cert 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'.
+ * @response: user's response from a trust prompt for @cert
*
- * Note: The #E_TRUST_PROMPT_RESPONSE_REJECT is returned on any errors, such as
- * invalid parameters.
+ * Updates 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
+ * Since: 3.14
**/
-ETrustPromptResponse
-e_source_webdav_prepare_ssl_trust_prompt (ESourceWebdav *extension,
- SoupMessage *message,
- GTlsCertificate *cert,
- GTlsCertificateFlags cert_errors,
- ESourceRegistry *registry,
- ENamedParameters *parameters)
+void
+e_source_webdav_update_ssl_trust (ESourceWebdav *extension,
+ const gchar *host,
+ GTlsCertificate *cert,
+ ETrustPromptResponse response)
{
- ESource *parent_source = NULL;
- ETrustPromptResponse res;
-
- g_return_val_if_fail (
- E_IS_SOURCE_WEBDAV (extension),
- E_TRUST_PROMPT_RESPONSE_REJECT);
- g_return_val_if_fail (
- SOUP_IS_MESSAGE (message),
- E_TRUST_PROMPT_RESPONSE_REJECT);
- if (registry)
- 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 (registry != NULL) {
- ESource *source;
-
- source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
- if (source != NULL) {
- const gchar *parent_uid;
-
- parent_uid = e_source_get_parent (source);
-
- if (parent_uid != NULL)
- parent_source = e_source_registry_ref_source (
- registry, parent_uid);
-
- g_object_unref (source);
- }
- }
+ GByteArray *bytes = NULL;
+ gchar *hash;
- res = e_source_webdav_prepare_ssl_trust_prompt_with_parent (
- extension, message, cert, cert_errors, parent_source, parameters);
+ g_return_if_fail (E_IS_SOURCE_WEBDAV (extension));
+ g_return_if_fail (host != NULL);
+ g_return_if_fail (cert != NULL);
- if (parent_source)
- g_object_unref (parent_source);
+ g_object_get (cert, "certificate", &bytes, NULL);
+
+ if (!bytes)
+ return;
- return res;
+ hash = g_compute_checksum_for_data (G_CHECKSUM_SHA1, bytes->data, bytes->len);
+
+ encode_ssl_trust (extension, response, host, hash);
+
+ g_byte_array_unref (bytes);
+ g_free (hash);
}
/**
- * e_source_webdav_prepare_ssl_trust_prompt_with_parent:
+ * e_source_webdav_verify_ssl_trust:
* @extension: an #ESourceWebdav
- * @message: a #SoupMessage
- * @cert: the invalid certificate of the connection over which @message is about
+ * @host: a host name to store the certificate for
+ * @cert: the invalid certificate of the connection over which @host is about
* to be sent
- * @cert_errors: the error flags for @cert
- * @parent_source: an #ESource, parent of the @extension<!-- -->'s source
- * @parameters: an #ENamedParameters to be populated
- *
- * The same as e_source_webdav_prepare_ssl_trust_prompt(), only takes
- * @parent_source directly, instead of an #ESourceRegistry.
- *
- * See e_source_webdav_prepare_ssl_trust_prompt() for more details.
*
- * Since: 3.8
+ * Verifies SSL trust for the given @host and @cert, as previously stored in the @extension
+ * with e_source_webdav_update_ssl_trust().
**/
ETrustPromptResponse
-e_source_webdav_prepare_ssl_trust_prompt_with_parent (ESourceWebdav *extension,
- SoupMessage *message,
- GTlsCertificate *cert,
- GTlsCertificateFlags cert_errors,
- ESource *parent_source,
- ENamedParameters *parameters)
+e_source_webdav_verify_ssl_trust (ESourceWebdav *extension,
+ const gchar *host,
+ GTlsCertificate *cert,
+ GTlsCertificateFlags cert_errors)
{
ETrustPromptResponse response;
- ESource *source;
GByteArray *bytes = NULL;
- SoupURI *soup_uri;
- const gchar *host;
- gchar *base64;
gchar *old_host = NULL;
gchar *old_hash = NULL;
- gchar *cert_errs_str;
- gchar *markup = NULL;
-
- 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);
- if (parent_source)
- g_return_val_if_fail (
- E_IS_SOURCE (parent_source),
- E_TRUST_PROMPT_RESPONSE_REJECT);
- g_return_val_if_fail (
- parameters != NULL,
- E_TRUST_PROMPT_RESPONSE_REJECT);
+
+ g_return_val_if_fail (E_IS_SOURCE_WEBDAV (extension), E_TRUST_PROMPT_RESPONSE_REJECT);
+ g_return_val_if_fail (host != NULL, E_TRUST_PROMPT_RESPONSE_REJECT);
+ g_return_val_if_fail (cert != NULL, E_TRUST_PROMPT_RESPONSE_REJECT);
/* Always reject revoked certificates */
if ((cert_errors & G_TLS_CERTIFICATE_REVOKED) != 0)
return E_TRUST_PROMPT_RESPONSE_REJECT;
- soup_uri = soup_message_get_uri (message);
-
- if (soup_uri == NULL)
- return E_TRUST_PROMPT_RESPONSE_REJECT;
-
- if (soup_uri_get_host (soup_uri) == NULL)
- return E_TRUST_PROMPT_RESPONSE_REJECT;
-
- g_return_val_if_fail (cert != NULL, E_TRUST_PROMPT_RESPONSE_REJECT);
g_object_get (cert, "certificate", &bytes, NULL);
if (bytes == NULL)
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;
- hash = g_compute_checksum_for_data (
- G_CHECKSUM_SHA1, bytes->data, bytes->len);
+ 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 &&
@@ -1532,159 +1383,11 @@ e_source_webdav_prepare_ssl_trust_prompt_with_parent (ESourceWebdav *extension,
g_free (hash);
}
- source = e_source_extension_ref_source (E_SOURCE_EXTENSION (extension));
- if (source != NULL) {
- const gchar *display_name;
- gchar *bhost = g_strconcat ("<b>", host, "</b>", NULL);
- gchar *bname = NULL;
-
- display_name = e_source_get_display_name (source);
-
- if (parent_source != NULL) {
- const gchar *parent_display_name;
-
- parent_display_name =
- e_source_get_display_name (parent_source);
- bname = g_strdup_printf (
- "<b>%s: %s</b>",
- parent_display_name, display_name);
- }
-
- if (bname == NULL)
- bname = g_strdup_printf ("<b>%s</b>", display_name);
-
- 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);
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
- * @cert: the invalid certificate of the connection over which @message is about
- * to be sent
- * @response: user's response from a trust prompt for @cert
- *
- * 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,
- GTlsCertificate *cert,
- ETrustPromptResponse response)
-{
- 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));
-
- soup_uri = soup_message_get_uri (message);
- if (!soup_uri || !soup_uri_get_host (soup_uri))
- return;
-
- g_return_if_fail (cert != NULL);
- g_object_get (cert, "certificate", &bytes, NULL);
-
- 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
diff --git a/libedataserver/e-source-webdav.h b/libedataserver/e-source-webdav.h
index 7fff9d7..e7532b1 100644
--- a/libedataserver/e-source-webdav.h
+++ b/libedataserver/e-source-webdav.h
@@ -129,38 +129,20 @@ void e_source_webdav_set_ssl_trust (ESourceWebdav *extension,
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
+void e_source_webdav_update_ssl_trust
(ESourceWebdav *extension,
- SoupMessage *message,
+ const gchar *host,
GTlsCertificate *cert,
- GTlsCertificateFlags cert_errors,
- struct _ESourceRegistry *registry,
- struct _ENamedParameters *parameters);
+ ETrustPromptResponse response);
ETrustPromptResponse
- e_source_webdav_prepare_ssl_trust_prompt_with_parent
- (ESourceWebdav *extension,
- SoupMessage *message,
- GTlsCertificate *cert,
- GTlsCertificateFlags cert_errors,
- ESource *parent_source,
- struct _ENamedParameters *parameters);
-void e_source_webdav_store_ssl_trust_prompt
+ e_source_webdav_verify_ssl_trust
(ESourceWebdav *extension,
- SoupMessage *message,
+ const gchar *host,
GTlsCertificate *cert,
- ETrustPromptResponse response);
+ GTlsCertificateFlags cert_errors);
void e_source_webdav_unset_temporary_ssl_trust
(ESourceWebdav *extension);
-#ifndef EDS_DISABLE_DEPRECATED
-gboolean e_source_webdav_get_ignore_invalid_cert
- (ESourceWebdav *extension);
-void e_source_webdav_set_ignore_invalid_cert
- (ESourceWebdav *extension,
- gboolean ignore_invalid_cert);
-#endif /* EDS_DISABLE_DEPRECATED */
-
G_END_DECLS
#endif /* E_SOURCE_WEBDAV_H */
diff --git a/libedataserver/e-source.c b/libedataserver/e-source.c
index 42e58a8..1a48583 100644
--- a/libedataserver/e-source.c
+++ b/libedataserver/e-source.c
@@ -77,6 +77,7 @@
#include <e-dbus-source.h>
#include "e-data-server-util.h"
+#include "e-source-enumtypes.h"
#include "e-source-extension.h"
#include "e-uid.h"
@@ -129,6 +130,13 @@ struct _ESourcePrivate {
GSource *data_change;
GMutex data_change_lock;
+ GSource *connection_status_change;
+ GMutex connection_status_change_lock;
+ ESourceConnectionStatus connection_status;
+
+ GSource *credentials_required_call;
+ GMutex credentials_required_call_lock;
+
GMutex property_lock;
gchar *display_name;
@@ -171,11 +179,14 @@ enum {
PROP_REMOTE_DELETABLE,
PROP_REMOVABLE,
PROP_UID,
- PROP_WRITABLE
+ PROP_WRITABLE,
+ PROP_CONNECTION_STATUS
};
enum {
CHANGED,
+ CREDENTIALS_REQUIRED,
+ AUTHENTICATE,
LAST_SIGNAL
};
@@ -1032,6 +1043,211 @@ source_notify_dbus_data_cb (EDBusSource *dbus_source,
}
static gboolean
+source_update_connection_status_internal (ESource *source,
+ EDBusSource *dbus_source)
+{
+ ESourceConnectionStatus connection_status_value;
+ gchar *connection_status;
+ gboolean changed = FALSE;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (dbus_source != NULL, FALSE);
+
+ connection_status_value = E_SOURCE_CONNECTION_STATUS_DISCONNECTED;
+ connection_status = e_dbus_source_dup_connection_status (dbus_source);
+
+ if (connection_status) {
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CONNECTION_STATUS);
+ enum_value = g_enum_get_value_by_nick (enum_class, connection_status);
+
+ if (enum_value) {
+ connection_status_value = enum_value->value;
+ } else if (!*connection_status) {
+ connection_status_value = E_SOURCE_CONNECTION_STATUS_DISCONNECTED;
+ } else {
+ g_warning ("%s: Unknown connection status: '%s'", G_STRFUNC, connection_status);
+ }
+
+ g_type_class_unref (enum_class);
+ g_free (connection_status);
+ }
+
+ if (source->priv->connection_status != connection_status_value) {
+ source->priv->connection_status = connection_status_value;
+ changed = TRUE;
+ }
+
+ return changed;
+}
+
+static gboolean
+source_idle_connection_status_change_cb (gpointer user_data)
+{
+ ESource *source = E_SOURCE (user_data);
+ EDBusObject *dbus_object;
+ EDBusSource *dbus_source;
+ gboolean changed;
+
+ /* If the ESource is still initializing itself in a different
+ * thread, skip the signal emission and try again on the next
+ * main loop iteration. This is a busy wait but it should be
+ * a very short wait. */
+ if (!source->priv->initialized)
+ return TRUE;
+
+ g_mutex_lock (&source->priv->connection_status_change_lock);
+ if (source->priv->connection_status_change != NULL) {
+ g_source_unref (source->priv->connection_status_change);
+ source->priv->connection_status_change = NULL;
+ }
+ g_mutex_unlock (&source->priv->connection_status_change_lock);
+
+ g_object_freeze_notify (G_OBJECT (source));
+ g_mutex_lock (&source->priv->property_lock);
+
+ dbus_object = E_DBUS_OBJECT (source->priv->dbus_object);
+
+ dbus_source = e_dbus_object_get_source (dbus_object);
+ changed = source_update_connection_status_internal (source, dbus_source);
+ g_object_unref (dbus_source);
+
+ if (changed)
+ g_object_notify (G_OBJECT (source), "connection-status");
+
+ g_mutex_unlock (&source->priv->property_lock);
+ g_object_thaw_notify (G_OBJECT (source));
+
+ return FALSE;
+}
+
+static void
+source_notify_dbus_connection_status_cb (EDBusSource *dbus_source,
+ GParamSpec *pspec,
+ ESource *source)
+{
+ g_mutex_lock (&source->priv->connection_status_change_lock);
+ if (source->priv->connection_status_change == NULL) {
+ source->priv->connection_status_change = g_idle_source_new ();
+ g_source_set_callback (
+ source->priv->connection_status_change,
+ source_idle_connection_status_change_cb,
+ source, NULL);
+ g_source_attach (
+ source->priv->connection_status_change,
+ source->priv->main_context);
+ }
+ g_mutex_unlock (&source->priv->connection_status_change_lock);
+}
+
+static ESourceCredentialsReason
+source_credentials_reason_from_text (const gchar *arg_reason)
+{
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
+
+ if (arg_reason && *arg_reason) {
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
+ enum_value = g_enum_get_value_by_nick (enum_class, arg_reason);
+
+ if (enum_value) {
+ reason = enum_value->value;
+ } else {
+ g_warning ("%s: Unknown reason enum: '%s'", G_STRFUNC, arg_reason);
+ }
+
+ g_type_class_unref (enum_class);
+ }
+
+ return reason;
+}
+
+static GTlsCertificateFlags
+source_certificate_errors_from_text (const gchar *arg_certificate_errors)
+{
+ GTlsCertificateFlags certificate_errors = 0;
+
+ if (arg_certificate_errors && *arg_certificate_errors) {
+ GFlagsClass *flags_class;
+ gchar **flags_strv;
+ gsize ii;
+
+ flags_class = g_type_class_ref (G_TYPE_TLS_CERTIFICATE_FLAGS);
+ flags_strv = g_strsplit (arg_certificate_errors, ":", -1);
+ for (ii = 0; flags_strv[ii] != NULL; ii++) {
+ GFlagsValue *flags_value;
+
+ flags_value = g_flags_get_value_by_nick (flags_class, flags_strv[ii]);
+ if (flags_value != NULL) {
+ certificate_errors |= flags_value->value;
+ } else {
+ g_warning ("%s: Unknown flag: '%s'", G_STRFUNC, flags_strv[ii]);
+ }
+ }
+ g_strfreev (flags_strv);
+ g_type_class_unref (flags_class);
+ }
+
+ return certificate_errors;
+}
+
+static void
+source_dbus_credentials_required_cb (EDBusSource *dbus_source,
+ const gchar *arg_reason,
+ const gchar *arg_certificate_pem,
+ const gchar *arg_certificate_errors,
+ const gchar *arg_dbus_error_name,
+ const gchar *arg_dbus_error_message,
+ ESource *source)
+{
+ ESourceCredentialsReason reason;
+ GTlsCertificateFlags certificate_errors;
+ GError *op_error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (arg_reason != NULL);
+ g_return_if_fail (arg_certificate_pem != NULL);
+ g_return_if_fail (arg_certificate_errors != NULL);
+ g_return_if_fail (arg_dbus_error_name != NULL);
+ g_return_if_fail (arg_dbus_error_message != NULL);
+
+ reason = source_credentials_reason_from_text (arg_reason);
+ certificate_errors = source_certificate_errors_from_text (arg_certificate_errors);
+
+ if (*arg_dbus_error_name) {
+ op_error = g_dbus_error_new_for_dbus_error (arg_dbus_error_name, arg_dbus_error_message);
+ g_dbus_error_strip_remote_error (op_error);
+ }
+
+ /* This is delivered in the GDBus thread */
+ e_source_emit_credentials_required (source, reason, arg_certificate_pem, certificate_errors,
op_error);
+
+ g_clear_error (&op_error);
+}
+
+static gboolean
+source_dbus_authenticate_cb (EDBusSource *dbus_interface,
+ const gchar *const *arg_credentials,
+ ESource *source)
+{
+ ENamedParameters *credentials;
+
+ credentials = e_named_parameters_new_strv (arg_credentials);
+
+ /* This is delivered in the GDBus thread */
+ g_signal_emit (source, signals[AUTHENTICATE], 0, credentials);
+
+ e_named_parameters_free (credentials);
+
+ return TRUE;
+}
+
+
+static gboolean
source_idle_changed_cb (gpointer user_data)
{
ESource *source = E_SOURCE (user_data);
@@ -1136,6 +1352,11 @@ source_set_property (GObject *object,
E_SOURCE (object),
g_value_get_string (value));
return;
+
+ case PROP_CONNECTION_STATUS:
+ e_source_set_connection_status (E_SOURCE (object),
+ g_value_get_enum (value));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1207,6 +1428,11 @@ source_get_property (GObject *object,
value, e_source_get_writable (
E_SOURCE (object)));
return;
+
+ case PROP_CONNECTION_STATUS:
+ g_value_set_enum (value,
+ e_source_get_connection_status (E_SOURCE (object)));
+ return;
}
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
@@ -1259,6 +1485,22 @@ source_dispose (GObject *object)
}
g_mutex_unlock (&priv->data_change_lock);
+ g_mutex_lock (&priv->connection_status_change_lock);
+ if (priv->connection_status_change != NULL) {
+ g_source_destroy (priv->connection_status_change);
+ g_source_unref (priv->connection_status_change);
+ priv->connection_status_change = NULL;
+ }
+ g_mutex_unlock (&priv->connection_status_change_lock);
+
+ g_mutex_lock (&priv->credentials_required_call_lock);
+ if (priv->credentials_required_call != NULL) {
+ g_source_destroy (priv->credentials_required_call);
+ g_source_unref (priv->credentials_required_call);
+ priv->credentials_required_call = NULL;
+ }
+ g_mutex_unlock (&priv->credentials_required_call_lock);
+
g_hash_table_remove_all (priv->extensions);
/* Chain up to parent's dispose() method. */
@@ -1274,6 +1516,8 @@ source_finalize (GObject *object)
g_mutex_clear (&priv->changed_lock);
g_mutex_clear (&priv->data_change_lock);
+ g_mutex_clear (&priv->connection_status_change_lock);
+ g_mutex_clear (&priv->credentials_required_call_lock);
g_mutex_clear (&priv->property_lock);
g_free (priv->display_name);
@@ -1891,6 +2135,41 @@ source_get_oauth2_access_token_finish (ESource *source,
return TRUE;
}
+
+static gboolean
+source_invoke_credentials_required_impl (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar *arg_reason,
+ const gchar *arg_certificate_pem,
+ const gchar *arg_certificate_errors,
+ const gchar *arg_dbus_error_name,
+ const gchar *arg_dbus_error_message,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
+
+ return e_dbus_source_call_invoke_credentials_required_sync (dbus_source,
+ arg_reason ? arg_reason : "",
+ arg_certificate_pem ? arg_certificate_pem : "",
+ arg_certificate_errors ? arg_certificate_errors : "",
+ arg_dbus_error_name ? arg_dbus_error_name : "",
+ arg_dbus_error_message ? arg_dbus_error_message : "",
+ cancellable, error);
+}
+
+static gboolean
+source_invoke_authenticate_impl (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar * const *arg_credentials,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_DBUS_IS_SOURCE (dbus_source), FALSE);
+
+ return e_dbus_source_call_invoke_authenticate_sync (dbus_source, arg_credentials, cancellable, error);
+}
+
static gboolean
source_initable_init (GInitable *initable,
GCancellable *cancellable,
@@ -1922,9 +2201,20 @@ source_initable_init (GInitable *initable,
g_free (source->priv->uid);
source->priv->uid = e_dbus_source_dup_uid (dbus_source);
+ source_update_connection_status_internal (source, dbus_source);
+
g_signal_connect (
dbus_source, "notify::data",
G_CALLBACK (source_notify_dbus_data_cb), source);
+ g_signal_connect (
+ dbus_source, "notify::connection-status",
+ G_CALLBACK (source_notify_dbus_connection_status_cb), source);
+ g_signal_connect (
+ dbus_source, "credentials-required",
+ G_CALLBACK (source_dbus_credentials_required_cb), source);
+ g_signal_connect (
+ dbus_source, "authenticate",
+ G_CALLBACK (source_dbus_authenticate_cb), source);
success = source_parse_dbus_data (source, error);
@@ -2075,6 +2365,8 @@ e_source_class_init (ESourceClass *class)
class->get_oauth2_access_token_sync = source_get_oauth2_access_token_sync;
class->get_oauth2_access_token = source_get_oauth2_access_token;
class->get_oauth2_access_token_finish = source_get_oauth2_access_token_finish;
+ class->invoke_credentials_required_impl = source_invoke_credentials_required_impl;
+ class->invoke_authenticate_impl = source_invoke_authenticate_impl;
g_object_class_install_property (
object_class,
@@ -2197,6 +2489,18 @@ e_source_class_init (ESourceClass *class)
G_PARAM_READABLE |
G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (
+ object_class,
+ PROP_CONNECTION_STATUS,
+ g_param_spec_enum (
+ "connection-status",
+ "Connection Status",
+ "Connection status of the source",
+ E_TYPE_SOURCE_CONNECTION_STATUS,
+ E_SOURCE_CONNECTION_STATUS_DISCONNECTED,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
/**
* ESource::changed:
* @source: the #ESource that received the signal
@@ -2214,6 +2518,47 @@ e_source_class_init (ESourceClass *class)
NULL, NULL, NULL,
G_TYPE_NONE, 0);
+ /**
+ * ESource::credentials-required:
+ * @source: the #ESource that received the signal
+ * @reason: an #ESourceCredentialsReason indicating why the credentials are requested
+ * @certificate_pem: PEM-encoded secure connection certificate for failed SSL checks
+ * @certificate_errors: what failed with the SSL certificate
+ * @error: a text description of the error, if any
+ *
+ * The ::credentials-required signal is emitted when the @source
+ * requires credentials to connect to (possibly remote)
+ * data store. The credentials can be passed to the backend using
+ * e_source_authenticate() function.
+ **/
+ signals[CREDENTIALS_REQUIRED] = g_signal_new (
+ "credentials-required",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+ G_STRUCT_OFFSET (ESourceClass, credentials_required),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 4,
+ E_TYPE_SOURCE_CREDENTIALS_REASON,
+ G_TYPE_STRING,
+ G_TYPE_TLS_CERTIFICATE_FLAGS,
+ G_TYPE_ERROR);
+
+ /**
+ * ESource::authenticate
+ * @source: the #ESource that received the signal
+ * @credentials: an #ENamedParameters with provided credentials
+ *
+ * Let's the backend know provided credentials to use to login
+ * to (possibly remote) data store.
+ **/
+ signals[AUTHENTICATE] = g_signal_new (
+ "authenticate",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE,
+ G_STRUCT_OFFSET (ESourceClass, authenticate),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 1, E_TYPE_NAMED_PARAMETERS);
+
/* Register built-in ESourceExtension types. */
g_type_ensure (E_TYPE_SOURCE_ADDRESS_BOOK);
g_type_ensure (E_TYPE_SOURCE_ALARMS);
@@ -2278,9 +2623,12 @@ e_source_init (ESource *source)
source->priv = E_SOURCE_GET_PRIVATE (source);
g_mutex_init (&source->priv->changed_lock);
g_mutex_init (&source->priv->data_change_lock);
+ g_mutex_init (&source->priv->connection_status_change_lock);
+ g_mutex_init (&source->priv->credentials_required_call_lock);
g_mutex_init (&source->priv->property_lock);
source->priv->key_file = g_key_file_new ();
source->priv->extensions = extensions;
+ source->priv->connection_status = E_SOURCE_CONNECTION_STATUS_DISCONNECTED;
g_rec_mutex_init (&source->priv->lock);
}
@@ -3205,6 +3553,68 @@ e_source_parameter_to_key (const gchar *param_name)
}
/**
+ * e_source_get_connection_status:
+ * @source: an #ESource
+ *
+ * Obtain current connection status of the @source.
+ *
+ * Returns: Current connection status of the @source.
+ *
+ * Since: 3.14
+ **/
+ESourceConnectionStatus
+e_source_get_connection_status (ESource *source)
+{
+ g_return_val_if_fail (E_IS_SOURCE (source), E_SOURCE_CONNECTION_STATUS_DISCONNECTED);
+
+ return source->priv->connection_status;
+}
+
+/**
+ * e_source_set_connection_status:
+ * @source: an #ESource
+ * @connection_status: one of the #ESourceConnectionStatus
+ *
+ * Set's current connection status of the @source.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_set_connection_status (ESource *source,
+ ESourceConnectionStatus connection_status)
+{
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ if (source->priv->connection_status == connection_status)
+ return;
+
+ source->priv->connection_status = connection_status;
+
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CONNECTION_STATUS);
+ enum_value = g_enum_get_value (enum_class, connection_status);
+
+ if (enum_value) {
+ GDBusObject *dbus_object;
+ EDBusSource *dbus_source;
+
+ dbus_object = e_source_ref_dbus_object (E_SOURCE (source));
+ dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
+ e_dbus_source_set_connection_status (dbus_source, enum_value->value_nick);
+ g_object_unref (dbus_source);
+ g_object_unref (dbus_object);
+ } else {
+ g_warning ("%s: Unknown connection status: %x", G_STRFUNC, connection_status);
+ }
+
+ g_type_class_unref (enum_class);
+
+ g_object_notify (G_OBJECT (source), "connection-status");
+}
+
+/**
* e_source_remove_sync:
* @source: the #ESource to be removed
* @cancellable: (allow-none): optional #GCancellable object, or %NULL
@@ -4189,15 +4599,31 @@ e_source_delete_password_finish (ESource *source,
}
/**
- * e_source_allow_auth_prompt_sync:
+ * e_source_invoke_credentials_required_sync:
* @source: an #ESource
- * @cancellable: optional #GCancellable object, or %NULL
- * @error: return location for a #GError, or %NULL
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
*
- * Tells the source registry that it can ask for passwords, if necessary.
- * Password prompts are disabled automatically when a user cancels
- * the password prompt. This function is to reverse the effect. It does
- * nothing, if the password prompt is not disabled.
+ * Let's the client-side know that credentials are required. The @reason defines which
+ * parameters are used. The client passed the credentials with an e_source_authenitcate()
+ * call.
+ *
+ * The %E_SOURCE_CREDENTIALS_REASON_REQUIRED is used for the first credentials prompt,
+ * when the client can return credentials as stored from the previous success login.
+ *
+ * The %E_SOURCE_CREDENTIALS_REASON_REJECTED is used when the previously used credentials
+ * had been rejected by the server. That usually means that the user should be asked
+ * to provide/correct the credentials.
+ *
+ * The %E_SOURCE_CREDENTIALS_REASON_SSL_FAILED is used when a secured connection failed
+ * due to some server-side certificate issues.
+ *
+ * The %E_SOURCE_CREDENTIALS_REASON_ERROR is used when the server returned an error.
+ * It is not possible to connect to it at the moment usually.
*
* If an error occurs, the function sets @error and returns %FALSE.
*
@@ -4206,16 +4632,31 @@ e_source_delete_password_finish (ESource *source,
* Since: 3.14
**/
gboolean
-e_source_allow_auth_prompt_sync (ESource *source,
- GCancellable *cancellable,
- GError **error)
+e_source_invoke_credentials_required_sync (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ GError **error)
{
GDBusObject *dbus_object;
EDBusSource *dbus_source = NULL;
+ ESourceClass *klass;
+ gchar *arg_reason, *arg_certificate_errors;
+ GEnumClass *enum_class;
+ GEnumValue *enum_value;
+ GFlagsClass *flags_class;
+ GFlagsValue *flags_value;
+ GString *certificate_errors_str;
+ gchar *dbus_error_name = NULL;
GError *local_error = NULL;
g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ klass = E_SOURCE_GET_CLASS (source);
+ g_return_val_if_fail (klass->invoke_credentials_required_impl != NULL, FALSE);
+
dbus_object = e_source_ref_dbus_object (source);
if (dbus_object != NULL) {
dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
@@ -4227,8 +4668,48 @@ e_source_allow_auth_prompt_sync (ESource *source,
return FALSE;
}
- e_dbus_source_call_allow_auth_prompt_sync (dbus_source, cancellable, &local_error);
+ enum_class = g_type_class_ref (E_TYPE_SOURCE_CREDENTIALS_REASON);
+ enum_value = g_enum_get_value (enum_class, reason);
+
+ g_return_val_if_fail (enum_value != NULL, FALSE);
+
+ arg_reason = g_strdup (enum_value->value_nick);
+ g_type_class_unref (enum_class);
+
+ certificate_errors_str = g_string_new ("");
+
+ flags_class = g_type_class_ref (G_TYPE_TLS_CERTIFICATE_FLAGS);
+ for (flags_value = g_flags_get_first_value (flags_class, certificate_errors);
+ flags_value;
+ flags_value = g_flags_get_first_value (flags_class, certificate_errors)) {
+ if (certificate_errors_str->len)
+ g_string_append_c (certificate_errors_str, ':');
+ g_string_append (certificate_errors_str, flags_value->value_nick);
+ certificate_errors &= ~flags_value->value;
+ }
+ g_type_class_unref (flags_class);
+
+ arg_certificate_errors = g_string_free (certificate_errors_str, FALSE);
+
+ if (reason == E_SOURCE_CREDENTIALS_REASON_SSL_FAILED)
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_SSL_FAILED);
+ else if (reason != E_SOURCE_CREDENTIALS_REASON_ERROR)
+ e_source_set_connection_status (source, E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS);
+ if (op_error)
+ dbus_error_name = g_dbus_error_encode_gerror (op_error);
+
+ klass->invoke_credentials_required_impl (source, dbus_source,
+ arg_reason ? arg_reason : "",
+ certificate_pem ? certificate_pem : "",
+ arg_certificate_errors ? arg_certificate_errors : "",
+ dbus_error_name ? dbus_error_name : "",
+ op_error ? op_error->message : "",
+ cancellable, &local_error);
+
+ g_free (arg_reason);
+ g_free (arg_certificate_errors);
+ g_free (dbus_error_name);
g_object_unref (dbus_source);
if (local_error != NULL) {
@@ -4240,17 +4721,38 @@ e_source_allow_auth_prompt_sync (ESource *source,
return TRUE;
}
+typedef struct _InvokeCredentialsRequiredData {
+ ESourceCredentialsReason reason;
+ gchar *certificate_pem;
+ GTlsCertificateFlags certificate_errors;
+ GError *op_error;
+} InvokeCredentialsRequiredData;
+
+static void
+invoke_credentials_required_data_free (gpointer ptr)
+{
+ InvokeCredentialsRequiredData *data = ptr;
+
+ if (data) {
+ g_free (data->certificate_pem);
+ g_clear_error (&data->op_error);
+ g_free (data);
+ }
+}
+
static void
-source_allow_auth_prompt_thread (GTask *task,
- gpointer source_object,
- gpointer task_data,
- GCancellable *cancellable)
+source_invoke_credentials_required_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
{
+ InvokeCredentialsRequiredData *data = task_data;
gboolean success;
GError *local_error = NULL;
- success = e_source_allow_auth_prompt_sync (
- E_SOURCE (source_object),
+ success = e_source_invoke_credentials_required_sync (
+ E_SOURCE (source_object), data->reason, data->certificate_pem,
+ data->certificate_errors, data->op_error,
cancellable, &local_error);
if (local_error != NULL) {
@@ -4261,47 +4763,61 @@ source_allow_auth_prompt_thread (GTask *task,
}
/**
- * e_source_allow_auth_prompt:
+ * e_source_invoke_credentials_required:
* @source: an #ESource
- * @cancellable: optional #GCancellable object, or %NULL
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
+ * @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
*
- * Asynchronously tells the source registry that it can ask for passwords,
- * if necessary. Password prompts are disabled automatically when a user cancels
- * the password prompt. This function is to reverse the effect. It does
- * nothing, if the password prompt is not disabled.
+ * Asynchronously calls the InvokeCredentialsRequired method on the server side,
+ * to inform clients that credentials are required.
*
* When the operation is finished, @callback will be called. You can then
- * call e_source_allow_auth_prompt_finish() to get the result of the operation.
+ * call e_source_invoke_credentials_required_finish() to get the result of the operation.
*
* Since: 3.14
**/
void
-e_source_allow_auth_prompt (ESource *source,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+e_source_invoke_credentials_required (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
+ InvokeCredentialsRequiredData *data;
GTask *task;
g_return_if_fail (E_IS_SOURCE (source));
+ data = g_new0 (InvokeCredentialsRequiredData, 1);
+ data->reason = reason;
+ data->certificate_pem = g_strdup (certificate_pem);
+ data->certificate_errors = certificate_errors;
+ data->op_error = op_error ? g_error_copy (op_error) : NULL;
+
task = g_task_new (source, cancellable, callback, user_data);
- g_task_set_source_tag (task, e_source_allow_auth_prompt);
+ g_task_set_source_tag (task, e_source_invoke_credentials_required);
+ g_task_set_task_data (task, data, invoke_credentials_required_data_free);
- g_task_run_in_thread (task, source_allow_auth_prompt_thread);
+ g_task_run_in_thread (task, source_invoke_credentials_required_thread);
g_object_unref (task);
}
/**
- * e_source_allow_auth_prompt_finish:
+ * e_source_invoke_credentials_required_finish:
* @source: an #ESource
* @result: a #GAsyncResult
- * @error: return location for a #GError, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
*
- * Finishes the operation started with e_source_allow_auth_prompt().
+ * Finishes the operation started with e_source_invoke_credentials_required().
*
* If an error occurs, the function sets @error and returns %FALSE.
*
@@ -4310,16 +4826,429 @@ e_source_allow_auth_prompt (ESource *source,
* Since: 3.14
**/
gboolean
-e_source_allow_auth_prompt_finish (ESource *source,
- GAsyncResult *result,
+e_source_invoke_credentials_required_finish (ESource *source,
+ GAsyncResult *result,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_source_invoke_credentials_required), FALSE);
+
+ return g_task_propagate_boolean (G_TASK (result), error);
+}
+
+/**
+ * e_source_invoke_authenticate_sync:
+ * @source: an #ESource
+ * @credentials: (allow none): an #ENamedParameters structure with credentials to use; can be %NULL
+ * to use those from the last call
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Calls the InvokeAuthenticate method on the server side, thus the backend
+ * knows what credentials to use to connect to its (possibly remote) data store.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_invoke_authenticate_sync (ESource *source,
+ const ENamedParameters *credentials,
+ GCancellable *cancellable,
GError **error)
{
+ GDBusObject *dbus_object;
+ EDBusSource *dbus_source = NULL;
+ ESourceClass *klass;
+ gchar **credentials_strv;
+ gboolean success;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ klass = E_SOURCE_GET_CLASS (source);
+ g_return_val_if_fail (klass->invoke_authenticate_impl != NULL, FALSE);
+
+ dbus_object = e_source_ref_dbus_object (source);
+ if (dbus_object != NULL) {
+ dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
+ g_object_unref (dbus_object);
+ }
+
+ if (!dbus_source) {
+ g_warn_if_fail (dbus_source != NULL);
+ return FALSE;
+ }
+
+ if (credentials) {
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND) &&
+ !e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_SSL_TRUST)) {
+ ENamedParameters *clone;
+ ESourceWebdav *webdav_extension;
+
+ clone = e_named_parameters_new_clone (credentials);
+
+ webdav_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+ e_named_parameters_set (clone, E_SOURCE_CREDENTIAL_SSL_TRUST,
+ e_source_webdav_get_ssl_trust (webdav_extension));
+
+ credentials_strv = e_named_parameters_to_strv (clone);
+
+ e_named_parameters_free (clone);
+ } else {
+ credentials_strv = e_named_parameters_to_strv (credentials);
+ }
+ } else {
+ ENamedParameters *empty_credentials;
+
+ empty_credentials = e_named_parameters_new ();
+ credentials_strv = e_named_parameters_to_strv (empty_credentials);
+ e_named_parameters_free (empty_credentials);
+ }
+
+ success = klass->invoke_authenticate_impl (source, dbus_source, (const gchar * const *)
credentials_strv, cancellable, &local_error);
+
+ g_strfreev (credentials_strv);
+ g_object_unref (dbus_source);
+
+ if (local_error != NULL) {
+ g_dbus_error_strip_remote_error (local_error);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ return success;
+}
+
+static void
+source_invoke_authenticate_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ gboolean success;
+ GError *local_error = NULL;
+
+ success = e_source_invoke_authenticate_sync (
+ E_SOURCE (source_object), task_data,
+ cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, success);
+ }
+}
+
+/**
+ * e_source_invoke_authenticate:
+ * @source: an #ESource
+ * @credentials: (allow none): an #ENamedParameters structure with credentials to use; can be %NULL
+ * to use those from the last call
+ * @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
+ *
+ * Asynchronously calls the InvokeAuthenticate method on the server side,
+ * thus the backend knows what credentials to use to connect to its (possibly
+ * remote) data store.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_source_invoke_authenticate_finish() to get the result of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_invoke_authenticate (ESource *source,
+ const ENamedParameters *credentials,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ENamedParameters *credentials_copy;
+ GTask *task;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ credentials_copy = e_named_parameters_new_clone (credentials);
+
+ task = g_task_new (source, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_source_invoke_authenticate);
+ g_task_set_task_data (task, credentials_copy, (GDestroyNotify) e_named_parameters_free);
+
+ g_task_run_in_thread (task, source_invoke_authenticate_thread);
+
+ g_object_unref (task);
+}
+
+/**
+ * e_source_invoke_authenticate_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_invoke_authenticate().
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_invoke_authenticate_finish (ESource *source,
+ GAsyncResult *result,
+ GError **error)
+{
g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
g_return_val_if_fail (
g_async_result_is_tagged (
- result, e_source_allow_auth_prompt), FALSE);
+ result, e_source_invoke_authenticate), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
+
+/**
+ * e_source_emit_credentials_required:
+ * @reason: an #ESourceCredentialsReason, why the credentials are required
+ * @certificate_pem: PEM-encoded secure connection certificate, or an empty string
+ * @certificate_errors: a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @op_error: (allow none): a #GError with a description of the previous credentials error, or %NULL
+ *
+ * Emits localy (in this process only) the ESource::credentials-required
+ * signal with given parameters. That's the difference with e_source_invoke_credentials_required(),
+ * which calls the signal globally, within each client.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_emit_credentials_required (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error)
+{
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ g_signal_emit (source, signals[CREDENTIALS_REQUIRED], 0, reason, certificate_pem, certificate_errors,
op_error);
+}
+
+/**
+ * e_source_get_last_credentials_required_arguments_sync:
+ * @source: an #ESource
+ * @out_reason: (out): an #ESourceCredentialsReason, why the credentials are required
+ * @out_certificate_pem: (out): PEM-encoded secure connection certificate, or an empty string
+ * @out_certificate_errors: (out): a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @out_op_error: (out): a #GError with a description of the previous credentials error
+ * @cancellable: (allow none): optional #GCancellable object, or %NULL
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Retrieves the last used arguments of the 'credentials-required' signal emission.
+ * If there was none emitted yet, or a corresponding 'authenitcate' had been emitted
+ * already, then the @out_reason is set to #E_SOURCE_CREDENTIALS_REASON_UNKNOWN
+ * and the value of other 'out' arguments is set to no values.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE. The result gchar
+ * values should be freed with g_free() when no longer needed.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_get_last_credentials_required_arguments_sync (ESource *source,
+ ESourceCredentialsReason *out_reason,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GError **out_op_error,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GDBusObject *dbus_object;
+ EDBusSource *dbus_source = NULL;
+ gboolean success;
+ gchar *arg_reason = NULL, *arg_certificate_errors = NULL;
+ gchar *arg_dbus_error_name = NULL, *arg_dbus_error_message = NULL;
+ GError *local_error = NULL;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_reason != NULL, FALSE);
+ g_return_val_if_fail (out_certificate_pem != NULL, FALSE);
+ g_return_val_if_fail (out_certificate_errors != NULL, FALSE);
+ g_return_val_if_fail (out_op_error != NULL, FALSE);
+
+ *out_reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
+ *out_certificate_pem = NULL;
+ *out_certificate_errors = 0;
+ *out_op_error = NULL;
+
+ dbus_object = e_source_ref_dbus_object (source);
+ if (dbus_object != NULL) {
+ dbus_source = e_dbus_object_get_source (E_DBUS_OBJECT (dbus_object));
+ g_object_unref (dbus_object);
+ }
+
+ if (!dbus_source) {
+ g_warn_if_fail (dbus_source != NULL);
+ return FALSE;
+ }
+
+ success = e_dbus_source_call_get_last_credentials_required_arguments_sync (dbus_source,
+ &arg_reason, out_certificate_pem, &arg_certificate_errors,
+ &arg_dbus_error_name, &arg_dbus_error_message, cancellable, error);
+
+ g_object_unref (dbus_source);
+
+ *out_reason = source_credentials_reason_from_text (arg_reason);
+ *out_certificate_errors = source_certificate_errors_from_text (arg_certificate_errors);
+
+ if (arg_dbus_error_name && *arg_dbus_error_name && arg_dbus_error_message) {
+ *out_op_error = g_dbus_error_new_for_dbus_error (arg_dbus_error_name, arg_dbus_error_message);
+ g_dbus_error_strip_remote_error (*out_op_error);
+ }
+
+ if (*out_certificate_pem && !**out_certificate_pem) {
+ g_free (*out_certificate_pem);
+ *out_certificate_pem = NULL;
+ }
+
+ g_free (arg_reason);
+ g_free (arg_certificate_errors);
+ g_free (arg_dbus_error_name);
+ g_free (arg_dbus_error_message);
+
+ if (local_error != NULL) {
+ g_dbus_error_strip_remote_error (local_error);
+ g_propagate_error (error, local_error);
+ return FALSE;
+ }
+
+ return success;
+}
+
+static void
+source_get_last_credentials_required_arguments_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ InvokeCredentialsRequiredData *data;
+ GError *local_error = NULL;
+
+ data = g_new0 (InvokeCredentialsRequiredData, 1);
+ data->reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
+ data->certificate_pem = NULL;
+ data->certificate_errors = 0;
+ data->op_error = NULL;
+
+ e_source_get_last_credentials_required_arguments_sync (
+ E_SOURCE (source_object), &data->reason, &data->certificate_pem,
+ &data->certificate_errors, &data->op_error,
+ cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_pointer (task, data, invoke_credentials_required_data_free);
+ }
+}
+
+/**
+ * e_source_get_last_credentials_required_arguments:
+ * @source: an #ESource
+ * @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
+ *
+ * Asynchronously calls the GetLastCredentialsRequiredArguments method
+ * on the server side, to get the last values used for the 'credentials-required'
+ * signal. See e_source_get_last_credentials_required_arguments_sync() for
+ * more information.
+ *
+ * When the operation is finished, @callback will be called. You can then
+ * call e_source_get_last_credentials_required_arguments_finish() to get
+ * the result of the operation.
+ *
+ * Since: 3.14
+ **/
+void
+e_source_get_last_credentials_required_arguments (ESource *source,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ task = g_task_new (source, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_source_get_last_credentials_required_arguments);
+
+ g_task_run_in_thread (task, source_get_last_credentials_required_arguments_thread);
+
+ g_object_unref (task);
+}
+
+/**
+ * e_source_get_last_credentials_required_arguments_finish:
+ * @source: an #ESource
+ * @result: a #GAsyncResult
+ * @out_reason: (out): an #ESourceCredentialsReason, why the credentials are required
+ * @out_certificate_pem: (out): PEM-encoded secure connection certificate, or an empty string
+ * @out_certificate_errors: (out): a bit-or of #GTlsCertificateFlags for secure connection certificate
+ * @out_op_error: (out): a #GError with a description of the previous credentials error
+ * @error: (allow none): return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_source_get_last_credentials_required_arguments().
+ * See e_source_get_last_credentials_required_arguments_sync() for more information
+ * about the output arguments.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_source_get_last_credentials_required_arguments_finish (ESource *source,
+ GAsyncResult *result,
+ ESourceCredentialsReason *out_reason,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GError **out_op_error,
+ GError **error)
+{
+ InvokeCredentialsRequiredData *data;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
+ g_return_val_if_fail (out_reason != NULL, FALSE);
+ g_return_val_if_fail (out_certificate_pem != NULL, FALSE);
+ g_return_val_if_fail (out_certificate_errors != NULL, FALSE);
+ g_return_val_if_fail (out_op_error != NULL, FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_source_get_last_credentials_required_arguments), FALSE);
+
+ data = g_task_propagate_pointer (G_TASK (result), error);
+ if (!data)
+ return FALSE;
+
+ *out_reason = data->reason;
+ *out_certificate_pem = g_strdup (data->certificate_pem);
+ *out_certificate_errors = data->certificate_errors;
+ *out_op_error = data->op_error ? g_error_copy (data->op_error) : NULL;
+
+ invoke_credentials_required_data_free (data);
+
+ return TRUE;
+}
diff --git a/libedataserver/e-source.h b/libedataserver/e-source.h
index 2d3c307..74d59d9 100644
--- a/libedataserver/e-source.h
+++ b/libedataserver/e-source.h
@@ -23,6 +23,8 @@
#define E_SOURCE_H
#include <gio/gio.h>
+#include <libedataserver/e-data-server-util.h>
+#include <libedataserver/e-source-enums.h>
/* Standard GObject macros */
#define E_TYPE_SOURCE \
@@ -54,6 +56,49 @@
**/
#define E_SOURCE_PARAM_SETTING (1 << G_PARAM_USER_SHIFT)
+/**
+ * E_SOURCE_CREDENTIAL_USERNAME:
+ *
+ * A name of the named parameter used for usernames in credentials,
+ * used to authenticate users with e_source_invoke_authenticate_sync()
+ * and e_source_invoke_authenticate(). The named parameter is optional,
+ * different authentication methods can use different names.
+ *
+ * Since: 3.14
+ **/
+#define E_SOURCE_CREDENTIAL_USERNAME "username"
+
+/**
+ * E_SOURCE_CREDENTIAL_PASSWORD:
+ *
+ * A name of the named parameter used for passwords in credentials,
+ * used to authenticate users with e_source_invoke_authenticate_sync()
+ * and e_source_invoke_authenticate(). The named parameter is optional,
+ * different authentication methods can use different names.
+ *
+ * Since: 3.14
+ **/
+#define E_SOURCE_CREDENTIAL_PASSWORD "password"
+
+/**
+ * E_SOURCE_CREDENTIAL_SSL_TRUST:
+ *
+ * A name of the named parameter used for SSL trust in credentials,
+ * used to authenticate users with e_source_invoke_authenticate_sync()
+ * and e_source_invoke_authenticate(). The named parameter is optional.
+ * Its value corresponds to current ESourceWebdav::ssl-trust property,
+ * in case the ESource has that extension available. This is required
+ * to have up-to-date data on the server side, when the client side
+ * just saved the SSL trust change, which might not be propagated
+ * into the server (factory) side quickly enough. The key is added into
+ * the credentials in invoke_authneticate() automatically, if the
+ * corresponding ESource contain a WebDAV extension and the key
+ * is not part of the credentials already.
+ *
+ * Since: 3.14
+ **/
+#define E_SOURCE_CREDENTIAL_SSL_TRUST "ssl-trust"
+
G_BEGIN_DECLS
typedef struct _ESource ESource;
@@ -78,6 +123,13 @@ struct _ESourceClass {
/* Signals */
void (*changed) (ESource *source);
+ void (*credentials_required) (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error);
+ void (* authenticate) (ESource *source,
+ const ENamedParameters *credentials);
/* Methods */
gboolean (*remove_sync) (ESource *source,
@@ -139,6 +191,22 @@ struct _ESourceClass {
gchar **out_access_token,
gint *out_expires_in,
GError **error);
+ gboolean (*invoke_credentials_required_impl)
+ (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar *arg_reason,
+ const gchar *arg_certificate_pem,
+ const gchar *arg_certificate_errors,
+ const gchar *arg_dbus_error_name,
+ const gchar *arg_dbus_error_message,
+ GCancellable *cancellable,
+ GError **error);
+ gboolean (*invoke_authenticate_impl)
+ (ESource *source,
+ gpointer dbus_source, /* EDBusSource * */
+ const gchar * const *arg_credentials,
+ GCancellable *cancellable,
+ GError **error);
/* Reserved slots. */
gpointer reserved[7];
@@ -185,6 +253,10 @@ gint e_source_compare_by_display_name
gchar * e_source_to_string (ESource *source,
gsize *length);
gchar * e_source_parameter_to_key (const gchar *param_name);
+ESourceConnectionStatus
+ e_source_get_connection_status (ESource *source);
+void e_source_set_connection_status (ESource *source,
+ ESourceConnectionStatus connection_status);
gboolean e_source_remove_sync (ESource *source,
GCancellable *cancellable,
GError **error);
@@ -282,16 +354,67 @@ void e_source_delete_password (ESource *source,
gboolean e_source_delete_password_finish (ESource *source,
GAsyncResult *result,
GError **error);
-gboolean e_source_allow_auth_prompt_sync (ESource *source,
+gboolean e_source_invoke_credentials_required_sync
+ (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
GCancellable *cancellable,
GError **error);
-void e_source_allow_auth_prompt (ESource *source,
+void e_source_invoke_credentials_required
+ (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_source_invoke_credentials_required_finish
+ (ESource *source,
+ GAsyncResult *result,
+ GError **error);
+gboolean e_source_invoke_authenticate_sync
+ (ESource *source,
+ const ENamedParameters *credentials,
+ GCancellable *cancellable,
+ GError **error);
+void e_source_invoke_authenticate (ESource *source,
+ const ENamedParameters *credentials,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_source_invoke_authenticate_finish
+ (ESource *source,
+ GAsyncResult *result,
+ GError **error);
+void e_source_emit_credentials_required
+ (ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error);
+gboolean e_source_get_last_credentials_required_arguments_sync
+ (ESource *source,
+ ESourceCredentialsReason *out_reason,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GError **out_op_error,
+ GCancellable *cancellable,
+ GError **error);
+void e_source_get_last_credentials_required_arguments
+ (ESource *source,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
-gboolean e_source_allow_auth_prompt_finish
+gboolean e_source_get_last_credentials_required_arguments_finish
(ESource *source,
GAsyncResult *result,
+ ESourceCredentialsReason *out_reason,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GError **out_op_error,
GError **error);
G_END_DECLS
diff --git a/libedataserver/libedataserver-private.h b/libedataserver/libedataserver-private.h
index e71e8f4..42f366e 100644
--- a/libedataserver/libedataserver-private.h
+++ b/libedataserver/libedataserver-private.h
@@ -23,10 +23,10 @@
#ifdef G_OS_WIN32
-const gchar * _libedataserver_get_extensiondir
- (void) G_GNUC_CONST;
-const gchar * _libedataserver_get_imagesdir (void) G_GNUC_CONST;
-const gchar * _libedataserver_get_ui_uidir (void) G_GNUC_CONST;
+const gchar * _libedataserver_get_extensiondir (void) G_GNUC_CONST;
+const gchar * _libedataserver_get_imagesdir (void) G_GNUC_CONST;
+const gchar * _libedataserver_get_ui_uidir (void) G_GNUC_CONST;
+const gchar * _libedataserver_get_credentialmoduledir (void) G_GNUC_CONST;
#undef E_DATA_SERVER_EXTENSIONDIR
#define E_DATA_SERVER_EXTENSIONDIR _libedataserver_get_extensiondir ()
@@ -37,6 +37,9 @@ const gchar * _libedataserver_get_ui_uidir (void) G_GNUC_CONST;
#undef E_DATA_SERVER_UI_UIDIR
#define E_DATA_SERVER_UI_UIDIR _libedataserver_get_ui_uidir ()
+#undef E_DATA_SERVER_CREDENTIALMODULEDIR
+#define E_DATA_SERVER_CREDENTIALMODULEDIR _libedataserver_get_credentialmoduledir ()
+
#endif /* G_OS_WIN32 */
#endif /* LIBEDATASERVER_PRIVATE_H */
diff --git a/libedataserver/libedataserver.h b/libedataserver/libedataserver.h
index 26dbe6b..989f603 100644
--- a/libedataserver/libedataserver.h
+++ b/libedataserver/libedataserver.h
@@ -27,6 +27,8 @@
#include <libedataserver/e-credentials.h>
#include <libedataserver/e-data-server-util.h>
#include <libedataserver/e-debug-log.h>
+#include <libedataserver/e-extensible.h>
+#include <libedataserver/e-extension.h>
#include <libedataserver/e-flag.h>
#include <libedataserver/e-free-form-exp.h>
#include <libedataserver/e-gdbus-templates.h>
@@ -34,6 +36,7 @@
#include <libedataserver/e-list-iterator.h>
#include <libedataserver/e-list.h>
#include <libedataserver/e-memory.h>
+#include <libedataserver/e-module.h>
#include <libedataserver/e-operation-pool.h>
#include <libedataserver/e-proxy.h>
#include <libedataserver/e-sexp.h>
@@ -45,6 +48,9 @@
#include <libedataserver/e-source-calendar.h>
#include <libedataserver/e-source-camel.h>
#include <libedataserver/e-source-collection.h>
+#include <libedataserver/e-source-credentials-provider.h>
+#include <libedataserver/e-source-credentials-provider-impl.h>
+#include <libedataserver/e-source-credentials-provider-impl-password.h>
#include <libedataserver/e-source-enums.h>
#include <libedataserver/e-source-enumtypes.h>
#include <libedataserver/e-source-extension.h>
diff --git a/libedataserver/libedataserver.pc.in b/libedataserver/libedataserver.pc.in
index ab07293..7c5794e 100644
--- a/libedataserver/libedataserver.pc.in
+++ b/libedataserver/libedataserver.pc.in
@@ -5,8 +5,11 @@ includedir= includedir@
datarootdir= datarootdir@
datadir= datadir@
+privlibdir= privlibdir@
privincludedir= privincludedir@
+credentialmoduledir= credentialmoduledir@
+
Name: libedataserver
Description: Utility library for Evolution Data Server
Version: @VERSION@
diff --git a/libedataserverui/Makefile.am b/libedataserverui/Makefile.am
new file mode 100644
index 0000000..cf625b3
--- /dev/null
+++ b/libedataserverui/Makefile.am
@@ -0,0 +1,77 @@
+BUILT_SOURCES =
+
+lib_LTLIBRARIES = libedataserverui-1.2.la
+
+libedataserverui_1_2_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -I$(top_builddir) \
+ -DLIBEDATASERVERUI_COMPILATION \
+ -DG_LOG_DOMAIN=\"e-data-server-ui\" \
+ $(E_BACKEND_CFLAGS) \
+ $(E_DATA_SERVER_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(CODE_COVERAGE_CFLAGS) \
+ $(GNOME_PLATFORM_CFLAGS) \
+ $(GCR_BASE_CFLAGS) \
+ $(GCR_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(NULL)
+
+libedataserverui_1_2_la_LIBADD = \
+ $(top_builddir)/camel/libcamel-1.2.la \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(E_BACKEND_LIBS) \
+ $(E_DATA_SERVER_LIBS) \
+ $(CAMEL_LIBS) \
+ $(CODE_COVERAGE_LIBS) \
+ $(GNOME_PLATFORM_LIBS) \
+ $(GCR_BASE_LIBS) \
+ $(GCR_LIBS) \
+ $(GTK_LIBS) \
+ $(NULL)
+
+libedataserverui_1_2_la_LDFLAGS = \
+ -version-info $(LIBEDATASERVERUI_CURRENT):$(LIBEDATASERVERUI_REVISION):$(LIBEDATASERVERUI_AGE)
$(NO_UNDEFINED) \
+ $(CODE_COVERAGE_LDFLAGS) \
+ $(NULL)
+
+libedataserveruiincludedir = $(privincludedir)/libedataserverui
+
+libedataserveruiinclude_HEADERS = \
+ libedataserverui.h \
+ e-credentials-prompter.h \
+ e-credentials-prompter-impl.h \
+ e-credentials-prompter-impl-password.h \
+ e-trust-prompt.h \
+ $(NULL)
+
+libedataserverui_1_2_la_SOURCES = \
+ $(BUILT_SOURCES) \
+ e-credentials-prompter.c \
+ e-credentials-prompter-impl.c \
+ e-credentials-prompter-impl-password.c \
+ e-trust-prompt.c \
+ $(NULL)
+
+%-$(API_VERSION).pc: %.pc
+ cp $< $@
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = libedataserverui-$(API_VERSION).pc
+
+EXTRA_DIST = \
+ $(pkgconfig_DATA:-$(API_VERSION).pc=.pc.in) \
+ $(NULL)
+
+CLEANFILES = $(BUILT_SOURCES)
+
+DISTCLEANFILES = \
+ $(pkgconfig_DATA) \
+ $(NULL)
+
+dist-hook:
+ cd $(distdir); rm -f $(BUILT_SOURCES)
+
+-include $(top_srcdir)/git.mk
diff --git a/libedataserverui/e-credentials-prompter-impl-password.c
b/libedataserverui/e-credentials-prompter-impl-password.c
new file mode 100644
index 0000000..4affc7d
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-password.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include "e-credentials-prompter.h"
+#include "e-credentials-prompter-impl-password.h"
+
+struct _ECredentialsPrompterImplPasswordPrivate {
+ gpointer prompt_id;
+ ESource *auth_source;
+ ESource *cred_source;
+ gchar *error_text;
+ ENamedParameters *credentials;
+
+ GtkDialog *dialog;
+ gulong show_dialog_idle_id;
+};
+
+G_DEFINE_TYPE (ECredentialsPrompterImplPassword, e_credentials_prompter_impl_password,
E_TYPE_CREDENTIALS_PROMPTER_IMPL)
+
+static gboolean
+password_dialog_map_event_cb (GtkWidget *dialog,
+ GdkEvent *event,
+ GtkWidget *password_entry)
+{
+ gtk_widget_grab_focus (password_entry);
+
+ return FALSE;
+}
+
+static void
+credentials_prompter_impl_password_get_prompt_strings (ESource *source,
+ gchar **prompt_title,
+ GString **prompt_description)
+{
+ GString *description;
+ const gchar *message;
+ gchar *display_name;
+ gchar *host_name = NULL;
+
+ /* Known types */
+ enum {
+ TYPE_UNKNOWN,
+ TYPE_AMBIGUOUS,
+ TYPE_ADDRESS_BOOK,
+ TYPE_CALENDAR,
+ TYPE_MAIL_ACCOUNT,
+ TYPE_MAIL_TRANSPORT,
+ TYPE_MEMO_LIST,
+ TYPE_TASK_LIST
+ } type = TYPE_UNKNOWN;
+
+ /* XXX This is kind of a hack but it should work for now. Build a
+ * suitable password prompt by checking for various extensions
+ * in the ESource. If no recognizable extensions are found, or
+ * if the result is ambiguous, just refer to the data source as
+ * an "account". */
+
+ display_name = e_source_dup_display_name (source);
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *extension;
+
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ host_name = e_source_authentication_dup_host (extension);
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+ type = TYPE_ADDRESS_BOOK;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_CALENDAR;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MAIL_ACCOUNT;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MAIL_TRANSPORT;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_MEMO_LIST;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+ if (type == TYPE_UNKNOWN)
+ type = TYPE_TASK_LIST;
+ else
+ type = TYPE_AMBIGUOUS;
+ }
+
+ switch (type) {
+ case TYPE_ADDRESS_BOOK:
+ message = _("Address book authentication request");
+ break;
+ case TYPE_CALENDAR:
+ case TYPE_MEMO_LIST:
+ case TYPE_TASK_LIST:
+ message = _("Calendar authentication request");
+ break;
+ case TYPE_MAIL_ACCOUNT:
+ case TYPE_MAIL_TRANSPORT:
+ message = _("Mail authentication request");
+ break;
+ default: /* generic account prompt */
+ message = _("Authentication request");
+ break;
+ }
+
+ description = g_string_sized_new (256);
+
+ g_string_append_printf (description, "<big><b>%s</b></big>\n\n", message);
+
+ switch (type) {
+ case TYPE_ADDRESS_BOOK:
+ g_string_append_printf (description,
+ _("Please enter the password for address book \"%s\"."), display_name);
+ break;
+ case TYPE_CALENDAR:
+ g_string_append_printf (description,
+ _("Please enter the password for calendar \"%s\"."), display_name);
+ break;
+ case TYPE_MAIL_ACCOUNT:
+ g_string_append_printf (description,
+ _("Please enter the password for mail account \"%s\"."), display_name);
+ break;
+ case TYPE_MAIL_TRANSPORT:
+ g_string_append_printf (description,
+ _("Please enter the password for mail transport \"%s\"."), display_name);
+ break;
+ case TYPE_MEMO_LIST:
+ g_string_append_printf (description,
+ _("Please enter the password for memo list \"%s\"."), display_name);
+ break;
+ case TYPE_TASK_LIST:
+ g_string_append_printf (description,
+ _("Please enter the password for task list \"%s\"."), display_name);
+ break;
+ default: /* generic account prompt */
+ g_string_append_printf (description,
+ _("Please enter the password for account \"%s\"."), display_name);
+ break;
+ }
+
+ if (host_name != NULL)
+ g_string_append_printf (
+ description, "\n(host: %s)", host_name);
+
+ *prompt_title = g_strdup (message);
+ *prompt_description = description;
+
+ g_free (display_name);
+ g_free (host_name);
+}
+
+static gboolean
+e_credentials_prompter_impl_password_show_dialog (ECredentialsPrompterImplPassword *prompter_password)
+{
+ GtkWidget *dialog, *content_area, *widget;
+ GtkGrid *grid;
+ GtkEntry *username_entry = NULL;
+ GtkEntry *password_entry;
+ GtkToggleButton *remember_toggle = NULL;
+ GtkWindow *dialog_parent;
+ ECredentialsPrompter *prompter;
+ gchar *title;
+ GString *info_markup;
+ gint row = 0;
+ ESourceAuthentication *auth_extension = NULL;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password), FALSE);
+ g_return_val_if_fail (prompter_password->priv->prompt_id != NULL, FALSE);
+ g_return_val_if_fail (prompter_password->priv->dialog == NULL, FALSE);
+
+ prompter = e_credentials_prompter_impl_get_credentials_prompter (E_CREDENTIALS_PROMPTER_IMPL
(prompter_password));
+ g_return_val_if_fail (prompter != NULL, FALSE);
+
+ dialog_parent = e_credentials_prompter_get_dialog_parent (prompter);
+
+ credentials_prompter_impl_password_get_prompt_strings (prompter_password->priv->auth_source, &title,
&info_markup);
+ if (prompter_password->priv->error_text && *prompter_password->priv->error_text) {
+ gchar *escaped = g_markup_printf_escaped ("%s", prompter_password->priv->error_text);
+
+ g_string_append_printf (info_markup, "\n\n%s", escaped);
+ g_free (escaped);
+ }
+
+ dialog = gtk_dialog_new_with_buttons (title, dialog_parent, GTK_DIALOG_MODAL |
GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_OK"), GTK_RESPONSE_OK,
+ NULL);
+
+ prompter_password->priv->dialog = GTK_DIALOG (dialog);
+ gtk_dialog_set_default_response (prompter_password->priv->dialog, GTK_RESPONSE_OK);
+ gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
+ if (dialog_parent)
+ gtk_window_set_transient_for (GTK_WINDOW (dialog), dialog_parent);
+ gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 12);
+
+ widget = gtk_dialog_get_action_area (prompter_password->priv->dialog);
+ content_area = gtk_dialog_get_content_area (prompter_password->priv->dialog);
+
+ /* Override GtkDialog defaults */
+ gtk_box_set_spacing (GTK_BOX (widget), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (widget), 0);
+ gtk_box_set_spacing (GTK_BOX (content_area), 12);
+ gtk_container_set_border_width (GTK_CONTAINER (content_area), 0);
+
+ grid = GTK_GRID (gtk_grid_new ());
+ gtk_grid_set_column_spacing (grid, 12);
+ gtk_grid_set_row_spacing (grid, 6);
+
+ gtk_box_pack_start (GTK_BOX (content_area), GTK_WIDGET (grid), FALSE, TRUE, 0);
+
+ /* Password Image */
+ widget = gtk_image_new_from_icon_name ("dialog-password", GTK_ICON_SIZE_DIALOG);
+ g_object_set (
+ G_OBJECT (widget),
+ "halign", GTK_ALIGN_START,
+ "vexpand", TRUE,
+ "valign", GTK_ALIGN_START,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 0, row, 1, 1);
+
+ /* Password Label */
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_markup (GTK_LABEL (widget), info_markup->str);
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_CENTER,
+ "width-chars", 60,
+ "max-width-chars", 80,
+ "xalign", 0.0,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 1, row, 1, 1);
+ row++;
+
+ if (e_source_has_extension (prompter_password->priv->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION))
{
+ auth_extension = e_source_get_extension (prompter_password->priv->cred_source,
E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ if (e_source_get_writable (prompter_password->priv->cred_source)) {
+ gchar *username;
+
+ username = e_source_authentication_dup_user (auth_extension);
+ if ((!username || !*username) &&
+ e_source_has_extension (prompter_password->priv->cred_source,
E_SOURCE_EXTENSION_COLLECTION)) {
+ ESourceCollection *collection_extension;
+ gchar *tmp;
+
+ collection_extension = e_source_get_extension
(prompter_password->priv->cred_source, E_SOURCE_EXTENSION_COLLECTION);
+
+ tmp = e_source_collection_dup_identity (collection_extension);
+ if (tmp && *tmp) {
+ g_free (username);
+ username = tmp;
+ tmp = NULL;
+ }
+
+ g_free (tmp);
+ }
+
+ username_entry = GTK_ENTRY (gtk_entry_new ());
+ g_object_set (
+ G_OBJECT (username_entry),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+
+ gtk_grid_attach (grid, GTK_WIDGET (username_entry), 1, row, 1, 1);
+ row++;
+
+ if (username && *username) {
+ gtk_entry_set_text (username_entry, username);
+ }
+
+ g_free (username);
+ }
+ }
+
+ password_entry = GTK_ENTRY (gtk_entry_new ());
+ gtk_entry_set_visibility (password_entry, FALSE);
+ gtk_entry_set_activates_default (password_entry, TRUE);
+ g_object_set (
+ G_OBJECT (password_entry),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ NULL);
+ if (e_named_parameters_get (prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD))
+ gtk_entry_set_text (password_entry, e_named_parameters_get
(prompter_password->priv->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
+
+ g_signal_connect (dialog, "map-event", G_CALLBACK (password_dialog_map_event_cb), password_entry);
+
+ gtk_grid_attach (grid, GTK_WIDGET (password_entry), 1, row, 1, 1);
+ row++;
+
+ if (username_entry && password_entry) {
+ widget = gtk_label_new_with_mnemonic (_("_User Name:"));
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", FALSE,
+ "vexpand", FALSE,
+ "halign", GTK_ALIGN_END,
+ "valign", GTK_ALIGN_CENTER,
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (username_entry));
+ gtk_grid_attach (grid, widget, 0, row - 2, 1, 1);
+
+ widget = gtk_label_new_with_mnemonic (_("_Password:"));
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", FALSE,
+ "vexpand", FALSE,
+ "halign", GTK_ALIGN_END,
+ "valign", GTK_ALIGN_CENTER,
+ NULL);
+
+ gtk_label_set_mnemonic_widget (GTK_LABEL (widget), GTK_WIDGET (password_entry));
+
+ gtk_grid_attach (grid, widget, 0, row - 1, 1, 1);
+ }
+
+ if (auth_extension) {
+ /* Remember password check */
+ widget = gtk_check_button_new_with_mnemonic (_("_Add this password to your keyring"));
+ remember_toggle = GTK_TOGGLE_BUTTON (widget);
+ gtk_toggle_button_set_active (remember_toggle, e_source_authentication_get_remember_password
(auth_extension));
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_FILL,
+ "margin-top", 12,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 1, row, 1, 1);
+ }
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+
+ success = gtk_dialog_run (prompter_password->priv->dialog) == GTK_RESPONSE_OK;
+
+ if (success) {
+ if (username_entry)
+ e_named_parameters_set (prompter_password->priv->credentials,
+ E_SOURCE_CREDENTIAL_USERNAME, gtk_entry_get_text (username_entry));
+ e_named_parameters_set (prompter_password->priv->credentials,
+ E_SOURCE_CREDENTIAL_PASSWORD, gtk_entry_get_text (password_entry));
+
+ if (auth_extension && remember_toggle) {
+ e_source_authentication_set_remember_password (auth_extension,
+ gtk_toggle_button_get_active (remember_toggle));
+ }
+ }
+
+ gtk_widget_destroy (dialog);
+ prompter_password->priv->dialog = NULL;
+
+ g_string_free (info_markup, TRUE);
+ g_free (title);
+
+ return success;
+}
+
+static void
+e_credentials_prompter_impl_password_free_prompt_data (ECredentialsPrompterImplPassword *prompter_password)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password));
+
+ prompter_password->priv->prompt_id = NULL;
+
+ g_clear_object (&prompter_password->priv->auth_source);
+ g_clear_object (&prompter_password->priv->cred_source);
+
+ g_free (prompter_password->priv->error_text);
+ prompter_password->priv->error_text = NULL;
+
+ e_named_parameters_free (prompter_password->priv->credentials);
+ prompter_password->priv->credentials = NULL;
+}
+
+static gboolean
+e_credentials_prompter_impl_password_show_dialog_idle_cb (gpointer user_data)
+{
+ ECredentialsPrompterImplPassword *prompter_password = user_data;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_password), FALSE);
+
+ if (g_source_get_id (g_main_current_source ()) == prompter_password->priv->show_dialog_idle_id) {
+ gboolean success;
+
+ prompter_password->priv->show_dialog_idle_id = 0;
+
+ g_warn_if_fail (prompter_password->priv->dialog == NULL);
+
+ success = e_credentials_prompter_impl_password_show_dialog (prompter_password);
+
+ e_credentials_prompter_impl_prompt_finish (
+ E_CREDENTIALS_PROMPTER_IMPL (prompter_password),
+ prompter_password->priv->prompt_id,
+ success ? prompter_password->priv->credentials : NULL);
+
+ e_credentials_prompter_impl_password_free_prompt_data (prompter_password);
+ }
+
+ return FALSE;
+}
+
+static void
+e_credentials_prompter_impl_password_process_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials)
+{
+ ECredentialsPrompterImplPassword *prompter_password;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl));
+
+ prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl);
+ g_return_if_fail (prompter_password->priv->prompt_id == NULL);
+ g_return_if_fail (prompter_password->priv->show_dialog_idle_id == 0);
+
+ prompter_password->priv->prompt_id = prompt_id;
+ prompter_password->priv->auth_source = g_object_ref (auth_source);
+ prompter_password->priv->cred_source = g_object_ref (cred_source);
+ prompter_password->priv->error_text = g_strdup (error_text);
+ prompter_password->priv->credentials = e_named_parameters_new_clone (credentials);
+ prompter_password->priv->show_dialog_idle_id = g_idle_add (
+ e_credentials_prompter_impl_password_show_dialog_idle_cb,
+ prompter_password);
+}
+
+static void
+e_credentials_prompter_impl_password_cancel_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id)
+{
+ ECredentialsPrompterImplPassword *prompter_password;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl));
+
+ prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (prompter_impl);
+ g_return_if_fail (prompter_password->priv->prompt_id == prompt_id);
+
+ /* This also closes the dialog. */
+ gtk_dialog_response (prompter_password->priv->dialog, GTK_RESPONSE_CANCEL);
+}
+
+static void
+e_credentials_prompter_impl_password_dispose (GObject *object)
+{
+ ECredentialsPrompterImplPassword *prompter_password = E_CREDENTIALS_PROMPTER_IMPL_PASSWORD (object);
+
+ if (prompter_password->priv->show_dialog_idle_id) {
+ g_source_remove (prompter_password->priv->show_dialog_idle_id);
+ prompter_password->priv->show_dialog_idle_id = 0;
+ }
+
+ g_warn_if_fail (prompter_password->priv->prompt_id == NULL);
+ g_warn_if_fail (prompter_password->priv->dialog == NULL);
+
+ e_credentials_prompter_impl_password_free_prompt_data (prompter_password);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_impl_password_parent_class)->dispose (object);
+}
+
+static void
+e_credentials_prompter_impl_password_class_init (ECredentialsPrompterImplPasswordClass *class)
+{
+ static const gchar *authentication_methods[] = {
+ "", /* register as the default credentials prompter */
+ NULL
+ };
+
+ GObjectClass *object_class;
+ ECredentialsPrompterImplClass *prompter_impl_class;
+
+ g_type_class_add_private (class, sizeof (ECredentialsPrompterImplPasswordPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->dispose = e_credentials_prompter_impl_password_dispose;
+
+ prompter_impl_class = E_CREDENTIALS_PROMPTER_IMPL_CLASS (class);
+ prompter_impl_class->authentication_methods = (const gchar * const *) authentication_methods;
+ prompter_impl_class->process_prompt = e_credentials_prompter_impl_password_process_prompt;
+ prompter_impl_class->cancel_prompt = e_credentials_prompter_impl_password_cancel_prompt;
+}
+
+static void
+e_credentials_prompter_impl_password_init (ECredentialsPrompterImplPassword *prompter_password)
+{
+ prompter_password->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter_password,
+ E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, ECredentialsPrompterImplPasswordPrivate);
+}
+
+/**
+ * e_credentials_prompter_impl_password_new:
+ *
+ * Creates a new instance of an #ECredentialsPrompterImplPassword.
+ *
+ * Returns: (transfer full): a newly created #ECredentialsPrompterImplPassword,
+ * which should be freed with g_object_unref() when no longer needed.
+ *
+ * Since: 3.14
+ **/
+ECredentialsPrompterImpl *
+e_credentials_prompter_impl_password_new (void)
+{
+ return g_object_new (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, NULL);
+}
diff --git a/libedataserverui/e-credentials-prompter-impl-password.h
b/libedataserverui/e-credentials-prompter-impl-password.h
new file mode 100644
index 0000000..dd3a839
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl-password.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CREDENTIALS_PROMPTER_IMPL_PASSWORD_H
+#define E_CREDENTIALS_PROMPTER_IMPL_PASSWORD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserverui/e-credentials-prompter-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD \
+ (e_credentials_prompter_impl_password_get_type ())
+#define E_CREDENTIALS_PROMPTER_IMPL_PASSWORD(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, ECredentialsPrompterImplPassword))
+#define E_CREDENTIALS_PROMPTER_IMPL_PASSWORD_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, ECredentialsPrompterImplPasswordClass))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_PASSWORD_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD))
+#define E_CREDENTIALS_PROMPTER_IMPL_PASSWORD_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD, ECredentialsPrompterImplPasswordClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECredentialsPrompterImplPassword ECredentialsPrompterImplPassword;
+typedef struct _ECredentialsPrompterImplPasswordClass ECredentialsPrompterImplPasswordClass;
+typedef struct _ECredentialsPrompterImplPasswordPrivate ECredentialsPrompterImplPasswordPrivate;
+
+/**
+ * ECredentialsPrompterImplPassword:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.14
+ **/
+struct _ECredentialsPrompterImplPassword {
+ ECredentialsPrompterImpl parent;
+ ECredentialsPrompterImplPasswordPrivate *priv;
+};
+
+struct _ECredentialsPrompterImplPasswordClass {
+ ECredentialsPrompterImplClass parent_class;
+};
+
+GType e_credentials_prompter_impl_password_get_type (void) G_GNUC_CONST;
+ECredentialsPrompterImpl *
+ e_credentials_prompter_impl_password_new (void);
+
+G_END_DECLS
+
+#endif /* E_CREDENTIALS_PROMPTER_IMPL_PASSWORD_H */
diff --git a/libedataserverui/e-credentials-prompter-impl.c b/libedataserverui/e-credentials-prompter-impl.c
new file mode 100644
index 0000000..11b6eaf
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-credentials-prompter.h"
+#include "e-credentials-prompter-impl.h"
+
+struct _ECredentialsPrompterImplPrivate {
+ GCancellable *cancellable;
+};
+
+enum {
+ PROMPT_FINISHED,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_ABSTRACT_TYPE (ECredentialsPrompterImpl, e_credentials_prompter_impl, E_TYPE_EXTENSION)
+
+static void
+e_credentials_prompter_impl_constructed (GObject *object)
+{
+ ECredentialsPrompterImpl *prompter_impl = E_CREDENTIALS_PROMPTER_IMPL (object);
+ ECredentialsPrompterImplClass *klass;
+ ECredentialsPrompter *prompter;
+ gint ii;
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_impl_parent_class)->constructed (object);
+
+ prompter = E_CREDENTIALS_PROMPTER (e_extension_get_extensible (E_EXTENSION (prompter_impl)));
+
+ klass = E_CREDENTIALS_PROMPTER_IMPL_GET_CLASS (object);
+ g_return_if_fail (klass->authentication_methods != NULL);
+
+ for (ii = 0; klass->authentication_methods[ii]; ii++) {
+ e_credentials_prompter_register_impl (prompter, klass->authentication_methods[ii],
prompter_impl);
+ }
+}
+
+static void
+e_credentials_prompter_impl_dispose (GObject *object)
+{
+ ECredentialsPrompterImpl *prompter_impl = E_CREDENTIALS_PROMPTER_IMPL (object);
+
+ if (prompter_impl->priv->cancellable) {
+ g_cancellable_cancel (prompter_impl->priv->cancellable);
+ g_clear_object (&prompter_impl->priv->cancellable);
+ }
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_impl_parent_class)->dispose (object);
+}
+
+static void
+e_credentials_prompter_impl_class_init (ECredentialsPrompterImplClass *klass)
+{
+ GObjectClass *object_class;
+ EExtensionClass *extension_class;
+
+ g_type_class_add_private (klass, sizeof (ECredentialsPrompterImplPrivate));
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = e_credentials_prompter_impl_dispose;
+ object_class->constructed = e_credentials_prompter_impl_constructed;
+
+ extension_class = E_EXTENSION_CLASS (klass);
+ extension_class->extensible_type = E_TYPE_CREDENTIALS_PROMPTER;
+
+ /**
+ * ECredentialsPrompterImpl::prompt-finished:
+ * @prompter_impl: an #ECredentialsPrompterImpl which emitted the signal
+ * @prompt_id: an ID of the prompt which was finished
+ * @credentials: (allow none): entered credentials, or %NULL for cancelled prompts
+ *
+ * Emitted when a prompt of ID @prompt_id is finished.
+ *
+ * Since: 3.14
+ **/
+ signals[PROMPT_FINISHED] = g_signal_new (
+ "prompt-finished",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ECredentialsPrompterImplClass, prompt_finished),
+ NULL, NULL, NULL,
+ G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_POINTER);
+}
+
+static void
+e_credentials_prompter_impl_init (ECredentialsPrompterImpl *prompter_impl)
+{
+ prompter_impl->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter_impl,
+ E_TYPE_CREDENTIALS_PROMPTER_IMPL, ECredentialsPrompterImplPrivate);
+
+ prompter_impl->priv->cancellable = g_cancellable_new ();
+}
+
+/**
+ * e_credentials_prompter_impl_get_credentials_prompter:
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ *
+ * Returns an #ECredentialsPrompter with which the @prompter_impl is associated.
+ *
+ * Returns: an #ECredentialsPrompter
+ *
+ * Since: 3.14
+ **/
+ECredentialsPrompter *
+e_credentials_prompter_impl_get_credentials_prompter (ECredentialsPrompterImpl *prompter_impl)
+{
+ EExtensible *extensible;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl), NULL);
+
+ extensible = e_extension_get_extensible (E_EXTENSION (prompter_impl));
+ if (!extensible)
+ return NULL;
+
+ return E_CREDENTIALS_PROMPTER (extensible);
+}
+
+/**
+ * e_credentials_prompter_impl_prompt:
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ * @prompt_id: a prompt ID to be passed to e_credentials_prompter_impl_prompt_finish()
+ * @auth_source: an #ESource, to prompt the credentials for (the source which asked for credentials)
+ * @cred_source: a parent #ESource, from which credentials were taken, or should be stored to
+ * @error_text: (allow none): an optional error text from the previous credentials prompt; can be %NULL
+ * @credentials: credentials, as saved in keyring; can be empty, but not %NULL
+ *
+ * Runs a credentials prompt for the @prompter_impl. The actual prompter implementation
+ * receives the prompt through ECredentialsPrompterImplClass::process_prompt(), where the given
+ * @prompt_id is used for an identification. The prompt is left 'active' as long as it is
+ * not finished with a call of e_credentials_prompter_impl_prompt_finish(). This should be
+ * called even for cancelled prompts. The prompt can be cancelled before it's processed,
+ * using the e_credentials_prompter_impl_cancel_prompt().
+ *
+ * The @auth_source can be the same as @cred_source, in case the credentials
+ * are stored only for that particular source. If the sources share credentials,
+ * which can be a case when the @auth_source is part of a collection, then
+ * the @cred_stource can be that collection source.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_impl_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials)
+{
+ ECredentialsPrompterImplClass *klass;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
+ g_return_if_fail (E_IS_SOURCE (auth_source));
+ g_return_if_fail (E_IS_SOURCE (cred_source));
+ g_return_if_fail (credentials != NULL);
+
+ klass = E_CREDENTIALS_PROMPTER_IMPL_GET_CLASS (prompter_impl);
+ g_return_if_fail (klass->process_prompt != NULL);
+
+ klass->process_prompt (prompter_impl, prompt_id, auth_source, cred_source, error_text, credentials);
+}
+
+/**
+ * e_credentials_prompter_impl_prompt_finish:
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ * @prompt_id: a prompt ID
+ * @credentials: (allow none): credentials to use; can be %NULL for cancelled prompts
+ *
+ * The actual credentials prompt implementation finishes a previously started
+ * credentials prompt @prompt_id with ECredentialsPrompterImplClass::process_prompt()
+ * by a call to this function. This function should be called regardless the prompt
+ * was or was not cancelled with e_credentials_prompter_impl_cancel_prompt().
+ * Once the prompt is finished another queued is started, if any pending exists.
+ * Use %NULL @credentials for cancelled prompts, otherwise the credentials are used
+ * for authentication of the associated #ESource.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_impl_prompt_finish (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ const ENamedParameters *credentials)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
+ g_return_if_fail (prompt_id != NULL);
+
+ g_signal_emit (prompter_impl, signals[PROMPT_FINISHED], 0, prompt_id, credentials);
+}
+
+/**
+ * e_credentials_prompter_impl_cancel_prompt:
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ * @prompt_id: a prompt ID to cancel
+ *
+ * Asks the @prompt_impl to cancel current prompt, which should have ID @prompt_id.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_impl_cancel_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id)
+{
+ ECredentialsPrompterImplClass *klass;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
+
+ klass = E_CREDENTIALS_PROMPTER_IMPL_GET_CLASS (prompter_impl);
+ g_return_if_fail (klass->cancel_prompt != NULL);
+
+ klass->cancel_prompt (prompter_impl, prompt_id);
+}
diff --git a/libedataserverui/e-credentials-prompter-impl.h b/libedataserverui/e-credentials-prompter-impl.h
new file mode 100644
index 0000000..806fa6e
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter-impl.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CREDENTIALS_PROMPTER_IMPL_H
+#define E_CREDENTIALS_PROMPTER_IMPL_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <libedataserver/libedataserver.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CREDENTIALS_PROMPTER_IMPL \
+ (e_credentials_prompter_impl_get_type ())
+#define E_CREDENTIALS_PROMPTER_IMPL(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL, ECredentialsPrompterImpl))
+#define E_CREDENTIALS_PROMPTER_IMPL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL, ECredentialsPrompterImplClass))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL))
+#define E_IS_CREDENTIALS_PROMPTER_IMPL_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER_IMPL))
+#define E_CREDENTIALS_PROMPTER_IMPL_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER_IMPL, ECredentialsPrompterImplClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECredentialsPrompterImpl ECredentialsPrompterImpl;
+typedef struct _ECredentialsPrompterImplClass ECredentialsPrompterImplClass;
+typedef struct _ECredentialsPrompterImplPrivate ECredentialsPrompterImplPrivate;
+
+struct _ECredentialsPrompter;
+
+/**
+ * ECredentialsPrompterImpl:
+ *
+ * Credentials prompter implementation base structure. The descendants
+ * implement ECredentialsPrompterImpl::prompt(), which is used to
+ * prompt for credentials. The descendants are automatically registered
+ * into an #ECredentialsPrompter.
+ *
+ * Since: 3.14
+ **/
+struct _ECredentialsPrompterImpl {
+ EExtension parent;
+ ECredentialsPrompterImplPrivate *priv;
+};
+
+struct _ECredentialsPrompterImplClass {
+ EExtensionClass parent_class;
+
+ const gchar * const *authentication_methods; /* NULL-terminated array of methods to register with */
+
+ /* Methods */
+
+ void (*process_prompt) (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials);
+ void (*cancel_prompt) (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id);
+
+ /* Signals */
+
+ void (*prompt_finished) (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ const ENamedParameters *credentials);
+};
+
+GType e_credentials_prompter_impl_get_type (void);
+struct _ECredentialsPrompter *
+ e_credentials_prompter_impl_get_credentials_prompter
+ (ECredentialsPrompterImpl *prompter_impl);
+void e_credentials_prompter_impl_prompt (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials);
+void e_credentials_prompter_impl_prompt_finish
+ (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ const ENamedParameters *credentials);
+void e_credentials_prompter_impl_cancel_prompt
+ (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id);
+
+G_END_DECLS
+
+#endif /* E_CREDENTIALS_PROMPTER_IMPL_H */
diff --git a/libedataserverui/e-credentials-prompter.c b/libedataserverui/e-credentials-prompter.c
new file mode 100644
index 0000000..f295e41
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter.c
@@ -0,0 +1,1763 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#include <camel/camel.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-credentials-prompter.h"
+
+/* built-in credentials prompter implementations */
+#include "e-credentials-prompter-impl-password.h"
+
+typedef struct _ProcessPromptData {
+ GWeakRef *prompter;
+ ECredentialsPrompterImpl *prompter_impl;
+ ESource *auth_source;
+ ESource *cred_source;
+ ESourceConnectionStatus connection_status; /* of the auth_source */
+ gboolean remember_password; /* of the cred_source, to check for changes */
+ gulong notify_handler_id;
+ gchar *error_text;
+ ENamedParameters *credentials;
+ gboolean allow_source_save;
+ GSimpleAsyncResult *async_result;
+} ProcessPromptData;
+
+struct _ECredentialsPrompterPrivate {
+ ESourceRegistry *registry;
+ ESourceCredentialsProvider *provider;
+ gboolean auto_prompt;
+ GCancellable *cancellable;
+
+ GMutex disabled_auto_prompt_lock;
+ GHashTable *disabled_auto_prompt; /* gchar *source_uid ~> 1; Source UIDs for which the auto-prompt is
disabled */
+
+ GMutex prompters_lock;
+ GHashTable *prompters; /* gchar *method ~> ECredentialsPrompterImpl *impl */
+ GHashTable *known_prompters; /* gpointer [ECredentialsPrompterImpl] ~> UINT known instances; the
prompter_impl is not referenced */
+
+ GRecMutex queue_lock; /* guards all queue and schedule related properties */
+ GSList *queue; /* ProcessPromptData * */
+ ProcessPromptData *processing_prompt;
+ gulong schedule_idle_id;
+};
+
+enum {
+ PROP_0,
+ PROP_AUTO_PROMPT,
+ PROP_REGISTRY,
+ PROP_PROVIDER
+};
+
+enum {
+ GET_DIALOG_PARENT,
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL];
+
+G_DEFINE_TYPE_WITH_CODE (ECredentialsPrompter, e_credentials_prompter, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
+
+static void
+process_prompt_data_free (gpointer ptr)
+{
+ ProcessPromptData *ppd = ptr;
+
+ if (ppd) {
+ if (ppd->notify_handler_id > 0)
+ g_signal_handler_disconnect (ppd->auth_source, ppd->notify_handler_id);
+
+ if (ppd->async_result) {
+ ECredentialsPrompter *prompter;
+
+ prompter = g_weak_ref_get (ppd->prompter);
+ if (prompter) {
+ e_credentials_prompter_complete_prompt_call (prompter, ppd->async_result,
ppd->auth_source, NULL, NULL);
+ g_clear_object (&prompter);
+ }
+ }
+
+ e_weak_ref_free (ppd->prompter);
+ g_clear_object (&ppd->prompter_impl);
+ g_clear_object (&ppd->auth_source);
+ g_clear_object (&ppd->cred_source);
+ g_free (ppd->error_text);
+ e_named_parameters_free (ppd->credentials);
+ g_free (ppd);
+ }
+}
+
+typedef struct _LookupSourceDetailsData {
+ ESource *auth_source; /* an ESource which asked for credentials */
+ ESource *cred_source; /* this might be auth_source or a parent collection source, if applicable, from
where the credentials come */
+ ENamedParameters *credentials; /* actual stored credentials */
+} LookupSourceDetailsData;
+
+static void
+lookup_source_details_data_free (gpointer ptr)
+{
+ LookupSourceDetailsData *data = ptr;
+
+ if (data) {
+ g_clear_object (&data->auth_source);
+ g_clear_object (&data->cred_source);
+ e_named_parameters_free (data->credentials);
+ g_free (data);
+ }
+}
+
+static void
+credentials_prompter_lookup_source_details_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ESource *source, *cred_source = NULL;
+ ECredentialsPrompter *prompter;
+ ESourceCredentialsProvider *provider;
+ ENamedParameters *credentials = NULL;
+ GError *local_error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source_object));
+
+ source = E_SOURCE (source_object);
+
+ prompter = g_weak_ref_get (task_data);
+ if (!prompter)
+ return;
+
+ provider = e_credentials_prompter_get_provider (prompter);
+ cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
+
+ e_source_credentials_provider_lookup_sync (prompter->priv->provider, cred_source ? cred_source :
source, cancellable, &credentials, &local_error);
+
+ /* Interested only in the cancelled error, which means the prompter is freed. */
+ if (local_error != NULL && g_error_matches (local_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_task_return_error (task, local_error);
+ local_error = NULL;
+ } else {
+ LookupSourceDetailsData *data;
+
+ data = g_new0 (LookupSourceDetailsData, 1);
+ data->auth_source = g_object_ref (source);
+ data->cred_source = g_object_ref (cred_source ? cred_source : source); /* always set both,
for simplicity */
+ data->credentials = credentials; /* NULL for no credentials available */
+
+ /* To not be freed below. */
+ credentials = NULL;
+
+ g_task_return_pointer (task, data, lookup_source_details_data_free);
+ }
+
+ e_named_parameters_free (credentials);
+ g_clear_object (&cred_source);
+ g_clear_object (&prompter);
+ g_clear_error (&local_error);
+}
+
+static void
+credentials_prompter_lookup_source_details (ESource *source,
+ ECredentialsPrompter *prompter,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ task = g_task_new (source, prompter->priv->cancellable, callback, user_data);
+ g_task_set_source_tag (task, credentials_prompter_lookup_source_details_thread);
+ g_task_set_task_data (task, e_weak_ref_new (prompter), (GDestroyNotify) e_weak_ref_free);
+
+ g_task_run_in_thread (task, credentials_prompter_lookup_source_details_thread);
+
+ g_object_unref (task);
+}
+
+static gboolean
+credentials_prompter_lookup_source_details_finish (ESource *source,
+ GAsyncResult *result,
+ ECredentialsPrompter **out_prompter, /* will be
referenced, if not NULL */
+ LookupSourceDetailsData **out_data,
+ GError **error)
+{
+ LookupSourceDetailsData *data;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_prompter != NULL, FALSE);
+ g_return_val_if_fail (out_data != NULL, FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, credentials_prompter_lookup_source_details_thread), FALSE);
+
+ data = g_task_propagate_pointer (G_TASK (result), error);
+ if (!data)
+ return FALSE;
+
+ *out_data = data;
+ *out_prompter = g_weak_ref_get (g_task_get_task_data (G_TASK (result)));
+
+ return TRUE;
+}
+
+static void
+credentials_prompter_invoke_authenticate_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!e_source_invoke_authenticate_finish (E_SOURCE (source_object), result, &error) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_debug ("%s: Failed to invoke authenticate: %s", G_STRFUNC, error ? error->message :
"Unknown error");
+ }
+
+ g_clear_error (&error);
+}
+
+typedef struct _CredentialsPromptData {
+ ESource *source;
+ gchar *error_text;
+ ECredentialsPrompterPromptFlags flags;
+ GTask *complete_task;
+ GSimpleAsyncResult *async_result;
+} CredentialsPromptData;
+
+static void
+credentials_prompt_data_free (gpointer ptr)
+{
+ CredentialsPromptData *data = ptr;
+
+ if (data) {
+ if (data->async_result) {
+ g_simple_async_result_set_error (data->async_result,
+ G_IO_ERROR, G_IO_ERROR_CANCELLED, "%s", _("Credentials prompt was
cancelled"));
+ g_simple_async_result_complete_in_idle (data->async_result);
+ g_clear_object (&data->async_result);
+ }
+
+ g_clear_object (&data->source);
+ g_free (data->error_text);
+ g_free (data);
+ }
+}
+
+typedef struct _CredentialsResultData {
+ ESource *source;
+ ENamedParameters *credentials;
+} CredentialsResultData;
+
+static void
+credentials_result_data_free (gpointer ptr)
+{
+ CredentialsResultData *data = ptr;
+
+ if (data) {
+ g_clear_object (&data->source);
+ e_named_parameters_free (data->credentials);
+ g_free (data);
+ }
+}
+
+
+static void
+credentials_prompter_maybe_process_next_prompt (ECredentialsPrompter *prompter)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ /* Already processing one */
+ if (prompter->priv->processing_prompt) {
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+ return;
+ }
+
+ if (prompter->priv->queue) {
+ ProcessPromptData *ppd = prompter->priv->queue->data;
+
+ g_warn_if_fail (ppd != NULL);
+
+ prompter->priv->queue = g_slist_remove (prompter->priv->queue, ppd);
+ prompter->priv->processing_prompt = ppd;
+
+ e_credentials_prompter_impl_prompt (ppd->prompter_impl, ppd, ppd->auth_source,
+ ppd->cred_source, ppd->error_text, ppd->credentials);
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+}
+
+static gboolean
+credentials_prompter_process_next_prompt_idle_cb (gpointer user_data)
+{
+ ECredentialsPrompter *prompter = user_data;
+
+ if (g_source_is_destroyed (g_main_current_source ()))
+ return FALSE;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ if (g_source_get_id (g_main_current_source ()) == prompter->priv->schedule_idle_id) {
+ prompter->priv->schedule_idle_id = 0;
+
+ credentials_prompter_maybe_process_next_prompt (prompter);
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+
+ return FALSE;
+}
+
+static void
+credentials_prompter_schedule_process_next_prompt (ECredentialsPrompter *prompter)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ /* Already processing one */
+ if (prompter->priv->processing_prompt ||
+ prompter->priv->schedule_idle_id) {
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+ return;
+ }
+
+ prompter->priv->schedule_idle_id = g_idle_add_full (G_PRIORITY_HIGH_IDLE,
+ credentials_prompter_process_next_prompt_idle_cb,
+ prompter, NULL);
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+}
+
+static void
+credentials_prompter_connection_status_changed_cb (ESource *source,
+ GParamSpec *param,
+ ECredentialsPrompter *prompter)
+{
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ /* Do not cancel the prompt when the source is still waiting for the credentials. */
+ if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS)
+ return;
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ if (prompter->priv->processing_prompt &&
+ e_source_equal (prompter->priv->processing_prompt->auth_source, source)) {
+ e_credentials_prompter_impl_cancel_prompt (prompter->priv->processing_prompt->prompter_impl,
prompter->priv->processing_prompt);
+ } else {
+ GSList *link;
+
+ for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
+ ProcessPromptData *ppd = link->data;
+
+ g_warn_if_fail (ppd != NULL);
+
+ if (ppd && e_source_equal (ppd->auth_source, source)) {
+ if (ppd->connection_status != e_source_get_connection_status (source)) {
+ prompter->priv->queue = g_slist_remove (prompter->priv->queue, ppd);
+ process_prompt_data_free (ppd);
+ }
+ break;
+ }
+ }
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+}
+
+static gboolean
+e_credentials_prompter_eval_remember_password (ESource *source)
+{
+ gboolean remember_password = FALSE;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION))
+ remember_password = e_source_authentication_get_remember_password (
+ e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION));
+
+ return remember_password;
+}
+
+static void
+e_credentials_prompter_manage_impl_prompt (ECredentialsPrompter *prompter,
+ ECredentialsPrompterImpl *prompter_impl,
+ ESource *auth_source,
+ ESource *cred_source,
+ const gchar *error_text,
+ const ENamedParameters *credentials,
+ gboolean allow_source_save,
+ GSimpleAsyncResult *async_result)
+{
+ GSList *link;
+ gboolean success = TRUE;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
+ g_return_if_fail (E_IS_SOURCE (auth_source));
+ g_return_if_fail (E_IS_SOURCE (cred_source));
+ g_return_if_fail (credentials != NULL);
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
+ ProcessPromptData *ppd = link->data;
+
+ g_warn_if_fail (ppd != NULL);
+
+ if (ppd && e_source_equal (ppd->auth_source, auth_source)) {
+ break;
+ }
+ }
+
+ if (link != NULL || (prompter->priv->processing_prompt &&
+ e_source_equal (prompter->priv->processing_prompt->auth_source, auth_source))) {
+ /* have queued or already asking for credentials for this source */
+ success = FALSE;
+ } else {
+ ProcessPromptData *ppd;
+
+ ppd = g_new0 (ProcessPromptData, 1);
+ ppd->prompter = e_weak_ref_new (prompter);
+ ppd->prompter_impl = g_object_ref (prompter_impl);
+ ppd->auth_source = g_object_ref (auth_source);
+ ppd->cred_source = g_object_ref (cred_source);
+ ppd->connection_status = e_source_get_connection_status (ppd->auth_source);
+ ppd->remember_password = e_credentials_prompter_eval_remember_password (ppd->cred_source);
+ ppd->error_text = g_strdup (error_text);
+ ppd->credentials = e_named_parameters_new_clone (credentials);
+ ppd->allow_source_save = allow_source_save;
+ ppd->async_result = async_result ? g_object_ref (async_result) : NULL;
+
+ /* If the prompter doesn't auto-prompt, then it should not auto-close the prompt as well. */
+ if (e_credentials_prompter_get_auto_prompt (prompter)) {
+ ppd->notify_handler_id = g_signal_connect (ppd->auth_source,
"notify::connection-status",
+ G_CALLBACK (credentials_prompter_connection_status_changed_cb),
prompter_impl);
+ } else {
+ ppd->notify_handler_id = 0;
+ }
+
+ prompter->priv->queue = g_slist_append (prompter->priv->queue, ppd);
+
+ credentials_prompter_schedule_process_next_prompt (prompter);
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+
+ if (!success && async_result) {
+ e_credentials_prompter_complete_prompt_call (prompter, async_result, auth_source, NULL, NULL);
+ }
+}
+
+static void
+credentials_prompter_store_credentials_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!e_source_credentials_provider_store_finish (E_SOURCE_CREDENTIALS_PROVIDER (source_object),
result, &error) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("%s: Failed to store source credentials: %s", G_STRFUNC, error ? error->message :
"Unknown error");
+ }
+
+ g_clear_error (&error);
+}
+
+static void
+credentials_prompter_source_write_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ GError *error = NULL;
+
+ if (!e_source_write_finish (E_SOURCE (source_object), result, &error) &&
+ !g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("%s: Failed to write source changes: %s", G_STRFUNC, error ? error->message :
"Unknown error");
+ }
+
+ g_clear_error (&error);
+}
+
+static void
+e_credentials_prompter_prompt_finish_for_source (ECredentialsPrompter *prompter,
+ ProcessPromptData *ppd,
+ const ENamedParameters *credentials)
+{
+ gboolean changed = FALSE;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (ppd != NULL);
+
+ if (!credentials)
+ return;
+
+ if (e_source_has_extension (ppd->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *auth_extension = e_source_get_extension (ppd->cred_source,
E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ if (e_source_credentials_provider_can_store (e_credentials_prompter_get_provider (prompter),
ppd->cred_source)) {
+ e_source_credentials_provider_store (e_credentials_prompter_get_provider (prompter),
ppd->cred_source, credentials,
+ e_source_authentication_get_remember_password (auth_extension),
+ prompter->priv->cancellable,
+ credentials_prompter_store_credentials_cb, NULL);
+ }
+
+ if (e_source_get_writable (ppd->cred_source)) {
+ const gchar *username;
+
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (username && *username &&
+ g_strcmp0 (username, e_source_authentication_get_user (auth_extension)) != 0) {
+ e_source_authentication_set_user (auth_extension, username);
+ changed = TRUE;
+ }
+ }
+ }
+
+ if (ppd->allow_source_save && e_source_get_writable (ppd->cred_source) &&
+ (changed || (ppd->remember_password ? 1 : 0) != (e_credentials_prompter_eval_remember_password
(ppd->cred_source) ? 1 : 0))) {
+ e_source_write (ppd->cred_source, prompter->priv->cancellable,
+ credentials_prompter_source_write_cb, NULL);
+ }
+
+ if (ppd->async_result) {
+ ECredentialsPrompter *prompter;
+
+ prompter = g_weak_ref_get (ppd->prompter);
+ if (prompter) {
+ e_credentials_prompter_complete_prompt_call (prompter, ppd->async_result,
ppd->auth_source, credentials, NULL);
+ g_clear_object (&prompter);
+
+ /* To not be completed multiple times */
+ g_clear_object (&ppd->async_result);
+ }
+ } else {
+ e_source_invoke_authenticate (ppd->auth_source, credentials, prompter->priv->cancellable,
+ credentials_prompter_invoke_authenticate_cb, NULL);
+ }
+}
+
+static void
+credentials_prompter_prompt_finished_cb (ECredentialsPrompterImpl *prompter_impl,
+ gpointer prompt_id,
+ const ENamedParameters *credentials,
+ ECredentialsPrompter *prompter)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl));
+ g_return_if_fail (prompt_id != NULL);
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ if (prompt_id == prompter->priv->processing_prompt) {
+ ProcessPromptData *ppd = prompter->priv->processing_prompt;
+ GSList *link, *to_remove = NULL;
+
+ prompter->priv->processing_prompt = NULL;
+
+ e_credentials_prompter_prompt_finish_for_source (prompter, ppd, credentials);
+
+ /* Finish also any other pending prompts for the same credentials source
+ as was finished this one. This can be relevant to collection sources. */
+ for (link = prompter->priv->queue; link; link = g_slist_next (link)) {
+ ProcessPromptData *sub_ppd = link->data;
+
+ if (sub_ppd && sub_ppd->cred_source && e_source_equal (sub_ppd->cred_source,
ppd->cred_source)) {
+ to_remove = g_slist_prepend (to_remove, sub_ppd);
+ }
+ }
+
+ for (link = to_remove; link; link = g_slist_next (link)) {
+ ProcessPromptData *sub_ppd = link->data;
+
+ if (sub_ppd) {
+ prompter->priv->queue = g_slist_remove (prompter->priv->queue, sub_ppd);
+ e_credentials_prompter_prompt_finish_for_source (prompter, sub_ppd,
credentials);
+ }
+ }
+
+ g_slist_free_full (to_remove, process_prompt_data_free);
+ process_prompt_data_free (ppd);
+
+ credentials_prompter_schedule_process_next_prompt (prompter);
+ } else {
+ g_warning ("%s: Unknown prompt_id %p", G_STRFUNC, prompt_id);
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+}
+
+static gboolean
+credentials_prompter_prompt_with_source_details (ECredentialsPrompter *prompter,
+ LookupSourceDetailsData *data,
+ const gchar *error_text,
+ ECredentialsPrompterPromptFlags flags,
+ GSimpleAsyncResult *async_result)
+{
+ ECredentialsPrompterImpl *prompter_impl = NULL;
+ gchar *method = NULL;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ if (e_source_has_extension (data->cred_source, E_SOURCE_EXTENSION_AUTHENTICATION)) {
+ ESourceAuthentication *authentication = e_source_get_extension (data->cred_source,
E_SOURCE_EXTENSION_AUTHENTICATION);
+
+ method = e_source_authentication_dup_method (authentication);
+ }
+
+ g_mutex_lock (&prompter->priv->prompters_lock);
+
+ prompter_impl = g_hash_table_lookup (prompter->priv->prompters, method ? method : "");
+ if (!prompter_impl && method && *method)
+ prompter_impl = g_hash_table_lookup (prompter->priv->prompters, "");
+
+ if (prompter_impl)
+ g_object_ref (prompter_impl);
+
+ g_mutex_unlock (&prompter->priv->prompters_lock);
+
+ if (prompter_impl) {
+ ENamedParameters *credentials;
+
+ credentials = e_named_parameters_new ();
+ if (data->credentials)
+ e_named_parameters_assign (credentials, data->credentials);
+
+ if (async_result && data->credentials && (flags &
E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS) != 0) {
+ e_credentials_prompter_complete_prompt_call (prompter, async_result,
data->auth_source, credentials, NULL);
+ } else if (!e_source_credentials_provider_can_prompt (prompter->priv->provider,
data->auth_source)) {
+ /* This source cannot be asked for credentials, thus end with a 'not supported'
error. */
+ GError *error;
+
+ error = g_error_new (G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+ _("Source '%s' doesn't support prompt for credentials"),
+ e_source_get_display_name (data->cred_source));
+
+ if (async_result)
+ e_credentials_prompter_complete_prompt_call (prompter, async_result,
data->auth_source, NULL, error);
+
+ g_clear_error (&error);
+ } else {
+ e_credentials_prompter_manage_impl_prompt (prompter, prompter_impl,
+ data->auth_source, data->cred_source, error_text, credentials,
+ !async_result || (flags &
E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE) != 0,
+ async_result);
+ }
+
+ e_named_parameters_free (credentials);
+ } else {
+ /* Shoud not happen, because the password prompter is added as the default prompter. */
+ g_warning ("%s: No prompter impl found for an authentication method '%s'", G_STRFUNC, method
? method : "");
+ success = FALSE;
+ }
+
+ g_clear_object (&prompter_impl);
+ g_free (method);
+
+ return success;
+}
+
+static void
+credentials_prompter_lookup_source_details_before_prompt_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ CredentialsPromptData *prompt_data = user_data;
+ ECredentialsPrompter *prompter = NULL;
+ LookupSourceDetailsData *data = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (prompt_data != NULL);
+ g_return_if_fail (E_IS_SOURCE (source_object));
+
+ if (!credentials_prompter_lookup_source_details_finish (E_SOURCE (source_object), result, &prompter,
&data, &error)) {
+ g_clear_error (&error);
+ credentials_prompt_data_free (prompt_data);
+ return;
+ }
+
+ if (credentials_prompter_prompt_with_source_details (prompter, data, prompt_data->error_text,
+ prompt_data->flags, prompt_data->async_result)) {
+ /* To not finish the async_result multiple times */
+ g_clear_object (&prompt_data->async_result);
+ }
+
+ g_clear_object (&prompter);
+
+ credentials_prompt_data_free (prompt_data);
+ lookup_source_details_data_free (data);
+}
+
+static void
+credentials_prompter_lookup_source_details_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ LookupSourceDetailsData *data = NULL;
+ ECredentialsPrompter *prompter = NULL;
+ ESource *source;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source_object));
+
+ source = E_SOURCE (source_object);
+
+ if (!credentials_prompter_lookup_source_details_finish (source, result, &prompter, &data, &error)) {
+ g_clear_error (&error);
+ return;
+ }
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (data != NULL);
+
+ if (data->credentials) {
+ e_source_invoke_authenticate (E_SOURCE (data->auth_source), data->credentials,
prompter->priv->cancellable, credentials_prompter_invoke_authenticate_cb, NULL);
+ } else {
+ credentials_prompter_prompt_with_source_details (prompter, data, NULL, 0, NULL);
+ }
+
+ lookup_source_details_data_free (data);
+ g_clear_object (&prompter);
+}
+
+static void
+credentials_prompter_credentials_required_cb (ESourceRegistry *registry,
+ ESource *source,
+ ESourceCredentialsReason reason,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const GError *op_error,
+ ECredentialsPrompter *prompter)
+{
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ /* Only these two reasons are meant to be used to prompt the user for credentials. */
+ if (reason != E_SOURCE_CREDENTIALS_REASON_REQUIRED &&
+ reason != E_SOURCE_CREDENTIALS_REASON_REJECTED)
+ return;
+
+ /* Global auto-prompt or the source's auto-prompt is disabled. */
+ if (!e_credentials_prompter_get_auto_prompt (prompter) ||
+ e_credentials_prompter_get_auto_prompt_disabled_for (prompter, source))
+ return;
+
+ /* This is a re-prompt, but the source cannot be prompted for credentials. */
+ if (reason == E_SOURCE_CREDENTIALS_REASON_REJECTED &&
+ !e_source_credentials_provider_can_prompt (prompter->priv->provider, source)) {
+ return;
+ }
+
+ if (reason == E_SOURCE_CREDENTIALS_REASON_REQUIRED) {
+ credentials_prompter_lookup_source_details (source, prompter,
+ credentials_prompter_lookup_source_details_cb, NULL);
+ return;
+ }
+
+ e_credentials_prompter_prompt (prompter, source, op_error ? op_error->message : NULL, 0, NULL, NULL);
+}
+
+static gboolean
+credentials_prompter_get_dialog_parent_accumulator (GSignalInvocationHint *ihint,
+ GValue *return_accu,
+ const GValue *handler_return,
+ gpointer data)
+{
+ if (handler_return && g_value_get_object (handler_return) != NULL) {
+ g_value_set_object (return_accu, g_value_get_object (handler_return));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+credentials_prompter_set_registry (ECredentialsPrompter *prompter,
+ ESourceRegistry *registry)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (E_IS_SOURCE_REGISTRY (registry));
+ g_return_if_fail (prompter->priv->registry == NULL);
+
+ prompter->priv->registry = g_object_ref (registry);
+ prompter->priv->provider = e_source_credentials_provider_new (prompter->priv->registry);
+
+ g_signal_connect (prompter->priv->registry, "credentials-required",
+ G_CALLBACK (credentials_prompter_credentials_required_cb), prompter);
+}
+
+static void
+credentials_prompter_set_property (GObject *object,
+ guint property_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REGISTRY:
+ credentials_prompter_set_registry (
+ E_CREDENTIALS_PROMPTER (object),
+ g_value_get_object (value));
+ return;
+
+ case PROP_AUTO_PROMPT:
+ e_credentials_prompter_set_auto_prompt (
+ E_CREDENTIALS_PROMPTER (object),
+ g_value_get_boolean (value));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+credentials_prompter_get_property (GObject *object,
+ guint property_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (property_id) {
+ case PROP_REGISTRY:
+ g_value_set_object (value,
+ e_credentials_prompter_get_registry (
+ E_CREDENTIALS_PROMPTER (object)));
+ return;
+
+ case PROP_PROVIDER:
+ g_value_set_object (value,
+ e_credentials_prompter_get_provider (
+ E_CREDENTIALS_PROMPTER (object)));
+ return;
+
+ case PROP_AUTO_PROMPT:
+ g_value_set_boolean (value,
+ e_credentials_prompter_get_auto_prompt (
+ E_CREDENTIALS_PROMPTER (object)));
+ return;
+ }
+
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+credentials_prompter_constructed (GObject *object)
+{
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_parent_class)->constructed (object);
+
+ e_extensible_load_extensions (E_EXTENSIBLE (object));
+}
+
+static void
+credentials_prompter_dispose (GObject *object)
+{
+ ECredentialsPrompter *prompter = E_CREDENTIALS_PROMPTER (object);
+ GHashTableIter iter;
+ gpointer key, value;
+
+ if (prompter->priv->cancellable) {
+ g_cancellable_cancel (prompter->priv->cancellable);
+ g_clear_object (&prompter->priv->cancellable);
+ }
+
+ if (prompter->priv->registry) {
+ g_signal_handlers_disconnect_by_data (prompter->priv->registry, prompter);
+ g_clear_object (&prompter->priv->registry);
+ }
+
+ g_rec_mutex_lock (&prompter->priv->queue_lock);
+
+ if (prompter->priv->schedule_idle_id) {
+ g_source_remove (prompter->priv->schedule_idle_id);
+ prompter->priv->schedule_idle_id = 0;
+ }
+
+ g_rec_mutex_unlock (&prompter->priv->queue_lock);
+
+ g_clear_object (&prompter->priv->provider);
+
+ g_mutex_lock (&prompter->priv->prompters_lock);
+
+ g_hash_table_iter_init (&iter, prompter->priv->prompters);
+ while (g_hash_table_iter_next (&iter, &key, &value)) {
+ ECredentialsPrompterImpl *prompter_impl = key;
+
+ g_signal_handlers_disconnect_by_func (prompter_impl, credentials_prompter_prompt_finished_cb,
prompter);
+ }
+
+ g_hash_table_remove_all (prompter->priv->prompters);
+ g_hash_table_remove_all (prompter->priv->known_prompters);
+ g_mutex_unlock (&prompter->priv->prompters_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_parent_class)->dispose (object);
+}
+
+static void
+credentials_prompter_finalize (GObject *object)
+{
+ ECredentialsPrompter *prompter = E_CREDENTIALS_PROMPTER (object);
+
+ g_hash_table_destroy (prompter->priv->prompters);
+ g_hash_table_destroy (prompter->priv->known_prompters);
+ g_mutex_clear (&prompter->priv->prompters_lock);
+
+ g_hash_table_destroy (prompter->priv->disabled_auto_prompt);
+ g_mutex_clear (&prompter->priv->disabled_auto_prompt_lock);
+
+ g_rec_mutex_clear (&prompter->priv->queue_lock);
+
+ /* Chain up to parent's method. */
+ G_OBJECT_CLASS (e_credentials_prompter_parent_class)->finalize (object);
+}
+
+static void
+e_credentials_prompter_class_init (ECredentialsPrompterClass *class)
+{
+ GObjectClass *object_class;
+
+ g_type_class_add_private (class, sizeof (ECredentialsPrompterPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->set_property = credentials_prompter_set_property;
+ object_class->get_property = credentials_prompter_get_property;
+ object_class->constructed = credentials_prompter_constructed;
+ object_class->dispose = credentials_prompter_dispose;
+ object_class->finalize = credentials_prompter_finalize;
+
+ /**
+ * ECredentialsPrompter:auto-prompt:
+ *
+ * Whether the #ECredentialsPrompter can response to credential
+ * requests automatically.
+ *
+ * Since: 3.14
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_AUTO_PROMPT,
+ g_param_spec_boolean (
+ "auto-prompt",
+ "Auto Prompt",
+ "Whether can response to credential requests automatically",
+ TRUE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * ECredentialsPrompter:registry:
+ *
+ * The #ESourceRegistry object, to whose credential requests the prompter listens.
+ *
+ * Since: 3.14
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_REGISTRY,
+ g_param_spec_object (
+ "registry",
+ "Registry",
+ "An ESourceRegistry",
+ E_TYPE_SOURCE_REGISTRY,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * ECredentialsPrompter:provider:
+ *
+ * The #ESourceCredentialsProvider object, which the prompter uses.
+ *
+ * Since: 3.14
+ **/
+ g_object_class_install_property (
+ object_class,
+ PROP_PROVIDER,
+ g_param_spec_object (
+ "provider",
+ "Provider",
+ "An ESourceCredentialsProvider",
+ E_TYPE_SOURCE_CREDENTIALS_PROVIDER,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ /**
+ * ECredentialsPrompter::get-dialog-parent:
+ * @prompter: the #ECredentialsPrompter which emitted the signal
+ *
+ * Emitted when a new dialog will be shown, to get the right parent
+ * window for it. If the result of the call is %NULL, then it tries
+ * to get the window from the default GtkApplication.
+ *
+ * Since: 3.14
+ **/
+ signals[GET_DIALOG_PARENT] = g_signal_new (
+ "get-dialog-parent",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (ECredentialsPrompterClass, get_dialog_parent),
+ credentials_prompter_get_dialog_parent_accumulator, NULL, NULL,
+ GTK_TYPE_WINDOW, 0, G_TYPE_NONE);
+
+ /* Ensure built-in credential providers implementation types */
+ g_type_ensure (E_TYPE_CREDENTIALS_PROMPTER_IMPL_PASSWORD);
+}
+
+static void
+e_credentials_prompter_init (ECredentialsPrompter *prompter)
+{
+ prompter->priv = G_TYPE_INSTANCE_GET_PRIVATE (prompter, E_TYPE_CREDENTIALS_PROMPTER,
ECredentialsPrompterPrivate);
+
+ prompter->priv->auto_prompt = TRUE;
+ prompter->priv->provider = NULL;
+ prompter->priv->cancellable = g_cancellable_new ();
+
+ g_mutex_init (&prompter->priv->prompters_lock);
+ prompter->priv->prompters = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free,
g_object_unref);
+ prompter->priv->known_prompters = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ g_mutex_init (&prompter->priv->disabled_auto_prompt_lock);
+ prompter->priv->disabled_auto_prompt = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+
+ g_rec_mutex_init (&prompter->priv->queue_lock);
+}
+
+/**
+ * e_credentials_prompter_new:
+ * @registry: an #ESourceRegistry to have the prompter listen to
+ *
+ * Creates a new #ECredentialsPrompter, which listens for credential requests
+ * from @registry.
+ *
+ * Returns: (transfer full): a new #ECredentialsPrompter
+ *
+ * Since: 3.14
+ **/
+ECredentialsPrompter *
+e_credentials_prompter_new (ESourceRegistry *registry)
+{
+ return g_object_new (E_TYPE_CREDENTIALS_PROMPTER,
+ "registry", registry,
+ NULL);
+}
+
+/**
+ * e_credentials_prompter_get_registry:
+ * @prompter: an #ECredentialsPrompter
+ *
+ * Returns an #ESourceRegistry, to which the @prompter listens.
+ *
+ * Returns: (transfer none): an #ESourceRegistry, to which the @prompter listens.
+ *
+ * Since: 3.14
+ **/
+ESourceRegistry *
+e_credentials_prompter_get_registry (ECredentialsPrompter *prompter)
+{
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
+
+ return prompter->priv->registry;
+}
+
+/**
+ * e_credentials_prompter_get_provider:
+ * @prompter: an #ECredentialsPrompter
+ *
+ * Returns an #ESourceCredentialsProvider, which the @prompter uses.
+ *
+ * Returns: (transfer none): an #ESourceCredentialsProvider, which the @prompter uses.
+ *
+ * Since: 3.14
+ **/
+ESourceCredentialsProvider *
+e_credentials_prompter_get_provider (ECredentialsPrompter *prompter)
+{
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
+ g_return_val_if_fail (prompter->priv->provider != NULL, NULL);
+
+ return prompter->priv->provider;
+}
+
+/**
+ * e_credentials_prompter_get_auto_prompt:
+ * @prompter: an #ECredentialsPrompter
+ *
+ * Returns, whether can respond to credential prompts automatically.
+ * Default value is %TRUE.
+ *
+ * This property does not influence direct calls of e_credentials_prompter_prompt().
+ *
+ * Returns: Whether can respond to credential prompts automatically.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_get_auto_prompt (ECredentialsPrompter *prompter)
+{
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+
+ return prompter->priv->auto_prompt;
+}
+
+/**
+ * e_credentials_prompter_set_auto_prompt:
+ * @prompter: an #ECredentialsPrompter
+ * @auto_prompt: new value of the auto-prompt property
+ *
+ * Sets whether can respond to credential prompts automatically. That means that
+ * whenever any ESource will ask for credentials, it'll try to provide them.
+ *
+ * Use e_credentials_prompter_set_auto_prompt_disabled_for() to influence
+ * auto-prompt per an #ESource.
+ *
+ * This property does not influence direct calls of e_credentials_prompter_prompt().
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_set_auto_prompt (ECredentialsPrompter *prompter,
+ gboolean auto_prompt)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ if ((prompter->priv->auto_prompt ? 1 : 0) == (auto_prompt ? 1 : 0))
+ return;
+
+ prompter->priv->auto_prompt = auto_prompt;
+
+ g_object_notify (G_OBJECT (prompter), "auto-prompt");
+}
+
+/**
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource
+ * @is_disabled: whether the auto-prompt should be disabled for this @source
+ *
+ * Sets whether the auto-prompt should be disabled for the given @source.
+ * All sources can be auto-prompted by default. This is a complementary
+ * value for the ECredentialsPrompter::auto-prompt property.
+ *
+ * This value does not influence direct calls of e_credentials_prompter_prompt().
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_set_auto_prompt_disabled_for (ECredentialsPrompter *prompter,
+ ESource *source,
+ gboolean is_disabled)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (e_source_get_uid (source) != NULL);
+
+ g_mutex_lock (&prompter->priv->disabled_auto_prompt_lock);
+
+ if (is_disabled)
+ g_hash_table_insert (prompter->priv->disabled_auto_prompt, g_strdup (e_source_get_uid
(source)), GINT_TO_POINTER (1));
+ else
+ g_hash_table_remove (prompter->priv->disabled_auto_prompt, e_source_get_uid (source));
+
+ g_mutex_unlock (&prompter->priv->disabled_auto_prompt_lock);
+}
+
+/**
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource
+ *
+ * Returns whether the auto-prompt is disabled for the given @source.
+ * All sources can be auto-prompted by default. This is a complementary
+ * value for the ECredentialsPrompter::auto-prompt property.
+ *
+ * This value does not influence direct calls of e_credentials_prompter_prompt().
+ *
+ * Returns: Whether the auto-prompt is disabled for the given @source
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_get_auto_prompt_disabled_for (ECredentialsPrompter *prompter,
+ ESource *source)
+{
+ gboolean is_disabled;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), TRUE);
+ g_return_val_if_fail (E_IS_SOURCE (source), TRUE);
+ g_return_val_if_fail (e_source_get_uid (source) != NULL, TRUE);
+
+ g_mutex_lock (&prompter->priv->disabled_auto_prompt_lock);
+
+ is_disabled = g_hash_table_contains (prompter->priv->disabled_auto_prompt, e_source_get_uid (source));
+
+ g_mutex_unlock (&prompter->priv->disabled_auto_prompt_lock);
+
+ return is_disabled;
+}
+
+static GtkWindow *
+credentials_prompter_guess_dialog_parent (ECredentialsPrompter *prompter)
+{
+ GApplication *app;
+
+ app = g_application_get_default ();
+ if (!app)
+ return NULL;
+
+ if (GTK_IS_APPLICATION (app))
+ return gtk_application_get_active_window (GTK_APPLICATION (app));
+
+ return NULL;
+}
+
+/**
+ * e_credentials_prompter_get_dialog_parent:
+ * @prompter: an #ECredentialsPrompter
+ *
+ * Returns a #GtkWindow, which should be used as a dialog parent. This is determined
+ * by an ECredentialsPrompter::get-dialog-parent signal emission. If there is no callback
+ * registered or the current callbacks don't have any suitable window, then there's
+ * chosen the last active window from the default GApplication, if any available.
+ *
+ * Returns: (transfer none): a #GtkWindow, to be used as a dialog parent, or %NULL.
+ *
+ * Since: 3.14
+ **/
+GtkWindow *
+e_credentials_prompter_get_dialog_parent (ECredentialsPrompter *prompter)
+{
+ GtkWindow *parent = NULL;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), NULL);
+
+ g_signal_emit (prompter, signals[GET_DIALOG_PARENT], 0, &parent);
+
+ if (!parent)
+ parent = credentials_prompter_guess_dialog_parent (prompter);
+
+ return parent;
+}
+
+/**
+ * e_credentials_prompter_register_impl:
+ * @prompter: an #ECredentialsPrompter
+ * @authentication_method: (allow none): an authentication method to registr @prompter_impl for; or %NULL
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ *
+ * Registers a prompter implementation for a given authentication method. If there is
+ * registered a prompter for the same @authentication_method, then the function does
+ * nothing, otherwise it adds its own reference on the @prompter_impl, and uses it
+ * for that authentication method. One @prompter_impl can be registered for multiple
+ * authentication methods.
+ *
+ * A special value %NULL can be used for the @authentication_method, which means
+ * a default credentials prompter, that is to be used when there is no prompter
+ * registered for the exact authentication method.
+ *
+ * Returns: %TRUE on success, %FALSE on failure or when there was another prompter
+ * implementation registered for the given authentication method.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_register_impl (ECredentialsPrompter *prompter,
+ const gchar *authentication_method,
+ ECredentialsPrompterImpl *prompter_impl)
+{
+ guint known_prompters;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER_IMPL (prompter_impl), FALSE);
+
+ if (!authentication_method)
+ authentication_method = "";
+
+ g_mutex_lock (&prompter->priv->prompters_lock);
+
+ if (g_hash_table_lookup (prompter->priv->prompters, authentication_method) != NULL) {
+ g_mutex_unlock (&prompter->priv->prompters_lock);
+ return FALSE;
+ }
+
+ g_hash_table_insert (prompter->priv->prompters, g_strdup (authentication_method), g_object_ref
(prompter_impl));
+
+ known_prompters = GPOINTER_TO_UINT (g_hash_table_lookup (prompter->priv->known_prompters,
prompter_impl));
+ if (!known_prompters) {
+ g_signal_connect (prompter_impl, "prompt-finished", G_CALLBACK
(credentials_prompter_prompt_finished_cb), prompter);
+ }
+ g_hash_table_insert (prompter->priv->known_prompters, prompter_impl, GUINT_TO_POINTER
(known_prompters + 1));
+
+ g_mutex_unlock (&prompter->priv->prompters_lock);
+
+ return TRUE;
+}
+
+/**
+ * e_credentials_prompter_unregister_impl:
+ * @prompter: an #ECredentialsPrompter
+ * @authentication_method: (allow none): an authentication method to registr @prompter_impl for; or %NULL
+ * @prompter_impl: an #ECredentialsPrompterImpl
+ *
+ * Unregisters previously registered @prompter_impl for the given @autnetication_method with
+ * e_credentials_prompter_register_impl(). Function does nothing, if no such authentication
+ * method is registered or if it has set a different prompter implementation.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_unregister_impl (ECredentialsPrompter *prompter,
+ const gchar *authentication_method,
+ ECredentialsPrompterImpl *prompter_impl)
+{
+ ECredentialsPrompterImpl *current_prompter_impl;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ if (!authentication_method)
+ authentication_method = "";
+
+ g_mutex_lock (&prompter->priv->prompters_lock);
+
+ current_prompter_impl = g_hash_table_lookup (prompter->priv->prompters, authentication_method);
+ if (current_prompter_impl == prompter_impl) {
+ guint known_prompters;
+
+ known_prompters = GPOINTER_TO_UINT (g_hash_table_lookup (prompter->priv->known_prompters,
prompter_impl));
+ if (known_prompters == 1) {
+ g_signal_handlers_disconnect_by_func (prompter_impl,
credentials_prompter_prompt_finished_cb, prompter);
+ g_hash_table_remove (prompter->priv->known_prompters, prompter_impl);
+ } else {
+ known_prompters--;
+ g_hash_table_insert (prompter->priv->known_prompters, prompter_impl, GUINT_TO_POINTER
(known_prompters + 1));
+ }
+
+ g_hash_table_remove (prompter->priv->prompters, authentication_method);
+ }
+
+ g_mutex_unlock (&prompter->priv->prompters_lock);
+}
+
+static void
+credentials_prompter_get_last_credentials_required_arguments_cb (GObject *source_object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ECredentialsPrompter *prompter = user_data;
+ ESource *source;
+ ESourceCredentialsReason reason = E_SOURCE_CREDENTIALS_REASON_UNKNOWN;
+ gchar *certificate_pem = NULL;
+ GTlsCertificateFlags certificate_errors = 0;
+ GError *op_error = NULL;
+ GError *error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source_object));
+
+ source = E_SOURCE (source_object);
+
+ if (!e_source_get_last_credentials_required_arguments_finish (source, result,
+ &reason, &certificate_pem, &certificate_errors, &op_error, &error)) {
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+ g_warning ("%s: Failed to get last credential values: %s", G_STRFUNC, error ?
error->message : "Unknown error");
+ }
+
+ g_clear_error (&error);
+ return;
+ }
+
+ /* Can check only now, when know the operation was not cancelled and the prompter freed. */
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ /* Check once again, as this was called asynchronously and anything could change meanwhile. */
+ if (e_source_get_connection_status (source) == E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS) {
+ credentials_prompter_credentials_required_cb (prompter->priv->registry,
+ source, reason, certificate_pem, certificate_errors, op_error, prompter);
+ }
+
+ g_free (certificate_pem);
+ g_clear_error (&op_error);
+}
+
+/**
+ * e_credentials_prompter_process_awaiting_credentials:
+ * @prompter: an #ECredentialsPrompter
+ *
+ * Process all enabled sources with connection state #E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS,
+ * like if they just asked for its credentials for the first time.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_process_awaiting_credentials (ECredentialsPrompter *prompter)
+{
+ GList *sources, *link;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+
+ sources = e_source_registry_list_enabled (prompter->priv->registry, NULL);
+ for (link = sources; link; link = g_list_next (link)) {
+ ESource *source = link->data;
+
+ if (!source)
+ continue;
+
+ if (e_source_get_connection_status (source) ==
E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS) {
+ /* Check what failed the last time */
+ e_credentials_prompter_process_source (prompter, source);
+ }
+ }
+
+ g_list_free_full (sources, g_object_unref);
+}
+
+/**
+ * e_credentials_prompter_process_source:
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource
+ *
+ * Continues a credential prompt for @source. Returns, whether anything wil be done.
+ * The %FALSE either means that the @source<!-- -->'s connection status is not
+ * the %E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS.
+ *
+ * Returns: Whether continues with the credentials prompt.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_process_source (ECredentialsPrompter *prompter,
+ ESource *source)
+{
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS)
+ return FALSE;
+
+ e_source_get_last_credentials_required_arguments (source, prompter->priv->cancellable,
+ credentials_prompter_get_last_credentials_required_arguments_cb, prompter);
+
+ return TRUE;
+}
+
+/**
+ * e_credentials_prompter_prompt:
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource, which prompt the credentials for
+ * @error_text: (allow none): Additional error text to show to a user, or %NULL
+ * @flags: a bit-or of #ECredentialsPrompterPromptFlags
+ * @callback: (allow none): a callback to call when the credentials are ready, or %NULL
+ * @user_data: user data passed into @callback
+ *
+ * Asks the @prompter to prompt for credentials, which are returned
+ * to the caller through @callback, when available.The @flags are ignored,
+ * when the @callback is %NULL; the credentials are passed to the @source
+ * with e_source_invoke_authenticate() directly, in this case.
+ * Call e_credentials_prompter_prompt_finish() in @callback to get to
+ * the provided credentials.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_prompt (ECredentialsPrompter *prompter,
+ ESource *source,
+ const gchar *error_text,
+ ECredentialsPrompterPromptFlags flags,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ CredentialsPromptData *prompt_data;
+
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ prompt_data = g_new0 (CredentialsPromptData, 1);
+ prompt_data->source = g_object_ref (source);
+ prompt_data->error_text = g_strdup (error_text);
+ prompt_data->flags = flags;
+ prompt_data->async_result = callback ? g_simple_async_result_new (G_OBJECT (prompter),
+ callback, user_data, e_credentials_prompter_prompt) : NULL;
+
+ /* Just it can be shown in the UI as a prefilled value and the right source (collection) is used. */
+ credentials_prompter_lookup_source_details (source, prompter,
+ credentials_prompter_lookup_source_details_before_prompt_cb, prompt_data);
+}
+
+/**
+ * e_credentials_prompter_prompt_finish:
+ * @prompter: an #ECredentialsPrompter
+ * @result: a #GAsyncResult
+ * @out_source: (transfer full): (allow none): optionally set to an #ESource, on which the prompt was
started; can be %NULL
+ * @out_credentials: (transfer full): set to an #ENamedParameters with provied credentials
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes a credentials prompt previously started with e_credentials_prompter_prompt().
+ * The @out_source will have set a referenced #ESource, for which the prompt
+ * was started. Unref it, when no longer needed. Similarly the @out_credentials
+ * will have set a newly allocated #ENamedParameters structure with provided credentials,
+ * which should be freed with e_named_credentials_free() when no longer needed.
+ * Both output arguments will be set to %NULL on error and %FALSE will be returned.
+ *
+ * Returns: %TRUE on success, %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_prompt_finish (ECredentialsPrompter *prompter,
+ GAsyncResult *result,
+ ESource **out_source,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ CredentialsResultData *data;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (g_simple_async_result_get_source_tag (G_SIMPLE_ASYNC_RESULT (result))
+ == e_credentials_prompter_prompt, FALSE);
+ g_return_val_if_fail (out_credentials, FALSE);
+
+ if (out_source)
+ *out_source = NULL;
+ *out_credentials = NULL;
+
+ if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error))
+ return FALSE;
+
+ data = g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (result));
+ g_return_val_if_fail (data != NULL, FALSE);
+
+ if (data->credentials) {
+ if (out_source)
+ *out_source = g_object_ref (data->source);
+ *out_credentials = e_named_parameters_new_clone (data->credentials);
+ } else {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, _("Credentials prompt was
cancelled"));
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/**
+ * e_credentials_prompter_complete_prompt_call:
+ * @prompter: an #ECredentialsPrompter
+ * @async_result: a #GSimpleAsyncResult
+ * @source: an #ESource, on which the prompt was started
+ * @credentials: (allow none): credentials, as provided by a user, on %NULL, when the prompt was cancelled
+ * @error: (allow none): a resulting #GError, or %NULL
+ *
+ * Completes an ongoing credentials prompt on idle, by finishing the @async_result.
+ * This function is meant to be used by an #ECredentialsPrompterImpl implementation.
+ * To actually finish the credentials prompt previously started with
+ * e_credentials_prompter_prompt(), the e_credentials_prompter_prompt_finish() should
+ * be called from the provided callback.
+ *
+ * Using %NULL @credentials will result in a G_IO_ERROR_CANCELLED error, if
+ * no other @error is provided.
+ *
+ * Since: 3.14
+ **/
+void
+e_credentials_prompter_complete_prompt_call (ECredentialsPrompter *prompter,
+ GSimpleAsyncResult *async_result,
+ ESource *source,
+ const ENamedParameters *credentials,
+ const GError *error)
+{
+ g_return_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter));
+ g_return_if_fail (G_IS_SIMPLE_ASYNC_RESULT (async_result));
+ g_return_if_fail (g_simple_async_result_get_source_tag (async_result) ==
e_credentials_prompter_prompt);
+ g_return_if_fail (source == NULL || E_IS_SOURCE (source));
+ if (credentials)
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ if (error) {
+ g_simple_async_result_set_from_error (async_result, error);
+ } else if (!credentials) {
+ g_simple_async_result_set_error (async_result, G_IO_ERROR, G_IO_ERROR_CANCELLED,
_("Credentials prompt was cancelled"));
+ } else {
+ CredentialsResultData *result;
+
+ result = g_new0 (CredentialsResultData, 1);
+ result->source = g_object_ref (source);
+ result->credentials = e_named_parameters_new_clone (credentials);
+
+ g_simple_async_result_set_op_res_gpointer (async_result, result,
credentials_result_data_free);
+ }
+
+ g_simple_async_result_complete_in_idle (async_result);
+}
+
+static gboolean
+credentials_prompter_prompt_sync (ECredentialsPrompter *prompter,
+ ESource *source,
+ gboolean is_retry,
+ ECredentialsPrompterPromptFlags *flags,
+ const gchar *error_text,
+ ENamedParameters **out_credentials,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean res = FALSE;
+ ESourceCredentialsProvider *credentials_provider;
+ ENamedParameters *credentials = NULL;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (flags != NULL, FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ credentials_provider = e_credentials_prompter_get_provider (prompter);
+
+ if (!is_retry) {
+ ESource *cred_source;
+ GError *local_error = NULL;
+
+ cred_source = e_source_credentials_provider_ref_credentials_source (credentials_provider,
source);
+
+ if (e_source_credentials_provider_lookup_sync (credentials_provider, cred_source ?
cred_source : source,
+ cancellable, &credentials, &local_error)) {
+ res = TRUE;
+ } else if (!g_cancellable_is_cancelled (cancellable)) {
+ /* To prompt for the password directly */
+ is_retry = TRUE;
+ g_clear_error (&local_error);
+ } else {
+ g_propagate_error (error, local_error);
+ }
+
+ g_clear_object (&cred_source);
+ }
+
+ if (is_retry) {
+ EAsyncClosure *closure;
+ GAsyncResult *result;
+
+ *flags = (*flags) & (~E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS);
+
+ closure = e_async_closure_new ();
+
+ e_credentials_prompter_prompt (prompter, source, error_text, *flags,
+ e_async_closure_callback, closure);
+
+ result = e_async_closure_wait (closure);
+
+ if (e_credentials_prompter_prompt_finish (prompter, result, NULL, &credentials, error)) {
+ res = TRUE;
+ }
+
+ e_async_closure_free (closure);
+ }
+
+ if (res && credentials)
+ *out_credentials = e_named_parameters_new_clone (credentials);
+
+ e_named_parameters_free (credentials);
+
+ return res;
+}
+
+/**
+ * e_credentials_prompter_loop_prompt_sync:
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource to be prompted credentials for
+ * @flags: a bit-or of #ECredentialsPrompterPromptFlags initial flags
+ * @func: an #ECredentialsPrompterLoopPromptFunc user function to call to check provided credentials
+ * @user_data: user data to pass to @func
+ * @cancellable: (allow none): an optional #GCancellable, or %NULL
+ * @error: (allow none): a #GError, to store any errors to, or %NULL
+ *
+ * Runs a credentials prompt loop for @source, as long as the @func doesn't
+ * indicate that the provided credentials can be used to successfully
+ * authenticate against @source<!-- -->'s server, or that the @func
+ * returns %FALSE. The loop is also teminated when a used cancels
+ * the credentials prompt or the @cancellable is cancelled, though
+ * not sooner than the credentials prompt dialog is closed.
+ *
+ * Note: The function doesn't return until the loop is terminated, either
+ * successfully or unsuccessfully. The function can be called from any
+ * thread, though a dedicated thread is preferred.
+ *
+ * Returns: %TRUE, when the credentials were provided sucessfully and they
+ * can be used to authenticate the @source; %FALSE otherwise.
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_credentials_prompter_loop_prompt_sync (ECredentialsPrompter *prompter,
+ ESource *source,
+ ECredentialsPrompterPromptFlags flags,
+ ECredentialsPrompterLoopPromptFunc func,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gboolean is_retry, authenticated;
+ ENamedParameters *credentials = NULL;
+
+ g_return_val_if_fail (E_IS_CREDENTIALS_PROMPTER (prompter), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (func != NULL, FALSE);
+
+ is_retry = FALSE;
+ authenticated = FALSE;
+
+ while (!authenticated && !g_cancellable_is_cancelled (cancellable)) {
+ GError *local_error = NULL;
+
+ e_named_parameters_free (credentials);
+ credentials = NULL;
+
+ if (!credentials_prompter_prompt_sync (prompter, source, is_retry, &flags, local_error ?
local_error->message : NULL,
+ &credentials, cancellable, error))
+ break;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ break;
+
+ g_clear_error (&local_error);
+
+ if (!func (prompter, source, credentials, &authenticated, user_data, cancellable,
&local_error)) {
+ if (local_error)
+ g_propagate_error (error, local_error);
+ break;
+ }
+
+ is_retry = TRUE;
+ }
+
+ e_named_parameters_free (credentials);
+
+ return authenticated;
+}
diff --git a/libedataserverui/e-credentials-prompter.h b/libedataserverui/e-credentials-prompter.h
new file mode 100644
index 0000000..5fcbef9
--- /dev/null
+++ b/libedataserverui/e-credentials-prompter.h
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_CREDENTIALS_PROMPTER_H
+#define E_CREDENTIALS_PROMPTER_H
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+#include <libedataserverui/e-credentials-prompter-impl.h>
+
+/* Standard GObject macros */
+#define E_TYPE_CREDENTIALS_PROMPTER \
+ (e_credentials_prompter_get_type ())
+#define E_CREDENTIALS_PROMPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER, ECredentialsPrompter))
+#define E_CREDENTIALS_PROMPTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_CAST \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER, ECredentialsPrompterClass))
+#define E_IS_CREDENTIALS_PROMPTER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER))
+#define E_IS_CREDENTIALS_PROMPTER_CLASS(cls) \
+ (G_TYPE_CHECK_CLASS_TYPE \
+ ((cls), E_TYPE_CREDENTIALS_PROMPTER))
+#define E_CREDENTIALS_PROMPTER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS \
+ ((obj), E_TYPE_CREDENTIALS_PROMPTER, ECredentialsPrompterClass))
+
+G_BEGIN_DECLS
+
+typedef struct _ECredentialsPrompter ECredentialsPrompter;
+typedef struct _ECredentialsPrompterClass ECredentialsPrompterClass;
+typedef struct _ECredentialsPrompterPrivate ECredentialsPrompterPrivate;
+
+/**
+ * ECredentialsPrompterPromptFlags:
+ * @E_CREDENTIALS_PROMPTER_PROMPT_FLAG_NONE:
+ * No flag is set.
+ * @E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE:
+ * If set, any source changes during the credentials prompts, like
+ * the "remember-password" or user name changes, will be automatically
+ * stored in the source (written on the disk).
+ * @E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS:
+ * If set, the stored credentials will be returned first. If there are no
+ * credentials saved, then the user will be asked. Any credentials
+ * reprompt should not have set this flag.
+ *
+ * An #ECredentialsPrompter prompt flags, influencing behaviour
+ * of the e_credentials_prompter_prompt().
+ *
+ * Since: 3.14
+ **/
+typedef enum {
+ E_CREDENTIALS_PROMPTER_PROMPT_FLAG_NONE = 0,
+ E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_SOURCE_SAVE = 1 << 0,
+ E_CREDENTIALS_PROMPTER_PROMPT_FLAG_ALLOW_STORED_CREDENTIALS = 1 << 1
+} ECredentialsPrompterPromptFlags;
+
+/**
+ * ECredentialsPrompterLoopPromptFunc:
+ * @prompter: an #ECredentialsPrompter
+ * @source: an #ESource, as passed to e_credentials_prompter_loop_prompt_sync()
+ * @credentials: an #ENamedParameters with provided credentials
+ * @out_authenticated: (out): set to %TRUE, when the authentication was successful
+ * @user_data: user data, as passed to e_credentials_prompter_loop_prompt_sync()
+ * @cancellable: a #GCancellable, as passed to e_credentials_prompter_loop_prompt_sync()
+ * @error: a #GError, to get an error, or %NULL
+ *
+ * Returns: %TRUE to continue the loop (reprompt credentials), unless @authenticated is
+ * also set to %TRUE, or %FALSE on error, as an indication that the loop should
+ * be terminated.
+ **/
+typedef gboolean (*ECredentialsPrompterLoopPromptFunc) (ECredentialsPrompter *prompter,
+ ESource *source,
+ const ENamedParameters *credentials,
+ gboolean *out_authenticated,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+/**
+ * ECredentialsPrompter:
+ *
+ * Contains only private data that should be read and manipulated using the
+ * functions below.
+ *
+ * Since: 3.14
+ **/
+struct _ECredentialsPrompter {
+ GObject parent;
+ ECredentialsPrompterPrivate *priv;
+};
+
+struct _ECredentialsPrompterClass {
+ GObjectClass parent_class;
+
+ /* Signals */
+ GtkWindow * (*get_dialog_parent) (ECredentialsPrompter *prompter);
+};
+
+GType e_credentials_prompter_get_type (void) G_GNUC_CONST;
+ECredentialsPrompter *
+ e_credentials_prompter_new (ESourceRegistry *registry);
+ESourceRegistry *
+ e_credentials_prompter_get_registry
+ (ECredentialsPrompter *prompter);
+ESourceCredentialsProvider *
+ e_credentials_prompter_get_provider
+ (ECredentialsPrompter *prompter);
+gboolean e_credentials_prompter_get_auto_prompt
+ (ECredentialsPrompter *prompter);
+void e_credentials_prompter_set_auto_prompt
+ (ECredentialsPrompter *prompter,
+ gboolean auto_prompt);
+void e_credentials_prompter_set_auto_prompt_disabled_for
+ (ECredentialsPrompter *prompter,
+ ESource *source,
+ gboolean is_disabled);
+gboolean e_credentials_prompter_get_auto_prompt_disabled_for
+ (ECredentialsPrompter *prompter,
+ ESource *source);
+GtkWindow * e_credentials_prompter_get_dialog_parent
+ (ECredentialsPrompter *prompter);
+gboolean e_credentials_prompter_register_impl
+ (ECredentialsPrompter *prompter,
+ const gchar *authentication_method,
+ ECredentialsPrompterImpl *prompter_impl);
+void e_credentials_prompter_unregister_impl
+ (ECredentialsPrompter *prompter,
+ const gchar *authentication_method,
+ ECredentialsPrompterImpl *prompter_impl);
+void e_credentials_prompter_process_awaiting_credentials
+ (ECredentialsPrompter *prompter);
+gboolean e_credentials_prompter_process_source
+ (ECredentialsPrompter *prompter,
+ ESource *source);
+void e_credentials_prompter_prompt (ECredentialsPrompter *prompter,
+ ESource *source,
+ const gchar *error_text,
+ ECredentialsPrompterPromptFlags flags,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_credentials_prompter_prompt_finish
+ (ECredentialsPrompter *prompter,
+ GAsyncResult *result,
+ ESource **out_source,
+ ENamedParameters **out_credentials,
+ GError **error);
+void e_credentials_prompter_complete_prompt_call
+ (ECredentialsPrompter *prompter,
+ GSimpleAsyncResult *async_result,
+ ESource *source,
+ const ENamedParameters *credentials,
+ const GError *error);
+gboolean e_credentials_prompter_loop_prompt_sync
+ (ECredentialsPrompter *prompter,
+ ESource *source,
+ ECredentialsPrompterPromptFlags flags,
+ ECredentialsPrompterLoopPromptFunc func,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+G_END_DECLS
+
+#endif /* E_CREDENTIALS_PROMPTER_H */
diff --git a/libedataserverui/e-trust-prompt.c b/libedataserverui/e-trust-prompt.c
new file mode 100644
index 0000000..8c1772a
--- /dev/null
+++ b/libedataserverui/e-trust-prompt.c
@@ -0,0 +1,672 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib.h>
+#include <glib/gi18n-lib.h>
+
+#define GCR_API_SUBJECT_TO_CHANGE
+#include <gcr/gcr.h>
+#undef GCR_API_SUBJECT_TO_CHANGE
+
+#include <camel/camel.h>
+#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
+
+#include "e-trust-prompt.h"
+
+static void
+trust_prompt_add_info_line (GtkGrid *grid,
+ const gchar *label_text,
+ const gchar *value_text,
+ gboolean ellipsize,
+ gboolean wrap,
+ gboolean use_bold,
+ gint *at_row)
+{
+ GtkWidget *widget;
+ PangoAttribute *attr;
+ PangoAttrList *bold;
+
+ g_return_if_fail (grid != NULL);
+ g_return_if_fail (label_text != NULL);
+ g_return_if_fail (at_row != NULL);
+
+ if (!value_text || !*value_text)
+ return;
+
+ bold = pango_attr_list_new ();
+ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD);
+ pango_attr_list_insert (bold, attr);
+
+ widget = gtk_label_new (label_text);
+ gtk_misc_set_padding (GTK_MISC (widget), 0, 0);
+ gtk_misc_set_alignment (GTK_MISC (widget), 0.0, 0.0);
+
+ gtk_grid_attach (grid, widget, 1, *at_row, 1, 1);
+
+ widget = gtk_label_new (value_text);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), wrap);
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "justify", GTK_JUSTIFY_LEFT,
+ "attributes", use_bold ? bold : NULL,
+ "selectable", TRUE,
+ "ellipsize", ellipsize ? PANGO_ELLIPSIZE_END : PANGO_ELLIPSIZE_NONE,
+ "width-chars", 60,
+ "max-width-chars", 80,
+ "xalign", 0.0,
+ "yalign", 0.0,
+ NULL);
+
+ gtk_grid_attach (grid, widget, 2, *at_row, 1, 1);
+
+ *at_row = (*at_row) + 1;
+
+ pango_attr_list_unref (bold);
+}
+
+static ETrustPromptResponse
+trust_prompt_show (GtkWindow *parent,
+ const gchar *source_extension,
+ const gchar *source_display_name,
+ const gchar *host,
+ const gchar *error_text,
+ GcrParsed *parsed,
+ const gchar *reason,
+ void (* dialog_ready_cb) (GtkDialog *dialog, gpointer user_data),
+ gpointer user_data)
+{
+ ETrustPromptResponse response;
+ GcrCertificateWidget *certificate_widget;
+ GcrCertificate *certificate;
+ GckAttributes *attributes;
+ GtkWidget *dialog, *widget;
+ GtkGrid *grid;
+ const guchar *data;
+ gchar *bhost, *tmp;
+ gsize length;
+ gint row = 0;
+
+ dialog = gtk_dialog_new_with_buttons (
+ _("Certificate trust..."), parent, GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
+ _("_Cancel"), GTK_RESPONSE_CANCEL,
+ _("_Reject"), GTK_RESPONSE_REJECT,
+ _("Accept _Temporarily"), GTK_RESPONSE_YES,
+ _("_Accept Permanently"), GTK_RESPONSE_ACCEPT,
+ NULL);
+
+ widget = gtk_dialog_get_content_area (GTK_DIALOG (dialog));
+
+ gtk_container_set_border_width (GTK_CONTAINER (dialog), 5);
+
+ grid = g_object_new (
+ GTK_TYPE_GRID,
+ "orientation", GTK_ORIENTATION_HORIZONTAL,
+ "row-homogeneous", FALSE,
+ "row-spacing", 6,
+ "column-homogeneous", FALSE,
+ "column-spacing", 12,
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "vexpand", TRUE,
+ "valign", GTK_ALIGN_FILL,
+ NULL);
+
+ gtk_container_set_border_width (GTK_CONTAINER (grid), 5);
+ gtk_container_add (GTK_CONTAINER (widget), GTK_WIDGET (grid));
+
+ widget = gtk_image_new_from_icon_name ("dialog-warning", GTK_ICON_SIZE_DIALOG);
+ g_object_set (
+ G_OBJECT (widget),
+ "vexpand", FALSE,
+ "valign", GTK_ALIGN_START,
+ NULL);
+ gtk_grid_attach (grid, widget, 0, row, 1, 3);
+
+ tmp = g_markup_escape_text (host, -1);
+ bhost = g_strconcat ("<b>", tmp, "</b>", NULL);
+ g_free (tmp);
+ tmp = NULL;
+ if (source_extension && source_display_name) {
+ gchar *bsource_display_name = g_strconcat ("<b>", source_display_name, "</b>", NULL);
+
+ if (g_str_equal (source_extension, E_SOURCE_EXTENSION_ADDRESS_BOOK)) {
+ tmp = g_strdup_printf (
+ "An address book '%s' cannot connect, because an SSL certificate for '%s' is
not trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else if (g_str_equal (source_extension, E_SOURCE_EXTENSION_CALENDAR)) {
+ tmp = g_strdup_printf (
+ "A calendar '%s' cannot connect, because an SSL certificate for '%s' is not
trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else if (g_str_equal (source_extension, E_SOURCE_EXTENSION_MEMO_LIST)) {
+ tmp = g_strdup_printf (
+ "A memo list '%s' cannot connect, because an SSL certificate for '%s' is not
trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else if (g_str_equal (source_extension, E_SOURCE_EXTENSION_TASK_LIST)) {
+ tmp = g_strdup_printf (
+ "A task list '%s' cannot connect, because an SSL certificate for '%s' is not
trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else if (g_str_equal (source_extension, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+ tmp = g_strdup_printf (
+ "A mail account '%s' cannot connect, because an SSL certificate for '%s' is
not trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else if (g_str_equal (source_extension, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+ tmp = g_strdup_printf (
+ "A mail transport '%s' cannot connect, because an SSL certificate for '%s' is
not trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ } else {
+ tmp = g_strdup_printf (
+ "An account '%s' cannot connect, because an SSL certificate for '%s' is not
trusted. Do you wish to accept it?",
+ bsource_display_name, bhost);
+ }
+
+ g_free (bsource_display_name);
+ }
+ if (!tmp)
+ tmp = g_strdup_printf (_("SSL certificate for '%s' is not trusted. Do you wish to accept
it?"), bhost);
+ g_free (bhost);
+
+ widget = gtk_label_new (NULL);
+ gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
+ gtk_label_set_markup (GTK_LABEL (widget), tmp);
+ g_object_set (
+ G_OBJECT (widget),
+ "hexpand", TRUE,
+ "halign", GTK_ALIGN_FILL,
+ "valign", GTK_ALIGN_CENTER,
+ "width-chars", 60,
+ "max-width-chars", 80,
+ "xalign", 0.0,
+ "yalign", 0.0,
+ NULL);
+
+ g_free (tmp);
+
+ gtk_grid_attach (grid, widget, 1, row, 2, 1);
+ row++;
+
+ trust_prompt_add_info_line (grid, _("Reason:"), reason, FALSE, FALSE, TRUE, &row);
+
+ if (error_text)
+ trust_prompt_add_info_line (grid, _("Detailed error:"), error_text, FALSE, TRUE, FALSE, &row);
+
+ data = gcr_parsed_get_data (parsed, &length);
+ attributes = gcr_parsed_get_attributes (parsed);
+
+ certificate = gcr_simple_certificate_new (data, length);
+
+ certificate_widget = gcr_certificate_widget_new (certificate);
+ gcr_certificate_widget_set_attributes (certificate_widget, attributes);
+
+ widget = GTK_WIDGET (certificate_widget);
+ gtk_grid_attach (grid, widget, 1, row, 2, 1);
+ gtk_widget_show (widget);
+
+ g_clear_object (&certificate);
+
+ gtk_widget_show_all (GTK_WIDGET (grid));
+
+ if (dialog_ready_cb)
+ dialog_ready_cb (GTK_DIALOG (dialog), user_data);
+
+ switch (gtk_dialog_run (GTK_DIALOG (dialog))) {
+ case GTK_RESPONSE_REJECT:
+ response = E_TRUST_PROMPT_RESPONSE_REJECT;
+ break;
+ case GTK_RESPONSE_ACCEPT:
+ response = E_TRUST_PROMPT_RESPONSE_ACCEPT;
+ break;
+ case GTK_RESPONSE_YES:
+ response = E_TRUST_PROMPT_RESPONSE_ACCEPT_TEMPORARILY;
+ break;
+ default:
+ response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+ break;
+ }
+
+ gtk_widget_destroy (dialog);
+
+ return response;
+}
+
+/**
+ * e_trust_prompt_describe_certificate_errors:
+ * @flags: a #GTlsCertificateFlags to describe
+ *
+ * Converts @flags into a localized text description of the set bits, one
+ * bit description per line. If no bit is set, then an empty string is
+ * returned.
+ *
+ * Returns: (transfer-full): A newly allocated string with text description
+ * of @flags. Free the returned pointer with g_free() when no longer needed.
+ *
+ * Since: 3.14
+ **/
+gchar *
+e_trust_prompt_describe_certificate_errors (GTlsCertificateFlags flags)
+{
+ struct _convert_table {
+ GTlsCertificateFlags flag;
+ const gchar *description;
+ } convert_table[] = {
+ { G_TLS_CERTIFICATE_UNKNOWN_CA,
+ N_("The signing certificate authority is not known.") },
+ { G_TLS_CERTIFICATE_BAD_IDENTITY,
+ N_("The certificate does not match the expected identity of the site that it was retrieved
from.") },
+ { G_TLS_CERTIFICATE_NOT_ACTIVATED,
+ N_("The certificate's activation time is still in the future.") },
+ { G_TLS_CERTIFICATE_EXPIRED,
+ N_("The certificate has expired.") },
+ { G_TLS_CERTIFICATE_REVOKED,
+ N_("The certificate has been revoked according to the connection's certificate revocation
list.") },
+ { G_TLS_CERTIFICATE_INSECURE,
+ N_("The certificate's algorithm is considered insecure.") }
+ };
+
+ GString *reason = g_string_new ("");
+ gint ii;
+
+ for (ii = 0; ii < G_N_ELEMENTS (convert_table); ii++) {
+ if ((flags & convert_table[ii].flag) != 0) {
+ if (reason->len > 0)
+ g_string_append (reason, "\n");
+
+ g_string_append (reason, _(convert_table[ii].description));
+ }
+ }
+
+ return g_string_free (reason, FALSE);
+}
+
+static void
+trust_prompt_parser_parsed_cb (GcrParser *parser,
+ GcrParsed **out_parsed)
+{
+ GcrParsed *parsed;
+
+ parsed = gcr_parser_get_parsed (parser);
+ g_return_if_fail (parsed != NULL);
+
+ *out_parsed = gcr_parsed_ref (parsed);
+}
+
+static ETrustPromptResponse
+e_trust_prompt_run_with_dialog_ready_callback (GtkWindow *parent,
+ const gchar *source_extension,
+ const gchar *source_display_name,
+ const gchar *host,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const gchar *error_text,
+ void (* dialog_ready_cb) (GtkDialog *dialog, gpointer
user_data),
+ gpointer user_data)
+{
+ ETrustPromptResponse response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+ GcrParser *parser;
+ GcrParsed *parsed = NULL;
+ GError *local_error = NULL;
+
+ if (parent)
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+ g_return_val_if_fail (host != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+ g_return_val_if_fail (certificate_pem != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+ /* Continue even if PKCS#11 module registration fails.
+ * Certificate details won't display correctly but the
+ * user can still respond to the prompt. */
+ gcr_pkcs11_initialize (NULL, &local_error);
+ if (local_error != NULL) {
+ g_warning ("%s: gcr_pkcs11_initialize() call failed: %s", G_STRFUNC, local_error->message);
+ g_clear_error (&local_error);
+ }
+
+ parser = gcr_parser_new ();
+
+ g_signal_connect (
+ parser, "parsed",
+ G_CALLBACK (trust_prompt_parser_parsed_cb), &parsed);
+
+ gcr_parser_parse_data (parser, (const guchar *) certificate_pem, strlen (certificate_pem),
&local_error);
+
+ g_object_unref (parser);
+
+ /* Sanity check. */
+ g_warn_if_fail (
+ ((parsed != NULL) && (local_error == NULL)) ||
+ ((parsed == NULL) && (local_error != NULL)));
+
+ if (parsed != NULL) {
+ gchar *reason;
+
+ reason = e_trust_prompt_describe_certificate_errors (certificate_errors);
+
+ response = trust_prompt_show (parent, source_extension, source_display_name, host,
error_text, parsed, reason, dialog_ready_cb, user_data);
+
+ gcr_parsed_unref (parsed);
+ g_free (reason);
+ }
+
+ g_clear_error (&local_error);
+
+ return response;
+}
+
+/**
+ * e_trust_prompt_run_modal:
+ * @parent: A #GtkWindow to use as a parent for the trust prompt dialog
+ * @source_extension: (allow none): an #ESource extension, to identify a kind of the source; or %NULL
+ * @source_display_name: (allow none): an #ESource display name, to identify what prompts; or %NULL
+ * @host: a host name to which the certificate belongs
+ * @certificate_pem: a PEM-encoded certificate for which to show the trust prompt
+ * @certificate_errors: errors of the @certificate_pem
+ * @error_text: (allow none): an optional error text to show in the dialog; can be %NULL
+ *
+ * Runs modal (doesn't return until the dialog is closed) a trust prompt dialog,
+ * it is a prompt whether a user wants to accept or reject the @certificate_pem
+ * for the @host due to the @certificate_errors errors.
+ *
+ * The pair @source_extension and @source_display_name influences the trust prompt message.
+ * If both are set, then the message also contains which source failed to connect according
+ * to these two arguments.
+ *
+ * The dialog can contain a custom error text, passed in as @error_text.
+ * The error might be a detailed error string returned by the server. If set,
+ * it is prefixed with "Detailed error:" string.
+ *
+ * Returns: A code of the user's choice. The #E_TRUST_PROMPT_RESPONSE_UNKNOWN
+ * is returned, when the user cancelled the trust prompt dialog.
+ *
+ * Since: 3.14
+ **/
+ETrustPromptResponse
+e_trust_prompt_run_modal (GtkWindow *parent,
+ const gchar *source_extension,
+ const gchar *source_display_name,
+ const gchar *host,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const gchar *error_text)
+{
+ if (parent)
+ g_return_val_if_fail (GTK_IS_WINDOW (parent), E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+ g_return_val_if_fail (host != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+ g_return_val_if_fail (certificate_pem != NULL, E_TRUST_PROMPT_RESPONSE_UNKNOWN);
+
+ return e_trust_prompt_run_with_dialog_ready_callback (parent, source_extension, source_display_name,
host,
+ certificate_pem, certificate_errors, error_text, NULL, NULL);
+}
+
+static void
+source_connection_status_changed_cb (ESource *source,
+ GParamSpec *param,
+ GtkDialog *dialog)
+{
+ g_return_if_fail (GTK_IS_DIALOG (dialog));
+
+ /* Do not close the prompt when the source is still waiting for the credentials. */
+ if (e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_AWAITING_CREDENTIALS &&
+ e_source_get_connection_status (source) != E_SOURCE_CONNECTION_STATUS_SSL_FAILED)
+ gtk_dialog_response (dialog, GTK_RESPONSE_CANCEL);
+}
+
+static void
+trust_prompt_listen_for_source_changes_cb (GtkDialog *dialog,
+ gpointer user_data)
+{
+ ESource *source = user_data;
+
+ g_return_if_fail (GTK_IS_DIALOG (dialog));
+ g_return_if_fail (E_IS_SOURCE (source));
+
+ g_signal_connect (source, "notify::connection-status",
+ G_CALLBACK (source_connection_status_changed_cb), dialog);
+}
+
+typedef struct _SaveSourceData {
+ ETrustPromptResponse response;
+ gboolean call_save;
+ GError *error;
+} SaveSourceData;
+
+static void
+save_source_data_free (gpointer ptr)
+{
+ SaveSourceData *data = ptr;
+
+ if (data) {
+ g_clear_error (&data->error);
+ g_free (data);
+ }
+}
+
+static void
+save_source_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ ESource *source = source_object;
+ SaveSourceData *data = task_data;
+ GError *local_error = NULL;
+
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (data != NULL);
+
+ if (data->error)
+ local_error = g_error_copy (data->error);
+ else if (data->call_save)
+ e_source_write_sync (source, cancellable, &local_error);
+
+ if (local_error != NULL) {
+ g_task_return_error (task, local_error);
+ } else {
+ g_task_return_boolean (task, TRUE);
+ }
+}
+
+/**
+ * e_trust_prompt_run_for_source:
+ * @parent: A #GtkWindow to use as a parent for the trust prompt dialog
+ * @source: an #ESource, with %E_SOURCE_EXTENSION_AUTHENTICATION
+ * @certificate_pem: a PEM-encoded certificate for which to show the trust prompt
+ * @certificate_errors: errors of the @certificate_pem
+ * @error_text: (allow none): an optional error text to show in the dialog; can be %NULL
+ * @allow_source_save: whether can also save any @source changes
+ * @cancellable: (allow none): a #GCancellable, or %NULL
+ * @callback: a callback to call, when the prompt (an @source save) is done
+ * @user_data: user data passed into @callback
+ *
+ * Similar to e_trust_prompt_run_modal(), except it also manages all the necessary things
+ * around the @source<!-- -->'s SSL trust properties when it also contains %E_SOURCE_EXTENSION_WEBDAV,
+ * thus the SSL trust on the WebDAV @source is properly updated based on the user's choice.
+ * The call is finished with e_trust_prompt_run_for_source_finish(),
+ * which also returns the user's choice. The finish happens in the @callback.
+ * This is necessary, because the @source can be also saved.
+ *
+ * The function fails, if the @source doesn't contain the %E_SOURCE_EXTENSION_AUTHENTICATION.
+ *
+ * Note: The dialog is not shown when the stored certificate trust in the WebDAV @source
+ * matches the @certificate_pem and the stored result is #E_TRUST_PROMPT_RESPONSE_REJECT.
+ *
+ * Since: 3.14
+ **/
+void
+e_trust_prompt_run_for_source (GtkWindow *parent,
+ ESource *source,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const gchar *error_text,
+ gboolean allow_source_save,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ESourceAuthentication *extension_authentication;
+ ESourceWebdav *extension_webdav = NULL;
+ SaveSourceData *save_data;
+ GTlsCertificate *certificate;
+ gchar *host;
+ GTask *task;
+
+ if (parent)
+ g_return_if_fail (GTK_IS_WINDOW (parent));
+ g_return_if_fail (E_IS_SOURCE (source));
+ g_return_if_fail (certificate_pem != NULL);
+ g_return_if_fail (e_source_has_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION));
+
+ extension_authentication = e_source_get_extension (source, E_SOURCE_EXTENSION_AUTHENTICATION);
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND))
+ extension_webdav = e_source_get_extension (source, E_SOURCE_EXTENSION_WEBDAV_BACKEND);
+
+ save_data = g_new0 (SaveSourceData, 1);
+ save_data->response = E_TRUST_PROMPT_RESPONSE_UNKNOWN;
+ save_data->call_save = FALSE;
+
+ host = e_source_authentication_dup_host (extension_authentication);
+
+ certificate = g_tls_certificate_new_from_pem (certificate_pem, -1, &save_data->error);
+ if (certificate) {
+ if (extension_webdav)
+ save_data->response = e_source_webdav_verify_ssl_trust (extension_webdav, host,
certificate, 0);
+ else
+ save_data->response = E_TRUST_PROMPT_RESPONSE_REJECT_TEMPORARILY;
+
+ if (save_data->response != E_TRUST_PROMPT_RESPONSE_REJECT) {
+ const gchar *source_extension = NULL;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_ADDRESS_BOOK))
+ source_extension = E_SOURCE_EXTENSION_ADDRESS_BOOK;
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_CALENDAR)) {
+ if (!source_extension)
+ source_extension = E_SOURCE_EXTENSION_CALENDAR;
+ else
+ source_extension = E_SOURCE_EXTENSION_COLLECTION;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MEMO_LIST)) {
+ if (!source_extension)
+ source_extension = E_SOURCE_EXTENSION_MEMO_LIST;
+ else
+ source_extension = E_SOURCE_EXTENSION_COLLECTION;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_TASK_LIST)) {
+ if (!source_extension)
+ source_extension = E_SOURCE_EXTENSION_TASK_LIST;
+ else
+ source_extension = E_SOURCE_EXTENSION_COLLECTION;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT)) {
+ if (!source_extension)
+ source_extension = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
+ else
+ source_extension = E_SOURCE_EXTENSION_COLLECTION;
+ }
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT)) {
+ if (!source_extension)
+ source_extension = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
+ else
+ source_extension = E_SOURCE_EXTENSION_COLLECTION;
+ }
+
+ save_data->response = e_trust_prompt_run_with_dialog_ready_callback (parent,
+ source_extension, e_source_get_display_name (source), host,
+ certificate_pem, certificate_errors, error_text,
+ trust_prompt_listen_for_source_changes_cb, source);
+ }
+ }
+
+ g_signal_handlers_disconnect_matched (source, G_SIGNAL_MATCH_FUNC, 0, 0, NULL,
+ source_connection_status_changed_cb, NULL);
+
+ if (save_data->response != E_TRUST_PROMPT_RESPONSE_UNKNOWN) {
+ if (certificate && extension_webdav) {
+ e_source_webdav_update_ssl_trust (extension_webdav, host, certificate,
save_data->response);
+ save_data->call_save = allow_source_save;
+ }
+ }
+
+ g_clear_object (&certificate);
+ g_free (host);
+
+ task = g_task_new (source, cancellable, callback, user_data);
+ g_task_set_source_tag (task, e_trust_prompt_run_for_source);
+ g_task_set_task_data (task, save_data, save_source_data_free);
+
+ g_task_run_in_thread (task, save_source_thread);
+
+ g_object_unref (task);
+}
+
+/**
+ * e_trust_prompt_run_for_source_finish:
+ * @source: an #ESource which was used with e_trust_prompt_run_for_source()
+ * @result: a #GAsyncResult
+ * @response: an output argument, user's response to the trust prompt
+ * @error: return location for a #GError, or %NULL
+ *
+ * Finishes the operation started with e_trust_prompt_run_for_source().
+ * The @response will contain a code of the user's choice.
+ * The #E_TRUST_PROMPT_RESPONSE_UNKNOWN is used, when the user cancelled the trust
+ * prompt dialog and no changes are made with the @source.
+ *
+ * If an error occurs, the function sets @error and returns %FALSE.
+ *
+ * Returns: %TRUE on success, %FALSE on error
+ *
+ * Since: 3.14
+ **/
+gboolean
+e_trust_prompt_run_for_source_finish (ESource *source,
+ GAsyncResult *result,
+ ETrustPromptResponse *response,
+ GError **error)
+{
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (g_task_is_valid (result, source), FALSE);
+ g_return_val_if_fail (response != NULL, FALSE);
+
+ g_return_val_if_fail (
+ g_async_result_is_tagged (
+ result, e_trust_prompt_run_for_source), FALSE);
+
+ success = g_task_propagate_boolean (G_TASK (result), error);
+
+ if (success) {
+ SaveSourceData *save_data;
+
+ save_data = g_task_get_task_data (G_TASK (result));
+ g_return_val_if_fail (save_data != NULL, FALSE);
+
+ *response = save_data->response;
+ }
+
+ return success;
+}
diff --git a/libedataserverui/e-trust-prompt.h b/libedataserverui/e-trust-prompt.h
new file mode 100644
index 0000000..27b9835
--- /dev/null
+++ b/libedataserverui/e-trust-prompt.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015 Red Hat, Inc. (www.redhat.com)
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#if !defined (__LIBEDATASERVERUI_H_INSIDE__) && !defined (LIBEDATASERVERUI_COMPILATION)
+#error "Only <libedataserverui/libedataserverui.h> should be included directly."
+#endif
+
+#ifndef E_TRUST_PROMPT_H
+#define E_TRUST_PROMPT_H
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <gtk/gtk.h>
+
+#include <libedataserver/libedataserver.h>
+
+gchar * e_trust_prompt_describe_certificate_errors
+ (GTlsCertificateFlags certificate_errors);
+ETrustPromptResponse
+ e_trust_prompt_run_modal(GtkWindow *parent,
+ const gchar *source_extension,
+ const gchar *source_display_name,
+ const gchar *host,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const gchar *error_text);
+
+void e_trust_prompt_run_for_source
+ (GtkWindow *parent,
+ ESource *source,
+ const gchar *certificate_pem,
+ GTlsCertificateFlags certificate_errors,
+ const gchar *error_text,
+ gboolean allow_source_save,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean e_trust_prompt_run_for_source_finish
+ (ESource *source,
+ GAsyncResult *result,
+ ETrustPromptResponse *response,
+ GError **error);
+
+#endif /* E_TRUST_PROMPT_H */
diff --git a/libedataserverui/libedataserverui.h b/libedataserverui/libedataserverui.h
new file mode 100644
index 0000000..19c60cb
--- /dev/null
+++ b/libedataserverui/libedataserverui.h
@@ -0,0 +1,30 @@
+/*
+ * libedataserverui.h
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef LIBEDATASERVERUI_H
+#define LIBEDATASERVERUI_H
+
+#define __LIBEDATASERVERUI_H_INSIDE__
+
+#include <libedataserverui/e-credentials-prompter.h>
+#include <libedataserverui/e-credentials-prompter-impl.h>
+#include <libedataserverui/e-credentials-prompter-impl-password.h>
+#include <libedataserverui/e-trust-prompt.h>
+
+#undef __LIBEDATASERVERUI_H_INSIDE__
+
+#endif /* LIBEDATASERVERUI_H */
diff --git a/libedataserverui/libedataserverui.pc.in b/libedataserverui/libedataserverui.pc.in
new file mode 100644
index 0000000..083a3a0
--- /dev/null
+++ b/libedataserverui/libedataserverui.pc.in
@@ -0,0 +1,18 @@
+prefix= prefix@
+exec_prefix= exec_prefix@
+libdir= libdir@
+includedir= includedir@
+datarootdir= datarootdir@
+datadir= datadir@
+
+privlibdir= privlibdir@
+privincludedir= privincludedir@
+
+credentialmoduledir= credentialmoduledir@
+
+Name: libedataserverui
+Description: UI utility library for Evolution Data Server
+Version: @VERSION@
+Requires: gio-2.0 gmodule-2.0 camel- API_VERSION@ libsecret-1 libxml-2.0 libsoup-2.4 gtk+-3.0
libedataserver- API_VERSION@
+Libs: -L${libdir} -ledataserver- API_VERSION@ -ledataserverui- API_VERSION@
+Cflags: -I${privincludedir}
diff --git a/modules/gnome-online-accounts/Makefile.am b/modules/gnome-online-accounts/Makefile.am
index 67448c3..808c4fd 100644
--- a/modules/gnome-online-accounts/Makefile.am
+++ b/modules/gnome-online-accounts/Makefile.am
@@ -16,8 +16,6 @@ module_gnome_online_accounts_la_SOURCES = \
module-gnome-online-accounts.c \
e-goa-client.c \
e-goa-client.h \
- e-goa-password-based.c \
- e-goa-password-based.h \
goaewsclient.c \
goaewsclient.h \
$(NULL)
@@ -36,4 +34,38 @@ module_gnome_online_accounts_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) \
$(NULL)
+credentialmodule_LTLIBRARIES = module-credentials-goa.la
+
+module_credentials_goa_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"module-credentials-goa\" \
+ $(E_BACKEND_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(SOUP_CFLAGS) \
+ $(GOA_CFLAGS) \
+ $(NULL)
+
+module_credentials_goa_la_SOURCES = \
+ module-credentials-goa.c \
+ e-goa-client.c \
+ e-goa-client.h \
+ e-goa-password-based.c \
+ e-goa-password-based.h \
+ $(NULL)
+
+module_credentials_goa_la_LIBADD = \
+ $(top_builddir)/camel/libcamel-1.2.la \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(E_BACKEND_LIBS) \
+ $(CAMEL_LIBS) \
+ $(SOUP_LIBS) \
+ $(GOA_LIBS) \
+ $(NULL)
+
+module_credentials_goa_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED) \
+ $(NULL)
+
-include $(top_srcdir)/git.mk
diff --git a/modules/gnome-online-accounts/e-goa-password-based.c
b/modules/gnome-online-accounts/e-goa-password-based.c
index 30714b0..fd05671 100644
--- a/modules/gnome-online-accounts/e-goa-password-based.c
+++ b/modules/gnome-online-accounts/e-goa-password-based.c
@@ -15,15 +15,18 @@
*
*/
-#include "e-goa-password-based.h"
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
/* XXX Yeah, yeah... */
#define GOA_API_IS_SUBJECT_TO_CHANGE
-#include <config.h>
#include <goa/goa.h>
#include <glib/gi18n-lib.h>
+#include "e-goa-password-based.h"
+
#define E_GOA_PASSWORD_BASED_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE \
((obj), E_TYPE_GOA_PASSWORD_BASED, EGoaPasswordBasedPrivate))
@@ -32,34 +35,35 @@ struct _EGoaPasswordBasedPrivate {
gint placeholder;
};
-G_DEFINE_DYNAMIC_TYPE (
- EGoaPasswordBased,
- e_goa_password_based,
- E_TYPE_AUTHENTICATION_SESSION)
+G_DEFINE_DYNAMIC_TYPE (EGoaPasswordBased, e_goa_password_based, E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL)
static GoaObject *
-e_goa_password_based_ref_account (ESourceRegistryServer *server,
- ESource *source,
+e_goa_password_based_ref_account (ESourceCredentialsProvider *provider,
+ ESource *source,
GoaClient *goa_client)
{
+ ESource *cred_source = NULL;
GoaObject *match = NULL;
GList *list, *link;
- const gchar *extension_name;
gchar *account_id = NULL;
+ ESourceGoa *extension = NULL;
- extension_name = E_SOURCE_EXTENSION_GOA;
-
- source = e_source_registry_server_find_extension (
- server, source, extension_name);
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_GOA)) {
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
+ } else {
+ cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
+ if (cred_source && e_source_has_extension (cred_source, E_SOURCE_EXTENSION_GOA))
+ extension = e_source_get_extension (cred_source, E_SOURCE_EXTENSION_GOA);
+ }
- if (source != NULL) {
- ESourceGoa *extension;
+ if (!extension) {
+ g_clear_object (&cred_source);
+ return NULL;
+ }
- extension = e_source_get_extension (source, extension_name);
- account_id = e_source_goa_dup_account_id (extension);
+ account_id = e_source_goa_dup_account_id (extension);
- g_object_unref (source);
- }
+ g_clear_object (&cred_source);
if (account_id == NULL)
return NULL;
@@ -86,55 +90,83 @@ e_goa_password_based_ref_account (ESourceRegistryServer *server,
}
g_list_free_full (list, (GDestroyNotify) g_object_unref);
+ g_free (account_id);
return match;
}
-static EAuthenticationSessionResult
-e_goa_password_based_execute_sync (EAuthenticationSession *session,
- GCancellable *cancellable,
- GError **error)
+static gboolean
+e_goa_password_based_can_process (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source)
+{
+ gboolean can_process;
+
+ g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+
+ can_process = e_source_has_extension (source, E_SOURCE_EXTENSION_GOA);
+ if (!can_process) {
+ ESource *cred_source;
+
+ cred_source = e_source_credentials_provider_ref_credentials_source (
+ e_source_credentials_provider_impl_get_provider (provider_impl),
+ source);
+
+ if (cred_source) {
+ can_process = e_source_has_extension (cred_source, E_SOURCE_EXTENSION_GOA);
+ g_clear_object (&cred_source);
+ }
+ }
+
+ return can_process;
+}
+
+static gboolean
+e_goa_password_based_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
+
+ return FALSE;
+}
+
+static gboolean
+e_goa_password_based_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
+
+ return FALSE;
+}
+
+static gboolean
+e_goa_password_based_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
{
- EAuthenticationSessionResult session_result;
- ESourceAuthenticationResult auth_result;
- ESourceAuthenticator *authenticator;
- ESourceRegistryServer *server;
- ESource *source = NULL;
GoaClient *goa_client = NULL;
GoaObject *goa_object = NULL;
GoaAccount *goa_account = NULL;
GoaPasswordBased *goa_password_based = NULL;
- GString *password_string;
- const gchar *extension_name;
- const gchar *source_uid;
gchar *password = NULL;
gboolean use_imap_password;
gboolean use_smtp_password;
- gboolean success;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (E_IS_GOA_PASSWORD_BASED (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials, FALSE);
goa_client = goa_client_new_sync (cancellable, error);
if (goa_client == NULL) {
- session_result = E_AUTHENTICATION_SESSION_ERROR;
if (error && *error)
g_dbus_error_strip_remote_error (*error);
goto exit;
}
- server = e_authentication_session_get_server (session);
- source_uid = e_authentication_session_get_source_uid (session);
- source = e_source_registry_server_ref_source (server, source_uid);
-
- if (source == NULL) {
- g_set_error (
- error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
- _("No such data source for UID '%s'"),
- source_uid);
- session_result = E_AUTHENTICATION_SESSION_ERROR;
- goto exit;
- }
-
goa_object = e_goa_password_based_ref_account (
- server, source, goa_client);
+ e_source_credentials_provider_impl_get_provider (provider_impl),
+ source, goa_client);
if (goa_object == NULL) {
g_set_error (
@@ -143,7 +175,6 @@ e_goa_password_based_execute_sync (EAuthenticationSession *session,
"the org.gnome.OnlineAccounts service from "
"which to obtain a password for '%s'"),
e_source_get_display_name (source));
- session_result = E_AUTHENTICATION_SESSION_ERROR;
goto exit;
}
@@ -151,24 +182,18 @@ e_goa_password_based_execute_sync (EAuthenticationSession *session,
goa_password_based = goa_object_get_password_based (goa_object);
/* XXX We should only be here if the account is password based. */
- g_return_val_if_fail (
- goa_password_based != NULL,
- E_AUTHENTICATION_SESSION_ERROR);
+ g_return_val_if_fail (goa_password_based != NULL, FALSE);
success = goa_account_call_ensure_credentials_sync (
goa_account, NULL, cancellable, error);
if (!success) {
- session_result = E_AUTHENTICATION_SESSION_ERROR;
if (error && *error)
g_dbus_error_strip_remote_error (*error);
goto exit;
}
- extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT;
- use_imap_password = e_source_has_extension (source, extension_name);
-
- extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT;
- use_smtp_password = e_source_has_extension (source, extension_name);
+ use_imap_password = e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_ACCOUNT);
+ use_smtp_password = e_source_has_extension (source, E_SOURCE_EXTENSION_MAIL_TRANSPORT);
/* Use a suitable password ID for the ESource. */
if (use_imap_password) {
@@ -187,68 +212,38 @@ e_goa_password_based_execute_sync (EAuthenticationSession *session,
}
if (password == NULL) {
- session_result = E_AUTHENTICATION_SESSION_ERROR;
+ success = FALSE;
if (error && *error)
g_dbus_error_strip_remote_error (*error);
goto exit;
}
- authenticator = e_authentication_session_get_authenticator (session);
- password_string = g_string_new (password);
- auth_result = e_source_authenticator_try_password_sync (
- authenticator, password_string, cancellable, error);
- g_string_free (password_string, TRUE);
-
- switch (auth_result) {
- case E_SOURCE_AUTHENTICATION_ERROR:
- session_result = E_AUTHENTICATION_SESSION_ERROR;
- break;
-
- case E_SOURCE_AUTHENTICATION_ACCEPTED:
- session_result = E_AUTHENTICATION_SESSION_SUCCESS;
- break;
-
- case E_SOURCE_AUTHENTICATION_REJECTED:
- /* FIXME Apparently applications are expected to post
- * a desktop-wide notification about the failed
- * authentication attempt. */
- g_set_error (
- error, G_IO_ERROR,
- G_IO_ERROR_PERMISSION_DENIED,
- _("Invalid password for '%s'"),
- e_source_get_display_name (source));
- session_result = E_AUTHENTICATION_SESSION_ERROR;
- break;
-
- default:
- g_warn_if_reached ();
- session_result = E_AUTHENTICATION_SESSION_DISMISSED;
- break;
- }
+ *out_credentials = e_named_parameters_new ();
+ e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, password);
-exit:
- g_clear_object (&source);
+ exit:
g_clear_object (&goa_client);
g_clear_object (&goa_object);
g_clear_object (&goa_account);
g_clear_object (&goa_password_based);
- g_free (password);
+ e_util_safe_free_string (password);
- return session_result;
+ return success;
}
static void
e_goa_password_based_class_init (EGoaPasswordBasedClass *class)
{
- EAuthenticationSessionClass *authentication_session_class;
+ ESourceCredentialsProviderImplClass *provider_impl_class;
g_type_class_add_private (class, sizeof (EGoaPasswordBasedPrivate));
- authentication_session_class =
- E_AUTHENTICATION_SESSION_CLASS (class);
- authentication_session_class->execute_sync =
- e_goa_password_based_execute_sync;
+ provider_impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (class);
+ provider_impl_class->can_process = e_goa_password_based_can_process;
+ provider_impl_class->can_store = e_goa_password_based_can_store;
+ provider_impl_class->can_prompt = e_goa_password_based_can_prompt;
+ provider_impl_class->lookup_sync = e_goa_password_based_lookup_sync;
}
static void
diff --git a/modules/gnome-online-accounts/e-goa-password-based.h
b/modules/gnome-online-accounts/e-goa-password-based.h
index 40720fe..d0382e0 100644
--- a/modules/gnome-online-accounts/e-goa-password-based.h
+++ b/modules/gnome-online-accounts/e-goa-password-based.h
@@ -18,7 +18,7 @@
#ifndef E_GOA_PASSWORD_BASED_H
#define E_GOA_PASSWORD_BASED_H
-#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
/* Standard GObject macros */
#define E_TYPE_GOA_PASSWORD_BASED \
@@ -46,12 +46,12 @@ typedef struct _EGoaPasswordBasedClass EGoaPasswordBasedClass;
typedef struct _EGoaPasswordBasedPrivate EGoaPasswordBasedPrivate;
struct _EGoaPasswordBased {
- EAuthenticationSession parent;
+ ESourceCredentialsProviderImpl parent;
EGoaPasswordBasedPrivate *priv;
};
struct _EGoaPasswordBasedClass {
- EAuthenticationSessionClass parent_class;
+ ESourceCredentialsProviderImplClass parent_class;
};
GType e_goa_password_based_get_type (void) G_GNUC_CONST;
diff --git a/modules/gnome-online-accounts/module-credentials-goa.c
b/modules/gnome-online-accounts/module-credentials-goa.c
new file mode 100644
index 0000000..4d5ea1a
--- /dev/null
+++ b/modules/gnome-online-accounts/module-credentials-goa.c
@@ -0,0 +1,37 @@
+/*
+ * module-credentials-goa.c
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-goa-password-based.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_goa_password_based_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/gnome-online-accounts/module-gnome-online-accounts.c
b/modules/gnome-online-accounts/module-gnome-online-accounts.c
index 03daba0..7b34918 100644
--- a/modules/gnome-online-accounts/module-gnome-online-accounts.c
+++ b/modules/gnome-online-accounts/module-gnome-online-accounts.c
@@ -23,7 +23,6 @@
#include "goaewsclient.h"
#include "e-goa-client.h"
-#include "e-goa-password-based.h"
/* Standard GObject macros */
#define E_TYPE_GNOME_ONLINE_ACCOUNTS \
@@ -656,13 +655,6 @@ gnome_online_accounts_config_collection (EGnomeOnlineAccounts *extension,
e_server_side_source_set_removable (
E_SERVER_SIDE_SOURCE (source), FALSE);
- if (goa_object_peek_password_based (goa_object) != NULL) {
- /* Obtain passwords from the OnlineAccounts service. */
- e_server_side_source_set_auth_session_type (
- E_SERVER_SIDE_SOURCE (source),
- E_TYPE_GOA_PASSWORD_BASED);
- }
-
if (goa_object_peek_oauth2_based (goa_object) != NULL) {
/* This module provides OAuth 2.0 support to the collection.
* Note, children of the collection source will automatically
@@ -1337,7 +1329,6 @@ G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_goa_client_type_register (type_module);
- e_goa_password_based_type_register (type_module);
e_gnome_online_accounts_register_type (type_module);
}
diff --git a/modules/owncloud-backend/module-owncloud-backend.c
b/modules/owncloud-backend/module-owncloud-backend.c
index e13d5c5..0e898cc 100644
--- a/modules/owncloud-backend/module-owncloud-backend.c
+++ b/modules/owncloud-backend/module-owncloud-backend.c
@@ -29,6 +29,9 @@
#define E_OWNCLOUD_BACKEND(obj) \
(G_TYPE_CHECK_INSTANCE_CAST \
((obj), E_TYPE_OWNCLOUD_BACKEND, EOwncloudBackend))
+#define E_IS_OWNCLOUD_BACKEND(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE \
+ ((obj), E_TYPE_OWNCLOUD_BACKEND))
typedef struct _EOwncloudBackend EOwncloudBackend;
typedef struct _EOwncloudBackendClass EOwncloudBackendClass;
@@ -224,14 +227,21 @@ owncloud_add_uid_to_hashtable (gpointer source,
g_hash_table_insert (known_sources, rid, uid);
}
-static gpointer
-owncloud_populate_thread (gpointer data)
+static ESourceAuthenticationResult
+owncloud_backend_authenticate_sync (EBackend *backend,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
- ECollectionBackend *collection = data;
+ ECollectionBackend *collection = E_COLLECTION_BACKEND (backend);
+ ESourceAuthenticationResult result;
GHashTable *known_sources;
GList *sources;
+ GError *local_error = NULL;
- g_return_val_if_fail (collection != NULL, NULL);
+ g_return_val_if_fail (collection != NULL, E_SOURCE_AUTHENTICATION_ERROR);
/* resource-id => source's UID */
known_sources = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
@@ -244,7 +254,8 @@ owncloud_populate_thread (gpointer data)
g_list_foreach (sources, owncloud_add_uid_to_hashtable, known_sources);
g_list_free_full (sources, g_object_unref);
- if (owncloud_utils_search_server (collection, owncloud_source_found_cb, known_sources)) {
+ if (owncloud_utils_search_server (collection, credentials, out_certificate_pem,
out_certificate_errors,
+ owncloud_source_found_cb, known_sources, cancellable, &local_error)) {
ESourceRegistryServer *server;
server = e_collection_backend_ref_server (collection);
@@ -254,10 +265,23 @@ owncloud_populate_thread (gpointer data)
g_object_unref (server);
}
+ if (local_error == NULL) {
+ result = E_SOURCE_AUTHENTICATION_ACCEPTED;
+ e_collection_backend_authenticate_children (collection, credentials);
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+ result = E_SOURCE_AUTHENTICATION_REJECTED;
+ g_clear_error (&local_error);
+ } else if (g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_SSL_FAILED)) {
+ result = E_SOURCE_AUTHENTICATION_ERROR_SSL_FAILED;
+ g_propagate_error (error, local_error);
+ } else {
+ result = E_SOURCE_AUTHENTICATION_ERROR;
+ g_propagate_error (error, local_error);
+ }
+
g_hash_table_destroy (known_sources);
- g_object_unref (collection);
- return NULL;
+ return result;
}
static void
@@ -265,7 +289,6 @@ owncloud_backend_populate (ECollectionBackend *collection)
{
GList *list, *liter;
ESourceRegistryServer *server;
- GThread *thread;
/* Chain up to parent's populate() method. */
E_COLLECTION_BACKEND_CLASS (e_owncloud_backend_parent_class)->populate (collection);
@@ -292,17 +315,21 @@ owncloud_backend_populate (ECollectionBackend *collection)
g_list_free_full (list, g_object_unref);
g_object_unref (server);
- thread = g_thread_new (NULL, owncloud_populate_thread, g_object_ref (collection));
- g_thread_unref (thread);
+ e_backend_schedule_credentials_required (E_BACKEND (collection), E_SOURCE_CREDENTIALS_REASON_REQUIRED,
+ NULL, 0, NULL, NULL, G_STRFUNC);
}
static void
e_owncloud_backend_class_init (EOwncloudBackendClass *class)
{
- ECollectionBackendClass *backend_class;
+ EBackendClass *backend_class;
+ ECollectionBackendClass *collection_backend_class;
+
+ backend_class = E_BACKEND_CLASS (class);
+ backend_class->authenticate_sync = owncloud_backend_authenticate_sync;
- backend_class = E_COLLECTION_BACKEND_CLASS (class);
- backend_class->populate = owncloud_backend_populate;
+ collection_backend_class = E_COLLECTION_BACKEND_CLASS (class);
+ collection_backend_class->populate = owncloud_backend_populate;
}
static void
diff --git a/modules/owncloud-backend/owncloud-utils.c b/modules/owncloud-backend/owncloud-utils.c
index bb860e4..dadb05d 100644
--- a/modules/owncloud-backend/owncloud-utils.c
+++ b/modules/owncloud-backend/owncloud-utils.c
@@ -31,83 +31,6 @@
#include "owncloud-utils.h"
-typedef struct _EOwncloudAuthenticator EOwncloudAuthenticator;
-typedef struct _EOwncloudAuthenticatorClass EOwncloudAuthenticatorClass;
-
-struct _EOwncloudAuthenticator {
- GObject parent;
-
- ECollectionBackend *collection;
- gchar *username;
- GString *password;
-};
-
-struct _EOwncloudAuthenticatorClass {
- GObjectClass parent_class;
-};
-
-static ESourceAuthenticationResult
-owncloud_authenticator_try_password_sync (ESourceAuthenticator *auth,
- const GString *password,
- GCancellable *cancellable,
- GError **error)
-{
- EOwncloudAuthenticator *authenticator = (EOwncloudAuthenticator *) auth;
-
- if (authenticator->password)
- g_string_free (authenticator->password, TRUE);
- authenticator->password = g_string_new (password->str);
-
- return E_SOURCE_AUTHENTICATION_ACCEPTED;
-}
-
-#define E_TYPE_OWNCLOUD_AUTHENTICATOR (e_owncloud_authenticator_get_type ())
-
-GType e_owncloud_authenticator_get_type
- (void) G_GNUC_CONST;
-static void e_owncloud_authenticator_authenticator_init
- (ESourceAuthenticatorInterface *iface);
-
-G_DEFINE_TYPE_EXTENDED (
- EOwncloudAuthenticator,
- e_owncloud_authenticator,
- G_TYPE_OBJECT, 0,
- G_IMPLEMENT_INTERFACE (
- E_TYPE_SOURCE_AUTHENTICATOR,
- e_owncloud_authenticator_authenticator_init))
-
-static void
-owncloud_authenticator_finalize (GObject *object)
-{
- EOwncloudAuthenticator *authenticator = (EOwncloudAuthenticator *) object;
-
- g_free (authenticator->username);
- if (authenticator->password)
- g_string_free (authenticator->password, TRUE);
-
- G_OBJECT_CLASS (e_owncloud_authenticator_parent_class)->finalize (object);
-}
-
-static void
-e_owncloud_authenticator_class_init (EOwncloudAuthenticatorClass *class)
-{
- GObjectClass *object_class;
-
- object_class = G_OBJECT_CLASS (class);
- object_class->finalize = owncloud_authenticator_finalize;
-}
-
-static void
-e_owncloud_authenticator_authenticator_init (ESourceAuthenticatorInterface *iface)
-{
- iface->try_password_sync = owncloud_authenticator_try_password_sync;
-}
-
-static void
-e_owncloud_authenticator_init (EOwncloudAuthenticator *authenticator)
-{
-}
-
#define XPATH_STATUS "string(/D:multistatus/D:response[%d]/D:propstat/D:status)"
#define XPATH_HREF "string(/D:multistatus/D:response[%d]/D:href)"
#define XPATH_DISPLAY_NAME "string(/D:multistatus/D:response[%d]/D:propstat/D:prop/D:displayname)"
@@ -424,6 +347,11 @@ parse_propfind_response (ECollectionBackend *collection,
xmlFreeDoc (doc);
}
+typedef struct _AuthenticateData {
+ const gchar *username;
+ const ENamedParameters *credentials;
+} AuthenticateData;
+
static void
authenticate_cb (SoupSession *session,
SoupMessage *msg,
@@ -431,38 +359,17 @@ authenticate_cb (SoupSession *session,
gboolean retrying,
gpointer user_data)
{
- EOwncloudAuthenticator *authenticator = user_data;
-
- g_return_if_fail (authenticator != NULL);
-
- if (retrying || !authenticator->password) {
- ESourceRegistryServer *server;
- EAuthenticationSession *auth_session;
- ESource *source;
-
- source = e_backend_get_source (
- E_BACKEND (authenticator->collection));
- server = e_collection_backend_ref_server (
- authenticator->collection);
-
- auth_session = e_source_registry_server_new_auth_session (
- server,
- E_SOURCE_AUTHENTICATOR (authenticator),
- e_source_get_uid (source));
- if (!e_source_registry_server_authenticate_sync (server, auth_session, NULL, NULL)) {
- if (authenticator->password)
- g_string_free (authenticator->password, TRUE);
- authenticator->password = NULL;
- }
+ AuthenticateData *auth_data = user_data;
- g_object_unref (auth_session);
- g_object_unref (server);
- }
+ g_return_if_fail (auth_data != NULL);
+ g_return_if_fail (auth_data->credentials != NULL);
+
+ if (retrying)
+ return;
- if (authenticator->username && authenticator->password)
- soup_auth_authenticate (
- auth, authenticator->username,
- authenticator->password->str);
+ if (auth_data->username && e_named_parameters_get (auth_data->credentials,
E_SOURCE_CREDENTIAL_PASSWORD))
+ soup_auth_authenticate (auth, auth_data->username,
+ e_named_parameters_get (auth_data->credentials, E_SOURCE_CREDENTIAL_PASSWORD));
}
static gboolean
@@ -471,7 +378,12 @@ find_sources (ECollectionBackend *collection,
gpointer user_data,
const gchar *base_url,
const gchar *base_collection_path,
- EOwncloudAuthenticator *authenticator)
+ const ENamedParameters *credentials,
+ const gchar *default_username,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
+ GCancellable *cancellable,
+ GError **error)
{
const gchar *req_body =
"<D:propfind "
@@ -486,21 +398,26 @@ find_sources (ECollectionBackend *collection,
" </D:prop>\n"
"</D:propfind>\n";
+ AuthenticateData auth_data;
SoupSession *session;
SoupMessage *msg;
GString *url;
+ const gchar *username;
gboolean tested = FALSE;
g_return_val_if_fail (base_url && *base_url, FALSE);
g_return_val_if_fail (base_collection_path && *base_collection_path, FALSE);
- g_return_val_if_fail (authenticator, FALSE);
+
+ username = e_named_parameters_get (credentials, E_SOURCE_CREDENTIAL_USERNAME);
+ if (!username || !*username)
+ username = default_username;
url = g_string_new (base_url);
if (url->str[url->len - 1] != '/')
g_string_append_c (url, '/');
g_string_append (url, base_collection_path);
g_string_append_c (url, '/');
- g_string_append (url, authenticator->username);
+ g_string_append (url, username);
g_string_append_c (url, '/');
msg = soup_message_new ("PROPFIND", url->str);
@@ -510,6 +427,9 @@ find_sources (ECollectionBackend *collection,
return FALSE;
}
+ auth_data.username = username;
+ auth_data.credentials = credentials;
+
session = soup_session_sync_new ();
g_object_set (
session,
@@ -519,7 +439,7 @@ find_sources (ECollectionBackend *collection,
NULL);
g_signal_connect (
session, "authenticate",
- G_CALLBACK (authenticate_cb), authenticator);
+ G_CALLBACK (authenticate_cb), &auth_data);
g_object_bind_property (
collection, "proxy-resolver",
@@ -532,9 +452,7 @@ find_sources (ECollectionBackend *collection,
msg, "application/xml; charset=utf-8",
SOUP_MEMORY_STATIC, req_body, strlen (req_body));
- /* this is the master source, thus there is no parent_source */
- e_soup_ssl_trust_connect (
- msg, e_backend_get_source (E_BACKEND (collection)), NULL, NULL);
+ e_soup_ssl_trust_connect (msg, e_backend_get_source (E_BACKEND (collection)));
soup_session_send_message (session, msg);
@@ -551,6 +469,22 @@ find_sources (ECollectionBackend *collection,
soup_uri_free (suri);
tested = TRUE;
+ } else {
+ g_set_error_literal (error, SOUP_HTTP_ERROR, msg->status_code, msg->reason_phrase);
+
+ if (msg->status_code == SOUP_STATUS_SSL_FAILED && out_certificate_pem &&
out_certificate_errors) {
+ GTlsCertificate *certificate = NULL;
+
+ g_object_get (G_OBJECT (msg),
+ "tls-certificate", &certificate,
+ "tls-errors", out_certificate_errors,
+ NULL);
+
+ if (certificate) {
+ g_object_get (certificate, "certificate-pem", out_certificate_pem, NULL);
+ g_object_unref (certificate);
+ }
+ }
}
g_object_unref (msg);
@@ -561,16 +495,21 @@ find_sources (ECollectionBackend *collection,
gboolean
owncloud_utils_search_server (ECollectionBackend *collection,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
OwnCloudSourceFoundCb found_cb,
- gpointer user_data)
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
{
ESourceCollection *collection_extension;
ESourceGoa *goa_extension;
ESource *source;
- EOwncloudAuthenticator *authenticator;
- gchar *url;
+ gchar *url, *username;
gboolean res_calendars = FALSE;
gboolean res_contacts = FALSE;
+ GError *local_error = NULL;
g_return_val_if_fail (collection != NULL, FALSE);
g_return_val_if_fail (found_cb != NULL, FALSE);
@@ -579,9 +518,7 @@ owncloud_utils_search_server (ECollectionBackend *collection,
collection_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_COLLECTION);
goa_extension = e_source_get_extension (source, E_SOURCE_EXTENSION_GOA);
- authenticator = g_object_new (E_TYPE_OWNCLOUD_AUTHENTICATOR, NULL);
- authenticator->collection = collection;
- authenticator->username = e_source_collection_dup_identity (collection_extension);
+ username = e_source_collection_dup_identity (collection_extension);
if (e_source_collection_get_calendar_enabled (collection_extension)) {
url = e_source_goa_dup_calendar_url (goa_extension);
@@ -589,23 +526,30 @@ owncloud_utils_search_server (ECollectionBackend *collection,
if (url && *url)
res_calendars = find_sources (
collection, found_cb, user_data,
- url, "calendars", authenticator);
+ url, "calendars", credentials, username,
+ out_certificate_pem, out_certificate_errors,
+ cancellable, &local_error);
g_free (url);
}
- if (e_source_collection_get_contacts_enabled (collection_extension)) {
+ if (e_source_collection_get_contacts_enabled (collection_extension) && !local_error) {
url = e_source_goa_dup_contacts_url (goa_extension);
if (url && *url)
res_contacts = find_sources (
collection, found_cb, user_data,
- url, "addressbooks", authenticator);
+ url, "addressbooks", credentials, username,
+ out_certificate_pem, out_certificate_errors,
+ cancellable, &local_error);
g_free (url);
}
- g_object_unref (authenticator);
+ if (local_error)
+ g_propagate_error (error, local_error);
+
+ g_free (username);
return res_calendars || res_contacts;
}
diff --git a/modules/owncloud-backend/owncloud-utils.h b/modules/owncloud-backend/owncloud-utils.h
index 00bea18..fe184d8 100644
--- a/modules/owncloud-backend/owncloud-utils.h
+++ b/modules/owncloud-backend/owncloud-utils.h
@@ -38,8 +38,13 @@ typedef void (*OwnCloudSourceFoundCb) (ECollectionBackend *collection,
gpointer user_data);
gboolean owncloud_utils_search_server (ECollectionBackend *collection,
+ const ENamedParameters *credentials,
+ gchar **out_certificate_pem,
+ GTlsCertificateFlags *out_certificate_errors,
OwnCloudSourceFoundCb found_cb,
- gpointer user_data);
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
G_END_DECLS
diff --git a/modules/ubuntu-online-accounts/Makefile.am b/modules/ubuntu-online-accounts/Makefile.am
index 0a268d4..e3ba0e6 100644
--- a/modules/ubuntu-online-accounts/Makefile.am
+++ b/modules/ubuntu-online-accounts/Makefile.am
@@ -16,8 +16,6 @@ module_ubuntu_online_accounts_la_CPPFLAGS = \
module_ubuntu_online_accounts_la_SOURCES = \
module-ubuntu-online-accounts.c \
- e-signon-session-password.c \
- e-signon-session-password.h \
uoa-utils.c \
uoa-utils.h \
$(NULL)
@@ -37,6 +35,42 @@ module_ubuntu_online_accounts_la_LDFLAGS = \
-module -avoid-version $(NO_UNDEFINED) \
$(NULL)
+credentialmodule_LTLIBRARIES = module-credentials-uoa.la
+
+module_credentials_uoa_la_CPPFLAGS = \
+ $(AM_CPPFLAGS) \
+ -I$(top_srcdir) \
+ -DG_LOG_DOMAIN=\"module-credentials-uoa\" \
+ $(LIBACCOUNTS_GLIB_CFLAGS) \
+ $(LIBSIGNON_GLIB_CFLAGS) \
+ $(E_BACKEND_CFLAGS) \
+ $(JSON_GLIB_CFLAGS) \
+ $(CAMEL_CFLAGS) \
+ $(REST_CFLAGS) \
+ $(NULL)
+
+module_credentials_uoa_la_SOURCES = \
+ module-credentials-uoa.c \
+ e-signon-session-password.c \
+ e-signon-session-password.h \
+ $(NULL)
+
+module_credentials_uoa_la_LIBADD = \
+ $(top_builddir)/camel/libcamel-1.2.la \
+ $(top_builddir)/libebackend/libebackend-1.2.la \
+ $(top_builddir)/libedataserver/libedataserver-1.2.la \
+ $(LIBACCOUNTS_GLIB_LIBS) \
+ $(LIBSIGNON_GLIB_LIBS) \
+ $(E_BACKEND_LIBS) \
+ $(JSON_GLIB_LIBS) \
+ $(CAMEL_LIBS) \
+ $(REST_LIBS) \
+ $(NULL)
+
+module_credentials_uoa_la_LDFLAGS = \
+ -module -avoid-version $(NO_UNDEFINED) \
+ $(NULL)
+
%.application: %.application.in
$(AM_V_GEN) $(INTLTOOL_MERGE) --no-translations -x -u $< $@
diff --git a/modules/ubuntu-online-accounts/e-signon-session-password.c
b/modules/ubuntu-online-accounts/e-signon-session-password.c
index 83d4bad..ff8c4fa 100644
--- a/modules/ubuntu-online-accounts/e-signon-session-password.c
+++ b/modules/ubuntu-online-accounts/e-signon-session-password.c
@@ -37,50 +37,37 @@ struct _ESignonSessionPasswordPrivate {
};
struct _AsyncContext {
- ESourceAuthenticator *authenticator;
SignonAuthSession *signon_auth_session;
EAuthenticationSessionResult session_result;
AgAuthData *ag_auth_data;
GCancellable *cancellable;
+ GString *password;
};
-/* Forward Declarations */
-static void signon_session_password_msg
- (EAuthenticationSession *session,
- const gchar *format,
- ...) G_GNUC_PRINTF (2, 3);
-static void signon_session_password_process_cb
- (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data);
-
-G_DEFINE_DYNAMIC_TYPE (
- ESignonSessionPassword,
- e_signon_session_password,
- E_TYPE_AUTHENTICATION_SESSION)
+G_DEFINE_DYNAMIC_TYPE (ESignonSessionPassword, e_signon_session_password,
E_TYPE_SOURCE_CREDENTIALS_PROVIDER_IMPL)
static void
async_context_free (AsyncContext *async_context)
{
- if (async_context->authenticator != NULL)
- g_object_unref (async_context->authenticator);
-
- if (async_context->signon_auth_session != NULL)
- g_object_unref (async_context->signon_auth_session);
+ g_clear_object (&async_context->signon_auth_session);
+ g_clear_object (&async_context->cancellable);
if (async_context->ag_auth_data != NULL)
ag_auth_data_unref (async_context->ag_auth_data);
- if (async_context->cancellable != NULL)
- g_object_unref (async_context->cancellable);
+ if (async_context->password) {
+ if (async_context->password->len)
+ memset (async_context->password->str, 0, async_context->password->len);
+ g_string_free (async_context->password, TRUE);
+ }
g_slice_free (AsyncContext, async_context);
}
static void
-signon_session_password_msg (EAuthenticationSession *session,
- const gchar *format,
- ...)
+e_signon_session_password_msg (ESource *source,
+ const gchar *format,
+ ...)
{
GString *buffer;
const gchar *source_uid;
@@ -88,7 +75,7 @@ signon_session_password_msg (EAuthenticationSession *session,
buffer = g_string_sized_new (256);
- source_uid = e_authentication_session_get_source_uid (session);
+ source_uid = e_source_get_uid (source);
g_string_append_printf (buffer, "AUTH (%s): ", source_uid);
va_start (args, format);
@@ -104,34 +91,43 @@ static void
signon_session_password_state_changed_cb (SignonAuthSession *signon_auth_session,
gint state,
const gchar *message,
- EAuthenticationSession *session)
+ ESource *source)
{
- signon_session_password_msg (session, "(signond) %s", message);
+ e_signon_session_password_msg (source, "(signond) %s", message);
}
static AgAccountService *
-signon_session_password_new_account_service (EAuthenticationSession *session,
+signon_session_password_new_account_service (ESourceCredentialsProviderImpl *provider_impl,
ESource *source,
GError **error)
{
ESignonSessionPasswordPrivate *priv;
- ESourceUoa *extension;
+ ESource *cred_source = NULL;
+ ESourceUoa *extension = NULL;
AgAccountId account_id;
AgAccount *ag_account = NULL;
AgAccountService *ag_account_service;
GList *list;
- gboolean has_uoa_extension;
- const gchar *extension_name;
- priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (session);
+ priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (provider_impl);
+
+ if (e_source_has_extension (source, E_SOURCE_EXTENSION_UOA)) {
+ extension = e_source_get_extension (source, E_SOURCE_EXTENSION_UOA);
+ } else {
+ ESourceCredentialsProvider *provider;
+
+ provider = e_source_credentials_provider_impl_get_provider (provider_impl);
+
+ cred_source = e_source_credentials_provider_ref_credentials_source (provider, source);
+ if (cred_source && e_source_has_extension (cred_source, E_SOURCE_EXTENSION_UOA))
+ extension = e_source_get_extension (cred_source, E_SOURCE_EXTENSION_UOA);
+ }
- /* XXX The ESource should be a collection source with an
- * [Ubuntu Online Accounts] extension. Verify this. */
- extension_name = E_SOURCE_EXTENSION_UOA;
- has_uoa_extension = e_source_has_extension (source, extension_name);
- g_return_val_if_fail (has_uoa_extension, NULL);
+ if (!extension) {
+ g_clear_object (&cred_source);
+ return NULL;
+ }
- extension = e_source_get_extension (source, extension_name);
account_id = e_source_uoa_get_account_id (extension);
ag_account = ag_manager_load_account (
@@ -157,60 +153,46 @@ signon_session_password_new_account_service (EAuthenticationSession *session,
return ag_account_service;
}
-static void
-signon_session_password_try_password_cb (GObject *source_object,
- GAsyncResult *result,
- gpointer user_data)
+static gboolean
+e_signon_session_password_can_process (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source)
{
- GSimpleAsyncResult *simple;
- AsyncContext *async_context;
- ESourceAuthenticationResult auth_result;
- GVariantBuilder builder;
- GVariant *session_data;
- GError *error = NULL;
+ gboolean can_process;
- simple = G_SIMPLE_ASYNC_RESULT (user_data);
- async_context = g_simple_async_result_get_op_res_gpointer (simple);
+ g_return_val_if_fail (E_IS_SIGNON_SESSION_PASSWORD (provider_impl), FALSE);
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
- auth_result = e_source_authenticator_try_password_finish (
- E_SOURCE_AUTHENTICATOR (source_object), result, &error);
+ can_process = e_source_has_extension (source, E_SOURCE_EXTENSION_UOA);
+ if (!can_process) {
+ ESource *cred_source;
- if (error != NULL) {
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- goto exit;
- }
+ cred_source = e_source_credentials_provider_ref_credentials_source (
+ e_source_credentials_provider_impl_get_provider (provider_impl),
+ source);
- if (auth_result == E_SOURCE_AUTHENTICATION_ACCEPTED) {
- async_context->session_result =
- E_AUTHENTICATION_SESSION_SUCCESS;
- g_simple_async_result_complete (simple);
- goto exit;
+ if (cred_source) {
+ can_process = e_source_has_extension (cred_source, E_SOURCE_EXTENSION_UOA);
+ g_clear_object (&cred_source);
+ }
}
- g_variant_builder_init (&builder, G_VARIANT_TYPE_VARDICT);
+ return can_process;
+}
- /* Force the signon service to prompt for a password by adding
- * SIGNON_POLICY_REQUEST_PASSWORD to the session data dictionary. */
- g_variant_builder_add (
- &builder, "{sv}", SIGNON_SESSION_DATA_UI_POLICY,
- g_variant_new_int32 (SIGNON_POLICY_REQUEST_PASSWORD));
+static gboolean
+e_signon_session_password_can_store (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SIGNON_SESSION_PASSWORD (provider_impl), FALSE);
- /* This returns a floating reference. */
- session_data = ag_auth_data_get_login_parameters (
- async_context->ag_auth_data,
- g_variant_builder_end (&builder));
+ return FALSE;
+}
- signon_auth_session_process_async (
- async_context->signon_auth_session,
- session_data,
- SIGNON_MECHANISM_PASSWORD,
- async_context->cancellable,
- signon_session_password_process_cb,
- g_object_ref (simple));
+static gboolean
+e_signon_session_password_can_prompt (ESourceCredentialsProviderImpl *provider_impl)
+{
+ g_return_val_if_fail (E_IS_SIGNON_SESSION_PASSWORD (provider_impl), FALSE);
-exit:
- g_object_unref (simple);
+ return FALSE;
}
static void
@@ -238,7 +220,6 @@ signon_session_password_process_cb (GObject *source_object,
if (error != NULL) {
g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
goto exit;
}
@@ -254,123 +235,52 @@ signon_session_password_process_cb (GObject *source_object,
simple, SIGNON_ERROR,
SIGNON_ERROR_MISSING_DATA,
_("Signon service did not return a secret"));
- g_simple_async_result_complete (simple);
goto exit;
}
- /* XXX It occurs to me now a GVariant might have been a better
- * choice for the password parameter in ESourceAuthenticator. */
- string = g_string_new (g_variant_get_string (secret, NULL));
-
- e_source_authenticator_try_password (
- async_context->authenticator,
- string,
- async_context->cancellable,
- signon_session_password_try_password_cb,
- g_object_ref (simple));
+ async_context->password = g_string_new (g_variant_get_string (secret, NULL));
g_string_free (string, TRUE);
g_variant_unref (secret);
exit:
+ g_simple_async_result_complete (simple);
g_object_unref (simple);
}
static void
-signon_session_password_dispose (GObject *object)
-{
- ESignonSessionPasswordPrivate *priv;
-
- priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (object);
-
- g_clear_object (&priv->ag_manager);
-
- /* Chain up to parent's dispose() method. */
- G_OBJECT_CLASS (e_signon_session_password_parent_class)->
- dispose (object);
-}
-
-static EAuthenticationSessionResult
-signon_session_password_execute_sync (EAuthenticationSession *session,
- GCancellable *cancellable,
- GError **error)
-{
- EAuthenticationSessionResult auth_result;
- EAsyncClosure *async_closure;
- GAsyncResult *async_result;
-
- async_closure = e_async_closure_new ();
-
- e_authentication_session_execute (
- session, G_PRIORITY_DEFAULT, cancellable,
- e_async_closure_callback, async_closure);
-
- async_result = e_async_closure_wait (async_closure);
-
- auth_result = e_authentication_session_execute_finish (
- session, async_result, error);
-
- e_async_closure_free (async_closure);
-
- return auth_result;
-}
-
-static void
-signon_session_password_execute (EAuthenticationSession *session,
- gint io_priority,
- GCancellable *cancellable,
- GAsyncReadyCallback callback,
- gpointer user_data)
+e_signon_session_password_get (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ gint io_priority,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
- ESourceAuthenticator *authenticator;
- ESourceRegistryServer *server;
- ESource *source;
AgAccountService *ag_account_service;
AgAuthData *ag_auth_data;
SignonAuthSession *signon_auth_session;
- const gchar *source_uid;
guint credentials_id;
GError *error = NULL;
- signon_session_password_msg (session, "Initiated");
-
- authenticator = e_authentication_session_get_authenticator (session);
+ e_signon_session_password_msg (source, "Initiated");
async_context = g_slice_new0 (AsyncContext);
- async_context->authenticator = g_object_ref (authenticator);
if (G_IS_CANCELLABLE (cancellable))
async_context->cancellable = g_object_ref (cancellable);
simple = g_simple_async_result_new (
- G_OBJECT (session), callback, user_data,
- signon_session_password_execute);
+ G_OBJECT (provider_impl), callback, user_data,
+ e_signon_session_password_get);
g_simple_async_result_set_check_cancellable (simple, cancellable);
g_simple_async_result_set_op_res_gpointer (
simple, async_context, (GDestroyNotify) async_context_free);
- server = e_authentication_session_get_server (session);
- source_uid = e_authentication_session_get_source_uid (session);
- source = e_source_registry_server_ref_source (server, source_uid);
-
- if (source == NULL) {
- g_simple_async_result_set_error (
- simple, G_IO_ERROR,
- G_IO_ERROR_NOT_FOUND,
- _("No such data source for UID '%s'"),
- source_uid);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
- return;
- }
-
- ag_account_service =
- signon_session_password_new_account_service (
- session, source, &error);
+ ag_account_service = signon_session_password_new_account_service (provider_impl, source, &error);
g_object_unref (source);
@@ -407,7 +317,7 @@ signon_session_password_execute (EAuthenticationSession *session,
g_signal_connect (
signon_auth_session, "state-changed",
G_CALLBACK (signon_session_password_state_changed_cb),
- session);
+ source);
/* Need to hold on to these in case of retries. */
async_context->signon_auth_session = signon_auth_session;
@@ -435,74 +345,115 @@ signon_session_password_execute (EAuthenticationSession *session,
g_object_unref (simple);
}
-static EAuthenticationSessionResult
-signon_session_password_execute_finish (EAuthenticationSession *session,
- GAsyncResult *result,
- GError **error)
+static gboolean
+e_signon_session_password_get_finish (ESourceCredentialsProviderImpl *provider_impl,
+ GAsyncResult *result,
+ GString *out_password,
+ GError **error)
{
GSimpleAsyncResult *simple;
AsyncContext *async_context;
- EAuthenticationSessionResult session_result;
+ gboolean success;
g_return_val_if_fail (
g_simple_async_result_is_valid (
- result, G_OBJECT (session),
- signon_session_password_execute),
- E_AUTHENTICATION_SESSION_DISMISSED);
+ result, G_OBJECT (provider_impl),
+ e_signon_session_password_get),
+ FALSE);
simple = G_SIMPLE_ASYNC_RESULT (result);
async_context = g_simple_async_result_get_op_res_gpointer (simple);
- if (g_simple_async_result_propagate_error (simple, error))
- session_result = E_AUTHENTICATION_SESSION_ERROR;
- else
- session_result = async_context->session_result;
-
- switch (session_result) {
- case E_AUTHENTICATION_SESSION_ERROR:
- if (error != NULL && *error != NULL)
- signon_session_password_msg (
- session, "Complete (ERROR - %s)",
- (*error)->message);
- else
- signon_session_password_msg (
- session, "Complete (ERROR)");
- break;
- case E_AUTHENTICATION_SESSION_SUCCESS:
- signon_session_password_msg (
- session, "Complete (SUCCESS)");
- break;
- case E_AUTHENTICATION_SESSION_DISMISSED:
- signon_session_password_msg (
- session, "Complete (DISMISSED)");
- break;
- default:
- g_warn_if_reached ();
+ if (g_simple_async_result_propagate_error (simple, error)) {
+ success = FALSE;
+ } else {
+ success = async_context->password != NULL;
+ if (success && out_password)
+ g_string_assign (out_password, async_context->password->str);
}
- return session_result;
+ return success;
+}
+
+static gboolean
+e_signon_session_password_lookup_sync (ESourceCredentialsProviderImpl *provider_impl,
+ ESource *source,
+ GCancellable *cancellable,
+ ENamedParameters **out_credentials,
+ GError **error)
+{
+ EAsyncClosure *async_closure;
+ GAsyncResult *async_result;
+ gboolean success;
+ GString *password;
+
+ g_return_val_if_fail (E_IS_SOURCE (source), FALSE);
+ g_return_val_if_fail (out_credentials != NULL, FALSE);
+
+ async_closure = e_async_closure_new ();
+
+ e_signon_session_password_get (provider_impl, source,
+ G_PRIORITY_DEFAULT, cancellable,
+ e_async_closure_callback, async_closure);
+
+ async_result = e_async_closure_wait (async_closure);
+
+ password = g_string_new ("");
+
+ success = e_signon_session_password_get_finish (provider_impl, async_result, password, error);
+ if (success) {
+ *out_credentials = e_named_parameters_new ();
+ e_named_parameters_set (*out_credentials, E_SOURCE_CREDENTIAL_PASSWORD, password->str);
+ }
+
+ if (password->str)
+ memset (password->str, 0, password->len);
+ g_string_free (password, TRUE);
+
+ e_async_closure_free (async_closure);
+
+ if (success) {
+ e_signon_session_password_msg (source, "Complete (SUCCESS)");
+ } else if (error && *error) {
+ e_signon_session_password_msg (source, "Complete (ERROR - %s)", (*error)->message);
+ } else {
+ e_signon_session_password_msg (source, "Complete (ERROR)");
+ }
+
+
+ return success;
+}
+
+static void
+signon_session_password_dispose (GObject *object)
+{
+ ESignonSessionPasswordPrivate *priv;
+
+ priv = E_SIGNON_SESSION_PASSWORD_GET_PRIVATE (object);
+
+ g_clear_object (&priv->ag_manager);
+
+ /* Chain up to parent's dispose() method. */
+ G_OBJECT_CLASS (e_signon_session_password_parent_class)->
+ dispose (object);
}
static void
e_signon_session_password_class_init (ESignonSessionPasswordClass *class)
{
GObjectClass *object_class;
- EAuthenticationSessionClass *authentication_session_class;
+ ESourceCredentialsProviderImplClass *provider_impl_class;
- g_type_class_add_private (
- class, sizeof (ESignonSessionPasswordPrivate));
+ g_type_class_add_private (class, sizeof (ESignonSessionPasswordPrivate));
object_class = G_OBJECT_CLASS (class);
object_class->dispose = signon_session_password_dispose;
- authentication_session_class =
- E_AUTHENTICATION_SESSION_CLASS (class);
- authentication_session_class->execute_sync =
- signon_session_password_execute_sync;
- authentication_session_class->execute =
- signon_session_password_execute;
- authentication_session_class->execute_finish =
- signon_session_password_execute_finish;
+ provider_impl_class = E_SOURCE_CREDENTIALS_PROVIDER_IMPL_CLASS (class);
+ provider_impl_class->can_process = e_signon_session_password_can_process;
+ provider_impl_class->can_store = e_signon_session_password_can_store;
+ provider_impl_class->can_prompt = e_signon_session_password_can_prompt;
+ provider_impl_class->lookup_sync = e_signon_session_password_lookup_sync;
}
static void
diff --git a/modules/ubuntu-online-accounts/e-signon-session-password.h
b/modules/ubuntu-online-accounts/e-signon-session-password.h
index fec7179..89d1dba 100644
--- a/modules/ubuntu-online-accounts/e-signon-session-password.h
+++ b/modules/ubuntu-online-accounts/e-signon-session-password.h
@@ -18,7 +18,7 @@
#ifndef E_SIGNON_SESSION_PASSWORD_H
#define E_SIGNON_SESSION_PASSWORD_H
-#include <libebackend/libebackend.h>
+#include <libedataserver/libedataserver.h>
/* Standard GObject macros */
#define E_TYPE_SIGNON_SESSION_PASSWORD \
@@ -46,12 +46,12 @@ typedef struct _ESignonSessionPasswordClass ESignonSessionPasswordClass;
typedef struct _ESignonSessionPasswordPrivate ESignonSessionPasswordPrivate;
struct _ESignonSessionPassword {
- EAuthenticationSession parent;
+ ESourceCredentialsProviderImpl parent;
ESignonSessionPasswordPrivate *priv;
};
struct _ESignonSessionPasswordClass {
- EAuthenticationSessionClass parent_class;
+ ESourceCredentialsProviderImplClass parent_class;
};
GType e_signon_session_password_get_type
diff --git a/modules/ubuntu-online-accounts/module-credentials-uoa.c
b/modules/ubuntu-online-accounts/module-credentials-uoa.c
new file mode 100644
index 0000000..30c2a46
--- /dev/null
+++ b/modules/ubuntu-online-accounts/module-credentials-uoa.c
@@ -0,0 +1,37 @@
+/*
+ * module-credentials-uoa.c
+ *
+ * This library is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "e-signon-session-password.h"
+
+/* Module Entry Points */
+void e_module_load (GTypeModule *type_module);
+void e_module_unload (GTypeModule *type_module);
+
+G_MODULE_EXPORT void
+e_module_load (GTypeModule *type_module)
+{
+ e_signon_session_password_type_register (type_module);
+}
+
+G_MODULE_EXPORT void
+e_module_unload (GTypeModule *type_module)
+{
+}
diff --git a/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
index 4caec66..6e5577a 100644
--- a/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
+++ b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c
@@ -21,7 +21,6 @@
#include <libaccounts-glib/accounts-glib.h>
#include "uoa-utils.h"
-#include "e-signon-session-password.h"
/* Standard GObject macros */
#define E_TYPE_UBUNTU_ONLINE_ACCOUNTS \
@@ -375,11 +374,6 @@ ubuntu_online_accounts_config_collection (EUbuntuOnlineAccounts *extension,
e_server_side_source_set_removable (
E_SERVER_SIDE_SOURCE (source), FALSE);
- /* Obtain passwords from the signond service. */
- e_server_side_source_set_auth_session_type (
- E_SERVER_SIDE_SOURCE (source),
- E_TYPE_SIGNON_SESSION_PASSWORD);
-
if (supports_oauth2) {
/* This module provides OAuth 2.0 support to the collection.
* Note, children of the collection source will automatically
@@ -1139,7 +1133,6 @@ G_MODULE_EXPORT void
e_module_load (GTypeModule *type_module)
{
e_ubuntu_online_accounts_register_type (type_module);
- e_signon_session_password_type_register (type_module);
}
G_MODULE_EXPORT void
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 8c78a8d..9f8ff65 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -173,8 +173,6 @@ camel/providers/smtp/camel-smtp-transport.c
data/org.gnome.evolution-data-server.addressbook.gschema.xml.in
data/org.gnome.evolution-data-server.calendar.gschema.xml.in
data/org.gnome.evolution.shell.network-config.gschema.xml.in
-libebackend/e-authentication-mediator.c
-libebackend/e-authentication-session.c
libebackend/e-backend.c
libebackend/e-collection-backend.c
libebackend/e-data-factory.c
@@ -184,13 +182,17 @@ libebackend/e-subprocess-factory.c
libebackend/e-user-prompter-server.c
libedataserver/e-categories.c
libedataserver/e-client.c
-libedataserver/e-source-authenticator.c
libedataserver/e-source.c
+libedataserver/e-source-credentials-provider-impl.c
+libedataserver/e-source-credentials-provider-impl-password.c
libedataserver/e-source-mail-signature.c
libedataserver/e-source-proxy.c
libedataserver/e-source-registry.c
libedataserver/e-source-webdav.c
libedataserver/e-time-utils.c
+libedataserverui/e-credentials-prompter.c
+libedataserverui/e-credentials-prompter-impl-password.c
+libedataserverui/e-trust-prompt.c
modules/gnome-online-accounts/e-goa-password-based.c
modules/gnome-online-accounts/goaewsclient.c
modules/gnome-online-accounts/module-gnome-online-accounts.c
diff --git a/private/org.gnome.evolution.dataserver.AddressBook.xml
b/private/org.gnome.evolution.dataserver.AddressBook.xml
index f8ff126..b91f82b 100644
--- a/private/org.gnome.evolution.dataserver.AddressBook.xml
+++ b/private/org.gnome.evolution.dataserver.AddressBook.xml
@@ -28,6 +28,10 @@
<arg name="error_message" type="s"/>
</signal>
+ <method name="RetrieveProperties">
+ <arg name="properties" direction="out" type="as"/>
+ </method>
+
<method name="Open">
<arg name="properties" direction="out" type="as"/>
</method>
diff --git a/private/org.gnome.evolution.dataserver.Calendar.xml
b/private/org.gnome.evolution.dataserver.Calendar.xml
index 8d76194..5c21327 100644
--- a/private/org.gnome.evolution.dataserver.Calendar.xml
+++ b/private/org.gnome.evolution.dataserver.Calendar.xml
@@ -32,6 +32,10 @@
<arg name="ics_objects" type="as"/>
</signal>
+ <method name="RetrieveProperties">
+ <arg name="properties" direction="out" type="as"/>
+ </method>
+
<method name="Open">
<arg name="properties" direction="out" type="as"/>
</method>
diff --git a/private/org.gnome.evolution.dataserver.Source.xml
b/private/org.gnome.evolution.dataserver.Source.xml
index dc3f0f5..cf31fa2 100644
--- a/private/org.gnome.evolution.dataserver.Source.xml
+++ b/private/org.gnome.evolution.dataserver.Source.xml
@@ -14,14 +14,105 @@
<property name="UID" type="s" access="read"/>
<!-- Data: Raw key file data describing the source. -->
<property name="Data" type="s" access="read"/>
+ <!-- ConnectionStatus: Current ESourceConnectionStatus of the source. @since: 3.14 -->
+ <property name="ConnectionStatus" type="s" access="readwrite"/>
<!--
- AllowAuthPrompt:
+ CredentialsRequired:
+ @reason: one of the ESourceCredentialsReason values
+ @certificate_pem: either an empty string or a PEM-encoded certificate
+ being used for the SSL connection
+ @certificate_errors: a GTlsCertificateFlags bit-or of certificate errors
+ @dbus_error_name: a GDBus' error name for a failed authentication
+ @dbus_error_message: actual error message corresponding to @dbus_error_name
+ @since: 3.14
+
+ Emitted, when the source requires credentials, like for login, to be
+ able to connect to its destination. The client side may listen to it
+ to let a user know that the credentials are required, eventually ask
+ for the credentials (password) straight away.
+ The ConnectionStatus property changes accordingly, when one of
+ the listeners will provide the credentials using Authenticate method.
+ The reason argument says why the credentials are required.
+
+ The value E_SOURCE_CREDENTIALS_REASON_SSL_FAILED indicates a failed
+ certificate check on a secured connection, then the certificate_pem and
+ certificate_errors are populated with the actual certificate being used
+ and what failed with it. Otherwise these two arguments are meaningless.
+ The error argument can contain a text description of the certificate_errors too.
+
+ The value E_SOURCE_CREDENTIALS_REASON_ERROR indicates a failure
+ on the server side, other than rejected credentials or failed SSL
+ checks. In this case the 'error' argument holds a text description of
+ the error. The 'error' argument is meaningless or optional in other cases.
+ -->
+ <signal name="CredentialsRequired">
+ <arg name="reason" direction="in" type="s"/>
+ <arg name="certificate_pem" direction="in" type="s"/>
+ <arg name="certificate_errors" direction="in" type="s"/>
+ <arg name="dbus_error_name" direction="in" type="s"/>
+ <arg name="dbus_error_message" direction="in" type="s"/>
+ </signal>
+
+ <!--
+ InvokeCredentialsRequired:
+ @since: 3.14
+
+ Invokes CredentialsRequired signal on the server side, thus it can
+ be received by all clients.
+ -->
+ <method name="InvokeCredentialsRequired">
+ <arg name="reason" direction="in" type="s"/>
+ <arg name="certificate_pem" direction="in" type="s"/>
+ <arg name="certificate_errors" direction="in" type="s"/>
+ <arg name="dbus_error_name" direction="in" type="s"/>
+ <arg name="dbus_error_message" direction="in" type="s"/>
+ </method>
+
+ <!--
+ GetLastCredentialsRequiredArguments:
+ @reason: (out): The 'reason' argument used in the last call of CredentialsRequired()
+ @certificate_pem: (out): The 'certificate_pem' argument used in the last call of CredentialsRequired()
+ @certificate_errors: (out): The 'certificate_errors' argument used in the last call of
CredentialsRequired()
+ @dbus_error_name: (out): The 'dbus_error_name' argument used in the last call of CredentialsRequired()
+ @dbus_error_message: (out): The 'dbus_error_message' argument used in the last call of
CredentialsRequired()
+ @since: 3.14
+
+ Returns tha last arguments used to the call of CredentialsRequired().
+ The values are automatically unset with the Authenticate() call.
+ -->
+ <method name="GetLastCredentialsRequiredArguments">
+ <arg name="reason" direction="out" type="s"/>
+ <arg name="certificate_pem" direction="out" type="s"/>
+ <arg name="certificate_errors" direction="out" type="s"/>
+ <arg name="dbus_error_name" direction="out" type="s"/>
+ <arg name="dbus_error_message" direction="out" type="s"/>
+ </method>
+
+ <!--
+ Authenticate:
+ @credentials: provided credentials, in a GKeyFile format
+ @since: 3.14
- If the user declines to provide a secret when prompted, further
- authentication prompts are inhibited until this method is called.
+ This is a response method for a signal CredentialsRequired, when
+ the credentials had been obtained from a user. The @credentials is
+ an array of strings suitable for ECredentials structure.
-->
- <method name="AllowAuthPrompt"/>
+ <signal name="Authenticate">
+ <arg name="credentials" direction="in" type="as"/>
+ </signal>
+
+ <!--
+ InvokeAuthenticate:
+ @since: 3.14
+
+ Invokes Authenticate signal on the server side, thus it can
+ be received by all clients (and served by backends).
+ -->
+ <method name="InvokeAuthenticate">
+ <arg name="credentials" direction="in" type="as"/>
+ </method>
+
</interface>
<!--
diff --git a/private/org.gnome.evolution.dataserver.SourceManager.xml
b/private/org.gnome.evolution.dataserver.SourceManager.xml
index ac58f97..3c5fad8 100644
--- a/private/org.gnome.evolution.dataserver.SourceManager.xml
+++ b/private/org.gnome.evolution.dataserver.SourceManager.xml
@@ -11,40 +11,6 @@
-->
<interface name="org.gnome.evolution.dataserver.SourceManager">
<!--
- AllowAuthPromptAll:
- @since: 3.8
-
- This method is equivalent to calling AllowAuthPrompt() on each
- managed object, but does so in a single method invocation.
- -->
- <method name="AllowAuthPromptAll"/>
-
- <!--
- Authenticate:
- @uid: Unique identifier for the authenticating source
- @prompt_title: The title of the prompt
- @prompt_message: The prompt message for the user
- @prompt_description: The detailed description of the prompt
- @object_path: Object path of a new authentication session
-
- Initiates a new authentication session at the returned object
- path. The client should prepare to receive Response signals
- from the Authenticator interface at that object path, then
- call the interface's Ready method.
-
- The @prompt_title, @prompt_message and @prompt_description
- arguments are used to construct an authentication prompt if
- necessary. (See #GcrPrompt for details.)
- -->
- <method name="Authenticate">
- <arg name="uid" direction="in" type="s"/>
- <arg name="prompt_title" direction="in" type="s"/>
- <arg name="prompt_message" direction="in" type="s"/>
- <arg name="prompt_description" direction="in" type="s"/>
- <arg name="object_path" direction="out" type="s"/>
- </method>
-
- <!--
CreateSources:
@array: An array of "uid" and "data" pairs
diff --git a/tests/book-migration/setup-migration-test.c b/tests/book-migration/setup-migration-test.c
index 2350da7..4cfcc36 100644
--- a/tests/book-migration/setup-migration-test.c
+++ b/tests/book-migration/setup-migration-test.c
@@ -130,7 +130,7 @@ source_added (ESourceRegistry *registry,
/* Open the address book */
#if EDS_CHECK_VERSION(3,8,0)
- added_data->book = (EBookClient *) e_book_client_connect_sync (source, NULL, &error);
+ added_data->book = (EBookClient *) e_book_client_connect_sync (source, 30, NULL, &error);
#else
/* With 3.6 it's a bit more tricky */
added_data->book = e_book_client_new (source, &error);
diff --git a/tests/libebook/client/test-book-client-self.c b/tests/libebook/client/test-book-client-self.c
index 5c2e2da..8c7f438 100644
--- a/tests/libebook/client/test-book-client-self.c
+++ b/tests/libebook/client/test-book-client-self.c
@@ -56,7 +56,7 @@ test_set_self (ETestServerFixture *fixture,
/* Open the system addressbook */
source = e_source_registry_ref_builtin_address_book (fixture->registry);
- client = (EBookClient *) e_book_client_connect_sync (source, NULL, &error);
+ client = (EBookClient *) e_book_client_connect_sync (source, 30, NULL, &error);
g_object_unref (source);
if (!client)
g_error ("Error connecting to system addressbook: %s", error->message);
diff --git a/tests/libebook/client/test-book-client-view-operations.c
b/tests/libebook/client/test-book-client-view-operations.c
index baf16cc..4e61469 100644
--- a/tests/libebook/client/test-book-client-view-operations.c
+++ b/tests/libebook/client/test-book-client-view-operations.c
@@ -198,7 +198,7 @@ test_view_thread_async (ThreadData *data)
if (data->closure->type == E_TEST_SERVER_DIRECT_ADDRESS_BOOK) {
/* There is no Async API to open a direct book for now, let's stick with the sync API
*/
- data->client = (EBookClient *) e_book_client_connect_direct_sync (registry, source, NULL,
&error);
+ data->client = (EBookClient *) e_book_client_connect_direct_sync (registry, source, 30, NULL,
&error);
if (!data->client)
g_error ("Unable to create EBookClient for uid '%s': %s", data->book_uid,
error->message);
@@ -208,7 +208,7 @@ test_view_thread_async (ThreadData *data)
} else {
/* Connect asynchronously */
- e_book_client_connect (source, NULL, connect_ready, data);
+ e_book_client_connect (source, 30, NULL, connect_ready, data);
}
g_main_loop_run (data->loop);
@@ -280,9 +280,9 @@ test_view_thread_sync (ThreadData *data)
g_error ("Unable to fetch source uid '%s' from the registry", data->book_uid);
if (data->closure->type == E_TEST_SERVER_DIRECT_ADDRESS_BOOK)
- data->client = (EBookClient *) e_book_client_connect_direct_sync (registry, source, NULL,
&error);
+ data->client = (EBookClient *) e_book_client_connect_direct_sync (registry, source, 30, NULL,
&error);
else
- data->client = (EBookClient *) e_book_client_connect_sync (source, NULL, &error);
+ data->client = (EBookClient *) e_book_client_connect_sync (source, 30, NULL, &error);
if (!data->client)
g_error ("Unable to create EBookClient for uid '%s': %s", data->book_uid, error->message);
diff --git a/tests/test-server-utils/e-test-server-utils.c b/tests/test-server-utils/e-test-server-utils.c
index 7417da1..34a5808 100644
--- a/tests/test-server-utils/e-test-server-utils.c
+++ b/tests/test-server-utils/e-test-server-utils.c
@@ -358,19 +358,19 @@ e_test_server_utils_source_added (ESourceRegistry *registry,
if (pair->closure->type == E_TEST_SERVER_DIRECT_ADDRESS_BOOK) {
if (pair->closure->use_async_connect)
- e_book_client_connect_direct (source, NULL, e_test_server_utils_client_ready,
pair);
+ e_book_client_connect_direct (source, 30, NULL,
e_test_server_utils_client_ready, pair);
else
pair->fixture->service.book_client = (EBookClient *)
e_book_client_connect_direct_sync (
pair->fixture->registry,
- source, NULL, &error);
+ source, 30, NULL, &error);
} else {
if (pair->closure->use_async_connect)
- e_book_client_connect (source, NULL, e_test_server_utils_client_ready, pair);
+ e_book_client_connect (source, 30, NULL, e_test_server_utils_client_ready,
pair);
else
pair->fixture->service.book_client = (EBookClient *)
- e_book_client_connect_sync (source, NULL, &error);
+ e_book_client_connect_sync (source, 30, NULL, &error);
}
if (!pair->closure->use_async_connect &&
@@ -395,7 +395,7 @@ e_test_server_utils_source_added (ESourceRegistry *registry,
if (pair->closure->use_async_connect) {
e_cal_client_connect (
- source, pair->closure->calendar_source_type,
+ source, pair->closure->calendar_source_type, 30,
NULL, e_test_server_utils_client_ready, pair);
} else {
@@ -403,7 +403,7 @@ e_test_server_utils_source_added (ESourceRegistry *registry,
pair->fixture->service.calendar_client = (ECalClient *)
e_cal_client_connect_sync (
source,
- pair->closure->calendar_source_type, NULL, &error);
+ pair->closure->calendar_source_type, 30, NULL, &error);
if (!pair->fixture->service.calendar_client)
g_error ("Unable to create the test calendar: %s", error->message);
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]