diff options
Diffstat (limited to 'modules')
14 files changed, 1705 insertions, 0 deletions
diff --git a/modules/Makefile.am b/modules/Makefile.am index 21e72523f..42ebdfae8 100644 --- a/modules/Makefile.am +++ b/modules/Makefile.am @@ -4,12 +4,17 @@ if HAVE_GOA GNOME_ONLINE_ACCOUNTS_DIR = gnome-online-accounts endif +if HAVE_UOA +UBUNTU_ONLINE_ACCOUNTS_DIR = ubuntu-online-accounts +endif + SUBDIRS = \ cache-reaper \ google-backend \ trust-prompt \ yahoo-backend \ $(GNOME_ONLINE_ACCOUNTS_DIR) \ + $(UBUNTU_ONLINE_ACCOUNTS_DIR) \ $(NULL) -include $(top_srcdir)/git.mk diff --git a/modules/ubuntu-online-accounts/Makefile.am b/modules/ubuntu-online-accounts/Makefile.am new file mode 100644 index 000000000..2dc77a0b8 --- /dev/null +++ b/modules/ubuntu-online-accounts/Makefile.am @@ -0,0 +1,84 @@ +NULL = + +module_LTLIBRARIES = module-ubuntu-online-accounts.la + +module_ubuntu_online_accounts_la_CPPFLAGS = \ + $(AM_CPPFLAGS) \ + -I$(top_srcdir) \ + -DG_LOG_DOMAIN=\"module-ubuntu-online-accounts\" \ + $(LIBACCOUNTS_GLIB_CFLAGS) \ + $(LIBSIGNON_GLIB_CFLAGS) \ + $(E_BACKEND_CFLAGS) \ + $(JSON_GLIB_CFLAGS) \ + $(CAMEL_CFLAGS) \ + $(REST_CFLAGS) \ + $(NULL) + +module_ubuntu_online_accounts_la_SOURCES = \ + module-ubuntu-online-accounts.c \ + uoa-utils.c \ + uoa-utils.h \ + $(NULL) + +module_ubuntu_online_accounts_la_LIBADD = \ + $(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_ubuntu_online_accounts_la_LDFLAGS = \ + -module -avoid-version $(NO_UNDEFINED) \ + $(NULL) + +%.application: %.application.in + $(AM_V_GEN) $(INTLTOOL_MERGE) --no-translations -x -u $< $@ + +%.service-type: %.service-type.in + $(AM_V_GEN) $(INTLTOOL_MERGE) --no-translations -x -u $< $@ + +%.service: %.service.in + $(AM_V_GEN) $(INTLTOOL_MERGE) --no-translations -x -u $< $@ + +applicationdir = `$(PKG_CONFIG) --variable=applicationfilesdir libaccounts-glib` +application_DATA = evolution-data-server.application + +servicetypedir = `$(PKG_CONFIG) --variable=servicetypefilesdir libaccounts-glib` +servicetype_DATA = \ + mail.service-type \ + calendar.service-type \ + contacts.service-type \ + $(NULL) + +servicedir = `$(PKG_CONFIG) --variable=servicefilesdir libaccounts-glib` +service_DATA = \ + google-gmail.service \ + google-calendar.service \ + google-contacts.service \ + yahoo-mail.service \ + yahoo-calendar.service \ + $(NULL) + +EXTRA_DIST = \ + evolution-data-server.application.in.in \ + mail.service-type.in.in \ + calendar.service-type.in.in \ + contacts.service-type.in.in \ + google-gmail.service.in.in \ + google-calendar.service.in.in \ + google-contacts.service.in.in \ + yahoo-mail.service.in.in \ + yahoo-calendar.service.in.in \ + $(NULL) + +DISTCLEANFILES = \ + $(application_DATA) \ + $(servicetype_DATA) \ + $(service_DATA) \ + $(NULL) + +-include $(top_srcdir)/git.mk diff --git a/modules/ubuntu-online-accounts/calendar.service-type.in.in b/modules/ubuntu-online-accounts/calendar.service-type.in.in new file mode 100644 index 000000000..ac03f140a --- /dev/null +++ b/modules/ubuntu-online-accounts/calendar.service-type.in.in @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service-type id="calendar"> + <_name>Calendar</_name> + <_description>Integrate your calendars</_description> + <icon>x-office-calendar</icon> + <translations>@GETTEXT_PACKAGE@</translations> +</service-type> diff --git a/modules/ubuntu-online-accounts/contacts.service-type.in.in b/modules/ubuntu-online-accounts/contacts.service-type.in.in new file mode 100644 index 000000000..3ced915ea --- /dev/null +++ b/modules/ubuntu-online-accounts/contacts.service-type.in.in @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service-type id="contacts"> + <_name>Contacts</_name> + <_description>Integrate your contacts</_description> + <icon>x-office-address-book</icon> + <translations>@GETTEXT_PACKAGE@</translations> +</service-type> + diff --git a/modules/ubuntu-online-accounts/evolution-data-server.application.in.in b/modules/ubuntu-online-accounts/evolution-data-server.application.in.in new file mode 100644 index 000000000..8d9277ec6 --- /dev/null +++ b/modules/ubuntu-online-accounts/evolution-data-server.application.in.in @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<application id="evolution-data-server"> + <description>Evolution-Data-Server</description> + <translations>@GETTEXT_PACKAGE@</translations> + <service-types> + <service-type id="mail"/> + <service-type id="calendar"/> + <service-type id="contacts"/> + </service-types> +</application> diff --git a/modules/ubuntu-online-accounts/google-calendar.service.in.in b/modules/ubuntu-online-accounts/google-calendar.service.in.in new file mode 100644 index 000000000..5810ff1e5 --- /dev/null +++ b/modules/ubuntu-online-accounts/google-calendar.service.in.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service id="google-calendar"> + <type>calendar</type> + <_name>Google Calendar</_name> + <provider>google</provider> + <translations>@GETTEXT_PACKAGE@</translations> + + <!-- default settings (account settings have precedence over these) --> + <template> + <group name="auth"> + <group name="oauth2"> + <group name="user_agent"> + <setting type="as" name="Scope">["https://www.googleapis.com/auth/calendar"]</setting> + </group> + </group> + </group> + </template> +</service> diff --git a/modules/ubuntu-online-accounts/google-contacts.service.in.in b/modules/ubuntu-online-accounts/google-contacts.service.in.in new file mode 100644 index 000000000..47ad7d811 --- /dev/null +++ b/modules/ubuntu-online-accounts/google-contacts.service.in.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service id="google-contacts"> + <type>contacts</type> + <_name>Google Contacts</_name> + <provider>google</provider> + <translations>@GETTEXT_PACKAGE@</translations> + + <!-- default settings (account settings have precedence over these) --> + <template> + <group name="auth"> + <group name="oauth2"> + <group name="user_agent"> + <setting type="as" name="Scope">["https://www.google.com/m8/feeds/"]</setting> + </group> + </group> + </group> + </template> +</service> diff --git a/modules/ubuntu-online-accounts/google-gmail.service.in.in b/modules/ubuntu-online-accounts/google-gmail.service.in.in new file mode 100644 index 000000000..972799e1d --- /dev/null +++ b/modules/ubuntu-online-accounts/google-gmail.service.in.in @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service id="google-gmail"> + <type>mail</type> + <_name>GMail</_name> + <provider>google</provider> + <translations>@GETTEXT_PACKAGE@</translations> + + <!-- default settings (account settings have precedence over these) --> + <template> + <group name="auth"> + <group name="oauth2"> + <group name="user_agent"> + <setting type="as" name="Scope">["https://mail.google.com/"]</setting> + </group> + </group> + </group> + </template> +</service> diff --git a/modules/ubuntu-online-accounts/mail.service-type.in.in b/modules/ubuntu-online-accounts/mail.service-type.in.in new file mode 100644 index 000000000..6917f3ef2 --- /dev/null +++ b/modules/ubuntu-online-accounts/mail.service-type.in.in @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service-type id="mail"> + <_name>Mail</_name> + <_description>Integrate your mailboxes</_description> + <icon>emblem-mail</icon> + <translations>@GETTEXT_PACKAGE@</translations> +</service-type> diff --git a/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c new file mode 100644 index 000000000..c61baa3c1 --- /dev/null +++ b/modules/ubuntu-online-accounts/module-ubuntu-online-accounts.c @@ -0,0 +1,1137 @@ +/* + * module-ubuntu-online-accounts.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 <http://www.gnu.org/licenses/> + * + */ + +#include <config.h> +#include <glib/gi18n-lib.h> +#include <libsignon-glib/signon-glib.h> +#include <libaccounts-glib/accounts-glib.h> + +/* XXX accounts-glib.h should include this */ +#include <libaccounts-glib/ag-auth-data.h> + +#include <libebackend/libebackend.h> + +#include "uoa-utils.h" + +/* Standard GObject macros */ +#define E_TYPE_UBUNTU_ONLINE_ACCOUNTS \ + (e_ubuntu_online_accounts_get_type ()) +#define E_UBUNTU_ONLINE_ACCOUNTS(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST \ + ((obj), E_TYPE_UBUNTU_ONLINE_ACCOUNTS, EUbuntuOnlineAccounts)) + +/* Service types we support. */ +#define SERVICE_TYPE_MAIL "mail" +#define SERVICE_TYPE_CALENDAR "calendar" +#define SERVICE_TYPE_CONTACTS "contacts" + +#define CAMEL_OAUTH2_MECHANISM_NAME "XOAUTH2" + +typedef struct _EUbuntuOnlineAccounts EUbuntuOnlineAccounts; +typedef struct _EUbuntuOnlineAccountsClass EUbuntuOnlineAccountsClass; +typedef struct _AsyncContext AsyncContext; + +struct _EUbuntuOnlineAccounts { + EExtension parent; + + AgManager *ag_manager; + + /* AgAccountId -> ESource UID */ + GHashTable *uoa_to_eds; +}; + +struct _EUbuntuOnlineAccountsClass { + EExtensionClass parent_class; +}; + +struct _AsyncContext { + EUbuntuOnlineAccounts *extension; + EBackendFactory *backend_factory; + gchar *access_token; + gint expires_in; +}; + +/* Module Entry Points */ +void e_module_load (GTypeModule *type_module); +void e_module_unload (GTypeModule *type_module); + +/* Forward Declarations */ +GType e_ubuntu_online_accounts_get_type (void); +static void e_ubuntu_online_accounts_oauth2_support_init + (EOAuth2SupportInterface *interface); + +G_DEFINE_DYNAMIC_TYPE_EXTENDED ( + EUbuntuOnlineAccounts, + e_ubuntu_online_accounts, + E_TYPE_EXTENSION, + 0, + G_IMPLEMENT_INTERFACE_DYNAMIC ( + E_TYPE_OAUTH2_SUPPORT, + e_ubuntu_online_accounts_oauth2_support_init)) + +static void +async_context_free (AsyncContext *async_context) +{ + if (async_context->extension != NULL) + g_object_unref (async_context->extension); + + if (async_context->backend_factory != NULL) + g_object_unref (async_context->backend_factory); + + g_free (async_context->access_token); + + g_slice_free (AsyncContext, async_context); +} + +static const gchar * +ubuntu_online_accounts_get_backend_name (const gchar *uoa_provider_name) +{ + const gchar *eds_backend_name = NULL; + + /* This is a mapping between AgAccount provider names and + * ESourceCollection backend names. It requires knowledge + * of other registry modules, possibly even from 3rd party + * packages. No way around it. */ + + if (g_strcmp0 (uoa_provider_name, "google") == 0) + eds_backend_name = "google"; + + if (g_strcmp0 (uoa_provider_name, "yahoo") == 0) + eds_backend_name = "yahoo"; + + return eds_backend_name; +} + +static ESourceRegistryServer * +ubuntu_online_accounts_get_server (EUbuntuOnlineAccounts *extension) +{ + EExtensible *extensible; + + extensible = e_extension_get_extensible (E_EXTENSION (extension)); + + return E_SOURCE_REGISTRY_SERVER (extensible); +} + +static gboolean +ubuntu_online_accounts_provider_name_to_backend_name (GBinding *binding, + const GValue *source_value, + GValue *target_value, + gpointer unused) +{ + const gchar *provider_name; + const gchar *backend_name; + + provider_name = g_value_get_string (source_value); + backend_name = ubuntu_online_accounts_get_backend_name (provider_name); + g_return_val_if_fail (backend_name != NULL, FALSE); + g_value_set_string (target_value, backend_name); + + return TRUE; +} + +static AgAccountService * +ubuntu_online_accounts_ref_account_service (EUbuntuOnlineAccounts *extension, + ESource *source) +{ + GHashTable *account_services; + ESourceRegistryServer *server; + AgAccountService *ag_account_service = NULL; + const gchar *extension_name; + const gchar *service_name = NULL; + + /* Figure out which AgAccountService to use based + * on which extensions are present in the ESource. */ + + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_CONTACTS; + + extension_name = E_SOURCE_EXTENSION_CALENDAR; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_CALENDAR; + + extension_name = E_SOURCE_EXTENSION_MEMO_LIST; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_CALENDAR; + + extension_name = E_SOURCE_EXTENSION_TASK_LIST; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_CALENDAR; + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_MAIL; + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + if (e_source_has_extension (source, extension_name)) + service_name = SERVICE_TYPE_MAIL; + + g_return_val_if_fail (service_name != NULL, NULL); + + extension_name = E_SOURCE_EXTENSION_UOA; + server = ubuntu_online_accounts_get_server (extension); + + source = e_source_registry_server_find_extension ( + server, source, extension_name); + + if (source != NULL) { + account_services = g_object_get_data ( + G_OBJECT (source), "ag-account-services"); + g_warn_if_fail (account_services != NULL); + + if (account_services != NULL) { + ag_account_service = g_hash_table_lookup ( + account_services, service_name); + if (ag_account_service != NULL) + g_object_ref (ag_account_service); + } + + g_object_unref (source); + } + + return ag_account_service; +} + +static gboolean +ubuntu_online_accounts_supports_oauth2 (AgAccountService *ag_account_service) +{ + AgAuthData *ag_auth_data; + gboolean supports_oauth2 = FALSE; + const gchar *method; + + ag_auth_data = ag_account_service_get_auth_data (ag_account_service); + method = ag_auth_data_get_method (ag_auth_data); + supports_oauth2 = (g_strcmp0 (method, "oauth2") == 0); + ag_auth_data_unref (ag_auth_data); + + return supports_oauth2; +} + +static ESource * +ubuntu_online_accounts_new_source (EUbuntuOnlineAccounts *extension) +{ + ESourceRegistryServer *server; + ESource *source; + GFile *file; + GError *error = NULL; + + /* This being a brand new data source, creating the instance + * should never fail but we'll check for errors just the same. */ + server = ubuntu_online_accounts_get_server (extension); + file = e_server_side_source_new_user_file (NULL); + source = e_server_side_source_new (server, file, &error); + g_object_unref (file); + + if (error != NULL) { + g_warn_if_fail (source == NULL); + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } + + return source; +} + +static GHashTable * +ubuntu_online_accounts_new_account_services (EUbuntuOnlineAccounts *extension, + AgAccount *ag_account) +{ + GHashTable *account_services; + GList *list, *link; + + account_services = g_hash_table_new_full ( + (GHashFunc) g_str_hash, + (GEqualFunc) g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + /* Populate the hash table with AgAccountService instances by + * service type. There should only be one AgService per type. + * + * XXX We really should not have to create AgAccountService + * instances ourselves. The AgAccount itself should own + * them and provide functions for listing them. Instead + * it only provides functions for listing its AgServices, + * which is decidedly less useful. */ + list = ag_account_list_services (ag_account); + for (link = list; link != NULL; link = g_list_next (link)) { + AgService *ag_service = link->data; + const gchar *service_type; + + service_type = ag_service_get_service_type (ag_service); + + g_hash_table_insert ( + account_services, + g_strdup (service_type), + ag_account_service_new (ag_account, ag_service)); + } + ag_service_list_free (list); + + return account_services; +} + +static void +ubuntu_online_accounts_config_oauth2 (EUbuntuOnlineAccounts *extension, + ESource *source, + GHashTable *account_services) +{ + AgAccountService *ag_account_service; + ESourceExtension *source_extension; + const gchar *extension_name; + + ag_account_service = g_hash_table_lookup ( + account_services, SERVICE_TYPE_MAIL); + if (ag_account_service == NULL) + return; + + if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service)) + return; + + extension_name = E_SOURCE_EXTENSION_AUTHENTICATION; + source_extension = e_source_get_extension (source, extension_name); + + e_source_authentication_set_method ( + E_SOURCE_AUTHENTICATION (source_extension), + CAMEL_OAUTH2_MECHANISM_NAME); +} + +static void +ubuntu_online_accounts_config_collection (EUbuntuOnlineAccounts *extension, + ESource *source, + AgAccount *ag_account, + GHashTable *account_services, + const gchar *user_identity) +{ + AgAccountService *ag_account_service; + ESourceExtension *source_extension; + gboolean supports_oauth2 = FALSE; + const gchar *extension_name; + + g_object_bind_property ( + ag_account, "display-name", + source, "display-name", + G_BINDING_SYNC_CREATE); + + g_object_bind_property ( + ag_account, "enabled", + source, "enabled", + G_BINDING_SYNC_CREATE); + + extension_name = E_SOURCE_EXTENSION_UOA; + source_extension = e_source_get_extension (source, extension_name); + + g_object_bind_property ( + ag_account, "id", + source_extension, "account-id", + G_BINDING_SYNC_CREATE); + + extension_name = E_SOURCE_EXTENSION_COLLECTION; + source_extension = e_source_get_extension (source, extension_name); + + g_object_bind_property_full ( + ag_account, "provider", + source_extension, "backend-name", + G_BINDING_SYNC_CREATE, + ubuntu_online_accounts_provider_name_to_backend_name, + NULL, + NULL, (GDestroyNotify) NULL); + + if (user_identity != NULL) + e_source_collection_set_identity ( + E_SOURCE_COLLECTION (source_extension), + user_identity); + + ag_account_service = g_hash_table_lookup ( + account_services, SERVICE_TYPE_MAIL); + if (ag_account_service != NULL) { + g_object_bind_property ( + ag_account_service , "enabled", + source_extension, "mail-enabled", + G_BINDING_SYNC_CREATE); + supports_oauth2 |= + ubuntu_online_accounts_supports_oauth2 ( + ag_account_service); + } + + ag_account_service = g_hash_table_lookup ( + account_services, SERVICE_TYPE_CALENDAR); + if (ag_account_service != NULL) { + g_object_bind_property ( + ag_account_service, "enabled", + source_extension, "calendar-enabled", + G_BINDING_SYNC_CREATE); + supports_oauth2 |= + ubuntu_online_accounts_supports_oauth2 ( + ag_account_service); + } + + ag_account_service = g_hash_table_lookup ( + account_services, SERVICE_TYPE_CONTACTS); + if (ag_account_service != NULL) { + g_object_bind_property ( + ag_account_service, "enabled", + source_extension, "contacts-enabled", + G_BINDING_SYNC_CREATE); + supports_oauth2 |= + ubuntu_online_accounts_supports_oauth2 ( + ag_account_service); + } + + /* Stash the AgAccountService hash table in the ESource + * to keep the property bindings alive. The hash table + * will be destroyed along with the ESource. */ + g_object_set_data_full ( + G_OBJECT (source), + "ag-account-services", + g_hash_table_ref (account_services), + (GDestroyNotify) g_hash_table_unref); + + /* The data source should not be removable by clients. */ + e_server_side_source_set_removable ( + E_SERVER_SIDE_SOURCE (source), FALSE); + + if (supports_oauth2) { + /* This module provides OAuth 2.0 support to the collection. + * Note, children of the collection source will automatically + * inherit our EOAuth2Support through the property binding in + * collection_backend_child_added(). */ + e_server_side_source_set_oauth2_support ( + E_SERVER_SIDE_SOURCE (source), + E_OAUTH2_SUPPORT (extension)); + } +} + +static void +ubuntu_online_accounts_config_mail_account (EUbuntuOnlineAccounts *extension, + ESource *source, + GHashTable *account_services) +{ + EServerSideSource *server_side_source; + + ubuntu_online_accounts_config_oauth2 ( + extension, source, account_services); + + /* Clients may change the source but may not remove it. */ + server_side_source = E_SERVER_SIDE_SOURCE (source); + e_server_side_source_set_writable (server_side_source, TRUE); + e_server_side_source_set_removable (server_side_source, FALSE); +} + +static void +ubuntu_online_accounts_config_mail_identity (EUbuntuOnlineAccounts *extension, + ESource *source, + GHashTable *account_services, + const gchar *email_address) +{ + EServerSideSource *server_side_source; + + if (email_address != NULL) { + ESourceMailIdentity *source_extension; + + source_extension = e_source_get_extension ( + source, E_SOURCE_EXTENSION_MAIL_IDENTITY); + e_source_mail_identity_set_address ( + source_extension, email_address); + } + + /* Clients may change the source but may not remove it. */ + server_side_source = E_SERVER_SIDE_SOURCE (source); + e_server_side_source_set_writable (server_side_source, TRUE); + e_server_side_source_set_removable (server_side_source, FALSE); +} + +static void +ubuntu_online_accounts_config_mail_transport (EUbuntuOnlineAccounts *extension, + ESource *source, + GHashTable *account_services) +{ + EServerSideSource *server_side_source; + + ubuntu_online_accounts_config_oauth2 ( + extension, source, account_services); + + /* Clients may change the source but may not remove it. */ + server_side_source = E_SERVER_SIDE_SOURCE (source); + e_server_side_source_set_writable (server_side_source, TRUE); + e_server_side_source_set_removable (server_side_source, FALSE); +} + +static void +ubuntu_online_accounts_config_sources (EUbuntuOnlineAccounts *extension, + ESource *source, + AgAccount *ag_account) +{ + ESourceRegistryServer *server; + ECollectionBackend *backend; + GHashTable *account_services; + GList *list, *link; + + account_services = ubuntu_online_accounts_new_account_services ( + extension, ag_account); + + ubuntu_online_accounts_config_collection ( + extension, source, ag_account, account_services, NULL); + + server = ubuntu_online_accounts_get_server (extension); + backend = e_source_registry_server_ref_backend (server, source); + g_return_if_fail (backend != NULL); + + list = e_collection_backend_list_mail_sources (backend); + + for (link = list; link != NULL; link = g_list_next (link)) { + const gchar *extension_name; + + source = E_SOURCE (link->data); + + extension_name = E_SOURCE_EXTENSION_MAIL_ACCOUNT; + if (e_source_has_extension (source, extension_name)) + ubuntu_online_accounts_config_mail_account ( + extension, source, account_services); + + extension_name = E_SOURCE_EXTENSION_MAIL_IDENTITY; + if (e_source_has_extension (source, extension_name)) + ubuntu_online_accounts_config_mail_identity ( + extension, source, account_services, NULL); + + extension_name = E_SOURCE_EXTENSION_MAIL_TRANSPORT; + if (e_source_has_extension (source, extension_name)) + ubuntu_online_accounts_config_mail_transport ( + extension, source, account_services); + } + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + + g_object_unref (backend); + + g_hash_table_unref (account_services); +} + +static void +ubuntu_online_accounts_create_collection (EUbuntuOnlineAccounts *extension, + EBackendFactory *backend_factory, + AgAccount *ag_account, + const gchar *user_identity, + const gchar *email_address) +{ + ESourceRegistryServer *server; + ESource *collection_source; + ESource *mail_account_source; + ESource *mail_identity_source; + ESource *mail_transport_source; + GHashTable *account_services; + const gchar *parent_uid; + + server = ubuntu_online_accounts_get_server (extension); + + collection_source = ubuntu_online_accounts_new_source (extension); + g_return_if_fail (E_IS_SOURCE (collection_source)); + + mail_account_source = ubuntu_online_accounts_new_source (extension); + g_return_if_fail (E_IS_SOURCE (mail_account_source)); + + mail_identity_source = ubuntu_online_accounts_new_source (extension); + g_return_if_fail (E_IS_SOURCE (mail_identity_source)); + + mail_transport_source = ubuntu_online_accounts_new_source (extension); + g_return_if_fail (E_IS_SOURCE (mail_transport_source)); + + /* Configure parent/child relationships. */ + parent_uid = e_source_get_uid (collection_source); + e_source_set_parent (mail_account_source, parent_uid); + e_source_set_parent (mail_identity_source, parent_uid); + e_source_set_parent (mail_transport_source, parent_uid); + + /* Give the factory first crack at mail configuration. */ + e_collection_backend_factory_prepare_mail ( + E_COLLECTION_BACKEND_FACTORY (backend_factory), + mail_account_source, + mail_identity_source, + mail_transport_source); + + /* Now it's our turn. */ + account_services = ubuntu_online_accounts_new_account_services ( + extension, ag_account); + ubuntu_online_accounts_config_collection ( + extension, collection_source, ag_account, + account_services, user_identity); + ubuntu_online_accounts_config_mail_account ( + extension, mail_account_source, account_services); + ubuntu_online_accounts_config_mail_identity ( + extension, mail_identity_source, + account_services, email_address); + ubuntu_online_accounts_config_mail_transport ( + extension, mail_transport_source, account_services); + g_hash_table_unref (account_services); + + /* Export the new source collection. */ + e_source_registry_server_add_source (server, collection_source); + e_source_registry_server_add_source (server, mail_account_source); + e_source_registry_server_add_source (server, mail_identity_source); + e_source_registry_server_add_source (server, mail_transport_source); + + g_hash_table_insert ( + extension->uoa_to_eds, + GUINT_TO_POINTER (ag_account->id), + g_strdup (parent_uid)); + + g_object_unref (collection_source); + g_object_unref (mail_account_source); + g_object_unref (mail_identity_source); + g_object_unref (mail_transport_source); +} + +static void +ubuntu_online_accounts_got_userinfo_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + AgAccount *ag_account; + AsyncContext *async_context = user_data; + gchar *user_identity = NULL; + gchar *email_address = NULL; + GError *error = NULL; + + ag_account = AG_ACCOUNT (source_object); + + e_ag_account_collect_userinfo_finish ( + ag_account, result, &user_identity, &email_address, &error); + + if (error == NULL) { + ubuntu_online_accounts_create_collection ( + async_context->extension, + async_context->backend_factory, + ag_account, + user_identity, + email_address); + } else { + g_warning ( + "%s: Failed to create ESource " + "collection for AgAccount '%s': %s", + G_STRFUNC, + ag_account_get_display_name (ag_account), + error->message); + g_error_free (error); + } + + g_free (user_identity); + g_free (email_address); + + async_context_free (async_context); +} + +static void +ubuntu_online_accounts_collect_userinfo (EUbuntuOnlineAccounts *extension, + EBackendFactory *backend_factory, + AgAccount *ag_account) +{ + AsyncContext *async_context; + + /* Before we create a collection we need to collect user info from + * the online service. GNOME Online Accounts does this for us, but + * no such luck with libaccounts-glib or libsignon-glib. */ + + async_context = g_slice_new0 (AsyncContext); + async_context->extension = g_object_ref (extension); + async_context->backend_factory = g_object_ref (backend_factory); + + e_ag_account_collect_userinfo ( + ag_account, NULL, + ubuntu_online_accounts_got_userinfo_cb, + async_context); +} + +static void +ubuntu_online_accounts_remove_collection (EUbuntuOnlineAccounts *extension, + ESource *source) +{ + GError *error = NULL; + + /* This removes the entire subtree rooted at source. + * Deletes the corresponding on-disk key files too. */ + e_source_remove_sync (source, NULL, &error); + + if (error != NULL) { + g_warning ("%s: %s", G_STRFUNC, error->message); + g_error_free (error); + } +} + +static void +ubuntu_online_accounts_account_created_cb (AgManager *ag_manager, + AgAccountId ag_account_id, + EUbuntuOnlineAccounts *extension) +{ + AgAccount *ag_account; + ESourceRegistryServer *server; + EBackendFactory *backend_factory = NULL; + const gchar *provider_name; + const gchar *backend_name; + const gchar *source_uid; + + server = ubuntu_online_accounts_get_server (extension); + + ag_account = ag_manager_get_account (ag_manager, ag_account_id); + g_return_if_fail (ag_account != NULL); + + provider_name = ag_account_get_provider_name (ag_account); + backend_name = ubuntu_online_accounts_get_backend_name (provider_name); + + source_uid = g_hash_table_lookup ( + extension->uoa_to_eds, + GUINT_TO_POINTER (ag_account_id)); + + if (source_uid == NULL && backend_name != NULL) + backend_factory = e_data_factory_ref_backend_factory ( + E_DATA_FACTORY (server), backend_name); + + if (backend_factory != NULL) { + ubuntu_online_accounts_collect_userinfo ( + extension, backend_factory, ag_account); + g_object_unref (backend_factory); + } + + g_object_unref (ag_account); +} + +static void +ubuntu_online_accounts_account_deleted_cb (AgManager *ag_manager, + AgAccountId ag_account_id, + EUbuntuOnlineAccounts *extension) +{ + ESource *source = NULL; + ESourceRegistryServer *server; + const gchar *source_uid; + + server = ubuntu_online_accounts_get_server (extension); + + source_uid = g_hash_table_lookup ( + extension->uoa_to_eds, + GUINT_TO_POINTER (ag_account_id)); + + if (source_uid != NULL) + source = e_source_registry_server_ref_source ( + server, source_uid); + + if (source != NULL) { + ubuntu_online_accounts_remove_collection (extension, source); + g_object_unref (source); + } +} + +static void +ubuntu_online_accounts_populate_accounts_table (EUbuntuOnlineAccounts *extension, + GList *ag_account_ids) +{ + ESourceRegistryServer *server; + GQueue trash = G_QUEUE_INIT; + GList *list, *link; + const gchar *extension_name; + + server = ubuntu_online_accounts_get_server (extension); + + extension_name = E_SOURCE_EXTENSION_UOA; + list = e_source_registry_server_list_sources (server, extension_name); + + for (link = list; link != NULL; link = g_list_next (link)) { + ESource *source; + ESourceUoa *uoa_ext; + AgAccount *ag_account = NULL; + AgAccountId ag_account_id; + const gchar *source_uid; + GList *match; + + source = E_SOURCE (link->data); + source_uid = e_source_get_uid (source); + + extension_name = E_SOURCE_EXTENSION_UOA; + uoa_ext = e_source_get_extension (source, extension_name); + ag_account_id = e_source_uoa_get_account_id (uoa_ext); + + if (ag_account_id == 0) + continue; + + /* Verify the UOA account still exists. */ + match = g_list_find ( + ag_account_ids, + GUINT_TO_POINTER (ag_account_id)); + if (match != NULL) + ag_account = ag_manager_get_account ( + extension->ag_manager, ag_account_id); + + /* If a matching AgAccountId was found, add it + * to our accounts hash table. Otherwise remove + * the ESource after we finish looping. */ + if (ag_account != NULL) { + g_hash_table_insert ( + extension->uoa_to_eds, + GUINT_TO_POINTER (ag_account_id), + g_strdup (source_uid)); + + ubuntu_online_accounts_config_sources ( + extension, source, ag_account); + } else { + g_queue_push_tail (&trash, source); + } + } + + /* Empty the trash. */ + while (!g_queue_is_empty (&trash)) { + ESource *source = g_queue_pop_head (&trash); + ubuntu_online_accounts_remove_collection (extension, source); + } + + g_list_free_full (list, (GDestroyNotify) g_object_unref); +} + +static void +ubuntu_online_accounts_bus_acquired_cb (EDBusServer *server, + GDBusConnection *connection, + EUbuntuOnlineAccounts *extension) +{ + GList *list, *link; + + extension->ag_manager = ag_manager_new (); + + list = ag_manager_list (extension->ag_manager); + + /* This populates a hash table of UOA ID -> ESource UID strings by + * searching through available data sources for ones with a "Ubuntu + * Online Accounts" extension. If such an extension is found, but + * no corresponding AgAccount (presumably meaning the UOA account + * was somehow deleted between E-D-S sessions) then the ESource in + * which the extension was found gets deleted. */ + ubuntu_online_accounts_populate_accounts_table (extension, list); + + for (link = list; link != NULL; link = g_list_next (link)) + ubuntu_online_accounts_account_created_cb ( + extension->ag_manager, + GPOINTER_TO_UINT (link->data), + extension); + + ag_manager_list_free (list); + + /* Listen for Online Account changes. */ + g_signal_connect ( + extension->ag_manager, "account-created", + G_CALLBACK (ubuntu_online_accounts_account_created_cb), + extension); + g_signal_connect ( + extension->ag_manager, "account-deleted", + G_CALLBACK (ubuntu_online_accounts_account_deleted_cb), + extension); +} + +static void +ubuntu_online_accounts_dispose (GObject *object) +{ + EUbuntuOnlineAccounts *extension; + + extension = E_UBUNTU_ONLINE_ACCOUNTS (object); + + if (extension->ag_manager != NULL) { + g_signal_handlers_disconnect_matched ( + extension->ag_manager, + G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, object); + g_object_unref (extension->ag_manager); + extension->ag_manager = NULL; + } + + /* Chain up to parent's dispose() method. */ + G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)-> + dispose (object); +} + +static void +ubuntu_online_accounts_finalize (GObject *object) +{ + EUbuntuOnlineAccounts *extension; + + extension = E_UBUNTU_ONLINE_ACCOUNTS (object); + + g_hash_table_destroy (extension->uoa_to_eds); + + /* Chain up to parent's finalize() method. */ + G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)-> + finalize (object); +} + +static void +ubuntu_online_accounts_constructed (GObject *object) +{ + EExtension *extension; + EExtensible *extensible; + + extension = E_EXTENSION (object); + extensible = e_extension_get_extensible (extension); + + /* Wait for the registry service to acquire its well-known + * bus name so we don't do anything destructive beforehand. */ + + g_signal_connect ( + extensible, "bus-acquired", + G_CALLBACK (ubuntu_online_accounts_bus_acquired_cb), + extension); + + /* Chain up to parent's constructed() method. */ + G_OBJECT_CLASS (e_ubuntu_online_accounts_parent_class)-> + constructed (object); +} + +static gboolean +ubuntu_online_accounts_get_access_token_sync (EOAuth2Support *support, + ESource *source, + GCancellable *cancellable, + gchar **out_access_token, + gint *out_expires_in, + GError **error) +{ + EAsyncClosure *closure; + GAsyncResult *result; + gboolean success; + + closure = e_async_closure_new (); + + e_oauth2_support_get_access_token ( + support, source, cancellable, + e_async_closure_callback, closure); + + result = e_async_closure_wait (closure); + + success = e_oauth2_support_get_access_token_finish ( + support, result, out_access_token, out_expires_in, error); + + e_async_closure_free (closure); + + return success; +} + +/* Helper for ubuntu_online_accounts_get_access_token() */ +static void +ubuntu_online_accounts_session_process_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + GVariant *session_data; + GError *error = NULL; + + simple = G_SIMPLE_ASYNC_RESULT (user_data); + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + session_data = signon_auth_session_process_finish ( + SIGNON_AUTH_SESSION (source_object), result, &error); + + /* Sanity check. */ + g_return_if_fail ( + ((session_data != NULL) && (error == NULL)) || + ((session_data == NULL) && (error != NULL))); + + if (session_data != NULL) { + g_variant_lookup ( + session_data, "AccessToken", "s", + &async_context->access_token); + + g_variant_lookup ( + session_data, "ExpiresIn", "i", + &async_context->expires_in); + + g_warn_if_fail (async_context->access_token != NULL); + g_variant_unref (session_data); + } + + if (error != NULL) + g_simple_async_result_take_error (simple, error); + + g_simple_async_result_complete (simple); + + g_object_unref (simple); +} + +static void +ubuntu_online_accounts_get_access_token (EOAuth2Support *support, + ESource *source, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + SignonAuthSession *session; + AgAccountService *ag_account_service; + AgAuthData *ag_auth_data; + GError *error = NULL; + + async_context = g_slice_new0 (AsyncContext); + + simple = g_simple_async_result_new ( + G_OBJECT (support), callback, user_data, + ubuntu_online_accounts_get_access_token); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + ag_account_service = ubuntu_online_accounts_ref_account_service ( + E_UBUNTU_ONLINE_ACCOUNTS (support), source); + + if (ag_account_service == NULL) { + g_simple_async_result_set_error ( + simple, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, + _("Cannot find a corresponding account " + "service in the accounts database from " + "which to obtain an access token for '%s'"), + e_source_get_display_name (source)); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + /* XXX This should never happen. But because libaccounts-glib + * splits authentication method by service-type instead of + * by provider, and because we broadcast OAuth 2.0 support + * across the entire collection (spanning multiple service + * types), it's conceivable that not all service-types for + * a provider use OAuth 2.0, and an ESource for one of the + * ones that DOESN'T could mistakenly request the token. */ + if (!ubuntu_online_accounts_supports_oauth2 (ag_account_service)) { + g_simple_async_result_set_error ( + simple, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, + _("Data source '%s' does not " + "support OAuth 2.0 authentication"), + e_source_get_display_name (source)); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + ag_auth_data = ag_account_service_get_auth_data (ag_account_service); + + session = signon_auth_session_new ( + ag_auth_data_get_credentials_id (ag_auth_data), + ag_auth_data_get_method (ag_auth_data), &error); + + /* Sanity check. */ + g_return_if_fail ( + ((session != NULL) && (error == NULL)) || + ((session == NULL) && (error != NULL))); + + if (session != NULL) { + signon_auth_session_process_async ( + session, + ag_auth_data_get_login_parameters (ag_auth_data, NULL), + ag_auth_data_get_mechanism (ag_auth_data), + cancellable, + ubuntu_online_accounts_session_process_cb, + g_object_ref (simple)); + g_object_unref (session); + } else { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + } + + ag_auth_data_unref (ag_auth_data); + + g_object_unref (ag_account_service); + g_object_unref (simple); +} + +static gboolean +ubuntu_online_accounts_get_access_token_finish (EOAuth2Support *support, + GAsyncResult *result, + gchar **out_access_token, + gint *out_expires_in, + GError **error) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (support), + ubuntu_online_accounts_get_access_token), 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)) + return FALSE; + + g_return_val_if_fail (async_context->access_token != NULL, FALSE); + + if (out_access_token != NULL) { + *out_access_token = async_context->access_token; + async_context->access_token = NULL; + } + + if (out_expires_in != NULL) + *out_expires_in = async_context->expires_in; + + return TRUE; +} + +static void +e_ubuntu_online_accounts_class_init (EUbuntuOnlineAccountsClass *class) +{ + GObjectClass *object_class; + EExtensionClass *extension_class; + + object_class = G_OBJECT_CLASS (class); + object_class->dispose = ubuntu_online_accounts_dispose; + object_class->finalize = ubuntu_online_accounts_finalize; + object_class->constructed = ubuntu_online_accounts_constructed; + + extension_class = E_EXTENSION_CLASS (class); + extension_class->extensible_type = E_TYPE_SOURCE_REGISTRY_SERVER; +} + +static void +e_ubuntu_online_accounts_class_finalize (EUbuntuOnlineAccountsClass *class) +{ +} + +static void +e_ubuntu_online_accounts_oauth2_support_init (EOAuth2SupportInterface *interface) +{ + interface->get_access_token_sync = ubuntu_online_accounts_get_access_token_sync; + interface->get_access_token = ubuntu_online_accounts_get_access_token; + interface->get_access_token_finish = ubuntu_online_accounts_get_access_token_finish; +} + +static void +e_ubuntu_online_accounts_init (EUbuntuOnlineAccounts *extension) +{ + extension->uoa_to_eds = g_hash_table_new_full ( + (GHashFunc) g_direct_hash, + (GEqualFunc) g_direct_equal, + (GDestroyNotify) NULL, + (GDestroyNotify) g_free); +} + +G_MODULE_EXPORT void +e_module_load (GTypeModule *type_module) +{ + e_ubuntu_online_accounts_register_type (type_module); +} + +G_MODULE_EXPORT void +e_module_unload (GTypeModule *type_module) +{ +} + diff --git a/modules/ubuntu-online-accounts/uoa-utils.c b/modules/ubuntu-online-accounts/uoa-utils.c new file mode 100644 index 000000000..dc65f9877 --- /dev/null +++ b/modules/ubuntu-online-accounts/uoa-utils.c @@ -0,0 +1,324 @@ +/* + * uoa-utils.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 <http://www.gnu.org/licenses/> + * + */ + +#include "uoa-utils.h" + +#include <config.h> +#include <glib/gi18n-lib.h> +#include <rest/rest-proxy.h> +#include <json-glib/json-glib.h> +#include <libsignon-glib/signon-glib.h> + +/* XXX accounts-glib.h should include this */ +#include <libaccounts-glib/ag-auth-data.h> + +#define GOOGLE_USERINFO_URI \ + "https://www.googleapis.com/oauth2/v2/userinfo" + +typedef struct _AsyncContext AsyncContext; + +struct _AsyncContext { + GCancellable *cancellable; + gchar *user_identity; + gchar *email_address; +}; + +static void +async_context_free (AsyncContext *async_context) +{ + if (async_context->cancellable != NULL) + g_object_unref (async_context->cancellable); + + g_free (async_context->user_identity); + g_free (async_context->email_address); + + g_slice_free (AsyncContext, async_context); +} + +/****************************** Google Provider ******************************/ + +static void +e_ag_account_google_got_userinfo_cb (RestProxyCall *call, + const GError *error, + GObject *weak_object, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + JsonParser *json_parser; + JsonObject *json_object; + JsonNode *json_node; + const gchar *email; + GError *local_error = NULL; + + simple = G_SIMPLE_ASYNC_RESULT (user_data); + async_context = g_simple_async_result_get_op_res_gpointer (simple); + + if (error != NULL) { + g_simple_async_result_set_from_error (simple, error); + goto exit; + } + + /* This is shamelessly stolen from goagoogleprovider.c */ + + if (rest_proxy_call_get_status_code (call) != 200) { + g_simple_async_result_set_error ( + simple, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Expected status 200 when requesting guid, " + "instead got status %d (%s)"), + rest_proxy_call_get_status_code (call), + rest_proxy_call_get_status_message (call)); + goto exit; + } + + json_parser = json_parser_new (); + json_parser_load_from_data ( + json_parser, + rest_proxy_call_get_payload (call), + rest_proxy_call_get_payload_length (call), + &local_error); + + if (local_error != NULL) { + g_prefix_error ( + &local_error, + _("Error parsing response as JSON: ")); + g_simple_async_result_take_error (simple, local_error); + g_object_unref (json_parser); + goto exit; + } + + json_node = json_parser_get_root (json_parser); + json_object = json_node_get_object (json_node); + email = json_object_get_string_member (json_object, "email"); + + if (email != NULL) { + async_context->user_identity = g_strdup (email); + async_context->email_address = g_strdup (email); + } else { + g_simple_async_result_set_error ( + simple, G_IO_ERROR, G_IO_ERROR_FAILED, + _("Didn't find email member in JSON data")); + } + + g_object_unref (json_parser); + +exit: + g_simple_async_result_complete (simple); + + g_object_unref (simple); +} + +static void +e_ag_account_google_session_process_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + GVariant *session_data; + GError *error = NULL; + + simple = G_SIMPLE_ASYNC_RESULT (user_data); + + session_data = signon_auth_session_process_finish ( + SIGNON_AUTH_SESSION (source_object), result, &error); + + /* Sanity check. */ + g_return_if_fail ( + ((session_data != NULL) && (error == NULL)) || + ((session_data == NULL) && (error != NULL))); + + /* Use the access token to obtain the user's email address. */ + + if (session_data != NULL) { + RestProxy *proxy; + RestProxyCall *call; + gchar *access_token = NULL; + + g_variant_lookup ( + session_data, "AccessToken", "s", &access_token); + + g_variant_unref (session_data); + + proxy = rest_proxy_new (GOOGLE_USERINFO_URI, FALSE); + call = rest_proxy_new_call (proxy); + rest_proxy_call_set_method (call, "GET"); + + /* XXX This should never be NULL, but if it is just let + * the call fail and pick up the resulting GError. */ + if (access_token != NULL) { + rest_proxy_call_add_param ( + call, "access_token", access_token); + g_free (access_token); + } + + /* XXX The 3rd argument is supposed to be a GObject + * that RestProxyCall weakly references such that + * its disposal cancels the call. This obviously + * predates GCancellable. Too bizarre to bother. */ + rest_proxy_call_async ( + call, e_ag_account_google_got_userinfo_cb, + NULL, g_object_ref (simple), &error); + + if (error != NULL) { + /* Undo the reference added to the async call. */ + g_object_unref (simple); + } + + g_object_unref (proxy); + g_object_unref (call); + } + + if (error != NULL) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + } + + g_object_unref (simple); +} + +static void +e_ag_account_collect_google_userinfo (GSimpleAsyncResult *simple, + AgAccount *ag_account, + GCancellable *cancellable) +{ + AgAccountService *ag_account_service = NULL; + SignonAuthSession *session; + AgAuthData *ag_auth_data; + GList *list; + GError *error = NULL; + + /* First obtain an OAuth 2.0 access token. */ + + list = ag_account_list_services_by_type (ag_account, "mail"); + if (list != NULL) { + ag_account_service = ag_account_service_new ( + ag_account, (AgService *) list->data); + ag_service_list_free (list); + } + + g_return_if_fail (ag_account_service != NULL); + + ag_auth_data = ag_account_service_get_auth_data (ag_account_service); + + session = signon_auth_session_new ( + ag_auth_data_get_credentials_id (ag_auth_data), + ag_auth_data_get_method (ag_auth_data), &error); + + /* Sanity check. */ + g_return_if_fail ( + ((session != NULL) && (error == NULL)) || + ((session == NULL) && (error != NULL))); + + if (session != NULL) { + signon_auth_session_process_async ( + session, + ag_auth_data_get_login_parameters (ag_auth_data, NULL), + ag_auth_data_get_mechanism (ag_auth_data), + cancellable, + e_ag_account_google_session_process_cb, + g_object_ref (simple)); + } else { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + } + + ag_auth_data_unref (ag_auth_data); + + g_object_unref (ag_account_service); + g_object_unref (simple); +} + +/************************ End Provider-Specific Code *************************/ + +void +e_ag_account_collect_userinfo (AgAccount *ag_account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + const gchar *provider_name; + + g_return_if_fail (AG_IS_ACCOUNT (ag_account)); + + async_context = g_slice_new0 (AsyncContext); + + if (G_IS_CANCELLABLE (cancellable)) + async_context->cancellable = g_object_ref (cancellable); + + simple = g_simple_async_result_new ( + G_OBJECT (ag_account), callback, + user_data, e_ag_account_collect_userinfo); + + g_simple_async_result_set_check_cancellable (simple, cancellable); + + g_simple_async_result_set_op_res_gpointer ( + simple, async_context, (GDestroyNotify) async_context_free); + + /* XXX This has to be done differently for each provider. */ + + provider_name = ag_account_get_provider_name (ag_account); + + if (g_strcmp0 (provider_name, "google") == 0) { + e_ag_account_collect_google_userinfo ( + g_object_ref (simple), ag_account, cancellable); + } else { + g_warn_if_reached (); + g_simple_async_result_complete_in_idle (simple); + } + + g_object_unref (simple); +} + +gboolean +e_ag_account_collect_userinfo_finish (AgAccount *ag_account, + GAsyncResult *result, + gchar **out_user_identity, + gchar **out_email_address, + GError **error) +{ + GSimpleAsyncResult *simple; + AsyncContext *async_context; + + g_return_val_if_fail ( + g_simple_async_result_is_valid ( + result, G_OBJECT (ag_account), + e_ag_account_collect_userinfo), 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)) + return FALSE; + + /* The result strings may be NULL without an error. */ + + if (out_user_identity != NULL) { + *out_user_identity = async_context->user_identity; + async_context->user_identity = NULL; + } + + if (out_email_address != NULL) { + *out_email_address = async_context->email_address; + async_context->email_address = NULL; + } + + return TRUE; +} + diff --git a/modules/ubuntu-online-accounts/uoa-utils.h b/modules/ubuntu-online-accounts/uoa-utils.h new file mode 100644 index 000000000..b4582c0c1 --- /dev/null +++ b/modules/ubuntu-online-accounts/uoa-utils.h @@ -0,0 +1,39 @@ +/* + * uoa-utils.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 <http://www.gnu.org/licenses/> + * + */ + +#ifndef UOA_UTILS_H +#define UOA_UTILS_H + +#include <libaccounts-glib/accounts-glib.h> + +G_BEGIN_DECLS + +void e_ag_account_collect_userinfo (AgAccount *ag_account, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean e_ag_account_collect_userinfo_finish + (AgAccount *ag_account, + GAsyncResult *result, + gchar **out_user_identity, + gchar **out_email_address, + GError **error); + +G_END_DECLS + +#endif /* UOA_UTILS_H */ diff --git a/modules/ubuntu-online-accounts/yahoo-calendar.service.in.in b/modules/ubuntu-online-accounts/yahoo-calendar.service.in.in new file mode 100644 index 000000000..b91f1c007 --- /dev/null +++ b/modules/ubuntu-online-accounts/yahoo-calendar.service.in.in @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service id="yahoo-calendar"> + <type>calendar</type> + <_name>Yahoo! Calendar</_name> + <provider>yahoo</provider> + <translations>@GETTEXT_PACKAGE@</translations> + + <!-- default settings (account settings have precedence over these) --> + <template> + <group name="auth"> + <setting name="method">password</setting> + <setting name="mechanism">password</setting> + </group> + </template> +</service> diff --git a/modules/ubuntu-online-accounts/yahoo-mail.service.in.in b/modules/ubuntu-online-accounts/yahoo-mail.service.in.in new file mode 100644 index 000000000..e75556fb8 --- /dev/null +++ b/modules/ubuntu-online-accounts/yahoo-mail.service.in.in @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<service id="yahoo-mail"> + <type>mail</type> + <_name>Yahoo! Mail</_name> + <provider>yahoo</provider> + <translations>@GETTEXT_PACKAGE@</translations> + + <!-- default settings (account settings have precedence over these) --> + <template> + <group name="auth"> + <setting name="method">password</setting> + <setting name="mechanism">password</setting> + </group> + </template> +</service> |
