mirror of
https://gh.wpcy.net/https://github.com/discourse/discourse.git
synced 2026-05-26 04:58:53 +08:00
133 lines
4.5 KiB
Ruby
Vendored
133 lines
4.5 KiB
Ruby
Vendored
# frozen_string_literal: true
|
|
|
|
require "net/smtp"
|
|
require "net/pop"
|
|
|
|
class EmailSettingsValidator
|
|
def self.validate_as_user(user, protocol, **kwargs)
|
|
DistributedMutex.synchronize("validate_#{protocol}_#{user.id}", validity: 10) do
|
|
public_send("validate_#{protocol}", **kwargs)
|
|
end
|
|
end
|
|
|
|
##
|
|
# Attempts to authenticate and disconnect a POP3 session and if that raises
|
|
# an error then it is assumed the credentials or some other settings are wrong.
|
|
#
|
|
# @param debug [Boolean] - When set to true, any errors will be logged at a warning
|
|
# level before being re-raised.
|
|
def self.validate_pop3(
|
|
host:,
|
|
port:,
|
|
username:,
|
|
password:,
|
|
ssl: SiteSetting.pop3_polling_ssl,
|
|
openssl_verify: SiteSetting.pop3_polling_openssl_verify,
|
|
debug: Rails.env.development?
|
|
)
|
|
pop3 = Net::POP3.new(host, port)
|
|
|
|
# Note that we do not allow which verification mode to be specified
|
|
# like we do for SMTP, we just pick TLS1_2 if the SSL and openSSL verify
|
|
# options have been enabled.
|
|
if ssl
|
|
if openssl_verify
|
|
pop3.enable_ssl(max_version: OpenSSL::SSL::TLS1_2_VERSION)
|
|
else
|
|
pop3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
|
|
end
|
|
end
|
|
|
|
# This disconnects itself, unlike SMTP.
|
|
pop3.auth_only(username, password)
|
|
rescue => err
|
|
log_and_raise(err, debug)
|
|
end
|
|
|
|
##
|
|
# Attempts to start an SMTP session and if that raises an error then it is
|
|
# assumed the credentials or other settings are wrong.
|
|
#
|
|
# @param domain [String] - Used for HELO, should be the FQDN of the server sending the mail
|
|
# localhost can be used in development mode.
|
|
# See https://datatracker.ietf.org/doc/html/rfc788#section-4
|
|
# @param debug [Boolean] - When set to true, any errors will be logged at a warning
|
|
# level before being re-raised.
|
|
def self.validate_smtp(
|
|
host:,
|
|
port:,
|
|
username:,
|
|
password:,
|
|
domain: nil,
|
|
authentication: nil,
|
|
enable_starttls_auto: !GlobalSetting.smtp_force_tls && GlobalSetting.smtp_enable_start_tls,
|
|
enable_tls: GlobalSetting.smtp_force_tls,
|
|
openssl_verify_mode: GlobalSetting.smtp_openssl_verify_mode,
|
|
debug: Rails.env.development?
|
|
)
|
|
if username || password
|
|
authentication = SmtpProviderOverrides.authentication_override(host) if authentication.nil?
|
|
authentication = authentication.to_sym
|
|
if !%i[plain login cram_md5].include?(authentication)
|
|
raise ArgumentError, "Invalid authentication method. Must be plain, login, or cram_md5."
|
|
end
|
|
else
|
|
authentication = nil
|
|
end
|
|
|
|
if domain.blank?
|
|
if Rails.env.development?
|
|
domain = "localhost"
|
|
else
|
|
# Because we are using the SMTP settings here to send emails,
|
|
# the domain should just be the TLD of the host.
|
|
domain = MiniSuffix.domain(host)
|
|
end
|
|
end
|
|
|
|
smtp = Net::SMTP.new(host, port)
|
|
|
|
# These SSL options are cribbed from the Mail gem, which is used internally
|
|
# by ActionMailer. Unfortunately the mail gem hides this setup in private
|
|
# methods, e.g. https://github.com/mikel/mail/blob/master/lib/mail/network/delivery_methods/smtp.rb#L112-L147
|
|
#
|
|
# Relying on the GlobalSetting options is a good idea here.
|
|
#
|
|
# For specific use cases, options should be passed in from higher up. For example
|
|
# Gmail needs either port 465 and tls enabled, or port 587 and starttls_auto.
|
|
if openssl_verify_mode.kind_of?(String)
|
|
openssl_verify_mode = OpenSSL::SSL.const_get("VERIFY_#{openssl_verify_mode.upcase}")
|
|
end
|
|
ssl_context = Net::SMTP.default_ssl_context
|
|
ssl_context.verify_mode = openssl_verify_mode if openssl_verify_mode
|
|
|
|
if enable_starttls_auto
|
|
# starttls is automatic, but we might need to change the context
|
|
smtp.enable_starttls_auto(ssl_context)
|
|
else
|
|
smtp.disable_starttls
|
|
end
|
|
smtp.enable_tls(ssl_context) if enable_tls
|
|
|
|
smtp.open_timeout = 5
|
|
|
|
# Some SMTP servers have a higher delay to respond with errors
|
|
# as a tarpit measure that slows down clients who are sending "bad" commands.
|
|
# 10s is the minimum, we might need to increase this in the future.
|
|
smtp.read_timeout = 10
|
|
|
|
smtp.start(domain, username, password, authentication)
|
|
smtp.finish
|
|
rescue => err
|
|
log_and_raise(err, debug)
|
|
end
|
|
|
|
def self.log_and_raise(err, debug)
|
|
if debug
|
|
Rails.logger.warn(
|
|
"[EmailSettingsValidator] Error encountered when validating email settings: #{err.message} #{err.backtrace.join("\n")}",
|
|
)
|
|
end
|
|
raise err
|
|
end
|
|
end
|