class FakeSMTPd::Server

Constants

USAGE
VERSION

Attributes

logfile[R]
message_dir[R]
messages[R]
pidfile[R]
port[R]

Public Class Methods

main(argv = []) click to toggle source
# File lib/fakesmtpd/server.rb, line 206
def main(argv = [])
  options = {
    pidfile: nil,
    logfile: $stderr.set_encoding('UTF-8'),
  }

  OptionParser.new do |opts|
    opts.banner = USAGE
    opts.on('--version', 'Show version and exit') do |*|
      puts "fakesmtpd #{FakeSMTPd::Server::VERSION}"
      exit 0
    end
    opts.on('-p PIDFILE', '--pidfile PIDFILE',
            'Optional file where process PID will be written') do |pidfile|
      options[:pidfile] = pidfile
    end
    opts.on('-l LOGFILE', '--logfile LOGFILE',
            'Optional file where all log messages will be written ' <<
            '(default $stderr)') do |logfile|
      options[:logfile] = File.open(logfile, 'a:UTF-8')
    end
  end.parse!(argv)

  unless argv.length == 2
    abort USAGE
  end

  @smtpd = FakeSMTPd::Server.new(
    port: Integer(argv.fetch(0)),
    dir: argv.fetch(1),
    pidfile: options[:pidfile],
    logfile: options[:logfile],
  )
  @httpd = FakeSMTPd::HTTPServer.new(
    port: Integer(argv.fetch(0)) + 1,
    smtpd: @smtpd,
    logfile: options[:logfile],
  )

  @httpd.start
  @smtpd.start
  @httpd.join && @smtpd.join
rescue Exception => e
  if @httpd && !@httpd.stopped?
    @httpd.stop
  end
  if @smtpd && !@smtpd.stopped?
    @smtpd.stop
  end
  unless e.is_a?(Interrupt)
    raise e
  end
end
new(options = {}) click to toggle source
Calls superclass method
# File lib/fakesmtpd/server.rb, line 261
def initialize(options = {})
  @port = options.fetch(:port)
  @message_dir = options.fetch(:dir)
  @pidfile = options[:pidfile]
  @messages = MessageStore.new(@message_dir)

  super(
    @port,
    options[:host] || '0.0.0.0',
    options[:max_connections] || 4,
    options[:logfile],
    options[:audit] || !!ENV['FAKESMTPD_AUDIT'] || false,
    options[:debug] || !!ENV['FAKESMTPD_DEBUG'] || false
  )
end

Public Instance Methods

record(client, from, recipients, body) click to toggle source
# File lib/fakesmtpd/server.rb, line 356
def record(client, from, recipients, body)
  messages.store(
    client.client_id, from, recipients, body
  )
end
serve(client) click to toggle source
# File lib/fakesmtpd/server.rb, line 292
def serve(client)
  client.set_encoding('UTF-8')

  class << client
    attr_reader :client_id

    def getline
      line = gets
      line.chomp! unless line.nil?
      line
    end

    def to_s
      @client_id ||= Time.now.utc.strftime('%Y%m%d%H%M%S%N')
      "<smtp client #{@client_id}>"
    end
  end

  client.puts '220 localhost fakesmtpd ready ESMTP'
  helo = client.getline
  log "#{client} Helo: #{helo.inspect}"

  if helo =~ /^EHLO\s+/
    log "#{client} Seen an EHLO"
    client.puts '250-localhost only has this one extension'
    client.puts '250 HELP'
  end

  from = client.getline
  client.puts '250 OK'
  log "#{client} From: #{from.inspect}"

  recipients = []
  loop do
    to = client.getline
    break if to.nil?

    if to =~ /^DATA/
      client.puts '354 Lemme have it'
      break
    else
      log "#{client} To: #{to.inspect}"
      recipients << to
      client.puts '250 OK'
    end
  end

  lines = []
  loop do
    line = client.getline
    break if line.nil? || line == '.'
    lines << line
    log "#{client} + #{line}"
  end

  client.puts '250 OK'
  client.gets
  client.puts '221 Buhbye'
  client.close
  log "#{client} ding!"

  record(client, from, recipients, lines)
end
start(*args) click to toggle source
Calls superclass method
# File lib/fakesmtpd/server.rb, line 277
def start(*args)
  super(*args)
  if pidfile
    File.open(pidfile, 'w') { |f| f.puts($$) }
  end
  log "FakeSMTPd SMTP server serving on #{port}, writing messages to " <<
      "#{message_dir.inspect}"
  log "PID=#{$$} Thread=#{Thread.current.inspect}"
end
stop(*args) click to toggle source
Calls superclass method
# File lib/fakesmtpd/server.rb, line 287
def stop(*args)
  log "FakeSMTPd SMTP server stopping"
  super(*args)
end