129 lines
3.7 KiB
Ruby
129 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
|