Skip to content
Browse files

Initial rough implementation of an SMTP server

  • Loading branch information...
1 parent 60b755d commit 7826ab67275c6120862d224d4516797249b1603e @kennethkalmer kennethkalmer committed with
Showing with 211 additions and 8 deletions.
  1. +5 −4 examples/example-server.js
  2. +5 −4 lib/smtp.js
  3. +201 −0 lib/smtp/server.js
View
9 examples/example-server.js
@@ -1,14 +1,15 @@
/*===============================================
File: example-server.js
-
+
Author: Micheil Smith
Description:
An example of the node-smtp server.
-
+
(Not Implemented)
===============================================*/
-
var smtp = require("../lib/smtp");
-var server = smtp.Server();
+var server = new smtp.Server();
+server.runServer();
+
View
9 lib/smtp.js
@@ -4,9 +4,9 @@
Author: Micheil Smith
Description:
- Implements two wrappers around the
- submodules, currently provides an SMTP
- Client, may in the future include an
+ Implements two wrappers around the
+ submodules, currently provides an SMTP
+ Client, may in the future include an
SMTP Server.
===============================================*/
@@ -23,7 +23,8 @@ var Client = function(){
SMTP Server (unimplemented, but possible)
-----------------------------------------------*/
var Server = function(){
- throw new Error("SMTP Server Not Implemented");
+ var SMTPServer = require("./smtp/server").Server;
+ return new SMTPServer(arguments);
};
/*-----------------------------------------------
View
201 lib/smtp/server.js
@@ -0,0 +1,201 @@
+/*
+ File: server.js
+
+ Author: Kenneth Kalmer
+ Description:
+ This file implements an SMTP server as defined in RFC2821.
+
+ Implements:
+ ESMTP: RFC2821
+
+ Issues:
+ Plenty, have patience.
+
+*/
+
+// Global
+var tcp = require('tcp');
+var sys = require('sys');
+
+// Local
+var enable_debug = true;
+var eol = "\r\n";
+
+/*
+ RFC2821 defines an SMTP Session
+*/
+var SMTPSession = function( socket ) {
+ var session = this;
+
+ this.from = null;
+ this.recipient = null;
+ this.callbacks = [];
+ this.socket = socket;
+ this.buffer = "";
+ this.data = null;
+ this.in_data = false;
+
+
+ // callbacks first
+ socket.addListener( 'receive', function(packet) {
+ session.receive( packet )
+ });
+ socket.addListener( 'eol', session.eol );
+ socket.addListener( 'close', session.close );
+
+ session.greeting();
+}
+
+sys.inherits( SMTPSession, process.EventEmitter );
+
+// patterns for commands
+SMTPSession.prototype.command_patterns = {
+ helo: /^HELO\s*/i,
+ ehlo: /^EHLO\s*/i,
+ quit: /^QUIT/i,
+ from: /^MAIL FROM:\s*/i,
+ rcpt: /^RCPT TO:\s*/i,
+ data: /^DATA/i,
+ noop: /^NOOP/i,
+ rset: /^RSET/i,
+ vrfy: /^VRFY\s+/i,
+ expn: /^EXPN\s+/,
+ help: /^HELP/i,
+ tls: /^STARTTLS/i,
+ auth: /^AUTH\s+/i
+}
+
+// Replies
+
+SMTPSession.prototype.send = function( s ) {
+ sys.print("SENDING: " + s + eol);
+ this.socket.send( s + eol );
+}
+
+SMTPSession.prototype.greeting = function() {
+ this.send("220 <hostname> ESMTP node.js");
+}
+
+SMTPSession.prototype.notSupported = function(){
+ this.send( "500 not supported" );
+}
+
+SMTPSession.prototype.sendOk = function() {
+ this.send( "250 OK" );
+}
+
+// Commands
+SMTPSession.prototype.ehlo = function() {
+ var hostname = this.extractArguments( 'EHLO' );
+ this.send('250-<hostname> Hello ' + this.socket.remoteAddress );
+ this.send('250 8BITMIME');
+}
+
+SMTPSession.prototype.helo = function() {
+ var hostname = this.extractArguments( 'HELO' );
+ this.send('250<hostname> Hello ' + this.socket.remoteAddress );
+}
+
+SMTPSession.prototype.receiver = function() {
+ this.sendOk();
+}
+
+SMTPSession.prototype.sender = function() {
+ this.sendOk();
+}
+
+SMTPSession.prototype.startData = function() {
+ this.in_data = true;
+ this.send("354 Terminate with line containing only '.'");
+}
+
+SMTPSession.prototype.quit = function() {
+ this.send( '221 <hostname> closing connection' );
+ this.socket.close();
+}
+
+// Handlers
+SMTPSession.prototype.receive = function( packet ) {
+ sys.puts('Received data: ' + packet);
+
+ this.buffer += packet;
+
+ if ( this.in_data ) {
+ this.dataReceived();
+
+ } else if ( this.buffer.indexOf( eol ) != 1 ) {
+ var command = null;
+
+ for( var cmd in this.command_patterns ) {
+ if (this.command_patterns[ cmd ].test( this.buffer ) ) {
+
+ command = cmd;
+ break;
+ }
+ }
+
+ sys.puts( 'Command: ' + sys.inspect(command) );
+ switch( cmd ) {
+ case 'ehlo':
+ this.ehlo();
+ break;
+ case 'helo':
+ this.helo();
+ break;
+ case 'rcpt':
+ this.receiver();
+ break;
+ case 'from':
+ this.sender();
+ break;
+ case 'data':
+ this.startData();
+ break;
+ case 'quit':
+ this.quit();
+ break;
+ default:
+ this.notSupported();
+ }
+
+ this.buffer = "";
+ }
+}
+
+SMTPSession.prototype.dataReceived = function(){
+ var re = new RegExp( eol + "." + eol );
+ if( re.test( this.buffer ) ) {
+ this.data = this.buffer;
+ this.buffer = "";
+ this.in_data = false;
+ sys.puts("End of data");
+ this.sendOk();
+ }
+}
+
+// Utilities
+
+SMTPSession.prototype.extractArguments = function( command ) {
+ return this.buffer.replace( command, '' ).replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+}
+
+var Server = exports.Server = function() {
+ process.EventEmitter.call( this );
+
+ this.host = 'localhost';
+ this.port = 10025;
+ this.callbacks = [];
+ this.server = null;
+
+ return this;
+}
+
+sys.inherits( Server, process.EventEmitter );
+
+Server.prototype.runServer = function() {
+ this.server = tcp.createServer( function( socket ){
+ new SMTPSession( socket );
+ });
+
+ this.server.listen( this.port, this.host );
+}

0 comments on commit 7826ab6

Please sign in to comment.
Something went wrong with that request. Please try again.