Skip to content

Instantly share code, notes, and snippets.

@krobertson
Created November 13, 2009 00:58
Show Gist options
  • Save krobertson/233480 to your computer and use it in GitHub Desktop.
Save krobertson/233480 to your computer and use it in GitHub Desktop.
Ruby SMTP to XMPP server/gateway
require 'gserver'
require 'rubygems'
require 'xmpp4r'
require 'xmpp4r/roster'
require 'daemons'
JABBER_USER = '[email protected]'
JABBER_PASS = 'secret'
EMAIL_DOMAIN = 'domain.com'
class Array
def ^(other)
(self | other) - (self & other)
end
end
# Based on code from here: http://snippets.dzone.com/posts/show/5152
class SMTPServer < GServer
def serve(io)
@rcpt = []
@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
ok, op = process_line(data)
break unless ok
puts "<<" + op if op
io.print op if op
end
break if io.closed?
end
io.print "221 bye\r\n"
io.close
puts "Closed"
end
def process_line(line)
if (@data_mode) && (line.chomp =~ /^\.$/)
@email_message += line.gsub(/^\.(?:\r\n)$/, '')
@data_mode = false
send_message(@email_message)
return true, "250 OK\r\n"
elsif @data_mode
@email_message += line
return true, false
elsif (line =~ /^(HELO|EHLO)/i)
return true, "250 #{EMAIL_DOMAIN}\r\n"
elsif (line =~ /^QUIT/i)
return false, "bye\r\n"
elsif (line =~ /^MAIL FROM\:/i)
return true, "250 OK\r\n"
elsif (line =~ /^RCPT TO\:/i)
username = line.scan(/\:(?:<).*@(.*)\.#{EMAIL_DOMAIN}(?:>)/).flatten.first
return true, "550 no such user here" unless username
return true, "550 mailbox unavailable\r\n" unless @@online.include?(username.downcase)
@rcpt |= [username]
return true, "250 OK\r\n"
elsif (line =~ /^DATA/i)
@data_mode = true
@email_message = ''
return true, "354 Enter message, ending with \".\" on a line by itself\r\n"
elsif (line =~ /^ONLINE/i)
return true, "#{@@online.size} users online:\r\n " + @@online.join("\r\n ") + "\r\n"
else
return true, "500 ERROR\r\n"
end
end
def send_message(email)
@rcpt.each do |user|
msg = Jabber::Message::new("#{user}@#{EMAIL_DOMAIN}", email)
msg.type = :chat
@@client.send(msg)
end
end
end
Daemons.run_proc('smtp.rb') do
loop do
@@client = Jabber::Client.new(Jabber::JID::new(JABBER_USER))
@@client.connect
@@client.auth(JABBER_PASS)
@@client.send(Jabber::Presence.new.set_type(:available))
@@client.add_message_callback do |m|
if m.body =~ /The user '(.*)' was just created on node/
puts "Requesting subscription to new user #{$1}"
npres = Jabber::Presence.new.set_type(:subscribe).set_to($1)
@@client.send(npres)
end
end
roster = Jabber::Roster::Helper.new(@@client)
roster.add_subscription_request_callback do |item,pres|
roster.accept_subscription(pres.from)
puts "Requesting subscription to #{pres.from}"
npres = Jabber::Presence.new.set_type(:subscribe).set_to(pres.from)
@@client.send(npres)
end
@@online = []
@@client.add_presence_callback do |old_presence, new_presence|
unless old_presence.from.node == "service"
@@online ^= [old_presence.from.node.downcase] if old_presence.type == :unavailable
@@online |= [old_presence.from.node.downcase] unless old_presence.type
end
end
a = SMTPServer.new(25, '0.0.0.0', 100)
a.start
a.join
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment