Permalink
Browse files

Implemented the beginnings of SMTP Client, added an error for SMTP Se…

…rver.

- Changed from protocol based to client/server based.
- Added check for process.EventEmitter.addOnce, patches if not available in Queue
- Changed the demos to be examples.
- Setup various modules for SMTP Client.

TODO:
- Write tests.
- Figure out best test suite to use.
  • Loading branch information...
1 parent e580656 commit 3fe4756120479b61743365b79428859e255c69f9 @miksago miksago committed Jan 15, 2010
View
@@ -0,0 +1 @@
+examples/config.js
View
No changes.
View
No changes.
View
@@ -0,0 +1,12 @@
+/*===============================================
+ File: config.example
+
+ Author: Micheil Smith
+ Description:
+ Example config file
+===============================================*/
+
+var config = exports;
+
+config.port = 25;
+config.host = "smtp.server.com";
View
@@ -0,0 +1,13 @@
+/*===============================================
+ File: example-client.js
+
+ Author: Micheil Smith
+ Description:
+ Demonstration of the smtp library.
+===============================================*/
+var config = require("./config");
+var smtp = require("../lib/smtp");
+
+var client = new smtp.Client();
+
+client.connect(config.port, config.host);
File renamed without changes.
View
@@ -0,0 +1,14 @@
+/*===============================================
+ 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();
View
No changes.
No changes.
View
@@ -0,0 +1,86 @@
+/*===============================================
+ File: queue.js
+
+ Author: Micheil Smith
+ Description:
+ Handles the parsing and sorting of
+ messages in a stack / queue loop.
+===============================================*/
+
+
+/*-----------------------------------------------
+ Extension to EventEmitter to allow listeners
+ to automatically be remove after being called.
+-----------------------------------------------*/
+if(process.EventEmitter.prototype.addOnce == undefined){
+ process.EventEmitter.prototype.addOnce = function(signal, callback){
+ var EventEmitter = this;
+
+ this.addListener(signal, function(){
+ EventEmitter.removeListener(signal, arguments.callee);
+ callback.apply(this, arguments);
+ });
+ };
+}
+
+
+/*-----------------------------------------------
+ Queue
+-----------------------------------------------*/
+var Queue = function(){
+ this._stack = [];
+ this._processing = false;
+};
+
+process.inherits(Queue, process.EventEmitter);
+
+Queue.prototype.push = function(item){
+ this._stack.push(item);
+ this.emit("push", item);
+
+ if( !this._processing ){
+ this._processor();
+ }
+};
+
+Queue.prototype.pop = function(){
+ var item = this._stack.pop();
+ this.emit("pop", item);
+
+ return item;
+};
+
+Queue.prototype.unshift = function(item){
+ this._stack.unshift(item);
+ this.emit("unshift", item);
+
+ if( !this._processing ){
+ this._processor();
+ }
+};
+
+Queue.prototype.shift = function(){
+ var item = this._stack.shift();
+ this.emit("shift", item);
+
+ return item;
+};
+
+Queue.prototype._processor = function(){
+ this._processing = true;
+
+ this.process.call(this);
+
+ if(this._stack.length > 0){
+ this._processor();
+ } else {
+ this._processing = false;
+ }
+};
+
+Queue.prototype.process = function(){
+ var item = this._stack.shift();
+ this.emit("processed", item);
+};
+
+exports.Queue = Queue;
View
@@ -0,0 +1,148 @@
+/*===============================================
+ File: smtp.js
+
+ Author: Micheil Smith
+ Description:
+ This files implements the raw protocol
+ that is SMTP. The version of SMTP used
+ is as defined in RFC2821.
+
+ Implements:
+ - SMTP: RFC821
+ - ESMTP: RFC2821
+ - SMTP Authentication: RFC4954
+ - SMTP 8Bit-Mime: RFC1652
+ - SMTP over SSL/TLS: RFC2487
+
+ Issues:
+ - SMTP over SSL/TLS does not work,
+ due to lack of TCP Client SSL/TLS
+ negotiation within node.js
+===============================================*/
+
+// Global
+var sys = require("sys");
+var tcp = require("tcp")
+// Local
+var PacketHandler = require("./smtp/packetHandler").PacketHandler;
+
+/*-----------------------------------------------
+ SMTP Error Definitions
+-----------------------------------------------*/
+var SMTPError = {
+ auth: new Error("SMTP Authentication Error"),
+ server: new Error("SMTP Server Error (code: 420, 450 or temporary error)"),
+ syntax: new Error("SMTP Syntax Error (code: 500)"),
+ fatal: new Error("SMTP Fatal Error (code: all 5xx except for 500)"),
+ unknown: new Error("SMTP Unknown reply code returned from server"),
+ unsupported: new Error("SMTP Command Unsupported on server"),
+
+
+ "504": new Error("Command parameter not implemented"),
+ "550": new Error("Requested action not taken: mailbox unavailable")
+};
+
+/*-----------------------------------------------
+ SMTP Client
+-----------------------------------------------*/
+var Client = function(){
+ this.socket = null;
+ this.packetHandler = new PacketHandler();
+ this.esmtp = false;
+ this.started = false;
+};
+
+process.inherits(Client, process.EventEmitter);
+
+Client.prototype.connect = function(port, host){
+ var client = this;
+
+ this.port = port;
+ this.host = host;
+
+ this.socket = new tcp.createConnection(this.port, this.host);
+ this.socket.setEncoding("ascii");
+
+ this.packetHandler.addOnce("ok", function(packet){
+ if(packet.data[0].indexOf("ESMTP") > -1){
+ client.esmtp = true;
+ }
+
+ client.handshake();
+ });
+
+ this.socket.addListener("receive", function(data){
+ client.packetHandler.push(data);
+ });
+};
+
+Client.prototype.writeline = function(line){
+ sys.puts("\033[0;32m"+line+"\033[0m")
+ this.socket.send(line+"\r\n");
+};
+
+Client.prototype.get = function(data, callback){
+ this.packetHandler.addOnce("packet", callback);
+ this.writeline(data);
+};
+
+Client.prototype.handshake = function(){
+ if(this.esmtp){
+ this.ehlo();
+ } else {
+ this.helo();
+ }
+};
+
+/* protocol implementation: */
+Client.prototype.helo = function(){
+ var client = this;
+
+ this.get("HELO "+this.host, function(packet){
+ switch(packet.status){
+ case "250":
+ client.capabilities = packet.data;
+ client.started = true;
+ break;
+ case "504":
+ throw SMTPError["504"];
+ break;
+ case "550":
+ throw SMTPError["550"];
+ break;
+ }
+ });
+};
+
+Client.prototype.ehlo = function(){
+ var client = this;
+
+ this.get("EHLO "+this.host, function(packet){
+ switch(packet.status){
+ case "250":
+ client.capabilities = packet.data;
+ client.started = true;
+ break;
+ case "504":
+ throw SMTPError["504"];
+ break;
+ case "550":
+ throw SMTPError["550"];
+ break;
+ }
+ });
+};
+
+
+
+/*-----------------------------------------------
+ SMTP Server (unimplemented, but possible)
+-----------------------------------------------*/
+var Server = function(){
+ throw new Error("SMTP Server Not Implemented");
+ return;
+};
+
+exports.Client = Client;
+exports.Server = Server;
+
View
@@ -0,0 +1,84 @@
+/*===============================================
+ File: packetHandler.js
+
+ Author: Micheil Smith
+ Description:
+ Handles the parsing and processing of
+ incoming SMTP packets.
+===============================================*/
+// Global
+var sys = require("sys");
+// Local
+var Queue = require("../queue").Queue;
+
+/*-----------------------------------------------
+ PacketHandler
+-----------------------------------------------*/
+var PacketHandler = Queue;
+
+PacketHandler.prototype.push = function(data){
+ var packets = data.split("\r\n");
+ while(packets.length && (packet = packets.shift())){
+ if(packet != ""){
+ this._stack.push(packet);
+
+ if( !this._processing ){
+ this._processor();
+ }
+ }
+ }
+};
+
+PacketHandler.prototype.process = function(){
+ var status = 0, data = [], continued = false, signal="other";
+
+ while(this._stack.length && (packet = this._stack.shift())){
+
+ sys.puts(">> "+packet);
+
+ if(continued == false){
+ // not a number, strings are easier to work with as status codes.
+ status = packet.substr(0,3);
+ }
+
+ data.push(packet.substr(4));
+
+ continued = (packet.substr(3,1) == "-");
+ if( !continued){
+ break;
+ }
+ }
+
+ switch(status.substr(0,1)){
+ case "1":
+ signal = "confirm";
+ break;
+ case "2":
+ signal = "ok";
+ break;
+ case "3":
+ signal = "continue";
+ break;
+ case "4":
+ signal = "fail";
+ break;
+ case "5":
+ signal = "error";
+ break;
+ }
+
+ this.emit("packet", {
+ status: status,
+ data: data
+ });
+
+ this.emit(signal, {
+ status: status,
+ data: data
+ });
+};
+
+/*-----------------------------------------------
+ The final export.
+-----------------------------------------------*/
+exports.PacketHandler = PacketHandler;
View
@@ -0,0 +1,10 @@
+var testkit = require("./testkit");
+var queue = require("../lib/queue");
+
+testkit.group("Queue", function(){
+ var testqueue = new queue.Queue();
+
+ this.equal(testqueue._stack.length, 0, "Queue should start empty");
+
+
+});
Oops, something went wrong.

0 comments on commit 3fe4756

Please sign in to comment.