From 5108a21def754c715eb98162739e6540c83e856c Mon Sep 17 00:00:00 2001 From: Oskar Viljasaar Date: Fri, 27 Oct 2017 16:19:35 +0200 Subject: [PATCH] Implement GOA support for password-based accounts --- po/POTFILES.in | 2 + src/CMakeLists.txt | 5 + src/client/accounts/account-manager.vala | 317 +++++++++++++----- .../accounts/goa-service-information.vala | 41 +++ .../accounts/local-service-information.vala | 12 +- src/client/application/geary-application.vala | 2 +- src/client/application/geary-controller.vala | 27 +- src/client/application/goa-mediator.vala | 64 ++++ src/engine/api/geary-service-information.vala | 18 +- src/engine/util/util-config-file.vala | 8 +- .../api/geary-service-information-mock.vala | 20 -- 11 files changed, 387 insertions(+), 129 deletions(-) create mode 100644 src/client/accounts/goa-service-information.vala create mode 100644 src/client/application/goa-mediator.vala diff --git a/po/POTFILES.in b/po/POTFILES.in index ef669378..e7f38cec 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -17,12 +17,14 @@ src/client/accounts/account-dialog.vala src/client/accounts/account-manager.vala src/client/accounts/account-spinner-page.vala src/client/accounts/add-edit-page.vala +src/client/accounts/goa-service-information.vala src/client/accounts/local-service-information.vala src/client/accounts/login-dialog.vala src/client/application/autostart-manager.vala src/client/application/geary-application.vala src/client/application/geary-args.vala src/client/application/geary-controller.vala +src/client/application/goa-mediator.vala src/client/application/main.vala src/client/application/secret-mediator.vala src/client/components/client-web-view.vala diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4cc3910c..3c4d50bf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -330,6 +330,7 @@ client/application/geary-application.vala client/application/geary-args.vala client/application/geary-config.vala client/application/geary-controller.vala +client/application/goa-mediator.vala client/application/secret-mediator.vala client/accounts/account-dialog.vala @@ -344,6 +345,7 @@ client/accounts/account-manager.vala client/accounts/account-spinner-page.vala client/accounts/add-edit-page.vala client/accounts/local-service-information.vala +client/accounts/goa-service-information.vala client/accounts/login-dialog.vala client/components/client-web-view.vala @@ -523,6 +525,7 @@ pkg_check_modules(DEPS REQUIRED javascriptcoregtk-4.0>=${TARGET_WEBKIT} enchant>=1.6 libunwind-generic>=1.1 + goa-1.0 ${EXTRA_CLIENT_PKG_CONFIG} ) @@ -558,6 +561,7 @@ set(CLIENT_PACKAGES libsecret-1 libsoup-2.4 webkit2gtk-4.0 + goa-1.0 ${EXTRA_CLIENT_PACKAGES} ) @@ -594,6 +598,7 @@ set(CFLAGS # code. Suppress them so we can actually see more useful warnings. -Wno-incompatible-pointer-types -Wno-discarded-qualifiers + -DGOA_API_IS_SUBJECT_TO_CHANGE ) if (REF_TRACKING) diff --git a/src/client/accounts/account-manager.vala b/src/client/accounts/account-manager.vala index 13e56462..941b50c5 100644 --- a/src/client/accounts/account-manager.vala +++ b/src/client/accounts/account-manager.vala @@ -10,23 +10,32 @@ */ public enum CredentialsProvider { /** Credentials are provided and stored by libsecret. */ - LIBSECRET; + LIBSECRET, + + /** Credentials are provided and stored by gnome-online-accounts. */ + GOA; public string to_string() { switch (this) { case LIBSECRET: return "libsecret"; + case GOA: + return "goa"; + default: assert_not_reached(); } } public static CredentialsProvider from_string(string str) throws Error { - switch (str) { + switch (str.ascii_down()) { case "libsecret": return LIBSECRET; + case "goa": + return GOA; + default: throw new KeyFileError.INVALID_VALUE( "Unknown credentials provider type: %s", str @@ -36,7 +45,9 @@ public enum CredentialsProvider { } errordomain AccountError { - INVALID; + INVALID, + GOA_UNAVAILABLE, + GOA_REMOVED; } public class AccountManager : GLib.Object { @@ -65,11 +76,18 @@ public class AccountManager : GLib.Object { private const string TRASH_FOLDER_KEY = "trash_folder"; private const string USE_EMAIL_SIGNATURE_KEY = "use_email_signature"; + private const string GOA_ID_PREFIX = "goa_"; + + + private Gee.Map enabled_accounts = + new Gee.HashMap(); + private Geary.Engine engine; private GLib.File user_config_dir; private GLib.File user_data_dir; - private Geary.CredentialsMediator libsecret; + private Geary.CredentialsMediator? libsecret = null; + private Goa.Client? goa_service = null; public AccountManager(Geary.Engine engine, @@ -78,18 +96,26 @@ public class AccountManager : GLib.Object { this.engine = engine; this.user_config_dir = user_config_dir; this.user_data_dir = user_data_dir; + } + public async void connect_libsecret(GLib.Cancellable? cancellable) + throws GLib.Error { this.libsecret = new SecretMediator(); } - public Geary.ServiceInformation new_libsecret_service(Geary.Service service, - Geary.CredentialsMethod method) { + public async void connect_goa(GLib.Cancellable? cancellable) + throws GLib.Error { + this.goa_service = yield new Goa.Client(cancellable); + } + + public LocalServiceInformation + new_libsecret_service(Geary.Service service, + Geary.CredentialsMethod method) { return new LocalServiceInformation(service, method, libsecret); } - public async void create_account_dirs(Geary.AccountInformation info, - Cancellable? cancellable = null) + Cancellable? cancellable) throws GLib.Error { GLib.File config = this.user_config_dir.get_child(info.id); GLib.File data = this.user_data_dir.get_child(info.id); @@ -100,8 +126,9 @@ public class AccountManager : GLib.Object { info.set_account_directories(config, data); } - public async void add_existing_accounts_async(GLib.Cancellable? cancellable = null) + public async void load_accounts(GLib.Cancellable? cancellable) throws GLib.Error { + // Step 1. Load existing accounts from the user config dir GLib.FileEnumerator? enumerator = null; try { enumerator = yield this.user_config_dir.enumerate_children_async( @@ -123,17 +150,19 @@ public class AccountManager : GLib.Object { uint len = info_list.length(); for (uint i = 0; i < len && !cancellable.is_cancelled(); i++) { - GLib.FileInfo info = info_list.nth_data(i); - if (info.get_file_type() == FileType.DIRECTORY) { + GLib.FileInfo file = info_list.nth_data(i); + if (file.get_file_type() == FileType.DIRECTORY) { try { - string id = info.get_name(); - this.engine.add_account(yield load_account(id, cancellable)); + Geary.AccountInformation info = yield load_account( + file.get_name(), cancellable + ); + enable_account(info); } catch (GLib.Error err) { // XXX want to report this problem to the user // somehow, but at this point in the app's // lifecycle we don't even have a main window. warning("Ignoring empty/bad config in %s: %s", - info.get_name(), err.message); + file.get_name(), err.message); } } } @@ -143,7 +172,31 @@ public class AccountManager : GLib.Object { enumerator = null; } } - } + + // Step 2. Load previously unseen accounts from GOA, if available. + if (this.goa_service != null) { + GLib.List list = this.goa_service.get_accounts(); + for (int i=0; i < list.length() && !cancellable.is_cancelled(); i++) { + Goa.Object account = list.nth_data(i); + string id = to_geary_id(account.get_account()); + if (!this.enabled_accounts.has_key(id)) { + Geary.AccountInformation? info = null; + try { + info = yield create_goa_account(account, cancellable); + } catch (GLib.Error err) { + // XXX want to report this problem to the user + // somehow, but at this point in the app's + // lifecycle we don't even have a main window. + warning("Error creating GOA account %s: %s", + account.get_account().id, err.message); + } + if (info != null) { + enable_account(info); + } + } + } + } + } /** * Loads an account info from a config directory. @@ -151,7 +204,7 @@ public class AccountManager : GLib.Object { * Throws an error if the config file was not found, could not be * parsed, or doesn't have all required fields. */ - public async Geary.AccountInformation? + private async Geary.AccountInformation load_account(string id, GLib.Cancellable? cancellable) throws Error { GLib.File config_dir = this.user_config_dir.get_child(id); @@ -164,13 +217,6 @@ public class AccountManager : GLib.Object { yield config_file.load(cancellable); Geary.ConfigFile.Group config = config_file.get_group(ACCOUNT_CONFIG_GROUP); - - Geary.ConfigFile.Group imap_config = config_file.get_group(IMAP_CONFIG_GROUP); - imap_config.set_fallback(ACCOUNT_CONFIG_GROUP, "imap_"); - - Geary.ConfigFile.Group smtp_config = config_file.get_group(SMTP_CONFIG_GROUP); - smtp_config.set_fallback(ACCOUNT_CONFIG_GROUP, "smtp_"); - CredentialsProvider provider = CredentialsProvider.from_string( config.get_string( CREDENTIALS_PROVIDER_KEY, @@ -178,35 +224,49 @@ public class AccountManager : GLib.Object { ) ); - Geary.CredentialsMethod method = Geary.CredentialsMethod.from_string( - config.get_string(CREDENTIALS_METHOD_KEY, - Geary.CredentialsMethod.PASSWORD.to_string()) - ); + string primary_email = config.get_string(PRIMARY_EMAIL_KEY); - Geary.ServiceInformation imap_info; - Geary.ServiceInformation smtp_info; + Geary.AccountInformation? info = null; switch (provider) { case CredentialsProvider.LIBSECRET: - imap_info = new_libsecret_service(Geary.Service.IMAP, method); - smtp_info = new_libsecret_service(Geary.Service.SMTP, method); + info = new_libsecret_account(id, config, primary_email); + break; + + case CredentialsProvider.GOA: + if (this.goa_service != null) { + Goa.Object? object = this.goa_service.lookup_by_id(to_goa_id(id)); + if (object != null) { + info = new_goa_account(id, object); + } else { + // Could not find the GOA object for this account, + // but have a working GOA connection, so it must + // have been removed. + throw new AccountError.GOA_REMOVED("Account not found"); + } + } + + if (info == null) { + // XXX We have a GOA account locally, but GOA is + // unavailable or the GOA account type is no longer + // supported. Create a dummy, disabled account and let + // the user deal with it? + } break; default: throw new AccountError.INVALID("Unhandled credentials provider"); } - Geary.AccountInformation info = new Geary.AccountInformation( - id, imap_info, smtp_info - ); info.set_account_directories(config_dir, data_dir); - // This is the only required value at the moment? - string primary_email = config.get_string(PRIMARY_EMAIL_KEY); - string real_name = config.get_string(REAL_NAME_KEY); + info.ordinal = config.get_int(ORDINAL_KEY, info.ordinal); + if (info.ordinal >= Geary.AccountInformation.next_ordinal) + Geary.AccountInformation.next_ordinal = info.ordinal + 1; info.primary_mailbox = new Geary.RFC822.MailboxAddress( - real_name, primary_email + config.get_string(REAL_NAME_KEY), primary_email ); + info.nickname = config.get_string(NICKNAME_KEY); // Store alternate emails in a list of case-insensitive strings @@ -221,22 +281,12 @@ public class AccountManager : GLib.Object { } } - info.imap.load_credentials(imap_config, primary_email); - info.smtp.load_credentials(smtp_config, primary_email); - - info.service_provider = Geary.ServiceProvider.from_string( - config.get_string(SERVICE_PROVIDER_KEY, - Geary.ServiceProvider.GMAIL.to_string()) - ); info.prefetch_period_days = config.get_int( PREFETCH_PERIOD_DAYS_KEY, info.prefetch_period_days ); info.save_sent_mail = config.get_bool( SAVE_SENT_MAIL_KEY, info.save_sent_mail ); - info.ordinal = config.get_int( - ORDINAL_KEY, info.ordinal - ); info.use_email_signature = config.get_bool( USE_EMAIL_SIGNATURE_KEY, info.use_email_signature ); @@ -244,19 +294,6 @@ public class AccountManager : GLib.Object { EMAIL_SIGNATURE_KEY, info.email_signature ); - if (info.ordinal >= Geary.AccountInformation.next_ordinal) - Geary.AccountInformation.next_ordinal = info.ordinal + 1; - - if (info.service_provider == Geary.ServiceProvider.OTHER) { - info.imap.load_settings(imap_config); - info.smtp.load_settings(smtp_config); - - if (info.smtp.smtp_use_imap_credentials) { - info.smtp.credentials.user = info.imap.credentials.user; - info.smtp.credentials.pass = info.imap.credentials.pass; - } - } - info.drafts_folder_path = Geary.AccountInformation.build_folder_path( config.get_string_list(DRAFTS_FOLDER_KEY) ); @@ -279,7 +316,7 @@ public class AccountManager : GLib.Object { } public async void save_account(Geary.AccountInformation info, - GLib.Cancellable? cancellable = null) + GLib.Cancellable? cancellable) throws GLib.Error { // Ensure only one async task is saving an info at once, since // at least the Engine can cause multiple saves to be called @@ -301,7 +338,7 @@ public class AccountManager : GLib.Object { } private async void save_account_locked(Geary.AccountInformation info, - GLib.Cancellable? cancellable = null) + GLib.Cancellable? cancellable) throws GLib.Error { File? file = info.settings_file; if (file == null) { @@ -321,15 +358,31 @@ public class AccountManager : GLib.Object { } Geary.ConfigFile.Group config = config_file.get_group(ACCOUNT_CONFIG_GROUP); - Geary.ConfigFile.Group imap_config = config_file.get_group(IMAP_CONFIG_GROUP); - Geary.ConfigFile.Group smtp_config = config_file.get_group(SMTP_CONFIG_GROUP); + if (info.imap is LocalServiceInformation) { + config.set_string( + CREDENTIALS_PROVIDER_KEY, CredentialsProvider.LIBSECRET.to_string() + ); + config.set_string( + CREDENTIALS_METHOD_KEY, info.imap.credentials_method.to_string() + ); + + if (info.service_provider == Geary.ServiceProvider.OTHER) { + Geary.ConfigFile.Group imap_config = config_file.get_group( + IMAP_CONFIG_GROUP + ); + ((LocalServiceInformation) info.imap).save_settings(imap_config); + + Geary.ConfigFile.Group smtp_config = config_file.get_group( + SMTP_CONFIG_GROUP + ); + ((LocalServiceInformation) info.smtp).save_settings(smtp_config); + } + } else if (info.imap is GoaServiceInformation) { + config.set_string( + CREDENTIALS_PROVIDER_KEY, CredentialsProvider.GOA.to_string() + ); + } - config.set_string( - CREDENTIALS_PROVIDER_KEY, CredentialsProvider.LIBSECRET.to_string() - ); - config.set_string( - CREDENTIALS_METHOD_KEY, info.imap.credentials_method.to_string() - ); config.set_string(REAL_NAME_KEY, info.primary_mailbox.name); config.set_string(PRIMARY_EMAIL_KEY, info.primary_mailbox.address); config.set_string(NICKNAME_KEY, info.nickname); @@ -349,11 +402,6 @@ public class AccountManager : GLib.Object { ); } - if (info.service_provider == Geary.ServiceProvider.OTHER) { - info.imap.save_settings(imap_config); - info.smtp.save_settings(smtp_config); - } - Gee.LinkedList empty = new Gee.LinkedList(); config.set_string_list(DRAFTS_FOLDER_KEY, (info.drafts_folder_path != null ? info.drafts_folder_path.as_list() : empty)); @@ -368,6 +416,7 @@ public class AccountManager : GLib.Object { config.set_bool(SAVE_DRAFTS_KEY, info.save_drafts); + debug("Writing config to: %s", file.get_path()); yield config_file.save(cancellable); } @@ -375,7 +424,8 @@ public class AccountManager : GLib.Object { * Deletes an account from disk. This is used by Geary.Engine and should not * normally be invoked directly. */ - public async void remove_async(Geary.AccountInformation info, Cancellable? cancellable = null) { + public async void remove_async(Geary.AccountInformation info, + GLib.Cancellable? cancellable) { if (info.data_dir == null) { warning("Cannot remove account storage directory; nothing to remove"); } else { @@ -393,6 +443,119 @@ public class AccountManager : GLib.Object { } catch (Error e) { debug("Error clearing passwords: %s", e.message); } + + this.enabled_accounts.unset(info.id); + } + + private void enable_account(Geary.AccountInformation account) + throws GLib.Error { + this.enabled_accounts.set(account.id, account); + this.engine.add_account(account); + } + + private inline string to_geary_id(Goa.Account account) { + return GOA_ID_PREFIX + account.id; + } + + private inline string to_goa_id(string id) { + return id.has_prefix(GOA_ID_PREFIX) + ? id.substring(GOA_ID_PREFIX.length) + : id; + } + + private Geary.AccountInformation + new_libsecret_account(string id, + Geary.ConfigFile.Group config, + string fallback_login) + throws GLib.Error { + + Geary.ServiceProvider provider = Geary.ServiceProvider.from_string( + config.get_string(SERVICE_PROVIDER_KEY, + Geary.ServiceProvider.GMAIL.to_string()) + ); + Geary.CredentialsMethod method = Geary.CredentialsMethod.from_string( + config.get_string(CREDENTIALS_METHOD_KEY, + Geary.CredentialsMethod.PASSWORD.to_string()) + ); + + Geary.ConfigFile.Group imap_config = + config.file.get_group(IMAP_CONFIG_GROUP); + LocalServiceInformation imap = new_libsecret_service( + Geary.Service.IMAP, method + ); + imap_config.set_fallback(config.name, "imap_"); + imap.load_credentials(imap_config, fallback_login); + + Geary.ConfigFile.Group smtp_config = + config.file.get_group(SMTP_CONFIG_GROUP); + LocalServiceInformation smtp = new_libsecret_service( + Geary.Service.SMTP, method + ); + smtp_config.set_fallback(config.name, "smtp_"); + smtp.load_credentials(smtp_config, fallback_login); + + // Generic IMAP accounts must load their settings from their + // config, GMail and others have it hard-coded hence don't + // need to load it. + if (provider == Geary.ServiceProvider.OTHER) { + imap.load_settings(imap_config); + + smtp.load_settings(imap_config); + if (smtp.smtp_use_imap_credentials) { + smtp.credentials.user = imap.credentials.user; + smtp.credentials.pass = imap.credentials.pass; + } + } + + Geary.AccountInformation info = new Geary.AccountInformation( + id, imap, smtp + ); + info.service_provider = provider; + return info; + } + + private Geary.AccountInformation? new_goa_account(string id, + Goa.Object account) { + Geary.AccountInformation info = null; + + Goa.Mail? mail = account.get_mail(); + Goa.PasswordBased? password = account.get_password_based(); + if (mail != null && password != null) { + Geary.CredentialsMediator mediator = new GoaMediator(password); + info = new Geary.AccountInformation( + id, + new GoaServiceInformation(Geary.Service.IMAP, mediator, mail), + new GoaServiceInformation(Geary.Service.SMTP, mediator, mail) + ); + info.service_provider = Geary.ServiceProvider.OTHER; + } + + return info; + } + + private async Geary.AccountInformation? + create_goa_account(Goa.Object account, + GLib.Cancellable? cancellable) + throws GLib.Error { + Geary.AccountInformation? info = new_goa_account( + to_geary_id(account.get_account()), account + ); + if (info != null) { + debug("GOA id: %s", info.id); + Goa.Mail? mail = account.get_mail(); + + info.ordinal = Geary.AccountInformation.next_ordinal++; + info.primary_mailbox = new Geary.RFC822.MailboxAddress( + mail.name, mail.email_address + ); + info.nickname = account.get_account().identity; + + yield create_account_dirs(info, cancellable); + debug("Created dirs: %s", info.id); + yield save_account(info, cancellable); + debug("Saved: %s", info.id); + } + return info; } } diff --git a/src/client/accounts/goa-service-information.vala b/src/client/accounts/goa-service-information.vala new file mode 100644 index 00000000..4969382b --- /dev/null +++ b/src/client/accounts/goa-service-information.vala @@ -0,0 +1,41 @@ +/* Copyright 2017 Software Freedom Conservancy Inc. + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/* A service implementation using GNOME Online Accounts. + * This loads IMAP and SMTP settings from GOA. + */ +public class GoaServiceInformation : Geary.ServiceInformation { + private Goa.Mail mail_object; + + public GoaServiceInformation(Geary.Service service, + Geary.CredentialsMediator? mediator, + Goa.Mail mail_object) { + this.service = service; + this.mediator = mediator; + this.mail_object = mail_object; + + switch (service) { + case Geary.Service.IMAP: + this.credentials.user = mail_object.imap_user_name; + this.host = mail_object.imap_host; + this.port = Geary.Imap.ClientConnection.DEFAULT_PORT_SSL; + this.use_ssl = mail_object.imap_use_ssl; + this.use_starttls = mail_object.imap_use_tls; + break; + case Geary.Service.SMTP: + this.credentials.user = mail_object.smtp_user_name; + this.host = mail_object.smtp_host; + this.port = Geary.Smtp.ClientConnection.DEFAULT_PORT_SSL; + this.use_ssl = mail_object.smtp_use_ssl; + this.use_starttls = mail_object.smtp_use_tls; + this.smtp_noauth = !(mail_object.smtp_use_auth); + if (smtp_noauth) + this.credentials = null; + break; + } + } + +} diff --git a/src/client/accounts/local-service-information.vala b/src/client/accounts/local-service-information.vala index 5818ad5b..912e4a1c 100644 --- a/src/client/accounts/local-service-information.vala +++ b/src/client/accounts/local-service-information.vala @@ -27,8 +27,7 @@ public class LocalServiceInformation : Geary.ServiceInformation { this.mediator = mediator; } - public override void load_settings(Geary.ConfigFile.Group config) - throws GLib.Error { + public void load_settings(Geary.ConfigFile.Group config) { this.host = config.get_string(HOST, this.host); this.port = config.get_uint16(PORT, this.port); this.use_ssl = config.get_bool(SSL, this.use_ssl); @@ -46,18 +45,17 @@ public class LocalServiceInformation : Geary.ServiceInformation { } - public override void load_credentials(Geary.ConfigFile.Group config, - string? email_address = null) - throws GLib.Error { + public void load_credentials(Geary.ConfigFile.Group config, + string? default_login = null) { this.credentials.user = config.get_string( - USERNAME_KEY, email_address + USERNAME_KEY, default_login ); this.remember_password = config.get_bool( REMEMBER_PASSWORD_KEY, this.remember_password ); } - public override void save_settings(Geary.ConfigFile.Group config) { + public void save_settings(Geary.ConfigFile.Group config) { config.set_string(HOST, this.host); config.set_int(PORT, this.port); config.set_bool(SSL, this.use_ssl); diff --git a/src/client/application/geary-application.vala b/src/client/application/geary-application.vala index 6a0a7fb2..06d35aea 100644 --- a/src/client/application/geary-application.vala +++ b/src/client/application/geary-application.vala @@ -245,7 +245,7 @@ public class GearyApplication : Gtk.Application { exec_dir.get_path(), is_installed().to_string()); config = new Configuration(APP_ID); - yield controller.open_async(); + yield controller.open_async(null); release(); } diff --git a/src/client/application/geary-controller.vala b/src/client/application/geary-controller.vala index d881c26a..a9b21a77 100644 --- a/src/client/application/geary-controller.vala +++ b/src/client/application/geary-controller.vala @@ -183,7 +183,7 @@ public class GearyController : Geary.BaseObject { /** * Starts the controller and brings up Geary. */ - public async void open_async() { + public async void open_async(GLib.Cancellable? cancellable) { Geary.Engine engine = this.application.engine; // This initializes the IconFactory, important to do before @@ -298,9 +298,24 @@ public class GearyController : Geary.BaseObject { this.application.get_user_config_directory(), this.application.get_user_data_directory() ); + try { - yield engine.open_async(this.application.get_resource_directory()); - yield this.account_manager.add_existing_accounts_async(null); + yield this.account_manager.connect_libsecret(cancellable); + } catch (GLib.Error err) { + warning("Error opening libsecret: %s", err.message); + } + + try { + yield this.account_manager.connect_goa(cancellable); + } catch (GLib.Error err) { + warning("Error opening GOA: %s", err.message); + } + + try { + yield engine.open_async( + this.application.get_resource_directory(), cancellable + ); + yield this.account_manager.load_accounts(cancellable); if (engine.get_accounts().size == 0) { create_account(); } @@ -864,10 +879,12 @@ public class GearyController : Geary.BaseObject { try { if (real_account_information.settings_file == null) { yield this.account_manager.create_account_dirs( - real_account_information + real_account_information, cancellable ); } - yield this.account_manager.save_account(real_account_information); + yield this.account_manager.save_account( + real_account_information, cancellable + ); yield do_update_stored_passwords_async( Geary.ServiceFlag.IMAP | Geary.ServiceFlag.SMTP, real_account_information diff --git a/src/client/application/goa-mediator.vala b/src/client/application/goa-mediator.vala new file mode 100644 index 00000000..cf6acb72 --- /dev/null +++ b/src/client/application/goa-mediator.vala @@ -0,0 +1,64 @@ +/* Copyright 2017 Software Freedom Conservancy Inc. + * + * This software is licensed under the GNU Lesser General Public License + * (version 2.1 or later). See the COPYING file in this distribution. + */ + +/* GNOME Online Accounts password adapter. */ +public class GoaMediator : Geary.CredentialsMediator, Object { + private Goa.PasswordBased password; + + public GoaMediator(Goa.PasswordBased password) { + this.password = password; + } + + public virtual async string? get_password_async(Geary.Service service, + Geary.AccountInformation account, + Cancellable? cancellable = null) + throws Error { + string pass; + + switch (service) { + case Geary.Service.IMAP: + if (!password.call_get_password_sync("imap-password", out pass, cancellable)) + return null; + break; + case Geary.Service.SMTP: + if (!password.call_get_password_sync("smtp-password", out pass, cancellable)) + return null; + break; + default: + return null; + } + return pass; + } + + public virtual async void set_password_async(Geary.Service service, + Geary.AccountInformation account, + Cancellable? cancellable = null) + throws Error { + return; + } + + public virtual async void clear_password_async(Geary.Service service, + Geary.AccountInformation account, + Cancellable? cancellable = null) + throws Error { + return; + } + + public virtual async bool prompt_passwords_async(Geary.ServiceFlag services, + Geary.AccountInformation account_information, + out string? imap_password, out string? smtp_password, + out bool imap_remember_password, out bool smtp_remember_password) throws Error { + + imap_password = yield get_password_async(Geary.Service.IMAP, account_information, null); + smtp_password = yield get_password_async(Geary.Service.SMTP, account_information, null); + + imap_remember_password = false; + smtp_remember_password = false; + + return false; + } + +} diff --git a/src/engine/api/geary-service-information.vala b/src/engine/api/geary-service-information.vala index a4f273e6..5fad1f53 100644 --- a/src/engine/api/geary-service-information.vala +++ b/src/engine/api/geary-service-information.vala @@ -34,6 +34,7 @@ public enum Geary.CredentialsMethod { } } + /** * This class encloses all the information used when connecting with the server, * how to authenticate with it and which credentials to use. Derived classes @@ -91,23 +92,6 @@ public abstract class Geary.ServiceInformation : GLib.Object { */ public bool smtp_use_imap_credentials { get; set; default = false; } - /** - * Loads the settings pertaining to this class's instance. - * - * This method depends on the concrete implementation used. - */ - public abstract void load_settings(ConfigFile.Group config) throws Error; - - /** - * Loads the credentials pertaining to this class's instance. - * - * This method depends on the concrete implementation used. - */ - public abstract void load_credentials(ConfigFile.Group config, string? email_address = null) throws Error; - - /** Saves settings pertaining to this class's instance to a key file. */ - public abstract void save_settings(ConfigFile.Group config); - public void copy_from(Geary.ServiceInformation from) { this.host = from.host; this.port = from.port; diff --git a/src/engine/util/util-config-file.vala b/src/engine/util/util-config-file.vala index 00972c34..8e62e113 100644 --- a/src/engine/util/util-config-file.vala +++ b/src/engine/util/util-config-file.vala @@ -26,6 +26,9 @@ public class Geary.ConfigFile { } + /** The config file this group was obtained from. */ + public ConfigFile file { get; private set; } + /** The name of this group, as specified by a [Name] heading. */ public string name { get; private set; } @@ -33,7 +36,8 @@ public class Geary.ConfigFile { private GroupLookup[] lookups; - internal Group(string name, GLib.KeyFile backing) { + internal Group(ConfigFile file, string name, GLib.KeyFile backing) { + this.file = file; this.name = name; this.backing = backing; @@ -172,7 +176,7 @@ public class Geary.ConfigFile { * Returns the config group under the given named heading. */ public Group get_group(string name) { - return new Group(name, this.backing); + return new Group(this, name, this.backing); } /** diff --git a/test/engine/api/geary-service-information-mock.vala b/test/engine/api/geary-service-information-mock.vala index b1592589..16e5d442 100644 --- a/test/engine/api/geary-service-information-mock.vala +++ b/test/engine/api/geary-service-information-mock.vala @@ -12,24 +12,4 @@ public class Geary.MockServiceInformation : ServiceInformation, MockObject { get; set; default = new Gee.LinkedList(); } - - public override void load_settings(Geary.ConfigFile.Group config) - throws Error { - void_call("load_settings", { box_arg(config) }); - } - - public override void load_credentials(Geary.ConfigFile.Group config, - string? email_address = null) - throws Error { - void_call("load_credentials", { box_arg(config), box_arg(email_address) }); - } - - public override void save_settings(Geary.ConfigFile.Group config) { - try { - void_call("save_settings", { box_arg(config) }); - } catch (Error err) { - assert_not_reached(); - } - } - }