Skip to content

Instantly share code, notes, and snippets.

@jonathansadowski
Created April 8, 2013 19:17
Show Gist options
  • Save jonathansadowski/5339631 to your computer and use it in GitHub Desktop.
Save jonathansadowski/5339631 to your computer and use it in GitHub Desktop.
var net = require('net');
var extend = require('node.extend');
function Session(lime){
this.lime = lime;
this.store = {};
this.mode = 0;
// 0 - expecting helo/ehlo
// 1 - expecting mail
// 2 - expecting rcpt
// 3 - expecting data (or more rcpt)
// 4 - data (expecting .)
this.from = '';
this.to = [];
this.body = '';
this.c = null;
var t = this;
this.listen = function(){
t.c.on('data', function(data){
var line = data.toString().trim();
var parts = line.split(' ');
var remainder = parts.slice(1).join(' ');
if(4 > t.mode) {
var command = parts[0].toLowerCase();
if(t.commands[command])
t.commands[command](remainder);
else
t.unknown(line);
} else {
t.body += data;
if(data.toString().match(/(^|\n)\.(\r?\n)?$/)){
t.body = t.body.replace(/\r?\n\.\r?\n$/,'');
t.lime.trigger('data_post',function(response){
if(2 === response){
t.c.write('554 I didn\'t like your message, so it wasn\'t saved\r\n');
} else {
t.mode = 1;
t.c.write('250 OK I haz all your data, now whatcha gonna do?\r\n');
}
},t,[t.from,t.to,t.body]);
}
}
});
}
this.commands = {
helo: function(remainder){
t.lime.trigger('helo',function(response){
if(2 === response)
t.c.end();
else {
t.mode = 1;
t.c.write('250 limebin.com Hello, you look nice today!\r\n');
}
},t);
},
ehlo: function(remainder){
t.lime.trigger('helo',function(response){
if(2 === response)
t.c.end();
else {
t.mode = 1;
t.c.write('250-limebin.com Hello, you look nice today!\r\n');
t.c.write('250 size 10485760\r\n');
}
},t);
},
mail: function(remainder){
var matches;
var from = '';
if(!(matches=remainder.match(/^from:\s?<?(.*?)>?\s?$/i))){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('501 Check your syntax, bro!\r\n');
},t);
return;
}
from = matches[1].trim();
if(0==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 That\'s a little rude, you should say HELO first!\r\n');
},t);
return;
}
t.lime.trigger('mail',function(response){
if(2 === response){
t.c.write('451 We do not accept mail from that address.\r\n');
} else {
t.mode = 2;
t.to = [];
t.from = from;
t.c.write('250 OK Now why don\'t you tell me where to send it?\r\n');
}
},t,from);
},
rcpt: function(remainder){
var matches;
var to = '';
if(!(matches=remainder.match(/^to:\s?<?(.*?)>?\s?$/i))){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('501 Check your syntax, bro!\r\n');
},t);
return;
}
to = matches[1].trim();
if(0==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 That\'s a little rude, you should say HELO first!\r\n');
},t);
return;
} else if(1==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 WHOA BUDDY, YOU ARE GETTING AHEAD OF YOURSELF, MAIL FIRST!\r\n');
},t);
return;
}
t.lime.trigger('rcpt',function(response){
if(2 === response){
t.c.write('550 WHO DAT?\r\n');
} else if(1 === response) {
t.mode = 3;
t.to.push(to);
t.c.write('250 OK Got it, now what?\r\n');
} else {
t.c.write('550 WHO DAT?\r\n');
}
},t,to);
},
data: function(remainder){
if(0==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 That\'s a little rude, you should say HELO first!\r\n');
},t);
return;
} else if(1==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 WHOA BUDDY, YOU ARE GETTING AHEAD OF YOURSELF, MAIL FIRST!\r\n');
},t);
return;
} else if(2==t.mode){
t.lime.trigger('invalid',function(response){
if(2 === response)
t.c.end();
else
t.c.write('503 Now who do you expect me to deliver this to? RCPT FIRST!\r\n');
},t);
return;
}
t.lime.trigger('data',function(response){
if(2 === response){
t.c.end();
} else {
t.mode = 4;
t.c.write('354 Go ahead, give me your tired, your poor, your huddled data yearning to breathe free.\r\n');
}
},t);
},
quit: function(remainder){
t.lime.trigger('helo',function(response){
if(2 === response)
t.c.end();
else {
t.c.write('221 Leaving me already? I\'ll miss you, visit again soon!\r\n');
t.c.end();
}
},t);
}
}
this.unknown = function(line){
t.lime.trigger('unknown',function(response){
if(2 === response)
t.c.end()
else
t.c.write('502 SAY WHAT?\r\n');
},t);
}
}
Session.prototype.start = function(c){
var t = this;
this.c=c;
this.lime.trigger('connect',function(response){
if(2 === response)
c.end();
else {
c.write('220 limebin.com ESMTP Welcome to the best mail server EVAR!!!1!!one!!eleven!!!11!\r\n');
t.listen();
}
},t);
}
function Lime(){
this.callbacks = {
'connect':[],
'helo':[],
'mail':[],
'rcpt':[],
'data':[],
'data_post':[],
'quit':[],
'unknown':[],
'invalid':[]
};
}
Lime.prototype.start = function(){
var lime = this;
var server = net.createServer(function(c) {
var s = new Session(lime);
s.start(c);
});
server.listen(25);
}
Lime.prototype.on = function(event, callback){
if(this.callbacks[event])
this.callbacks[event].push(callback);
}
Lime.prototype.trigger = function(event, callback, con, data, callbacks){
if(!this.callbacks[event] || !this.callbacks[event].length){
return callback(0);
}
if(!callbacks){
callbacks = extend([],this.callbacks[event]);
} else if(!callbacks.length) {
return callback(0);
}
var cb = callbacks.shift();
var t = this;
var timeoutTimer = setTimeout(function(){
callbacks = [];
callback(2);
},5000);
var next = function(resp, message){
timeoutFunction = function(){}
clearTimeout(timeoutTimer);
if(resp && (1===resp || 2===resp))
return callback(resp, message);
t.trigger(event,callback,con,data,callbacks);
}
if(cb)
cb(next,con,data);
}
exports.CONT = 0;
exports.OK = 1;
exports.DENY = 2;
exports.server = Lime;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment