132 lines
3.7 KiB
Ruby
132 lines
3.7 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require "net/imap"
|
|
require "net/smtp"
|
|
require "net/pop"
|
|
|
|
class EmailSettingsExceptionHandler
|
|
EXPECTED_EXCEPTIONS = [
|
|
Net::POPAuthenticationError,
|
|
Net::IMAP::NoResponseError,
|
|
Net::IMAP::Error,
|
|
Net::SMTPAuthenticationError,
|
|
Net::SMTPServerBusy,
|
|
Net::SMTPSyntaxError,
|
|
Net::SMTPFatalError,
|
|
Net::SMTPUnknownError,
|
|
Net::OpenTimeout,
|
|
Net::ReadTimeout,
|
|
SocketError,
|
|
Errno::ECONNREFUSED,
|
|
]
|
|
|
|
class GenericProvider
|
|
def initialize(exception)
|
|
@exception = exception
|
|
end
|
|
|
|
def message
|
|
case @exception
|
|
when Net::POPAuthenticationError
|
|
net_pop_authentication_error
|
|
when Net::IMAP::NoResponseError
|
|
net_imap_no_response_error
|
|
when Net::IMAP::Error
|
|
net_imap_unhandled_error
|
|
when Net::SMTPAuthenticationError
|
|
net_smtp_authentication_error
|
|
when Net::SMTPServerBusy
|
|
net_smtp_server_busy
|
|
when Net::SMTPSyntaxError, Net::SMTPFatalError, Net::SMTPUnknownError
|
|
net_smtp_unhandled_error
|
|
when SocketError, Errno::ECONNREFUSED
|
|
socket_connection_error
|
|
when Net::OpenTimeout, Net::ReadTimeout
|
|
net_timeout_error
|
|
else
|
|
unhandled_error
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def net_pop_authentication_error
|
|
I18n.t("email_settings.pop3_authentication_error")
|
|
end
|
|
|
|
def net_imap_no_response_error
|
|
# Most of IMAP's errors are lumped under the NoResponseError, including invalid
|
|
# credentials errors, because it is raised when a "NO" response is
|
|
# raised from the IMAP server https://datatracker.ietf.org/doc/html/rfc3501#section-7.1.2
|
|
#
|
|
# Generally, it should be fairly safe to just return the error message as is.
|
|
if @exception.message.match(/Invalid credentials/)
|
|
I18n.t("email_settings.imap_authentication_error")
|
|
else
|
|
I18n.t(
|
|
"email_settings.imap_no_response_error",
|
|
message: @exception.message.gsub(" (Failure)", ""),
|
|
)
|
|
end
|
|
end
|
|
|
|
def net_imap_unhandled_error
|
|
I18n.t("email_settings.imap_unhandled_error", message: @exception.message)
|
|
end
|
|
|
|
def net_smtp_authentication_error
|
|
I18n.t("email_settings.smtp_authentication_error")
|
|
end
|
|
|
|
def net_smtp_server_busy
|
|
I18n.t("email_settings.smtp_server_busy_error")
|
|
end
|
|
|
|
def net_smtp_unhandled_error
|
|
I18n.t("email_settings.smtp_unhandled_error", message: @exception.message)
|
|
end
|
|
|
|
def socket_connection_error
|
|
I18n.t("email_settings.connection_error")
|
|
end
|
|
|
|
def net_timeout_error
|
|
I18n.t("email_settings.timeout_error")
|
|
end
|
|
|
|
def unhandled_error
|
|
I18n.t("email_settings.unhandled_error", message: @exception.message)
|
|
end
|
|
end
|
|
|
|
class GmailProvider < GenericProvider
|
|
def net_smtp_authentication_error
|
|
# Gmail requires use of application-specific passwords when 2FA is enabled and return
|
|
# a special error message calling this out.
|
|
if @exception.message.match(/Application-specific password required/)
|
|
I18n.t("email_settings.authentication_error_gmail_app_password")
|
|
else
|
|
super
|
|
end
|
|
end
|
|
|
|
def net_imap_no_response_error
|
|
# Gmail requires use of application-specific passwords when 2FA is enabled and return
|
|
# a special error message calling this out.
|
|
if @exception.message.match(/Application-specific password required/)
|
|
I18n.t("email_settings.authentication_error_gmail_app_password")
|
|
else
|
|
super
|
|
end
|
|
end
|
|
end
|
|
|
|
def self.friendly_exception_message(exception, host)
|
|
if host.include?("gmail.com")
|
|
EmailSettingsExceptionHandler::GmailProvider.new(exception).message
|
|
else
|
|
EmailSettingsExceptionHandler::GenericProvider.new(exception).message
|
|
end
|
|
end
|
|
end
|