Created
November 13, 2009 00:58
-
-
Save krobertson/233480 to your computer and use it in GitHub Desktop.
Ruby SMTP to XMPP server/gateway
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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