Convert Credentials and CredentialsMediator to refer to tokens, tidy up.
* src/engine/api/geary-credentials.vala (Credentials): Refer to auth tokens, not passwords, so we can use the same object for OAuth2 auth. Update call sites. * src/engine/api/geary-credentials-mediator.vala (CredentialsMediator): Refer to auth tokens, not passwords, so we can use the same object for OAuth2 auth. Remove saving and clearing methods from the interface since that's only supported for password storage, not SSO like GOA. Update implementations and call sites. * src/engine/api/geary-account-information.vala (AccountInformation): Replace many byzantine auth management methods with just a few for handling loading and prompting. Remove ServiceFlag enum now it is no longer used. Clean up call sites. * src/client/accounts/account-manager.vala (AccountInformation): Manage initial saving and final deletion of libsecret based accounts here, rather than all over the code base.
This commit is contained in:
parent
a165d9682d
commit
13c92c3f7e
17 changed files with 360 additions and 533 deletions
|
|
@ -100,13 +100,7 @@ public class AccountDialog : Gtk.Dialog {
|
|||
Geary.AccountInformation? account = get_account_info(id);
|
||||
if (account == null)
|
||||
return;
|
||||
|
||||
try {
|
||||
yield account.get_passwords_async(Geary.ServiceFlag.IMAP | Geary.ServiceFlag.SMTP);
|
||||
} catch (Error err) {
|
||||
debug("Unable to fetch password(s) for account: %s", err.message);
|
||||
}
|
||||
|
||||
|
||||
add_edit_pane.set_mode(AddEditPage.PageMode.EDIT);
|
||||
add_edit_pane.set_account_information(account);
|
||||
add_edit_pane.present();
|
||||
|
|
|
|||
|
|
@ -128,16 +128,32 @@ public class AccountManager : GLib.Object {
|
|||
return new LocalServiceInformation(service, libsecret);
|
||||
}
|
||||
|
||||
public async void create_account_dirs(Geary.AccountInformation info,
|
||||
Cancellable? cancellable)
|
||||
public async void create_account(Geary.AccountInformation account,
|
||||
GLib.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);
|
||||
yield create_account_dirs(account, cancellable);
|
||||
yield save_account(account, cancellable);
|
||||
set_enabled(account, true);
|
||||
|
||||
yield Geary.Files.make_directory_with_parents(config, cancellable);
|
||||
yield Geary.Files.make_directory_with_parents(data, cancellable);
|
||||
SecretMediator? mediator = account.imap.mediator as SecretMediator;
|
||||
if (mediator != null) {
|
||||
try {
|
||||
yield mediator.update_token(account, account.imap, cancellable);
|
||||
} catch (Error e) {
|
||||
debug("Error saving IMAP password: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
info.set_account_directories(config, data);
|
||||
if (account.smtp.credentials != null) {
|
||||
mediator = account.smtp.mediator as SecretMediator;
|
||||
if (mediator != null) {
|
||||
try {
|
||||
yield mediator.update_token(account, account.smtp, cancellable);
|
||||
} catch (Error e) {
|
||||
debug("Error saving IMAP password: %s", e.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async void load_accounts(GLib.Cancellable? cancellable)
|
||||
|
|
@ -456,12 +472,23 @@ public class AccountManager : GLib.Object {
|
|||
yield Geary.Files.recursive_delete_async(info.config_dir, cancellable);
|
||||
}
|
||||
|
||||
try {
|
||||
yield info.clear_stored_passwords_async(
|
||||
Geary.ServiceFlag.IMAP | Geary.ServiceFlag.SMTP
|
||||
);
|
||||
} catch (Error e) {
|
||||
debug("Error clearing passwords: %s", e.message);
|
||||
|
||||
SecretMediator? mediator = info.imap.mediator as SecretMediator;
|
||||
if (mediator != null) {
|
||||
try {
|
||||
yield mediator.clear_token(info, info.imap, cancellable);
|
||||
} catch (Error e) {
|
||||
debug("Error clearing IMAP password: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
mediator = info.smtp.mediator as SecretMediator;
|
||||
if (mediator != null) {
|
||||
try {
|
||||
yield mediator.clear_token(info, info.smtp, cancellable);
|
||||
} catch (Error e) {
|
||||
debug("Error clearing IMAP password: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
this.enabled_accounts.unset(info.id);
|
||||
|
|
@ -475,6 +502,18 @@ public class AccountManager : GLib.Object {
|
|||
account_added(account);
|
||||
}
|
||||
|
||||
public async void create_account_dirs(Geary.AccountInformation info,
|
||||
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);
|
||||
|
||||
yield Geary.Files.make_directory_with_parents(config, cancellable);
|
||||
yield Geary.Files.make_directory_with_parents(data, cancellable);
|
||||
|
||||
info.set_account_directories(config, data);
|
||||
}
|
||||
|
||||
private inline string to_geary_id(Goa.Object account) {
|
||||
return GOA_ID_PREFIX + account.get_account().id;
|
||||
}
|
||||
|
|
@ -568,9 +607,7 @@ public class AccountManager : GLib.Object {
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -369,10 +369,10 @@ public class AddEditPage : Gtk.Box {
|
|||
info.nickname,
|
||||
info.primary_mailbox.address,
|
||||
info.imap.credentials.user,
|
||||
info.imap.credentials.pass,
|
||||
info.imap.credentials.token,
|
||||
info.imap.remember_password && info.smtp.remember_password,
|
||||
info.smtp.credentials != null ? info.smtp.credentials.user : null,
|
||||
info.smtp.credentials != null ? info.smtp.credentials.pass : null,
|
||||
info.smtp.credentials != null ? info.smtp.credentials.token : null,
|
||||
info.service_provider,
|
||||
info.save_sent_mail,
|
||||
info.allow_save_sent_mail(),
|
||||
|
|
|
|||
|
|
@ -898,17 +898,14 @@ public class GearyController : Geary.BaseObject {
|
|||
|
||||
try {
|
||||
if (real_account_information.settings_file == null) {
|
||||
yield this.account_manager.create_account_dirs(
|
||||
yield this.account_manager.create_account(
|
||||
real_account_information, cancellable
|
||||
);
|
||||
} else {
|
||||
yield this.account_manager.save_account(
|
||||
real_account_information, cancellable
|
||||
);
|
||||
}
|
||||
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
|
||||
);
|
||||
debug("Successfully validated account information");
|
||||
} catch (GLib.Error err) {
|
||||
report_problem(
|
||||
|
|
@ -979,15 +976,6 @@ public class GearyController : Geary.BaseObject {
|
|||
|
||||
return new_info;
|
||||
}
|
||||
|
||||
private async void do_update_stored_passwords_async(Geary.ServiceFlag services,
|
||||
Geary.AccountInformation account_information) {
|
||||
try {
|
||||
yield account_information.update_stored_passwords_async(services);
|
||||
} catch (Error e) {
|
||||
debug("Error updating stored passwords: %s", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
private void report_problem(Geary.ProblemReport report) {
|
||||
debug("Problem reported: %s", report.to_string());
|
||||
|
|
|
|||
|
|
@ -12,46 +12,45 @@ public class GoaMediator : Geary.CredentialsMediator, Object {
|
|||
this.password = password;
|
||||
}
|
||||
|
||||
public virtual async string? get_password_async(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
string pass;
|
||||
public virtual async bool load_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
string? pass = null;
|
||||
|
||||
switch (service.protocol) {
|
||||
case Geary.Protocol.IMAP:
|
||||
if (!password.call_get_password_sync("imap-password", out pass, cancellable))
|
||||
return null;
|
||||
break;
|
||||
case Geary.Protocol.SMTP:
|
||||
if (!password.call_get_password_sync("smtp-password", out pass, cancellable))
|
||||
return null;
|
||||
break;
|
||||
default:
|
||||
return null;
|
||||
case Geary.Protocol.IMAP:
|
||||
password.call_get_password_sync(
|
||||
"imap-password", out pass, cancellable
|
||||
);
|
||||
break;
|
||||
|
||||
case Geary.Protocol.SMTP:
|
||||
password.call_get_password_sync(
|
||||
"smtp-password", out pass, cancellable
|
||||
);
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return pass;
|
||||
|
||||
bool loaded = false;
|
||||
if (pass != null) {
|
||||
service.credentials = service.credentials.copy_with_token(pass);
|
||||
loaded = true;
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public virtual async void set_password_async(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
return;
|
||||
}
|
||||
|
||||
public virtual async void clear_password_async(Geary.ServiceInformation service,
|
||||
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 {
|
||||
|
||||
throw new Geary.EngineError.UNSUPPORTED(
|
||||
"Account password must be set in GOA"
|
||||
);
|
||||
public virtual async bool prompt_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
// XXX open a dialog that says "Click here to change your GOA
|
||||
// password". Connect to the GOA service and wait until we
|
||||
// hear that the account has changed.
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,72 +46,44 @@ public class SecretMediator : Geary.CredentialsMediator, Object {
|
|||
yield check_unlocked(cancellable);
|
||||
}
|
||||
|
||||
public virtual async string? get_password_async(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
string? password = yield Secret.password_lookupv(
|
||||
SecretMediator.schema, new_attrs(service), cancellable
|
||||
);
|
||||
|
||||
if (password == null) {
|
||||
password = yield migrate_old_password(service, cancellable);
|
||||
}
|
||||
|
||||
if (password == null)
|
||||
debug(
|
||||
"Unable to fetch password in libsecret keyring for %s: %s %s",
|
||||
service.protocol.to_string(),
|
||||
service.credentials.user,
|
||||
service.endpoint.remote_address.get_hostname()
|
||||
);
|
||||
|
||||
return password;
|
||||
}
|
||||
|
||||
public virtual async void set_password_async(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
try {
|
||||
yield do_store(service, service.credentials.pass, cancellable);
|
||||
} catch (Error e) {
|
||||
debug(
|
||||
"Unable to store password in libsecret keyring for %s: %s %s",
|
||||
service.protocol.to_string(),
|
||||
service.credentials.user,
|
||||
service.endpoint.remote_address.get_hostname()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public virtual async void clear_password_async(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null)
|
||||
throws Error {
|
||||
yield Secret.password_clearv(SecretMediator.schema,
|
||||
new_attrs(service),
|
||||
cancellable);
|
||||
|
||||
// Remove legacy formats
|
||||
// <= 0.11
|
||||
yield Secret.password_clear(
|
||||
compat_schema,
|
||||
cancellable,
|
||||
"user", get_legacy_user(service, service.credentials.user)
|
||||
);
|
||||
}
|
||||
|
||||
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)
|
||||
public virtual async bool load_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
// Our dialog doesn't support asking for both at once, even though this
|
||||
// API would indicate it does. We need to revamp the API.
|
||||
assert(!services.has_imap() || !services.has_smtp());
|
||||
bool loaded = false;
|
||||
if (service.remember_password) {
|
||||
string? password = yield Secret.password_lookupv(
|
||||
SecretMediator.schema, new_attrs(service), cancellable
|
||||
);
|
||||
|
||||
// to prevent multiple dialogs from popping up at the same time, use a nonblocking mutex
|
||||
// to serialize the code
|
||||
if (password == null) {
|
||||
password = yield migrate_old_password(service, cancellable);
|
||||
}
|
||||
|
||||
if (password != null) {
|
||||
service.credentials =
|
||||
service.credentials.copy_with_token(password);
|
||||
loaded = true;
|
||||
} else {
|
||||
debug(
|
||||
"Unable to fetch password in liqbsecret keyring for %s: %s %s",
|
||||
service.protocol.to_string(),
|
||||
service.credentials.user,
|
||||
service.endpoint.remote_address.get_hostname()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
loaded = yield prompt_token(account, service, cancellable);
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
public virtual async bool prompt_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
// to prevent multiple dialogs from popping up at the same
|
||||
// time, use a nonblocking mutex to serialize the code
|
||||
int token = yield dialog_mutex.claim_async(null);
|
||||
|
||||
// Ensure main window present to the window
|
||||
|
|
@ -119,44 +91,79 @@ public class SecretMediator : Geary.CredentialsMediator, Object {
|
|||
|
||||
PasswordDialog password_dialog = new PasswordDialog(
|
||||
this.application.get_active_window(),
|
||||
services.has_smtp(),
|
||||
account_information,
|
||||
services
|
||||
account,
|
||||
service
|
||||
);
|
||||
bool result = password_dialog.run();
|
||||
|
||||
dialog_mutex.release(ref token);
|
||||
|
||||
if (!result) {
|
||||
// user cancelled the dialog
|
||||
imap_password = null;
|
||||
smtp_password = null;
|
||||
imap_remember_password = false;
|
||||
smtp_remember_password = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
// password_dialog.password should never be null at this point. It will only be null when
|
||||
// password_dialog.run() returns false, in which case we have already returned.
|
||||
if (services.has_smtp()) {
|
||||
imap_password = null;
|
||||
imap_remember_password = false;
|
||||
smtp_password = password_dialog.password;
|
||||
smtp_remember_password = password_dialog.remember_password;
|
||||
} else {
|
||||
imap_password = password_dialog.password;
|
||||
imap_remember_password = password_dialog.remember_password;
|
||||
smtp_password = null;
|
||||
smtp_remember_password = false;
|
||||
if (result) {
|
||||
// password_dialog.password should never be null at this
|
||||
// point. It will only be null when password_dialog.run()
|
||||
// returns false, in which case we have already returned.
|
||||
service.credentials = service.credentials.copy_with_token(
|
||||
password_dialog.password
|
||||
);
|
||||
service.remember_password = password_dialog.remember_password;
|
||||
|
||||
yield update_token(account, service, cancellable);
|
||||
|
||||
account.information_changed();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public async void update_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
Cancellable? cancellable)
|
||||
throws Error {
|
||||
if (service.remember_password) {
|
||||
try {
|
||||
yield do_store(service, service.credentials.token, cancellable);
|
||||
} catch (Error e) {
|
||||
debug(
|
||||
"Unable to store password in libsecret keyring for %s: %s %s",
|
||||
service.protocol.to_string(),
|
||||
service.credentials.user,
|
||||
service.endpoint.remote_address.get_hostname()
|
||||
);
|
||||
}
|
||||
} else {
|
||||
yield clear_token(account, service, cancellable);
|
||||
}
|
||||
}
|
||||
|
||||
public async void clear_token(Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service,
|
||||
Cancellable? cancellable)
|
||||
throws Error {
|
||||
if (service.credentials != null) {
|
||||
yield Secret.password_clearv(SecretMediator.schema,
|
||||
new_attrs(service),
|
||||
cancellable);
|
||||
|
||||
// Remove legacy formats
|
||||
// <= 0.11
|
||||
yield Secret.password_clear(
|
||||
compat_schema,
|
||||
cancellable,
|
||||
"user", get_legacy_user(service, account.primary_mailbox.address)
|
||||
);
|
||||
// <= 0.6
|
||||
yield Secret.password_clear(
|
||||
compat_schema,
|
||||
cancellable,
|
||||
"user", get_legacy_user(service, service.credentials.user)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure the default collection unlocked. Try to unlock it since
|
||||
// the user may be running in a limited environment and it would
|
||||
// prevent us from prompting the user multiple times in one
|
||||
// session. See Bug 784300.
|
||||
private async void check_unlocked(Cancellable? cancellable = null)
|
||||
private async void check_unlocked(Cancellable? cancellable)
|
||||
throws Error {
|
||||
Secret.Service service = yield Secret.Service.get(
|
||||
Secret.ServiceFlags.OPEN_SESSION, cancellable
|
||||
|
|
@ -198,9 +205,10 @@ public class SecretMediator : Geary.CredentialsMediator, Object {
|
|||
);
|
||||
}
|
||||
|
||||
private HashTable<string,string> new_attrs(Geary.ServiceInformation service,
|
||||
Cancellable? cancellable = null) {
|
||||
HashTable<string,string> table = new HashTable<string,string>(str_hash, str_equal);
|
||||
private HashTable<string,string> new_attrs(Geary.ServiceInformation service) {
|
||||
HashTable<string,string> table = new HashTable<string,string>(
|
||||
str_hash, str_equal
|
||||
);
|
||||
table.insert(ATTR_PROTO, service.protocol.name());
|
||||
table.insert(ATTR_HOST, service.host);
|
||||
table.insert(ATTR_LOGIN, service.credentials.user);
|
||||
|
|
@ -208,7 +216,7 @@ public class SecretMediator : Geary.CredentialsMediator, Object {
|
|||
}
|
||||
|
||||
private async string? migrate_old_password(Geary.ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
// <= 0.11
|
||||
string user = get_legacy_user(service, service.credentials.user);
|
||||
|
|
|
|||
|
|
@ -19,12 +19,13 @@ public class PasswordDialog {
|
|||
private Gtk.Entry entry_password;
|
||||
private Gtk.CheckButton check_remember_password;
|
||||
private Gtk.Button ok_button;
|
||||
|
||||
|
||||
public string password { get; private set; default = ""; }
|
||||
public bool remember_password { get; private set; }
|
||||
|
||||
public PasswordDialog(Gtk.Window? parent, bool smtp, Geary.AccountInformation account_information,
|
||||
Geary.ServiceFlag password_flags) {
|
||||
|
||||
public PasswordDialog(Gtk.Window? parent,
|
||||
Geary.AccountInformation account,
|
||||
Geary.ServiceInformation service) {
|
||||
Gtk.Builder builder = GioUtil.create_builder("password-dialog.glade");
|
||||
|
||||
dialog = (Gtk.Dialog) builder.get_object("PasswordDialog");
|
||||
|
|
@ -42,18 +43,21 @@ public class PasswordDialog {
|
|||
Gtk.Label primary_text_label = (Gtk.Label) builder.get_object("primary_text_label");
|
||||
primary_text_label.set_markup(PRIMARY_TEXT_MARKUP.printf(PRIMARY_TEXT_FIRST_TRY));
|
||||
|
||||
if (smtp) {
|
||||
label_username.set_text(account_information.smtp.credentials.user ?? "");
|
||||
entry_password.set_text(account_information.smtp.credentials.pass ?? "");
|
||||
} else {
|
||||
label_username.set_text(account_information.imap.credentials.user ?? "");
|
||||
entry_password.set_text(account_information.imap.credentials.pass ?? "");
|
||||
bool is_smtp = service.protocol == Geary.Protocol.SMTP;
|
||||
|
||||
Geary.Credentials? credentials = (is_smtp)
|
||||
? account.get_smtp_credentials() : account.imap.credentials;
|
||||
|
||||
if (credentials != null) {
|
||||
label_username.set_text(credentials.user);
|
||||
entry_password.set_text(credentials.token ?? "");
|
||||
}
|
||||
check_remember_password.active = (smtp ? account_information.smtp.remember_password
|
||||
: account_information.imap.remember_password);
|
||||
if (smtp)
|
||||
check_remember_password.active = service.remember_password;
|
||||
|
||||
if (is_smtp) {
|
||||
label_smtp.show();
|
||||
|
||||
}
|
||||
|
||||
ok_button = (Gtk.Button) builder.get_object("authenticate_button");
|
||||
|
||||
refresh_ok_button_sensitivity();
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright 2016 Software Freedom Conservancy Inc.
|
||||
n *
|
||||
*
|
||||
* This software is licensed under the GNU Lesser General Public License
|
||||
* (version 2.1 or later). See the COPYING file in this distribution.
|
||||
*/
|
||||
|
|
@ -378,6 +378,23 @@ public class Geary.AccountInformation : BaseObject {
|
|||
information_changed();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this account contains a specific email address.
|
||||
*
|
||||
* Returns true if the address part of `email` is equal to (case
|
||||
* insensitive) the address part of this account's primary mailbox
|
||||
* or any of its secondary mailboxes.
|
||||
*/
|
||||
public bool has_email_address(Geary.RFC822.MailboxAddress email) {
|
||||
return (
|
||||
this.primary_mailbox.equal_to(email) ||
|
||||
(this.alternate_mailboxes != null &&
|
||||
this.alternate_mailboxes.fold<bool>((alt) => {
|
||||
return alt.equal_to(email);
|
||||
}, false))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the best credentials to use for SMTP authentication.
|
||||
*
|
||||
|
|
@ -397,190 +414,76 @@ public class Geary.AccountInformation : BaseObject {
|
|||
}
|
||||
|
||||
/**
|
||||
* Fetch the passwords for the given services. For each service, if the
|
||||
* password is unset, use get_passwords_async() first; if the password is
|
||||
* set or it's not in the key store, use prompt_passwords_async(). Return
|
||||
* true if all passwords were retrieved from the key store or the user
|
||||
* proceeded normally if/when prompted, false if the user tried to cancel
|
||||
* the prompt.
|
||||
* Loads this account's SMTP credentials from its mediator, if needed.
|
||||
*
|
||||
* If force_request is set to true, a prompt will appear regardless.
|
||||
* This method may cause the user to be prompted for their
|
||||
* secrets, thus it may yield for some time.
|
||||
*
|
||||
* Returns true if the credentials were successfully loaded or had
|
||||
* been previously loaded, the credentials could not be loaded and
|
||||
* the SMTP credentials are invalid.
|
||||
*/
|
||||
public async bool fetch_passwords_async(ServiceFlag services,
|
||||
bool force_request = false) throws Error {
|
||||
if (force_request) {
|
||||
// Delete the current password(s).
|
||||
if (services.has_imap()) {
|
||||
if (this.imap.credentials != null)
|
||||
this.imap.credentials =
|
||||
this.imap.credentials.copy_with_password(null);
|
||||
} else if (services.has_smtp()) {
|
||||
if (this.smtp.credentials != null)
|
||||
this.smtp.credentials =
|
||||
this.smtp.credentials.copy_with_password(null);
|
||||
public async bool load_smtp_credentials(GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
Credentials? creds = get_smtp_credentials();
|
||||
bool loaded = (creds == null || creds.is_complete());
|
||||
if (!loaded && creds != null) {
|
||||
ServiceInformation service = this.smtp;
|
||||
if (this.smtp.smtp_use_imap_credentials) {
|
||||
service = this.imap;
|
||||
}
|
||||
}
|
||||
|
||||
// Only call get_passwords on anything that hasn't been set
|
||||
// (incorrectly) previously.
|
||||
ServiceFlag get_services = 0;
|
||||
if (services.has_imap() && !this.imap.credentials.is_complete())
|
||||
get_services |= ServiceFlag.IMAP;
|
||||
|
||||
if (services.has_smtp() && this.smtp.credentials != null && !this.smtp.credentials.is_complete())
|
||||
get_services |= ServiceFlag.SMTP;
|
||||
|
||||
ServiceFlag unset_services = services;
|
||||
if (get_services != 0)
|
||||
unset_services = yield get_passwords_async(get_services);
|
||||
else
|
||||
return true;
|
||||
|
||||
if (unset_services == 0)
|
||||
return true;
|
||||
|
||||
return yield prompt_passwords_async(unset_services);
|
||||
}
|
||||
|
||||
private void check_mediator_instance() throws EngineError {
|
||||
if (this.imap.mediator == null || this.smtp.mediator == null)
|
||||
throw new EngineError.OPEN_REQUIRED(
|
||||
"Account %s needs to be open with valid Geary.CredentialsMediators".printf(this.id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Use Engine's authentication mediator to retrieve the passwords for the
|
||||
* given services. The passwords will be stored in the appropriate
|
||||
* credentials in this instance. Return any services that could *not* be
|
||||
* retrieved from the key store (in which case you may want to call
|
||||
* prompt_passwords_async() on the return value), or 0 if all were
|
||||
* retrieved.
|
||||
*/
|
||||
public async ServiceFlag get_passwords_async(ServiceFlag services) throws Error {
|
||||
check_mediator_instance();
|
||||
|
||||
ServiceFlag failed_services = 0;
|
||||
|
||||
if (services.has_imap()) {
|
||||
string? imap_password = yield this.imap.mediator.get_password_async(
|
||||
this.imap
|
||||
loaded = yield service.mediator.load_token(
|
||||
this, service, cancellable
|
||||
);
|
||||
|
||||
if (imap_password != null)
|
||||
this.imap.set_password(imap_password, this.imap.remember_password);
|
||||
else
|
||||
failed_services |= ServiceFlag.IMAP;
|
||||
}
|
||||
|
||||
if (services.has_smtp() && this.smtp.credentials != null) {
|
||||
string? smtp_password = yield this.smtp.mediator.get_password_async(
|
||||
this.smtp
|
||||
);
|
||||
|
||||
if (smtp_password != null)
|
||||
this.smtp.set_password(smtp_password, this.smtp.remember_password);
|
||||
else
|
||||
failed_services |= ServiceFlag.SMTP;
|
||||
}
|
||||
|
||||
return failed_services;
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Engine's authentication mediator to prompt for the passwords for
|
||||
* the given services. The passwords will be stored in the appropriate
|
||||
* credentials in this instance. After the prompt, the passwords will be
|
||||
* updated in the key store using update_stored_passwords_async(). Return
|
||||
* whether the user proceeded normally (false if they tried to cancel the
|
||||
* prompt).
|
||||
*/
|
||||
public async bool prompt_passwords_async(ServiceFlag services) throws Error {
|
||||
check_mediator_instance();
|
||||
|
||||
string? imap_password, smtp_password;
|
||||
bool imap_remember_password, smtp_remember_password;
|
||||
|
||||
if (this.smtp.credentials == null)
|
||||
services &= ~ServiceFlag.SMTP;
|
||||
|
||||
if (!yield this.imap.mediator.prompt_passwords_async(
|
||||
services, this, out imap_password, out smtp_password,
|
||||
out imap_remember_password, out smtp_remember_password))
|
||||
return false;
|
||||
|
||||
if (services.has_imap()) {
|
||||
imap.set_password(imap_password, imap_remember_password);
|
||||
}
|
||||
|
||||
if (services.has_smtp()) {
|
||||
smtp.set_password(smtp_password, smtp_remember_password);
|
||||
}
|
||||
|
||||
yield update_stored_passwords_async(services);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the Engine's authentication mediator to set or clear the passwords
|
||||
* for the given services in the key store.
|
||||
*/
|
||||
public async void update_stored_passwords_async(ServiceFlag services) throws Error {
|
||||
check_mediator_instance();
|
||||
|
||||
|
||||
if (services.has_imap()) {
|
||||
if (this.imap.remember_password)
|
||||
yield this.imap.mediator.set_password_async(this.imap);
|
||||
else
|
||||
yield this.imap.mediator.clear_password_async(this.imap);
|
||||
}
|
||||
|
||||
if (services.has_smtp() && this.smtp.credentials != null) {
|
||||
if (this.smtp.remember_password)
|
||||
yield this.smtp.mediator.set_password_async(this.smtp);
|
||||
else
|
||||
yield this.smtp.mediator.clear_password_async(this.smtp);
|
||||
}
|
||||
}
|
||||
|
||||
public async void clear_stored_passwords_async(ServiceFlag services) throws Error {
|
||||
Error? return_error = null;
|
||||
check_mediator_instance();
|
||||
|
||||
try {
|
||||
if (services.has_imap())
|
||||
yield this.imap.mediator.clear_password_async(this.imap);
|
||||
} catch (Error e) {
|
||||
return_error = e;
|
||||
}
|
||||
|
||||
try {
|
||||
if (services.has_smtp() && this.smtp.credentials != null)
|
||||
yield this.smtp.mediator.clear_password_async(this.smtp);
|
||||
} catch (Error e) {
|
||||
return_error = e;
|
||||
}
|
||||
|
||||
if (return_error != null)
|
||||
throw return_error;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines if this account contains a specific email address.
|
||||
* Prompts the user for their SMTP authentication secret.
|
||||
*
|
||||
* Returns true if the address part of `email` is equal to (case
|
||||
* insensitive) the address part of this account's primary mailbox
|
||||
* or any of its secondary mailboxes.
|
||||
* Returns true if the credentials were successfully entered, else
|
||||
* false if the user dismissed the prompt.
|
||||
*/
|
||||
public bool has_email_address(Geary.RFC822.MailboxAddress email) {
|
||||
return (
|
||||
this.primary_mailbox.equal_to(email) ||
|
||||
(this.alternate_mailboxes != null &&
|
||||
this.alternate_mailboxes.fold<bool>((alt) => {
|
||||
return alt.equal_to(email);
|
||||
}, false))
|
||||
public async bool prompt_smtp_credentials(GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
return yield this.smtp.mediator.prompt_token(
|
||||
this, this.smtp, cancellable
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads this account's IMAP credentials from its mediator, if needed.
|
||||
*
|
||||
* This method may cause the user to be prompted for their
|
||||
* secrets, thus it may yield for some time.
|
||||
*
|
||||
* Returns true if the credentials were successfully loaded or had
|
||||
* been previously loaded, the credentials could not be loaded and
|
||||
* the IMAP credentials are invalid.
|
||||
*/
|
||||
public async bool load_imap_credentials(GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
Credentials? creds = this.imap.credentials;
|
||||
bool loaded = creds.is_complete();
|
||||
if (!loaded) {
|
||||
loaded = yield this.imap.mediator.load_token(
|
||||
this, this.imap, cancellable
|
||||
);
|
||||
}
|
||||
return loaded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prompts the user for their IMAP authentication secret.
|
||||
*
|
||||
* Returns true if the credentials were successfully entered, else
|
||||
* false if the user dismissed the prompt.
|
||||
*/
|
||||
public async bool prompt_imap_credentials(GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
return yield this.imap.mediator.prompt_token(
|
||||
this, this.imap, cancellable
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,54 +7,33 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Denotes objects that can store and retrieve authentication secrets.
|
||||
*/
|
||||
* A store for authentication tokens.
|
||||
*/
|
||||
public interface Geary.CredentialsMediator : GLib.Object {
|
||||
|
||||
/**
|
||||
* Query the key store for the password for the given service.
|
||||
* Updates the token for a service's credential from the store.
|
||||
*
|
||||
* Return null if the password wasn't in the key store, or the
|
||||
* password if it was.
|
||||
* Returns true if the token was present and loaded, else false.
|
||||
*/
|
||||
public abstract async string?
|
||||
get_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
public abstract async bool load_token(AccountInformation account,
|
||||
ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error;
|
||||
|
||||
/**
|
||||
* Add or update the store's password entry for the given service.
|
||||
* Prompt the user to enter passwords for the given service.
|
||||
*
|
||||
* Updates authentication-related details for the given service,
|
||||
* possibly including user login names, authentication tokens, and
|
||||
* the remember password preference.
|
||||
*
|
||||
* Returns false if the user tried to cancel the interaction, or
|
||||
* true if they tried to proceed.
|
||||
*/
|
||||
public abstract async
|
||||
void set_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
public abstract async bool prompt_token(AccountInformation account,
|
||||
ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error;
|
||||
|
||||
/**
|
||||
* Deletes the key store's password entry for the given service.
|
||||
*
|
||||
* Do nothing (and do *not* throw an error) if the credentials
|
||||
* weren't in the key store.
|
||||
*/
|
||||
public abstract async void
|
||||
clear_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
throws GLib.Error;
|
||||
|
||||
/**
|
||||
* Prompt the user to enter passwords for the given services.
|
||||
*
|
||||
* Set the out parameters for the services to the values entered
|
||||
* by the user (out parameters for services not being prompted for
|
||||
* are ignored). Return false if the user tried to cancel the
|
||||
* interaction, or true if they tried to proceed.
|
||||
*/
|
||||
public abstract async bool
|
||||
prompt_passwords_async(ServiceFlag services,
|
||||
AccountInformation account_information,
|
||||
out string? imap_password,
|
||||
out string? smtp_password,
|
||||
out bool imap_remember_password,
|
||||
out bool smtp_remember_password)
|
||||
throws GLib.Error;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,19 +5,24 @@
|
|||
*/
|
||||
|
||||
/**
|
||||
* Credentials represent a username and a password authenticating a user for access to a resource.
|
||||
* More sophisticated schemes exist; this suffices for now.
|
||||
* Credentials provide a user's access details for authentication.
|
||||
*
|
||||
* Either property (user, pass) may be null. This indicates the Credentials are incomplete and
|
||||
* need further information (i.e. prompt user for username, fetch password from keyring, etc.)
|
||||
* Either field may be a non-null zero-length string; this is considered valid and is_complete()
|
||||
* will return true in this case.
|
||||
*
|
||||
* Note that Geary will hold Credentials in memory for the long-term, usually the duration of the
|
||||
* application. This is because network resources often have to be connected (or reconnected) to
|
||||
* in the background and asking the user to reauthenticate each time is deemed inconvenient.
|
||||
*/
|
||||
* The {@link user} property specifies the user's log in name, and the
|
||||
* {@link token} property is a shared secret between the user and a
|
||||
* service. For password-based schemes, this would be a password.
|
||||
|
||||
* The token property may be null. This indicates the Credentials are
|
||||
* incomplete and need further information (i.e. prompt user for
|
||||
* username, fetch password from keyring, etc.). The token may be a
|
||||
* non-null zero-length string; this is considered valid and
|
||||
* is_complete() will return true in this case.
|
||||
*
|
||||
* Note that Geary will hold Credentials in memory for the long-term,
|
||||
* usually the duration of the application. This is because network
|
||||
* resources often have to be connected (or reconnected) to in the
|
||||
* background and asking the user to reauthenticate each time is
|
||||
* deemed inconvenient.
|
||||
*/
|
||||
public class Geary.Credentials : BaseObject, Gee.Hashable<Geary.Credentials> {
|
||||
|
||||
|
||||
|
|
@ -54,24 +59,24 @@ public class Geary.Credentials : BaseObject, Gee.Hashable<Geary.Credentials> {
|
|||
|
||||
public Method supported_method { get; private set; }
|
||||
public string user { get; private set; }
|
||||
public string? pass { get; private set; }
|
||||
public string? token { get; private set; }
|
||||
|
||||
public Credentials(Method supported_method, string user, string? pass = null) {
|
||||
public Credentials(Method supported_method, string user, string? token = null) {
|
||||
this.supported_method = supported_method;
|
||||
this.user = user;
|
||||
this.pass = pass;
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public bool is_complete() {
|
||||
return (this.user != null) && (this.pass != null);
|
||||
return this.token != null;
|
||||
}
|
||||
|
||||
public Credentials copy_with_password(string? password) {
|
||||
return new Credentials(this.supported_method, this.user, password);
|
||||
public Credentials copy_with_token(string? token) {
|
||||
return new Credentials(this.supported_method, this.user, token);
|
||||
}
|
||||
|
||||
public Credentials copy() {
|
||||
return new Credentials(this.supported_method, this.user, this.pass);
|
||||
return new Credentials(this.supported_method, this.user, this.token);
|
||||
}
|
||||
|
||||
public string to_string() {
|
||||
|
|
@ -85,13 +90,13 @@ public class Geary.Credentials : BaseObject, Gee.Hashable<Geary.Credentials> {
|
|||
return (
|
||||
this.supported_method == c.supported_method &&
|
||||
this.user == c.user &&
|
||||
this.pass == c.pass
|
||||
this.token == c.token
|
||||
);
|
||||
}
|
||||
|
||||
public uint hash() {
|
||||
return "%d%s%s".printf(
|
||||
this.supported_method, this.user ?? "", this.pass ?? ""
|
||||
this.supported_method, this.user, this.token ?? ""
|
||||
).hash();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,23 +50,6 @@ public enum Geary.Protocol {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A bitfield to specify {@link ServiceInformation} types.
|
||||
*/
|
||||
[Flags]
|
||||
public enum Geary.ServiceFlag {
|
||||
IMAP,
|
||||
SMTP;
|
||||
|
||||
public bool has_imap() {
|
||||
return (this & IMAP) == IMAP;
|
||||
}
|
||||
|
||||
public bool has_smtp() {
|
||||
return (this & SMTP) == SMTP;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This class encloses all the information used when connecting with the server,
|
||||
|
|
@ -139,18 +122,6 @@ public abstract class Geary.ServiceInformation : GLib.Object {
|
|||
this.mediator = mediator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a new password for this instance's credentials, with the option
|
||||
* of remembering the password.
|
||||
*/
|
||||
public void set_password(string password, bool remember = false) {
|
||||
this.credentials = new Credentials(
|
||||
this.credentials.supported_method,
|
||||
this.credentials.user,
|
||||
password
|
||||
);
|
||||
this.remember_password = remember;
|
||||
}
|
||||
|
||||
public abstract ServiceInformation temp_copy();
|
||||
|
||||
|
|
|
|||
|
|
@ -445,14 +445,7 @@ private class Geary.SmtpOutboxFolder :
|
|||
// Get SMTP password if we haven't loaded it yet and the account needs credentials.
|
||||
// If the account needs a password but it's not set or incorrect in the keyring, we'll
|
||||
// prompt below after getting an AUTHENTICATION_FAILED error.
|
||||
Geary.Credentials? credentials = account.get_smtp_credentials();
|
||||
if (credentials != null && !credentials.is_complete()) {
|
||||
try {
|
||||
yield account.get_passwords_async(ServiceFlag.SMTP);
|
||||
} catch (Error e) {
|
||||
debug("SMTP password fetch error: %s", e.message);
|
||||
}
|
||||
}
|
||||
yield this.account.information.load_smtp_credentials(cancellable);
|
||||
|
||||
// only try sending if (a) no TLS issues or (b) user has
|
||||
// acknowledged them and says to continue
|
||||
|
|
@ -479,18 +472,7 @@ private class Geary.SmtpOutboxFolder :
|
|||
|
||||
// At this point we may already have a
|
||||
// password in memory -- but it's incorrect.
|
||||
// Delete the current password, prompt the
|
||||
// user for a new one, and try again.
|
||||
bool user_confirmed = false;
|
||||
try {
|
||||
user_confirmed = yield account.fetch_passwords_async(
|
||||
ServiceFlag.SMTP, true
|
||||
);
|
||||
} catch (Error e) {
|
||||
debug("Error prompting for SMTP password: %s", e.message);
|
||||
}
|
||||
|
||||
if (!user_confirmed) {
|
||||
if (!yield account.prompt_smtp_credentials(cancellable)) {
|
||||
// The user cancelled and hence they don't
|
||||
// want to be prompted again, so bail out.
|
||||
throw send_err;
|
||||
|
|
|
|||
|
|
@ -106,15 +106,13 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
|
|||
// Reset this so we start trying to authenticate again
|
||||
this.authentication_failures = 0;
|
||||
|
||||
// To prevent spurious connection failures, we make sure we have the
|
||||
// IMAP password before attempting a connection. This might have to be
|
||||
// reworked when we allow passwordless logins.
|
||||
if (!this.information.imap.credentials.is_complete())
|
||||
yield this.information.get_passwords_async(ServiceFlag.IMAP);
|
||||
|
||||
this.session_pool.credentials_updated(
|
||||
this.information.imap.credentials
|
||||
);
|
||||
// To prevent spurious connection failures, we make sure we
|
||||
// have the IMAP password before attempting a connection.
|
||||
if (yield this.information.load_imap_credentials(cancellable)) {
|
||||
this.session_pool.credentials_updated(
|
||||
this.information.imap.credentials
|
||||
);
|
||||
}
|
||||
|
||||
// This will cause the session manager to open at least one
|
||||
// connection if we are online
|
||||
|
|
@ -1003,11 +1001,11 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
|
|||
notify_imap_problem(ProblemType.SERVER_ERROR, login_error);
|
||||
} else {
|
||||
// Now, we should ask the user for their password
|
||||
this.information.fetch_passwords_async.begin(
|
||||
ServiceFlag.IMAP, true,
|
||||
this.information.prompt_imap_credentials.begin(
|
||||
this.open_cancellable,
|
||||
(obj, ret) => {
|
||||
try {
|
||||
if (this.information.fetch_passwords_async.end(ret)) {
|
||||
if (this.information.prompt_imap_credentials.end(ret)) {
|
||||
// Have a new password, so try that
|
||||
this.session_pool.credentials_updated(
|
||||
this.information.imap.credentials
|
||||
|
|
|
|||
|
|
@ -854,9 +854,9 @@ public class Geary.Imap.ClientSession : BaseObject {
|
|||
login_failed(null);
|
||||
throw new ImapError.UNAUTHENTICATED("No credentials provided for account: %s", credentials.to_string());
|
||||
}
|
||||
|
||||
LoginCommand cmd = new LoginCommand(credentials.user, credentials.pass);
|
||||
|
||||
|
||||
LoginCommand cmd = new LoginCommand(credentials.user, credentials.token);
|
||||
|
||||
MachineParams params = new MachineParams(cmd);
|
||||
fsm.issue(Event.LOGIN, null, params);
|
||||
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public class Geary.Smtp.LoginAuthenticator : Geary.Smtp.Authenticator {
|
|||
return new Memory.StringBuffer(Base64.encode(credentials.user.data));
|
||||
|
||||
case 1:
|
||||
return new Memory.StringBuffer(Base64.encode((credentials.pass ?? "").data));
|
||||
return new Memory.StringBuffer(Base64.encode((credentials.token ?? "").data));
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ public class Geary.Smtp.PlainAuthenticator : Geary.Smtp.Authenticator {
|
|||
growable.append(nul);
|
||||
growable.append(credentials.user.data);
|
||||
growable.append(nul);
|
||||
growable.append((credentials.pass ?? "").data);
|
||||
growable.append((credentials.token ?? "").data);
|
||||
|
||||
// convert to Base64
|
||||
return new Memory.StringBuffer(Base64.encode(growable.get_bytes().get_data()));
|
||||
|
|
|
|||
|
|
@ -13,44 +13,11 @@ public class Geary.MockCredentialsMediator :
|
|||
get; set; default = new Gee.LinkedList<ExpectedCall>();
|
||||
}
|
||||
|
||||
public virtual async string?
|
||||
get_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
public virtual async bool load_token(AccountInformation account,
|
||||
ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
return object_call<string>(
|
||||
"prompt_passwords_async",
|
||||
{ service, cancellable },
|
||||
""
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add or update the store's password entry for the given service.
|
||||
*/
|
||||
public virtual async void
|
||||
set_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
throws GLib.Error {
|
||||
void_call(
|
||||
"prompt_passwords_async",
|
||||
{ service, cancellable }
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the key store's password entry for the given service.
|
||||
*
|
||||
* Do nothing (and do *not* throw an error) if the credentials
|
||||
* weren't in the key store.
|
||||
*/
|
||||
public virtual async void
|
||||
clear_password_async(ServiceInformation service,
|
||||
GLib.Cancellable? cancellable = null)
|
||||
throws GLib.Error {
|
||||
void_call(
|
||||
"prompt_passwords_async",
|
||||
{ service, cancellable }
|
||||
);
|
||||
return object_call<bool>("load_token", { service, cancellable }, false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -61,21 +28,13 @@ public class Geary.MockCredentialsMediator :
|
|||
* are ignored). Return false if the user tried to cancel the
|
||||
* interaction, or true if they tried to proceed.
|
||||
*/
|
||||
public virtual async bool
|
||||
prompt_passwords_async(ServiceFlag services,
|
||||
AccountInformation account_information,
|
||||
out string? imap_password,
|
||||
out string? smtp_password,
|
||||
out bool imap_remember_password,
|
||||
out bool smtp_remember_password)
|
||||
public virtual async bool prompt_token(AccountInformation account,
|
||||
ServiceInformation service,
|
||||
GLib.Cancellable? cancellable)
|
||||
throws GLib.Error {
|
||||
imap_password = null;
|
||||
smtp_password = null;
|
||||
imap_remember_password = false;
|
||||
smtp_remember_password = false;
|
||||
return boolean_call(
|
||||
"prompt_passwords_async",
|
||||
{ box_arg(services), account_information },
|
||||
"prompt_token",
|
||||
{ account, service, cancellable },
|
||||
false
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue