Skip to content

Commit

Permalink
Add response to SMTPError exceptions
Browse files Browse the repository at this point in the history
Right now there is no way to get response status, which is useful for
metrics and similar stuff. Add response to the exception, so it could
be used for that.
  • Loading branch information
ojab committed Aug 31, 2021
1 parent 76fddd1 commit 3a39e96
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 6 deletions.
22 changes: 16 additions & 6 deletions lib/net/smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,17 @@ module Net
module SMTPError
# This *class* is a module for backward compatibility.
# In later release, this module becomes a class.

attr_reader :response

def initialize(response, message: nil)
@response = response
@message = message
end

def message
@message || response.message
end
end

# Represents an SMTP authentication error.
Expand Down Expand Up @@ -609,8 +620,7 @@ def do_start(helo_domain, user, secret, authtype)
do_helo helo_domain
if ! tls? and (starttls_always? or (capable_starttls? and starttls_auto?))
unless capable_starttls?
raise SMTPUnsupportedCommand,
"STARTTLS is not supported on this server"
raise SMTPUnsupportedCommand.new(nil, message: "STARTTLS is not supported on this server")
end
starttls
@socket = new_internet_message_io(tlsconnect(s, @ssl_context_starttls))
Expand Down Expand Up @@ -1013,25 +1023,25 @@ def critical

def check_response(res)
unless res.success?
raise res.exception_class, res.message
raise res.exception_class.new(res)
end
end

def check_continue(res)
unless res.continue?
raise SMTPUnknownError, "could not get 3xx (#{res.status}: #{res.string})"
raise SMTPUnknownError.new(res, message: "could not get 3xx (#{res.status}: #{res.string})")
end
end

def check_auth_response(res)
unless res.success?
raise SMTPAuthenticationError, res.message
raise SMTPAuthenticationError.new(res)
end
end

def check_auth_continue(res)
unless res.continue?
raise res.exception_class, res.message
raise res.exception_class.new(res)
end
end

Expand Down
87 changes: 87 additions & 0 deletions test/net/smtp/test_smtp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,93 @@ def test_auth_plain
assert_equal "AUTH PLAIN AGZvbwBiYXI=\r\n", sock.write_io.string
end

def test_unsucessful_auth_plain
sock = FakeSocket.new("535 Authentication failed: FAIL\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPAuthenticationError) { smtp.auth_plain("foo", "bar") }
assert_equal "535 Authentication failed: FAIL\n", err.message
assert_equal "535", err.response.status
end

def test_auth_login
sock = FakeSocket.new("334 VXNlcm5hbWU6\r\n334 UGFzc3dvcmQ6\r\n235 2.7.0 Authentication successful\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
assert smtp.auth_login("foo", "bar").success?
end

def test_unsucessful_auth_login
sock = FakeSocket.new("334 VXNlcm5hbWU6\r\n334 UGFzc3dvcmQ6\r\n535 Authentication failed: FAIL\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPAuthenticationError) { smtp.auth_login("foo", "bar") }
assert_equal "535 Authentication failed: FAIL\n", err.message
assert_equal "535", err.response.status
end

def test_non_continue_auth_login
sock = FakeSocket.new("334 VXNlcm5hbWU6\r\n235 2.7.0 Authentication successful\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPUnknownError) { smtp.auth_login("foo", "bar") }
assert_equal "235 2.7.0 Authentication successful\n", err.message
assert_equal "235", err.response.status
end

def test_unsuccessful_send_message_server_busy
sock = FakeSocket.new("400 BUSY\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPServerBusy) { smtp.send_message('message', '[email protected]') }
assert_equal "400 BUSY\n", err.message
assert_equal "400", err.response.status
end

def test_unsuccessful_send_message_syntax_error
sock = FakeSocket.new("502 SYNTAX ERROR\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPSyntaxError) { smtp.send_message('message', '[email protected]') }
assert_equal "502 SYNTAX ERROR\n", err.message
assert_equal "502", err.response.status
end

def test_unsuccessful_send_message_authentication_error
sock = FakeSocket.new("530 AUTH ERROR\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPAuthenticationError) { smtp.send_message('message', '[email protected]') }
assert_equal "530 AUTH ERROR\n", err.message
assert_equal "530", err.response.status
end

def test_unsuccessful_send_message_fatal_error
sock = FakeSocket.new("520 FATAL ERROR\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPFatalError) { smtp.send_message('message', '[email protected]') }
assert_equal "520 FATAL ERROR\n", err.message
assert_equal "520", err.response.status
end

def test_unsuccessful_send_message_unknown_error
sock = FakeSocket.new("300 UNKNOWN\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPUnknownError) { smtp.send_message('message', '[email protected]') }
assert_equal "300 UNKNOWN\n", err.message
assert_equal "300", err.response.status
end

def test_unsuccessful_data
sock = FakeSocket.new("250 OK\r\n")
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, sock
err = assert_raise(Net::SMTPUnknownError) { smtp.data('message') }
assert_equal "could not get 3xx (250: 250 OK\n)", err.response.status
end

def test_crlf_injection
smtp = Net::SMTP.new 'localhost', 25
smtp.instance_variable_set :@socket, FakeSocket.new
Expand Down
1 change: 1 addition & 0 deletions test/net/smtp/test_starttls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def test_enable_starttls_without_starttls_capable
smtp.enable_starttls
err = assert_raise(Net::SMTPUnsupportedCommand) { smtp.start }
assert_equal("STARTTLS is not supported on this server", err.message)
assert_nil(err.response)
end

def test_enable_starttls_auto_with_starttls_capable
Expand Down

0 comments on commit 3a39e96

Please sign in to comment.