class Groat::SMTPD::SMTP
Public Class Methods
new(*args)
click to toggle source
Calls superclass method
# File lib/groat/smtpd/smtp.rb, line 36 def initialize(*args) @hostname = "groat.example" if @hostname.nil? super(*args) end
Public Instance Methods
check_command_group()
click to toggle source
No pipelining allowed (not in RFC5321)
# File lib/groat/smtpd/smtp.rb, line 105 def check_command_group if clientdata? response_bad_sequence end end
deliver!()
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 41 def deliver! end
esmtp?()
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 129 def esmtp? @esmtp end
handle_hello(args, keyword)
click to toggle source
Generic handler for hello action Keyword determines Mail Service Type See: www.iana.org/assignments/mail-parameters
# File lib/groat/smtpd/smtp.rb, line 136 def handle_hello(args, keyword) keyword = keyword.to_s.upcase.intern check_command_group response_syntax_error if args.empty? hello, hello_extra = args.split(" ", 2) hello =~ DOMAIN_OR_LITERAL if $~.nil? respond_syntax_error :message=>"Syntax Error: expected hostname or IP literal" elsif hello.start_with? '[' and not valid_address_literal(hello) respond_syntax_error :message=>"Syntax Error: invalid IP literal" else @hello = hello @hello_extra = hello_extra end reset_buffers response_text = ["#{@hostname} at your service"] if (keyword == :EHLO) @esmtp = true ehlo_keywords.each do |kw, params| param_str = params.to_a.join(' ') if param_str.empty? response_text << "#{kw}" else response_text << "#{kw} #{param_str}" end end end reply :code => 250, :message => response_text end
in_mail_transaction?()
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 125 def in_mail_transaction? not @mailfrom.nil? end
reset_buffers()
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 119 def reset_buffers @mailfrom = nil @rcptto = [] @message = "" end
reset_connection()
click to toggle source
Calls superclass method
# File lib/groat/smtpd/smtp.rb, line 111 def reset_connection @hello = nil @hello_extra = nil @esmtp = false reset_buffers super end
response_bad_command(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 51 def response_bad_command(args = {}) defaults = {:code => 500, :message => "Bad command"} reply defaults.merge(args) end
response_bad_command_parameter(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 67 def response_bad_command_parameter(args = {}) defaults = {:code => 504, :message => "Invalid parameter"} reply defaults.merge(args) end
response_bad_parameter(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 77 def response_bad_parameter(args = {}) defaults = {:code => 555, :message => "Parameter not recognized"} reply defaults.merge(args) end
response_bad_sequence(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 61 def response_bad_sequence(args = {}) defaults = {:code => 503, :message => "Bad sequence of commands", :terminate => true } reply defaults.merge(args) end
response_no_valid_rcpt(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 72 def response_no_valid_rcpt(args = {}) defaults = {:code => 554, :message => "No valid recipients"} reply defaults.merge(args) end
response_ok(args = {})
click to toggle source
Reply methods
# File lib/groat/smtpd/smtp.rb, line 46 def response_ok(args = {}) defaults = {:code => 250, :message => "OK"} reply defaults.merge(args) end
response_service_shutdown(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 82 def response_service_shutdown(args = {}) defaults = {:code => 421, :message => "Server closing connection", :terminate => true} reply defaults.merge(args) end
response_syntax_error(args = {})
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 56 def response_syntax_error(args = {}) defaults = {:code => 501, :message => "Syntax error"} reply defaults.merge(args) end
send_greeting()
click to toggle source
Groat
framework methods
# File lib/groat/smtpd/smtp.rb, line 90 def send_greeting reply :code => 220, :message => "#{@hostname} ESMTP Ready" end
service_shutdown()
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 94 def service_shutdown response_service_shutdown end
smtp_verb_data(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 230 def smtp_verb_data(args) check_command_group response_syntax_error unless args.empty? return response_no_valid_rcpt if @rcptto.count < 1 toclient "354 Enter message, ending with \".\" on a line by itself.\r\n" loop do line = fromclient # RFC 5321 § 4.1.1.4 # "The custom of accepting lines ending only in <LF>, as a concession to # non-conforming behavior on the part of some UNIX systems, has proven # to cause more interoperability problems than it solves, and SMTP # server systems MUST NOT do this, even in the name of improved # robustness." break if line.chomp("\r\n").eql?('.') # RFC5321 sect 4.5.2, remove leading '.' if found line.slice!(0) if line.start_with? '.' @message << line end message = deliver! reset_buffers response_ok :message => message end
smtp_verb_ehlo(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 171 def smtp_verb_ehlo(args) handle_hello(args, :EHLO) end
smtp_verb_helo(args)
click to toggle source
Verb handlers
# File lib/groat/smtpd/smtp.rb, line 167 def smtp_verb_helo(args) handle_hello(args, :HELO) end
smtp_verb_mail(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 176 def smtp_verb_mail(args) check_command_group response_bad_sequence if @hello.nil? # This should be start_with? 'FROM:<', but Outlook puts a space # between the ':' and the '<' response_syntax_error unless args.upcase.start_with? 'FROM:' # Remove leading "FROM:" and following spaces args = args[5..-1].lstrip if args[0..2].rstrip.eql? '<>' path = '<>' param_str = args[3..-1].to_s else path, param_str = split_path(args) response_syntax_error :message => 'Path error' if path.nil? end unless param_str.strip.empty? response_syntax_error unless esmtp? end params = parse_params(param_str) response_bad_parameter unless mail_params_valid(params) # Validation complete # RFC5321 § 4.1.1.2 # "This command clears the reverse-path buffer, the forward-path # buffer, and the mail data buffer, and it inserts the reverse-path # information from its argument clause into the reverse-path buffer." reset_buffers process_mail_params(params) mailfrom = normalize_path(path) run_hook :validate_mailfrom, mailfrom @mailfrom = mailfrom response_ok end
smtp_verb_noop(args)
click to toggle source
RFC 5321 § 4.1.1.9 “If a parameter string is specified, servers SHOULD ignore it.”
# File lib/groat/smtpd/smtp.rb, line 271 def smtp_verb_noop(args) check_command_group response_ok end
smtp_verb_quit(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 260 def smtp_verb_quit(args) check_command_group response_syntax_error unless args.empty? reset_buffers reply :code=>221, :message=>"#{@hostname} Service closing transmission channel", :terminate=>true end
smtp_verb_rcpt(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 210 def smtp_verb_rcpt(args) check_command_group response_bad_sequence if @mailfrom.nil? # This should be start_with? 'TO:<', but Outlook puts a space # between the ':' and the '<' response_syntax_error unless args.upcase.start_with? 'TO:' # Remove leading "TO:" and the following spaces args = args[3..-1].lstrip path, param_str = split_path(args) response_syntax error :message => 'Path error' if path.nil? unless param_str.strip.empty? response_syntax_error unless esmtp? end params = parse_params(param_str) rcptto = normalize_path(path) run_hook :validate_rcptto, rcptto @rcptto << rcptto response_ok end
smtp_verb_rset(args)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 253 def smtp_verb_rset(args) check_command_group response_syntax_error unless args.empty? reset_buffers response_ok end
verb_missing(verb, parameters)
click to toggle source
# File lib/groat/smtpd/smtp.rb, line 98 def verb_missing(verb, parameters) response_bad_command :message => "Unknown command #{verb}" end