Commit 80912869 authored by Jim Nelson's avatar Jim Nelson
Browse files

Present user with dialog when TLS cert warnings detected: Bug #713247

When a TLS certificate warning is detected, the user will now be
presented with a warning dialog presenting them with three options:
Trust This Server, Always Trust This Server, and Don't Trust This
Server (the default).  The user must select one of the first two
buttons for Geary to continue connecting to the server, otherwise it
will close the Account object for the duration of the application
session.

This patch introduces a dependency on gcr-3, which is used to pin TLS
certificates (i.e. persist the user's choice of ignoring the TLS
warnings).
parent 0e5c3343
...@@ -20,7 +20,8 @@ Build-Depends: debhelper (>= 8), ...@@ -20,7 +20,8 @@ Build-Depends: debhelper (>= 8),
intltool, intltool,
libgirepository1.0-dev (>= 1.32.0), libgirepository1.0-dev (>= 1.32.0),
desktop-file-utils, desktop-file-utils,
gnome-doc-utils gnome-doc-utils,
libgcr-3-dev
Standards-Version: 3.8.3 Standards-Version: 3.8.3
Homepage: http://www.yorba.org Homepage: http://www.yorba.org
...@@ -38,7 +39,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends}, ...@@ -38,7 +39,8 @@ Depends: ${shlibs:Depends}, ${misc:Depends},
libgmime-2.6-0 (>= 2.6.0), libgmime-2.6-0 (>= 2.6.0),
libsecret-1-0 (>= 0.11), libsecret-1-0 (>= 0.11),
libmessaging-menu0 (>= 12.10.2), libmessaging-menu0 (>= 12.10.2),
libunity9 (>= 5.12.0) libunity9 (>= 5.12.0),
libgcr-3-1
Description: Email client Description: Email client
Geary is an email client built for the GNOME desktop environment. It Geary is an email client built for the GNOME desktop environment. It
allows you to read and send email with a simple, modern interface. allows you to read and send email with a simple, modern interface.
......
...@@ -48,6 +48,7 @@ src/client/conversation-viewer/conversation-viewer.vala ...@@ -48,6 +48,7 @@ src/client/conversation-viewer/conversation-viewer.vala
src/client/conversation-viewer/conversation-web-view.vala src/client/conversation-viewer/conversation-web-view.vala
src/client/dialogs/alert-dialog.vala src/client/dialogs/alert-dialog.vala
src/client/dialogs/attachment-dialog.vala src/client/dialogs/attachment-dialog.vala
src/client/dialogs/certificate-warning-dialog.vala
src/client/dialogs/password-dialog.vala src/client/dialogs/password-dialog.vala
src/client/dialogs/preferences-dialog.vala src/client/dialogs/preferences-dialog.vala
src/client/dialogs/upgrade-dialog.vala src/client/dialogs/upgrade-dialog.vala
...@@ -114,6 +115,7 @@ src/engine/api/geary-named-flag.vala ...@@ -114,6 +115,7 @@ src/engine/api/geary-named-flag.vala
src/engine/api/geary-progress-monitor.vala src/engine/api/geary-progress-monitor.vala
src/engine/api/geary-search-folder.vala src/engine/api/geary-search-folder.vala
src/engine/api/geary-search-query.vala src/engine/api/geary-search-query.vala
src/engine/api/geary-service.vala
src/engine/api/geary-service-provider.vala src/engine/api/geary-service-provider.vala
src/engine/api/geary-special-folder-type.vala src/engine/api/geary-special-folder-type.vala
src/engine/app/app-conversation-monitor.vala src/engine/app/app-conversation-monitor.vala
...@@ -352,6 +354,7 @@ src/engine/util/util-trillian.vala ...@@ -352,6 +354,7 @@ src/engine/util/util-trillian.vala
[type: gettext/glade]ui/account_list.glade [type: gettext/glade]ui/account_list.glade
[type: gettext/glade]ui/account_spinner.glade [type: gettext/glade]ui/account_spinner.glade
[type: gettext/glade]ui/app_menu.interface [type: gettext/glade]ui/app_menu.interface
[type: gettext/glade]ui/certificate_warning_dialog.glade
[type: gettext/glade]ui/composer_accelerators.ui [type: gettext/glade]ui/composer_accelerators.ui
[type: gettext/glade]ui/composer.glade [type: gettext/glade]ui/composer.glade
[type: gettext/glade]ui/find_bar.glade [type: gettext/glade]ui/find_bar.glade
......
...@@ -40,6 +40,7 @@ engine/api/geary-named-flags.vala ...@@ -40,6 +40,7 @@ engine/api/geary-named-flags.vala
engine/api/geary-progress-monitor.vala engine/api/geary-progress-monitor.vala
engine/api/geary-search-folder.vala engine/api/geary-search-folder.vala
engine/api/geary-search-query.vala engine/api/geary-search-query.vala
engine/api/geary-service.vala
engine/api/geary-service-provider.vala engine/api/geary-service-provider.vala
engine/api/geary-special-folder-type.vala engine/api/geary-special-folder-type.vala
...@@ -346,6 +347,7 @@ client/conversation-viewer/conversation-web-view.vala ...@@ -346,6 +347,7 @@ client/conversation-viewer/conversation-web-view.vala
client/dialogs/alert-dialog.vala client/dialogs/alert-dialog.vala
client/dialogs/attachment-dialog.vala client/dialogs/attachment-dialog.vala
client/dialogs/certificate-warning-dialog.vala
client/dialogs/password-dialog.vala client/dialogs/password-dialog.vala
client/dialogs/preferences-dialog.vala client/dialogs/preferences-dialog.vala
client/dialogs/upgrade-dialog.vala client/dialogs/upgrade-dialog.vala
...@@ -530,6 +532,7 @@ pkg_check_modules(DEPS REQUIRED ...@@ -530,6 +532,7 @@ pkg_check_modules(DEPS REQUIRED
gmime-2.6>=2.6.0 gmime-2.6>=2.6.0
libsecret-1>=0.11 libsecret-1>=0.11
libxml-2.0>=2.7.8 libxml-2.0>=2.7.8
gcr-3
${EXTRA_CLIENT_PKG_CONFIG} ${EXTRA_CLIENT_PKG_CONFIG}
) )
...@@ -540,7 +543,7 @@ set(ENGINE_PACKAGES ...@@ -540,7 +543,7 @@ set(ENGINE_PACKAGES
# webkitgtk-3.0 is listed as a custom VAPI (below) to ensure it's treated as a dependency and # webkitgtk-3.0 is listed as a custom VAPI (below) to ensure it's treated as a dependency and
# built before compilation # built before compilation
set(CLIENT_PACKAGES set(CLIENT_PACKAGES
gtk+-3.0 libsecret-1 libsoup-2.4 libnotify libcanberra ${EXTRA_CLIENT_PACKAGES} gtk+-3.0 libsecret-1 libsoup-2.4 libnotify libcanberra gcr-3 ${EXTRA_CLIENT_PACKAGES}
) )
set(CONSOLE_PACKAGES set(CONSOLE_PACKAGES
...@@ -558,6 +561,7 @@ set(CFLAGS ...@@ -558,6 +561,7 @@ set(CFLAGS
-D_GSETTINGS_DIR=\"${CMAKE_BINARY_DIR}/gsettings\" -D_GSETTINGS_DIR=\"${CMAKE_BINARY_DIR}/gsettings\"
-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"
-DLANGUAGE_SUPPORT_DIRECTORY=\"${LANGUAGE_SUPPORT_DIRECTORY}\" -DLANGUAGE_SUPPORT_DIRECTORY=\"${LANGUAGE_SUPPORT_DIRECTORY}\"
-DGCR_API_SUBJECT_TO_CHANGE
-g -g
) )
......
...@@ -92,8 +92,7 @@ public class AccountDialog : Gtk.Dialog { ...@@ -92,8 +92,7 @@ public class AccountDialog : Gtk.Dialog {
return; return;
try { try {
yield account.get_passwords_async(Geary.CredentialsMediator.ServiceFlag.IMAP | yield account.get_passwords_async(Geary.ServiceFlag.IMAP | Geary.ServiceFlag.SMTP);
Geary.CredentialsMediator.ServiceFlag.SMTP);
} catch (Error err) { } catch (Error err) {
debug("Unable to fetch password(s) for account: %s", err.message); debug("Unable to fetch password(s) for account: %s", err.message);
} }
...@@ -166,22 +165,34 @@ public class AccountDialog : Gtk.Dialog { ...@@ -166,22 +165,34 @@ public class AccountDialog : Gtk.Dialog {
options |= Geary.Engine.ValidationOption.CHECK_CONNECTIONS; options |= Geary.Engine.ValidationOption.CHECK_CONNECTIONS;
// Validate account. // Validate account.
GearyApplication.instance.controller.validate_async.begin(info, options, null, do_save_or_edit_async.begin(info, options);
on_save_add_or_edit_completed);
} }
private void on_save_add_or_edit_completed(Object? source, AsyncResult result) { private async void do_save_or_edit_async(Geary.AccountInformation account_information,
Geary.Engine.ValidationResult validation_result = Geary.Engine.ValidationOption options) {
GearyApplication.instance.controller.validate_async.end(result); Geary.Engine.ValidationResult validation_result = Geary.Engine.ValidationResult.OK;
for (;;) {
// If account was successfully added return to the account list. Otherwise, go back to the validation_result = yield GearyApplication.instance.controller.validate_async(
// account add page so the user can try again. account_information, options);
if (validation_result == Geary.Engine.ValidationResult.OK) {
account_list_pane.present(); // If account was successfully added return to the account list.
} else { if (validation_result == Geary.Engine.ValidationResult.OK) {
add_edit_pane.set_validation_result(validation_result); account_list_pane.present();
add_edit_pane.present();
return;
}
// check for TLS warnings
bool retry_required;
validation_result = yield GearyApplication.instance.controller.validation_check_for_tls_warnings_async(
account_information, validation_result, out retry_required);
if (!retry_required)
break;
} }
// Otherwise, go back to the account add page so the user can try again.
add_edit_pane.set_validation_result(validation_result);
add_edit_pane.present();
} }
private void on_cancel_back_to_list() { private void on_cancel_back_to_list() {
......
...@@ -23,6 +23,7 @@ private const OptionEntry[] options = { ...@@ -23,6 +23,7 @@ private const OptionEntry[] options = {
/// "Normalization" can also be called "synchronization" /// "Normalization" can also be called "synchronization"
{ "log-folder-normalization", 0, 0, OptionArg.NONE, ref log_folder_normalization, N_("Log folder normalization"), null }, { "log-folder-normalization", 0, 0, OptionArg.NONE, ref log_folder_normalization, N_("Log folder normalization"), null },
{ "inspector", 'i', 0, OptionArg.NONE, ref inspector, N_("Allow inspection of WebView"), null }, { "inspector", 'i', 0, OptionArg.NONE, ref inspector, N_("Allow inspection of WebView"), null },
{ "revoke-certs", 0, 0, OptionArg.NONE, ref revoke_certs, N_("Revoke all server certificates with TLS warnings"), null },
{ "version", 'V', 0, OptionArg.NONE, ref version, N_("Display program version"), null }, { "version", 'V', 0, OptionArg.NONE, ref version, N_("Display program version"), null },
{ null } { null }
}; };
...@@ -38,6 +39,7 @@ public bool log_periodic = false; ...@@ -38,6 +39,7 @@ public bool log_periodic = false;
public bool log_sql = false; public bool log_sql = false;
public bool log_folder_normalization = false; public bool log_folder_normalization = false;
public bool inspector = false; public bool inspector = false;
public bool revoke_certs = false;
public bool version = false; public bool version = false;
public bool parse(string[] args) { public bool parse(string[] args) {
......
...@@ -4,6 +4,16 @@ ...@@ -4,6 +4,16 @@
* (version 2.1 or later). See the COPYING file in this distribution. * (version 2.1 or later). See the COPYING file in this distribution.
*/ */
// Required because Gcr's VAPI is behind-the-times
// TODO: When bindings available, use async variants of these calls
extern const string GCR_PURPOSE_SERVER_AUTH;
extern bool gcr_trust_add_pinned_certificate(Gcr.Certificate cert, string purpose, string peer,
Cancellable? cancellable) throws Error;
extern bool gcr_trust_is_certificate_pinned(Gcr.Certificate cert, string purpose, string peer,
Cancellable? cancellable) throws Error;
extern bool gcr_trust_remove_pinned_certificate(Gcr.Certificate cert, string purpose, string peer,
Cancellable? cancellable) throws Error;
// Primary controller object for Geary. // Primary controller object for Geary.
public class GearyController : Geary.BaseObject { public class GearyController : Geary.BaseObject {
// Named actions. // Named actions.
...@@ -111,6 +121,8 @@ public class GearyController : Geary.BaseObject { ...@@ -111,6 +121,8 @@ public class GearyController : Geary.BaseObject {
private LoginDialog? login_dialog = null; private LoginDialog? login_dialog = null;
private UpgradeDialog upgrade_dialog; private UpgradeDialog upgrade_dialog;
private Gee.List<string> pending_mailtos = new Gee.ArrayList<string>(); private Gee.List<string> pending_mailtos = new Gee.ArrayList<string>();
private Geary.Nonblocking.Mutex untrusted_host_prompt_mutex = new Geary.Nonblocking.Mutex();
private Gee.HashSet<Geary.Endpoint> validating_endpoints = new Gee.HashSet<Geary.Endpoint>();
// List of windows we're waiting to close before Geary closes. // List of windows we're waiting to close before Geary closes.
private Gee.List<ComposerWidget> waiting_to_close = new Gee.ArrayList<ComposerWidget>(); private Gee.List<ComposerWidget> waiting_to_close = new Gee.ArrayList<ComposerWidget>();
...@@ -177,6 +189,7 @@ public class GearyController : Geary.BaseObject { ...@@ -177,6 +189,7 @@ public class GearyController : Geary.BaseObject {
Geary.Engine.instance.account_available.connect(on_account_available); Geary.Engine.instance.account_available.connect(on_account_available);
Geary.Engine.instance.account_unavailable.connect(on_account_unavailable); Geary.Engine.instance.account_unavailable.connect(on_account_unavailable);
Geary.Engine.instance.untrusted_host.connect(on_untrusted_host);
// Connect to various UI signals. // Connect to various UI signals.
main_window.conversation_list_view.conversations_selected.connect(on_conversations_selected); main_window.conversation_list_view.conversations_selected.connect(on_conversations_selected);
...@@ -499,6 +512,135 @@ public class GearyController : Geary.BaseObject { ...@@ -499,6 +512,135 @@ public class GearyController : Geary.BaseObject {
close_account(get_account_instance(account_information)); close_account(get_account_instance(account_information));
} }
private void on_untrusted_host(Geary.AccountInformation account_information,
Geary.Endpoint endpoint, Geary.Endpoint.SecurityType security, TlsConnection cx,
Geary.Service service) {
prompt_untrusted_host_async.begin(account_information, endpoint, security, cx, service);
}
private async void prompt_untrusted_host_async(Geary.AccountInformation account_information,
Geary.Endpoint endpoint, Geary.Endpoint.SecurityType security, TlsConnection cx,
Geary.Service service) {
// use a mutex to prevent multiple dialogs popping up at the same time
int token = Geary.Nonblocking.Mutex.INVALID_TOKEN;
try {
token = yield untrusted_host_prompt_mutex.claim_async();
} catch (Error err) {
message("Unable to lock mutex to prompt user about invalid certificate: %s", err.message);
return;
}
yield locked_prompt_untrusted_host_async(account_information, endpoint, security, cx,
service);
try {
untrusted_host_prompt_mutex.release(ref token);
} catch (Error err) {
message("Unable to release mutex after prompting user about invalid certificate: %s",
err.message);
}
}
private static void get_gcr_params(Geary.Endpoint endpoint, out Gcr.Certificate cert,
out string peer) {
cert = new Gcr.SimpleCertificate(endpoint.untrusted_certificate.certificate.data);
peer = "%s:%u".printf(endpoint.remote_address.hostname, endpoint.remote_address.port);
}
private async void locked_prompt_untrusted_host_async(Geary.AccountInformation account_information,
Geary.Endpoint endpoint, Geary.Endpoint.SecurityType security, TlsConnection cx,
Geary.Service service) {
// possible while waiting on mutex that this endpoint became trusted or untrusted
if (endpoint.trust_untrusted_host != Geary.Trillian.UNKNOWN)
return;
// get GCR parameters
Gcr.Certificate cert;
string peer;
get_gcr_params(endpoint, out cert, out peer);
// Geary allows for user to auto-revoke all questionable server certificates without
// digging around in a keyring/pk manager
if (Args.revoke_certs) {
debug("Auto-revoking certificate for %s...", peer);
try {
gcr_trust_remove_pinned_certificate(cert, GCR_PURPOSE_SERVER_AUTH, peer, null);
} catch (Error err) {
message("Unable to auto-revoke server certificate for %s: %s", peer, err.message);
// drop through, not absolutely valid to do this (might also mean certificate
// was never pinned)
}
}
// if pinned, the user has already made an exception for this server and its certificate,
// so go ahead w/o asking
try {
if (gcr_trust_is_certificate_pinned(cert, GCR_PURPOSE_SERVER_AUTH, peer, null)) {
debug("Certificate for %s is pinned, accepting connection...", peer);
endpoint.trust_untrusted_host = Geary.Trillian.TRUE;
return;
}
} catch (Error err) {
message("Unable to check if server certificate for %s is pinned, assuming not: %s",
peer, err.message);
}
// if these are in validation, there are complex GTK and workflow issues from simply
// presenting the prompt now, so caller who connected will need to do it on their own dime
if (!validating_endpoints.contains(endpoint))
prompt_for_untrusted_host(main_window, account_information, endpoint, service, false);
}
private void prompt_for_untrusted_host(Gtk.Window? parent, Geary.AccountInformation account_information,
Geary.Endpoint endpoint, Geary.Service service, bool is_validation) {
CertificateWarningDialog dialog = new CertificateWarningDialog(parent, account_information,
service, endpoint.tls_validation_warnings, is_validation);
switch (dialog.run()) {
case CertificateWarningDialog.Result.TRUST:
endpoint.trust_untrusted_host = Geary.Trillian.TRUE;
break;
case CertificateWarningDialog.Result.ALWAYS_TRUST:
endpoint.trust_untrusted_host = Geary.Trillian.TRUE;
// get GCR parameters for pinning
Gcr.Certificate cert;
string peer;
get_gcr_params(endpoint, out cert, out peer);
// pinning the certificate creates an exception for the next time a connection
// is attempted
debug("Pinning certificate for %s...", peer);
try {
gcr_trust_add_pinned_certificate(cert, GCR_PURPOSE_SERVER_AUTH, peer, null);
} catch (Error err) {
ErrorDialog error_dialog = new ErrorDialog(main_window,
_("Unable to store server trust exception"), err.message);
error_dialog.run();
}
break;
default:
endpoint.trust_untrusted_host = Geary.Trillian.FALSE;
// close the account; can't go any further w/o offline mode
try {
if (Geary.Engine.instance.get_accounts().has_key(account_information.email)) {
Geary.Account account = Geary.Engine.instance.get_account_instance(account_information);
close_account(account);
}
} catch (Error err) {
message("Unable to close account due to user trust issues: %s", err.message);
}
break;
}
}
private void create_account() { private void create_account() {
Geary.AccountInformation? account_information = request_account_information(null); Geary.AccountInformation? account_information = request_account_information(null);
if (account_information != null) if (account_information != null)
...@@ -516,6 +658,82 @@ public class GearyController : Geary.BaseObject { ...@@ -516,6 +658,82 @@ public class GearyController : Geary.BaseObject {
login_dialog.hide(); login_dialog.hide();
} }
// Returns possibly modified validation results
private Geary.Engine.ValidationResult validation_check_endpoint_for_tls_warnings(
Geary.AccountInformation account_information, Geary.Service service,
Geary.Engine.ValidationResult validation_result, out bool prompted, out bool retry_required) {
prompted = false;
retry_required = false;
// use LoginDialog for parent only if available and visible
Gtk.Window? parent;
if (login_dialog != null && login_dialog.visible)
parent = login_dialog;
else
parent = main_window;
Geary.Endpoint endpoint = account_information.get_endpoint_for_service(service);
// If Endpoint had unresolved TLS issues, prompt user about them
if (endpoint.tls_validation_warnings != 0 && endpoint.trust_untrusted_host != Geary.Trillian.TRUE) {
prompt_for_untrusted_host(parent, account_information, endpoint, service, true);
prompted = true;
}
// If there are still TLS connection issues that caused the connection to fail (happens on the
// first attempt), clear those errors and retry
if (endpoint.tls_validation_warnings != 0 && endpoint.trust_untrusted_host == Geary.Trillian.TRUE) {
Geary.Engine.ValidationResult flag = (service == Geary.Service.IMAP)
? Geary.Engine.ValidationResult.IMAP_CONNECTION_FAILED
: Geary.Engine.ValidationResult.SMTP_CONNECTION_FAILED;
if ((validation_result & flag) != 0) {
validation_result &= ~flag;
retry_required = true;
}
}
return validation_result;
}
// Use after validating to see if TLS warnings were handled by the user and need to retry the
// validation; this will also modify the validation results to better indicate issues to the user
//
// Returns possibly modified validation results
public async Geary.Engine.ValidationResult validation_check_for_tls_warnings_async(
Geary.AccountInformation account_information, Geary.Engine.ValidationResult validation_result,
out bool retry_required) {
retry_required = false;
// Because TLS warnings need cycles to process, sleep and give 'em a chance to do their
// thing ... note that the signal handler does *not* invoke the user prompt dialog when the
// login dialog is in play, so this sleep does not need to worry about user input
yield Geary.Scheduler.sleep_ms_async(100);
// check each service for problems, prompting user each time for verification
bool imap_prompted, imap_retry_required;
validation_result = validation_check_endpoint_for_tls_warnings(account_information,
Geary.Service.IMAP, validation_result, out imap_prompted, out imap_retry_required);
bool smtp_prompted, smtp_retry_required;
validation_result = validation_check_endpoint_for_tls_warnings(account_information,
Geary.Service.SMTP, validation_result, out smtp_prompted, out smtp_retry_required);
// if prompted for user acceptance of bad certificates and they agreed to both, try again
if (imap_prompted && smtp_prompted
&& account_information.get_imap_endpoint().is_trusted_or_never_connected
&& account_information.get_smtp_endpoint().is_trusted_or_never_connected) {
retry_required = true;
} else if (validation_result == Geary.Engine.ValidationResult.OK) {
retry_required = true;
} else {
// if prompt requires retry or otherwise detected it, retry
retry_required = imap_retry_required && smtp_retry_required;
}
return validation_result;
}
// Returns null if we are done validating, or the revised account information if we should retry. // Returns null if we are done validating, or the revised account information if we should retry.
private async Geary.AccountInformation? validate_or_retry_async(Geary.AccountInformation account_information, private async Geary.AccountInformation? validate_or_retry_async(Geary.AccountInformation account_information,
Cancellable? cancellable = null) { Cancellable? cancellable = null) {
...@@ -524,6 +742,16 @@ public class GearyController : Geary.BaseObject { ...@@ -524,6 +742,16 @@ public class GearyController : Geary.BaseObject {
if (result == Geary.Engine.ValidationResult.OK) if (result == Geary.Engine.ValidationResult.OK)
return null; return null;
// check Endpoints for trust (TLS) issues
bool retry_required;
result = yield validation_check_for_tls_warnings_async(account_information, result,
out retry_required);
// return for retry if required; check can also change validation results, in which case
// revalidate entirely to have them written out
if (retry_required)
return account_information;
debug("Validation failed. Prompting user for revised account information"); debug("Validation failed. Prompting user for revised account information");
Geary.AccountInformation? new_account_information = Geary.AccountInformation? new_account_information =
request_account_information(account_information, result); request_account_information(account_information, result);
...@@ -545,6 +773,10 @@ public class GearyController : Geary.BaseObject { ...@@ -545,6 +773,10 @@ public class GearyController : Geary.BaseObject {
public async Geary.Engine.ValidationResult validate_async( public async Geary.Engine.ValidationResult validate_async(
Geary.AccountInformation account_information, Geary.Engine.ValidationOption options, Geary.AccountInformation account_information, Geary.Engine.ValidationOption options,
Cancellable? cancellable = null) { Cancellable? cancellable = null) {
// add Endpoints to set of validating endpoints to prevent the prompt from appearing
validating_endpoints.add(account_information.get_imap_endpoint());
validating_endpoints.add(account_information.get_smtp_endpoint());
Geary.Engine.ValidationResult result = Geary.Engine.ValidationResult.OK; Geary.Engine.ValidationResult result = Geary.Engine.ValidationResult.OK;
try { try {
result = yield Geary.Engine.instance.validate_account_information_async(account_information, result = yield Geary.Engine.instance.validate_account_information_async(account_information,
...@@ -556,6 +788,9 @@ public class GearyController : Geary.BaseObject { ...@@ -556,6 +788,9 @@ public class GearyController : Geary.BaseObject {
return result; return result;
} }
validating_endpoints.remove(account_information.get_imap_endpoint());
validating_endpoints.remove(account_information.get_smtp_endpoint());
if (result == Geary.Engine.ValidationResult.OK) { if (result == Geary.Engine.ValidationResult.OK) {
Geary.AccountInformation real_account_information = account_information; Geary.AccountInformation real_account_information = account_information;
if (account_information.is_copy()) { if (account_information.is_copy()) {
...@@ -566,8 +801,8 @@ public class GearyController : Geary.BaseObject { ...@@ -566,8 +801,8 @@ public class GearyController : Geary.BaseObject {
} }
real_account_information.store_async.begin(cancellable); real_account_information.store_async.begin(cancellable);
do_update_stored_passwords_async.begin(Geary.CredentialsMediator.ServiceFlag.IMAP | do_update_stored_passwords_async.begin(Geary.ServiceFlag.IMAP | Geary.ServiceFlag.SMTP,
Geary.CredentialsMediator.ServiceFlag.SMTP, real_account_information); real_account_information);
debug("Successfully validated account information"); debug("Successfully validated account information");
} }
...@@ -635,7 +870,7 @@ public class GearyController : Geary.BaseObject { ...@@ -635,7 +870,7 @@ public class GearyController : Geary.BaseObject {
return new_info; return new_info;
} }
private async void do_update_stored_passwords_async(Geary.CredentialsMediator.ServiceFlag services, private async void do_update_stored_passwords_async(Geary.ServiceFlag services,
Geary.AccountInformation account_information) { Geary.AccountInformation account_information) {
try { try {
yield account_information.update_stored_passwords_async(services); yield account_information.update_stored_passwords_async(services);
......
...@@ -8,12 +8,12 @@ ...@@ -8,12 +8,12 @@
public class SecretMediator : Geary.CredentialsMediator, Object { public class SecretMediator : Geary.CredentialsMediator, Object {
private const string OLD_GEARY_USERNAME_PREFIX = "org.yorba.geary username:"; private const string OLD_GEARY_USERNAME_PREFIX = "org.yorba.geary username:";
private string get_key_name(Geary.CredentialsMediator.Service service, string user) { private string get_key_name(Geary.Service service, string user) {
switch (service) { switch (service) {
case Service.IMAP: case Geary.Service.IMAP:
return "org.yorba.geary imap_username:" + user; return "org.yorba.geary imap_username:" + user;
case Service.SMTP: case Geary.Service.SMTP:
return "org.yorba.geary smtp_username:" + user;