diff --git a/src/client/accounts/accounts-editor-add-pane.vala b/src/client/accounts/accounts-editor-add-pane.vala index 4b4c72f3..405d26ca 100644 --- a/src/client/accounts/accounts-editor-add-pane.vala +++ b/src/client/accounts/accounts-editor-add-pane.vala @@ -50,15 +50,20 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { [GtkChild] private Gtk.Button create_button; + [GtkChild] + private Gtk.Spinner create_spinner; + private NameRow real_name; private EmailRow email = new EmailRow(); private string last_valid_email = ""; private HostnameRow imap_hostname = new HostnameRow(Geary.Protocol.IMAP); + private TransportSecurityRow imap_tls = new TransportSecurityRow(); private LoginRow imap_login = new LoginRow(); private PasswordRow imap_password = new PasswordRow(); private HostnameRow smtp_hostname = new HostnameRow(Geary.Protocol.SMTP); + private TransportSecurityRow smtp_tls = new TransportSecurityRow(); private SmtpAuthRow smtp_auth = new SmtpAuthRow(); private LoginRow smtp_login = new LoginRow(); private PasswordRow smtp_password = new PasswordRow(); @@ -101,20 +106,24 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { this.email.value.changed.connect(on_email_changed); this.imap_hostname.validator.notify["state"].connect(on_validated); + this.imap_tls.hide(); this.imap_login.validator.notify["state"].connect(on_validated); this.imap_password.validator.notify["state"].connect(on_validated); this.smtp_hostname.validator.notify["state"].connect(on_validated); + this.smtp_tls.hide(); this.smtp_auth.value.changed.connect(on_smtp_auth_changed); this.smtp_login.validator.notify["state"].connect(on_validated); this.smtp_password.validator.notify["state"].connect(on_validated); if (provider == Geary.ServiceProvider.OTHER) { this.receiving_list.add(this.imap_hostname); + this.receiving_list.add(this.imap_tls); this.receiving_list.add(this.imap_login); this.receiving_list.add(this.imap_password); this.sending_list.add(this.smtp_hostname); + this.sending_list.add(this.smtp_tls); this.sending_list.add(this.smtp_auth); } else { this.details_list.add(this.imap_password); @@ -139,10 +148,15 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { } private async void validate_account(GLib.Cancellable? cancellable) { + this.create_spinner.show(); + this.create_spinner.start(); this.create_button.set_sensitive(false); this.set_sensitive(false); bool is_valid = false; + string message = ""; + Gtk.Widget? to_focus = null; + Geary.ServiceInformation imap = new_imap_service(); Geary.ServiceInformation smtp = new_smtp_service(); @@ -162,55 +176,92 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { try { yield this.engine.validate_imap(account, cancellable); imap_valid = true; + } catch (Geary.ImapError.UNAUTHENTICATED err) { + debug("Error authenticating IMAP service: %s", err.message); + to_focus = this.imap_login.value; + // Translators: In-app notification label + message = _("Check your receiving login and password"); } catch (GLib.Error err) { debug("Error validating IMAP service: %s", err.message); - // XXX do something with this + this.imap_tls.show(); + to_focus = this.imap_hostname.value; + // Translators: In-app notification label + message = _("Check your receiving server details"); } - // Only validate SMTP if not using IMAP creds, or if the - // IMAP creds are good, so we don't check known bad creds - if (!smtp.smtp_use_imap_credentials || imap_valid) { - //notification.label = _("Checking sending server…"); - + if (imap_valid) { + debug("Validating SMTP..."); try { yield this.engine.validate_smtp(account, cancellable); smtp_valid = true; + } catch (Geary.SmtpError.AUTHENTICATION_FAILED err) { + debug("Error authenticating SMTP service: %s", err.message); + // There was an SMTP auth error, but IMAP already + // succeeded, so the user probably needs to + // specify custom creds here + this.smtp_auth.source = Geary.SmtpCredentials.CUSTOM; + to_focus = this.smtp_login.value; + // Translators: In-app notification label + message = _("Check your sending login and password"); } catch (GLib.Error err) { debug("Error validating SMTP service: %s", err.message); - // XXX do something with this + this.smtp_tls.show(); + to_focus = this.smtp_hostname.value; + // Translators: In-app notification label + message = _("Check your sending server details"); } } is_valid = imap_valid && smtp_valid; } else { - //notification.label = _("Checking account…"); try { yield this.engine.validate_imap(account, cancellable); is_valid = true; + } catch (Geary.ImapError.UNAUTHENTICATED err) { + debug("Error authenticating provider: %s", err.message); + to_focus = this.email.value; + // Translators: In-app notification label + message = _("Check your email address and password"); } catch (GLib.Error err) { - debug("Error validating provider IMAP: %s", err.message); - // XXX do something with this + debug("Error validating provider service: %s", err.message); + is_valid = false; + // Translators: In-app notification label + message = _("Could not connect, check your network"); } } if (is_valid) { try { yield this.accounts.create_account(account, cancellable); + this.editor.pop(); } catch (GLib.Error err) { debug("Failed to create new local account: %s", err.message); - // XXX do something with this + is_valid = false; + // Translators: In-app notification label for a + // generic error creating an account + message = _("An unexpected problem occurred"); + } + } + + this.create_spinner.stop(); + this.create_spinner.hide(); + this.create_button.set_sensitive(true); + this.set_sensitive(true); + + // Focus and pop up the notification after re-sensitising + // so it actually succeeds. + if (!is_valid) { + if (to_focus != null) { + to_focus.grab_focus(); } - this.editor.pop(); - } else { add_notification( new InAppNotification( - _("Account not added, check the details below") + // Translators: In-app notification label, the + // string substitution is a more detailed reason. + _("Account not created: %s").printf(message) ) ); } - - this.create_button.set_sensitive(true); - this.set_sensitive(true); } private LocalServiceInformation new_imap_service() { @@ -230,6 +281,10 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { GLib.NetworkAddress address = host.validated_address; service.host = address.hostname; service.port = (uint16) address.port; + + Geary.TlsNegotiationMethod tls = this.imap_tls.value.method; + service.use_ssl = (tls == Geary.TlsNegotiationMethod.TRANSPORT); + service.use_starttls = (tls == Geary.TlsNegotiationMethod.START_TLS); } else { this.provider.setup_service(service); service.credentials = new Geary.Credentials( @@ -247,7 +302,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { this.accounts.new_libsecret_service(Geary.Protocol.SMTP); if (this.provider == Geary.ServiceProvider.OTHER) { - switch (this.smtp_auth.get_value()) { + switch (this.smtp_auth.source) { case Geary.SmtpCredentials.NONE: service.smtp_noauth = true; service.smtp_use_imap_credentials = false; @@ -276,6 +331,13 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { service.host = address.hostname; service.port = (uint16) address.port; + + Geary.TlsNegotiationMethod tls = this.smtp_tls.value.method; + service.use_ssl = (tls == Geary.TlsNegotiationMethod.TRANSPORT); + service.use_starttls = (tls == Geary.TlsNegotiationMethod.START_TLS); + + debug("SMTP service: TLS: %s, STARTTLS: %s", + service.use_ssl.to_string(), service.use_starttls.to_string()); } else { this.provider.setup_service(service); } @@ -319,7 +381,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { } private void on_smtp_auth_changed() { - if (this.smtp_auth.get_value() == Geary.SmtpCredentials.CUSTOM) { + if (this.smtp_auth.source == Geary.SmtpCredentials.CUSTOM) { this.sending_list.add(this.smtp_login); this.sending_list.add(this.smtp_password); } else if (this.smtp_login.parent != null) { @@ -346,6 +408,7 @@ internal class Accounts.EditorAddPane : Gtk.Grid, EditorPane { Gtk.Container? next = null; if (direction == Gtk.DirectionType.DOWN) { if (widget == this.details_list) { + debug("Have details!"); next = this.receiving_list; } else if (widget == this.receiving_list) { next = this.sending_list; @@ -512,9 +575,35 @@ private class Accounts.HostnameRow : EntryRow { } +private class Accounts.TransportSecurityRow : + LabelledEditorRow { + + public TransportSecurityRow() { + TlsComboBox value = new TlsComboBox(); + base(value.label, value); + // Set to Transport TLS by default per RFC 8314 + this.value.method = Geary.TlsNegotiationMethod.TRANSPORT; + } + +} + + private class Accounts.SmtpAuthRow : LabelledEditorRow { + public Geary.SmtpCredentials source { + get { + try { + return Geary.SmtpCredentials.for_value(this.value.active_id); + } catch { + return Geary.SmtpCredentials.IMAP; + } + } + set { + this.value.active_id = value.to_value(); + } + } + public SmtpAuthRow() { base( @@ -530,15 +619,7 @@ private class Accounts.SmtpAuthRow : this.value.append(Geary.SmtpCredentials.IMAP.to_value(), _("Use IMAP login")); this.value.append(Geary.SmtpCredentials.CUSTOM.to_value(), _("Use different login")); - this.value.active_id = Geary.SmtpCredentials.IMAP.to_value(); - } - - public Geary.SmtpCredentials get_value() { - try { - return Geary.SmtpCredentials.for_value(this.value.active_id); - } catch { - return Geary.SmtpCredentials.IMAP; - } + this.source = Geary.SmtpCredentials.IMAP; } } diff --git a/src/client/accounts/goa-service-information.vala b/src/client/accounts/goa-service-information.vala index 89b83ac6..d976a0c6 100644 --- a/src/client/accounts/goa-service-information.vala +++ b/src/client/accounts/goa-service-information.vala @@ -25,10 +25,16 @@ public class GoaServiceInformation : Geary.ServiceInformation { if (mail != null) { switch (this.protocol) { case Geary.Protocol.IMAP: - this.host = mail.imap_host; - this.port = Geary.Imap.ClientConnection.IMAP_TLS_PORT; + parse_host_name(mail.imap_host); this.use_ssl = mail.imap_use_ssl; this.use_starttls = mail.imap_use_tls; + + if (this.port == 0) { + this.port = this.use_ssl + ? Geary.Imap.ClientConnection.IMAP_TLS_PORT + : Geary.Imap.ClientConnection.IMAP_PORT; + } + this.credentials = new Geary.Credentials( ((GoaMediator) this.mediator).method, mail.imap_user_name @@ -36,12 +42,22 @@ public class GoaServiceInformation : Geary.ServiceInformation { break; case Geary.Protocol.SMTP: - this.host = mail.smtp_host; - this.port = Geary.Smtp.ClientConnection.SUBMISSION_TLS_PORT; + parse_host_name(mail.smtp_host); this.use_ssl = mail.smtp_use_ssl; this.use_starttls = mail.smtp_use_tls; this.smtp_noauth = !(mail.smtp_use_auth); this.smtp_use_imap_credentials = false; + + if (this.port == 0) { + if (this.use_ssl) { + this.port = Geary.Smtp.ClientConnection.SUBMISSION_TLS_PORT; + } else if (this.smtp_noauth) { + this.port = Geary.Smtp.ClientConnection.SMTP_PORT; + } else { + this.port = Geary.Smtp.ClientConnection.SUBMISSION_PORT; + } + } + if (!this.smtp_noauth) { this.credentials = new Geary.Credentials( ((GoaMediator) this.mediator).method, @@ -61,4 +77,29 @@ public class GoaServiceInformation : Geary.ServiceInformation { return copy; } + private void parse_host_name(string host_name) { + // Fall back to trying to use the host name as-is. + // At least the user can see it in the settings if + // they look. + this.host = host_name; + this.port = 0; + + try { + GLib.NetworkAddress address = GLib.NetworkAddress.parse( + host_name, this.port + ); + + this.host = address.hostname; + this.port = (uint16) address.port; + } catch (GLib.Error err) { + warning( + "GOA account \"%s\" %s hostname \"%s\": %", + this.account.get_account().id, + this.protocol.to_value(), + host_name, + err.message + ); + } + } + } diff --git a/src/engine/api/geary-engine.vala b/src/engine/api/geary-engine.vala index dbe1620a..b577e909 100644 --- a/src/engine/api/geary-engine.vala +++ b/src/engine/api/geary-engine.vala @@ -257,25 +257,33 @@ public class Geary.Engine : BaseObject { bool login_failed = false; imap_session.login_failed.connect(() => login_failed = true); + GLib.Error? login_err = null; try { yield imap_session.connect_async(cancellable); yield imap_session.initiate_session_async( account.imap.credentials, cancellable ); - } finally { - try { - yield imap_session.disconnect_async(cancellable); - } catch { - // Oh well - } - account.disconnect_service_endpoints(); - account.untrusted_host.disconnect(on_untrusted_host); + } catch (GLib.Error err) { + login_err = err; } + try { + yield imap_session.disconnect_async(cancellable); + } catch { + // Oh well + } + + account.disconnect_service_endpoints(); + account.untrusted_host.disconnect(on_untrusted_host); + if (login_failed) { // XXX This should be a LOGIN_FAILED error or something throw new ImapError.UNAUTHENTICATED("Login failed"); } + + if (login_err != null) { + throw login_err; + } } /** @@ -289,12 +297,10 @@ public class Geary.Engine : BaseObject { if (account.smtp.port == 0) { if (account.smtp.use_ssl) { account.smtp.port = Smtp.ClientConnection.SUBMISSION_TLS_PORT; - } else if (account.smtp.use_starttls) { - account.smtp.port = account.smtp.smtp_noauth - ? Smtp.ClientConnection.SMTP_PORT - : Smtp.ClientConnection.SUBMISSION_PORT; - } else { + } else if (account.smtp.smtp_noauth) { account.smtp.port = Smtp.ClientConnection.SMTP_PORT; + } else { + account.smtp.port = Smtp.ClientConnection.SUBMISSION_PORT; } } @@ -307,18 +313,26 @@ public class Geary.Engine : BaseObject { account.smtp.endpoint ); + GLib.Error? login_err = null; try { yield smtp_session.login_async( account.get_smtp_credentials(), cancellable ); - } finally { - try { - yield smtp_session.logout_async(true, cancellable); - } catch { - // Oh well - } - account.disconnect_service_endpoints(); - account.untrusted_host.disconnect(on_untrusted_host); + } catch (GLib.Error err) { + login_err = err; + } + + try { + yield smtp_session.logout_async(true, cancellable); + } catch { + // Oh well + } + + account.disconnect_service_endpoints(); + account.untrusted_host.disconnect(on_untrusted_host); + + if (login_err != null) { + throw login_err; } } diff --git a/ui/accounts_editor_add_pane.ui b/ui/accounts_editor_add_pane.ui index 1b27d776..03d0e4d7 100644 --- a/ui/accounts_editor_add_pane.ui +++ b/ui/accounts_editor_add_pane.ui @@ -2,6 +2,76 @@ + + True + False + Add an account + False + + + True + False + + + True + True + True + + + + True + False + True + go-previous-symbolic + + + + + 0 + 0 + + + + + + + True + False + 12 + + + True + False + + + 0 + 0 + + + + + Create + True + False + True + True + + + + + 1 + 0 + + + + + end + 1 + + + 100 1 @@ -170,63 +240,4 @@ - - True - False - Add an account - False - - - True - False - - - True - True - True - - - - True - False - True - go-previous-symbolic - - - - - 0 - 0 - - - - - - - True - False - - - Create - True - False - True - True - - - - - 0 - 0 - - - - - end - 1 - - -