From 1ed5b89c02656c9172957980c85e412a27b2e6a2 Mon Sep 17 00:00:00 2001 From: Matthew Barnes Date: Wed, 10 Oct 2012 22:06:59 -0400 Subject: Prototype CamelSaslXOAuth2. Untested, but conforms to the specification as well as I understand it. --- modules/online-accounts/Makefile.am | 13 +- modules/online-accounts/camel-sasl-xoauth2.c | 266 +++++++++++++++++++++ modules/online-accounts/camel-sasl-xoauth2.h | 65 +++++ .../online-accounts/evolution-online-accounts.c | 2 + 4 files changed, 343 insertions(+), 3 deletions(-) create mode 100644 modules/online-accounts/camel-sasl-xoauth2.c create mode 100644 modules/online-accounts/camel-sasl-xoauth2.h (limited to 'modules/online-accounts') diff --git a/modules/online-accounts/Makefile.am b/modules/online-accounts/Makefile.am index 9b30ced4f0..d12fd08d03 100644 --- a/modules/online-accounts/Makefile.am +++ b/modules/online-accounts/Makefile.am @@ -1,3 +1,5 @@ +NULL = + module_LTLIBRARIES = module-online-accounts.la module_online_accounts_la_CPPFLAGS = \ @@ -6,12 +8,16 @@ module_online_accounts_la_CPPFLAGS = \ -DG_LOG_DOMAIN=\"evolution-online-accounts\" \ $(EVOLUTION_DATA_SERVER_CFLAGS) \ $(GNOME_PLATFORM_CFLAGS) \ - $(GOA_CFLAGS) + $(GOA_CFLAGS) \ + $(NULL) module_online_accounts_la_SOURCES = \ evolution-online-accounts.c \ camel-sasl-xoauth.c \ - camel-sasl-xoauth.h + camel-sasl-xoauth.h \ + camel-sasl-xoauth2.c \ + camel-sasl-xoauth2.h \ + $(NULL) module_online_accounts_la_LIBADD = \ $(top_builddir)/e-util/libeutil.la \ @@ -20,7 +26,8 @@ module_online_accounts_la_LIBADD = \ $(top_builddir)/libemail-utils/libemail-utils.la \ $(EVOLUTION_DATA_SERVER_LIBS) \ $(GNOME_PLATFORM_LIBS) \ - $(GOA_LIBS) + $(GOA_LIBS) \ + $(NULL) module_online_accounts_la_LDFLAGS = \ -module -avoid-version $(NO_UNDEFINED) diff --git a/modules/online-accounts/camel-sasl-xoauth2.c b/modules/online-accounts/camel-sasl-xoauth2.c new file mode 100644 index 0000000000..eca048affa --- /dev/null +++ b/modules/online-accounts/camel-sasl-xoauth2.c @@ -0,0 +1,266 @@ +/* + * camel-sasl-xoauth2.c + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +/* XXX Yeah, yeah... */ +#define GOA_API_IS_SUBJECT_TO_CHANGE + +#include +#include + +#include + +#include + +#include "camel-sasl-xoauth2.h" + +#define CAMEL_SASL_XOAUTH2_GET_PRIVATE(obj) \ + (G_TYPE_INSTANCE_GET_PRIVATE \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2, CamelSaslXOAuth2Private)) + +struct _CamelSaslXOAuth2Private { + gint placeholder; +}; + +G_DEFINE_DYNAMIC_TYPE (CamelSaslXOAuth2, camel_sasl_xoauth2, CAMEL_TYPE_SASL) + +static void +sasl_xoauth2_append_request (GByteArray *byte_array, + const gchar *identity, + const gchar *access_token) +{ + GString *request; + + /* Compared to OAuth 1.0, this step is trivial. */ + + /* The request is easier to assemble with a GString. */ + request = g_string_sized_new (512); + + g_string_append (request, "user="); + g_string_append (request, identity); + g_string_append_c (request, 1); + g_string_append (request, "auth=Bearer "); + g_string_append (request, access_token); + g_string_append_c (request, 1); + g_string_append_c (request, 1); + + /* Copy the GString content to the GByteArray. */ + g_byte_array_append ( + byte_array, (guint8 *) request->str, request->len); + + g_string_free (request, TRUE); +} + +static gchar * +sasl_xoauth2_find_account_id (ESourceRegistry *registry, + const gchar *uid) +{ + ESource *source; + ESource *ancestor; + const gchar *extension_name; + gchar *account_id = NULL; + + extension_name = E_SOURCE_EXTENSION_GOA; + + source = e_source_registry_ref_source (registry, uid); + g_return_val_if_fail (source != NULL, NULL); + + ancestor = e_source_registry_find_extension ( + registry, source, extension_name); + + if (ancestor != NULL) { + ESourceGoa *extension; + + extension = e_source_get_extension (ancestor, extension_name); + account_id = e_source_goa_dup_account_id (extension); + + g_object_unref (ancestor); + } + + g_object_unref (source); + + return account_id; +} + +static GoaObject * +sasl_xoauth2_get_account_by_id (GoaClient *client, + const gchar *account_id) +{ + GoaObject *match = NULL; + GList *list, *iter; + + list = goa_client_get_accounts (client); + + for (iter = list; iter != NULL; iter = g_list_next (iter)) { + GoaObject *goa_object; + GoaAccount *goa_account; + const gchar *candidate_id; + + goa_object = GOA_OBJECT (iter->data); + goa_account = goa_object_get_account (goa_object); + candidate_id = goa_account_get_id (goa_account); + + if (g_strcmp0 (account_id, candidate_id) == 0) + match = g_object_ref (goa_object); + + g_object_unref (goa_account); + + if (match != NULL) + break; + } + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + + return match; +} + +static GByteArray * +sasl_xoauth2_challenge_sync (CamelSasl *sasl, + GByteArray *token, + GCancellable *cancellable, + GError **error) +{ + GoaClient *goa_client; + GoaObject *goa_object; + GoaAccount *goa_account; + GByteArray *byte_array = NULL; + CamelService *service; + CamelSession *session; + ESourceRegistry *registry; + const gchar *uid; + gchar *account_id; + gboolean success; + + service = camel_sasl_get_service (sasl); + session = camel_service_get_session (service); + registry = e_mail_session_get_registry (E_MAIL_SESSION (session)); + + goa_client = goa_client_new_sync (cancellable, error); + if (goa_client == NULL) + return NULL; + + uid = camel_service_get_uid (service); + account_id = sasl_xoauth2_find_account_id (registry, uid); + goa_object = sasl_xoauth2_get_account_by_id (goa_client, account_id); + + g_free (account_id); + + if (goa_object == NULL) { + g_set_error_literal ( + error, CAMEL_SERVICE_ERROR, + CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE, + _("Cannot find a corresponding account in " + "the org.gnome.OnlineAccounts service from " + "which to obtain an authentication token.")); + g_object_unref (goa_client); + return NULL; + } + + goa_account = goa_object_get_account (goa_object); + + success = goa_account_call_ensure_credentials_sync ( + goa_account, NULL, cancellable, error); + + if (success) { + GoaOAuth2Based *goa_oauth2_based; + gchar *access_token = NULL; + + goa_oauth2_based = goa_object_get_oauth2_based (goa_object); + + success = goa_oauth2_based_call_get_access_token_sync ( + goa_oauth2_based, + &access_token, NULL, + cancellable, error); + + if (success) { + gchar *identity; + + identity = goa_account_dup_identity (goa_account); + + byte_array = g_byte_array_new (); + sasl_xoauth2_append_request ( + byte_array, identity, access_token); + + g_free (identity); + } + + g_free (access_token); + + g_object_unref (goa_oauth2_based); + } + + g_object_unref (goa_account); + g_object_unref (goa_object); + g_object_unref (goa_client); + + /* IMAP and SMTP services will Base64-encode the request. */ + + return byte_array; +} + +static gpointer +camel_sasl_xoauth2_auth_type_init (gpointer unused) +{ + CamelServiceAuthType *auth_type; + + /* This is a one-time allocation, never freed. */ + auth_type = g_malloc0 (sizeof (CamelServiceAuthType)); + auth_type->name = _("OAuth2"); + auth_type->description = + _("This option will connect to the server by " + "way of the GNOME Online Accounts service"); + auth_type->authproto = "XOAUTH2"; + auth_type->need_password = FALSE; + + return auth_type; +} + +static void +camel_sasl_xoauth2_class_init (CamelSaslXOAuth2Class *class) +{ + static GOnce auth_type_once = G_ONCE_INIT; + CamelSaslClass *sasl_class; + + g_once (&auth_type_once, camel_sasl_xoauth2_auth_type_init, NULL); + + g_type_class_add_private (class, sizeof (CamelSaslXOAuth2Private)); + + sasl_class = CAMEL_SASL_CLASS (class); + sasl_class->auth_type = auth_type_once.retval; + sasl_class->challenge_sync = sasl_xoauth2_challenge_sync; +} + +static void +camel_sasl_xoauth2_class_finalize (CamelSaslXOAuth2Class *class) +{ +} + +static void +camel_sasl_xoauth2_init (CamelSaslXOAuth2 *sasl) +{ + sasl->priv = CAMEL_SASL_XOAUTH2_GET_PRIVATE (sasl); +} + +void +camel_sasl_xoauth2_type_register (GTypeModule *type_module) +{ + /* XXX G_DEFINE_DYNAMIC_TYPE declares a static type registration + * function, so we have to wrap it with a public function in + * order to register types from a separate compilation unit. */ + camel_sasl_xoauth2_register_type (type_module); +} + diff --git a/modules/online-accounts/camel-sasl-xoauth2.h b/modules/online-accounts/camel-sasl-xoauth2.h new file mode 100644 index 0000000000..3528d83f9b --- /dev/null +++ b/modules/online-accounts/camel-sasl-xoauth2.h @@ -0,0 +1,65 @@ +/* + * camel-sasl-xoauth2.h + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see + * + */ + +#ifndef CAMEL_SASL_XOAUTH2_H +#define CAMEL_SASL_XOAUTH2_H + +#include + +/* Standard GObject macros */ +#define CAMEL_TYPE_SASL_XOAUTH2 \ + (camel_sasl_xoauth2_get_type ()) +#define CAMEL_SASL_XOAUTH2(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2, CamelSaslXOAuth2)) +#define CAMEL_SASL_XOAUTH2_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST \ + ((cls), CAMEL_TYPE_SASL_XOAUTH2, CamelSaslXOAuth2Class)) +#define CAMEL_IS_SASL_XOAUTH2(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2)) +#define CAMEL_IS_SASL_XOAUTH2_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE \ + ((cls), CAMEL_TYPE_SASL_XOAUTH2)) +#define CAMEL_SASL_XOAUTH2_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS \ + ((obj), CAMEL_TYPE_SASL_XOAUTH2, CamelSaslXOAuth2Class)) + +G_BEGIN_DECLS + +typedef struct _CamelSaslXOAuth2 CamelSaslXOAuth2; +typedef struct _CamelSaslXOAuth2Class CamelSaslXOAuth2Class; +typedef struct _CamelSaslXOAuth2Private CamelSaslXOAuth2Private; + +struct _CamelSaslXOAuth2 { + CamelSasl parent; + CamelSaslXOAuth2Private *priv; +}; + +struct _CamelSaslXOAuth2Class { + CamelSaslClass parent_class; +}; + +GType camel_sasl_xoauth2_get_type (void); +void camel_sasl_xoauth2_type_register + (GTypeModule *type_module); + +G_END_DECLS + +#endif /* CAMEL_SASL_XOAUTH2_H */ + diff --git a/modules/online-accounts/evolution-online-accounts.c b/modules/online-accounts/evolution-online-accounts.c index 9f80c93c7a..1093e6d818 100644 --- a/modules/online-accounts/evolution-online-accounts.c +++ b/modules/online-accounts/evolution-online-accounts.c @@ -17,6 +17,7 @@ */ #include "camel-sasl-xoauth.h" +#include "camel-sasl-xoauth2.h" /* Module Entry Points */ void e_module_load (GTypeModule *type_module); @@ -26,6 +27,7 @@ G_MODULE_EXPORT void e_module_load (GTypeModule *type_module) { camel_sasl_xoauth_type_register (type_module); + camel_sasl_xoauth2_type_register (type_module); } G_MODULE_EXPORT void -- cgit v1.2.3