Never been to DZone Snippets before?

Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Ruby SMTP Server - Save to Database (See related posts)

This was something I've searched for in the past but have yet to find. It's a super simple server and I'm sure there's a few bugs left in it, but the idea is simple and in my opinion, necessary. It's easy to send email from websites, but receiving it still seems dubious.

I've simply used Peter's(http://snippets.dzone.com/user/peter) ultra simplistic SMTP server and routed the email to a database table called "Emails" via ActiveRecord. It's running GServer so it should be able to handle a decent load. Like I mentioned earlier, it's probably still got some bugs, but it's a decent start.

require 'gserver'
require 'rubygems'  
require 'active_record'  
require 'yaml'
   
dbconfig = YAML::load_file(File.dirname(__FILE__) + '/config/database.yml')
ActiveRecord::Base.establish_connection(dbconfig['development']) 

class Email < ActiveRecord::Base   
end 

class SMTPServer < GServer
  def serve(io)
    @data_mode = false
    @email_message = ""
    puts "Connected"
    io.print "220 hello\r\n"
    loop do
      if IO.select([io], nil, nil, 0.1)
	      data = io.readpartial(4096)
	      puts ">>" + data
	      @email_message << data
	      ok, op = process_line(data)
	      break unless ok
	      puts "<<" + op
	      io.print op
      end
      break if io.closed?
    end
    db_insert(@email_message)
    io.print "221 bye\r\n"
    io.close
  end

  def process_line(line)
    if (@data_mode) && (line.chomp =~ /^\.$/)
      @data_mode = false
      return true, "220 OK\r\n"
    elsif @data_mode
      return true, ""
    elsif (line =~ /^(HELO|EHLO)/)
      return true, "220 and..?\r\n"
    elsif (line =~ /^QUIT/)
      return false, "bye\r\n"
    elsif (line =~ /^MAIL FROM\:/)
      return true, "220 OK\r\n"
    elsif (line =~ /^RCPT TO\:/)
      return true, "220 OK\r\n"
    elsif (line =~ /^DATA/)
      @data_mode = true
      return true, "354 Enter message, ending with \".\" on a line by itself\r\n"
    else
      return true, "500 ERROR\r\n"
    end
  end
  
  def db_insert(email)
    mail_from = (/^MAIL FROM\:<(.+)>.*$/).match(email)[1]
    rcpt_to = (/^RCPT TO\:<(.+)>.*$/).match(email)[1]
    subject = (/^Subject\: (.+)$/).match(email)[1]
    Email.create(:mail_from => mail_from, :rcpt_to => rcpt_to, :subject => subject, :email => email)
  end
  
end

a = SMTPServer.new(25)
a.start
a.join

Comments on this post

peter posts on Feb 20, 2008 at 21:23
This is exactly why I wrote that code in the first place, but I never got round to actually doing the final part of the hookup as you have. Nice job!

I might even put this on Ruby Inside soon..!

You need to create an account or log in to post comments to this site.


Click here to browse all 5144 code snippets

Related Posts