geary/src/client/application/secret-mediator.vala
2015-02-06 12:43:33 -08:00

158 lines
6.6 KiB
Vala

/* Copyright 2011-2015 Yorba Foundation
*
* This software is licensed under the GNU Lesser General Public License
* (version 2.1 or later). See the COPYING file in this distribution.
*/
// LibSecret password adapter.
public class SecretMediator : Geary.CredentialsMediator, Object {
private const string OLD_GEARY_USERNAME_PREFIX = "org.yorba.geary username:";
private Geary.Nonblocking.Mutex dialog_mutex = new Geary.Nonblocking.Mutex();
private string get_key_name(Geary.Service service, string user) {
switch (service) {
case Geary.Service.IMAP:
return "org.yorba.geary imap_username:" + user;
case Geary.Service.SMTP:
return "org.yorba.geary smtp_username:" + user;
default:
assert_not_reached();
}
}
private Geary.Credentials get_credentials(Geary.Service service, Geary.AccountInformation account_information) {
switch (service) {
case Geary.Service.IMAP:
return account_information.imap_credentials;
case Geary.Service.SMTP:
return account_information.smtp_credentials;
default:
assert_not_reached();
}
}
private async string? migrate_old_password(string old_key, string new_key, Cancellable? cancellable)
throws Error {
string? password = yield Secret.password_lookup(Secret.SCHEMA_COMPAT_NETWORK, cancellable,
"user", old_key);
if (password != null) {
bool result = yield Secret.password_store(Secret.SCHEMA_COMPAT_NETWORK,
null, new_key, password, cancellable, "user", new_key);
if (result)
yield Secret.password_clear(Secret.SCHEMA_COMPAT_NETWORK, cancellable, "user", old_key);
}
return password;
}
public virtual async string? get_password_async(
Geary.Service service, Geary.AccountInformation account_information, Cancellable? cancellable = null)
throws Error {
string key_name = get_key_name(service, account_information.email);
string? password = yield Secret.password_lookup(Secret.SCHEMA_COMPAT_NETWORK, cancellable,
"user", key_name);
// fallback to the old keyring key string for upgrading users
if (password == null) {
Geary.Credentials creds = get_credentials(service, account_information);
// <= 0.6
password = yield migrate_old_password(get_key_name(service, creds.user),
key_name, cancellable);
// 0.1
if (password == null) {
password = yield migrate_old_password(OLD_GEARY_USERNAME_PREFIX + creds.user,
key_name, cancellable);
}
}
if (password == null)
debug("Unable to fetch password in libsecret keyring for %s", account_information.email);
return password;
}
public virtual async void set_password_async(
Geary.Service service, Geary.AccountInformation account_information,
Cancellable? cancellable = null) throws Error {
string key_name = get_key_name(service, account_information.email);
Geary.Credentials credentials = get_credentials(service, account_information);
bool result = yield Secret.password_store(Secret.SCHEMA_COMPAT_NETWORK,
null, key_name, credentials.pass, cancellable, "user", key_name);
if (!result)
debug("Unable to store password for \"%s\" in libsecret keyring", key_name);
}
public virtual async void clear_password_async(
Geary.Service service, Geary.AccountInformation account_information, Cancellable? cancellable = null)
throws Error {
// delete new-style and old-style locations
Geary.Credentials credentials = get_credentials(service, account_information);
// new-style
yield Secret.password_clear(Secret.SCHEMA_COMPAT_NETWORK, cancellable, "user",
get_key_name(service, account_information.email));
// <= 0.6
yield Secret.password_clear(Secret.SCHEMA_COMPAT_NETWORK, cancellable, "user",
get_key_name(service, credentials.user));
// 0.1
yield Secret.password_clear(Secret.SCHEMA_COMPAT_NETWORK, cancellable, "user",
OLD_GEARY_USERNAME_PREFIX + 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) throws 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());
// 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);
// If the main window is hidden, make it visible now and present to user as transient parent
Gtk.Window? main_window = GearyApplication.instance.controller.main_window;
if (main_window != null && !main_window.visible) {
main_window.show_all();
main_window.present_with_time(Gdk.CURRENT_TIME);
}
PasswordDialog password_dialog = new PasswordDialog(main_window, services.has_smtp(),
account_information, services);
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;
}
return true;
}
}