class QuickML::Session
Constants
- COMMAND_TABLE
Public Class Methods
new(config, logger, catalog, socket)
click to toggle source
def initialize (config, socket)
# File vendor/qwik/lib/qwik/ml-session.rb, line 43 def initialize (config, logger, catalog, socket) @socket = socket @config = config # @logger = @config.logger # @catalog = @config.catalog @logger = logger @catalog = catalog @hello_host = 'hello.host.invalid' @protocol = nil @peer_hostname = @socket.hostname @peer_address = @socket.address @remote_host = (@peer_hostname or @peer_address) @data_finished = false @my_hostname = 'localhost' @my_hostname = Socket.gethostname if @config.ml_port == 25 @message_charset = nil end
Public Instance Methods
start()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 63 def start elapsed = calc_time { _start } @logger.vlog "Session finished: #{elapsed} sec." end
Private Instance Methods
_start()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 79 def _start begin def_puts(@socket) connect timeout(@config.timeout) { process } rescue TimeoutError @logger.vlog "Timeout: #{@remote_host}" ensure close end end
calc_time() { || ... }
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 72 def calc_time start_time = Time.now yield elapsed = Time.now - start_time return elapsed end
cleanup_connection()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 257 def cleanup_connection unless @data_finished discard_data end @socket.puts '221 Bye' close end
close()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 303 def close return if @socket.closed? @socket.close @logger.vlog "Closed: #{@remote_host}" end
connect()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 104 def connect @socket.puts "220 #{@my_hostname} ESMTP QuickML" @logger.vlog "Connect: #{@remote_host}" end
content_type()
click to toggle source
FIXME: this is the same method of QuickML#content_type
# File vendor/qwik/lib/qwik/ml-session.rb, line 299 def content_type return Mail.content_type(@config.content_type, @message_charset) end
data(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 203 def data (mail, arg) if mail.recipients.empty? @socket.puts '503 Error: need RCPT command' else @socket.puts '354 send the mail data, end with .'; begin read_mail(mail) ensure @message_charset = mail.charset end @socket.puts '250 ok' end end
def_puts(socket)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 93 def def_puts(socket) def socket.puts(*objs) objs.each {|x| begin self.print x.xchomp, "\r\n" rescue Errno::EPIPE end } end end
discard_data()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 265 def discard_data begin while line = @socket.safe_gets break if end_of_data?(line) end rescue TooLongLine retry end end
ehlo(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 153 def ehlo (mail, arg) @hello_host = arg.split.first @socket.puts "250-#{@my_hostname}" @socket.puts '250 PIPELINING' @protocol = 'ESMTP' end
end_of_data?(line)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 240 def end_of_data? (line) # line.xchomp == '.' return line == ".\r\n" end
helo(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 146 def helo (mail, arg) return if arg.nil? @hello_host = arg.split.first @socket.puts "250 #{@my_hostname}" @protocol = 'SMTP' end
mail(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 192 def mail (mail, arg) if @protocol.nil? @socket.puts '503 Error: send HELO/EHLO first' elsif /^From:\s*<(.*)>/i =~ arg or /^From:\s*(.*)/i =~ arg mail.mail_from = $1 @socket.puts '250 ok' else @socket.puts "501 Syntax: MAIL FROM: <address>" end end
noop(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 160 def noop (mail, arg) @socket.puts '250 ok' end
process()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 109 def process until @socket.closed? begin mail = Mail.new receive_mail(mail) if mail.valid? processor = Processor.new(@config, mail) processor.process end rescue TooLargeMail cleanup_connection report_too_large_mail(mail) if mail.valid? @logger.log "Too Large Mail: #{mail.from}" rescue TooLongLine cleanup_connection @logger.log "Too Long Line: #{mail.from}" end end end
quit(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 164 def quit (mail, arg) @socket.puts '221 Bye' close end
rcpt(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 175 def rcpt (mail, arg) if mail.mail_from.nil? @socket.puts '503 Error: need MAIL command' elsif /^To:\s*<(.*)>/i =~ arg or /^To:\s*(.*)/i =~ arg address = $1 if Mail.address_of_domain?(address, @config.ml_domain) mail.add_recipient(address) @socket.puts '250 ok' else @socket.puts "554 <#{address}>: Recipient address rejected" @logger.vlog "Unacceptable RCPT TO:<#{address}>" end else @socket.puts "501 Syntax: RCPT TO: <address>" end end
read_mail(mail)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 217 def read_mail (mail) len = 0 lines = [] while line = @socket.safe_gets break if end_of_data?(line) len += line.length if @config.max_mail_length < len mail.read(lines.join('')) # Generate a header for an error report. raise TooLargeMail end line.sub!(/^\.\./, '.') # unescape line = line.normalize_eol lines << line # I do not know why but constructing mail_string with # String#<< here is very slow. # mail_string << line end mail_string = lines.join('') @data_finished = true mail.read(mail_string) mail.unshift_field('Received', received_field) end
receive_mail(mail)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 129 def receive_mail (mail) while line = @socket.safe_gets line = line.xchomp command, arg = line.split(/\s+/, 2) return if command.nil? || command.empty? command = command.downcase.intern # 'HELO' => :helo if COMMAND_TABLE.include?(command) @logger.vlog "Command: #{line}" send(command, mail, arg) else @logger.vlog "Unknown SMTP Command: #{command} #{arg}" @socket.puts '502 Error: command not implemented' end break if command == :quit or command == :data end end
received_field()
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 245 def received_field sprintf("from %s (%s [%s])\n" + " by %s (QuickML) with %s;\n" + " %s", @hello_host, @peer_hostname, @peer_address, @my_hostname, @protocol, Time.now.rfc2822) end
report_too_large_mail(mail)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 275 def report_too_large_mail (mail) header = [] subject = Mail.encode_field(_("[QuickML] Error: %s", mail['Subject'])) header.push(['To', mail.from], ['From', @config.ml_postmaster], ['Subject', subject], ['Content-Type', content_type]) max = @config.max_mail_length.commify body = _("Sorry, your mail exceeds the length limitation.\n") body << _("The max length is %s bytes.\n\n", max) orig_subject = codeconv(Mail.decode_subject(mail['Subject'])) body << "Subject: #{orig_subject}\n" body << "To: #{mail['To']}\n" body << "From: #{mail['From']}\n" body << "Date: #{mail['Date']}\n" Sendmail.send_mail(@config.smtp_host, @config.smtp_port, @logger, :mail_from => '', :recipient => mail.from, :header => header, :body => body) end
rset(mail, arg)
click to toggle source
# File vendor/qwik/lib/qwik/ml-session.rb, line 169 def rset (mail, arg) mail.mail_from = nil mail.clear_recipients @socket.puts '250 ok' end