aboutsummaryrefslogtreecommitdiffstats
path: root/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib')
-rw-r--r--tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/mockup.js113
-rw-r--r--tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/rai.js489
-rw-r--r--tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/starttls.js113
3 files changed, 715 insertions, 0 deletions
diff --git a/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/mockup.js b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/mockup.js
new file mode 100644
index 0000000..47bbc4a
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/mockup.js
@@ -0,0 +1,113 @@
+var net = require("net"),
+ crypto = require("crypto"),
+ tlslib = require("tls");
+
+// monkey patch net and tls to support nodejs 0.4
+if(!net.connect && net.createConnection){
+ net.connect = net.createConnection;
+}
+
+if(!tlslib.connect && tlslib.createConnection){
+ tlslib.connect = tlslib.createConnection;
+}
+
+/**
+ * @namespace Mockup module
+ * @name mockup
+ */
+module.exports = runClientMockup;
+
+/**
+ * <p>Runs a batch of commands against a server</p>
+ *
+ * <pre>
+ * var cmds = ["EHLO FOOBAR", "STARTTLS", "QUIT"];
+ * runClientMockup(25, "mail.hot.ee", cmds, function(resp){
+ * console.log("Final:", resp.toString("utf-8").trim());
+ * });
+ * </pre>
+ *
+ * @memberOf mockup
+ * @param {Number} port Port number
+ * @param {String} host Hostname to connect to
+ * @param {Array} commands Command list to be sent to server
+ * @param {Function} callback Callback function to run on completion,
+ * has the last response from the server as a param
+ * @param {Boolean} [debug] if set to true log all input/output
+ */
+function runClientMockup(port, host, commands, callback, debug){
+ host = host || "localhost";
+ port = port || 25;
+ commands = Array.isArray(commands) ? commands : [];
+
+ var command, ignore_data = false, sslcontext, pair;
+
+ var socket = net.connect(port, host);
+ socket.on("connect", function(){
+ socket.on("data", function(chunk){
+ if(ignore_data)return;
+
+ if(debug){
+ console.log("S: "+chunk.toString("utf-8").trim());
+ }
+
+ if(!commands.length){
+ socket.end();
+ if(typeof callback == "function"){
+ callback(chunk);
+ }
+ return;
+ }
+
+ if(["STARTTLS", "STLS"].indexOf((command || "").trim().toUpperCase())>=0){
+ ignore_data = true;
+ if(debug){
+ console.log("Initiated TLS connection");
+ }
+ sslcontext = crypto.createCredentials();
+ pair = tlslib.createSecurePair(sslcontext, false);
+
+ pair.encrypted.pipe(socket);
+ socket.pipe(pair.encrypted);
+ pair.fd = socket.fd;
+
+ pair.on("secure", function(){
+ if(debug){
+ console.log("TLS connection secured");
+ }
+ command = commands.shift();
+ if(debug){
+ console.log("C: "+command);
+ }
+ pair.cleartext.write(command+"\r\n");
+
+ pair.cleartext.on("data", function(chunk){
+ if(debug){
+ console.log("S: "+chunk.toString("utf-8").trim());
+ }
+
+ if(!commands.length){
+ pair.cleartext.end();
+ if(typeof callback == "function"){
+ callback(chunk);
+ }
+ return;
+ }
+ command = commands.shift();
+ pair.cleartext.write(command+"\r\n");
+ if(debug){
+ console.log("C: "+command);
+ }
+ });
+ });
+ }else{
+ command = commands.shift();
+ socket.write(command+"\r\n");
+ if(debug){
+ console.log("C: "+command);
+ }
+ }
+ });
+ });
+
+} \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/rai.js b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/rai.js
new file mode 100644
index 0000000..d472063
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/rai.js
@@ -0,0 +1,489 @@
+/**
+ * @fileOverview This is the main file for the RAI library to create text based servers
+ * @author <a href="mailto:andris@node.ee">Andris Reinman</a>
+ * @version 0.1.3
+ */
+
+var netlib = require("net"),
+ utillib = require("util"),
+ EventEmitter = require('events').EventEmitter,
+ starttls = require("./starttls").starttls,
+ tlslib = require("tls"),
+ crypto = require("crypto"),
+ fs = require("fs");
+
+// Default credentials for starting TLS server
+var defaultCredentials = {
+ key: fs.readFileSync(__dirname+"/../cert/key.pem"),
+ cert: fs.readFileSync(__dirname+"/../cert/cert.pem")
+};
+
+// Expose to the world
+module.exports.RAIServer = RAIServer;
+module.exports.runClientMockup = require("./mockup");
+
+/**
+ * <p>Creates instance of RAIServer</p>
+ *
+ * <p>Options object has the following properties:</p>
+ *
+ * <ul>
+ * <li><b>debug</b> - if set to true print traffic to console</li>
+ * <li><b>disconnectOnTimeout</b> - if set to true close the connection on disconnect</li>
+ * <li><b>secureConnection</b> - if set to true close the connection on disconnect</li>
+ * <li><b>credentials</b> - credentials for secureConnection and STARTTLS</li>
+ * <li><b>timeout</b> - timeout in milliseconds for disconnecting the client,
+ * defaults to 0 (no timeout)</li>
+ * </ul>
+ *
+ * <p><b>Events</b></p>
+ *
+ * <ul>
+ * <li><b>'connect'</b> - emitted if a client connects to the server, param
+ * is a client ({@link RAISocket}) object</li>
+ * <li><b>'error'</b> - emitted on error, has an error object as a param</li>
+ * </ul>
+ *
+ * @constructor
+ * @param {Object} [options] Optional options object
+ */
+function RAIServer(options){
+ EventEmitter.call(this);
+
+ this.options = options || {};
+
+ this._createServer();
+}
+utillib.inherits(RAIServer, EventEmitter);
+
+/**
+ * <p>Starts listening on selected port</p>
+ *
+ * @param {Number} port The port to listen
+ * @param {String} [host] The IP address to listen
+ * @param {Function} callback The callback function to be run after the server
+ * is listening, the only param is an error message if the operation failed
+ */
+RAIServer.prototype.listen = function(port, host, callback){
+ if(!callback && typeof host=="function"){
+ callback = host;
+ host = undefined;
+ }
+ this._port = port;
+ this._host = host;
+
+ this._connected = false;
+ if(callback){
+ this._server.on("listening", (function(){
+ this._connected = true;
+ callback(null);
+ }).bind(this));
+
+ this._server.on("error", (function(err){
+ if(!this._connected){
+ callback(err);
+ }
+ }).bind(this));
+ }
+
+ this._server.listen(this._port, this._host);
+};
+
+/**
+ * <p>Stops the server</p>
+ *
+ * @param {Function} callback Is run when the server is closed
+ */
+RAIServer.prototype.end = function(callback){
+ this._server.on("close", callback);
+ this._server.close();
+};
+
+/**
+ * <p>Creates a server with listener callback</p>
+ */
+RAIServer.prototype._createServer = function(){
+ if(this.options.secureConnection){
+ this._server = tlslib.createServer(
+ this.options.credentials || defaultCredentials,
+ this._serverListener.bind(this));
+ }else{
+ this._server = netlib.createServer(this._serverListener.bind(this));
+ }
+ this._server.on("error", this._onError.bind(this));
+};
+
+/**
+ * <p>Listens for errors</p>
+ *
+ * @event
+ * @param {Object} err Error object
+ */
+RAIServer.prototype._onError = function(err){
+ if(this._connected){
+ this.emit("error", err);
+ }
+};
+
+/**
+ * <p>Server listener that is run on client connection</p>
+ *
+ * <p>{@link RAISocket} object instance is created based on the client socket
+ * and a <code>'connection'</code> event is emitted</p>
+ *
+ * @param {Object} socket The socket to the client
+ */
+RAIServer.prototype._serverListener = function(socket){
+ if(this.options.debug){
+ console.log("CONNECTION FROM "+socket.remoteAddress);
+ }
+
+ var handler = new RAISocket(socket, this.options);
+
+ socket.on("data", handler._onReceiveData.bind(handler));
+ socket.on("end", handler._onEnd.bind(handler));
+ socket.on("error", handler._onError.bind(handler));
+ socket.on("timeout", handler._onTimeout.bind(handler));
+ socket.on("close", handler._onClose.bind(handler));
+
+ if("setKeepAlive" in socket){
+ socket.setKeepAlive(true); // plaintext server
+ }else if(socket.encrypted && "setKeepAlive" in socket.encrypted){
+ socket.encrypted.setKeepAlive(true); // secure server
+ }
+
+ this.emit("connect", handler);
+};
+
+/**
+ * <p>Creates a instance for interacting with a client (socket)</p>
+ *
+ * <p>Optional options object is the same that is passed to the parent
+ * {@link RAIServer} object</p>
+ *
+ * <p><b>Events</b></p>
+ *
+ * <ul>
+ * <li><b>'command'</b> - emitted if a client sends a command. Gets two
+ * params - command (String) and payload (Buffer)</li>
+ * <li><b>'data'</b> - emitted when a chunk is received in data mode, the
+ * param being the payload (Buffer)</li>
+ * <li><b>'ready'</b> - emitted when data stream ends and normal command
+ * flow is recovered</li>
+ * <li><b>'tls'</b> - emitted when the connection is secured by TLS</li>
+ * <li><b>'error'</b> - emitted when an error occurs. Connection to the
+ * client is disconnected automatically. Param is an error object.</l>
+ * <li><b>'timeout'</b> - emitted when a timeout occurs. Connection to the
+ * client is disconnected automatically if disconnectOnTimeout option
+ * is set to true.</l>
+ * <li><b>'end'</b> - emitted when the client disconnects</l>
+ * </ul>
+ *
+ * @constructor
+ * @param {Object} socket Socket for the client
+ * @param {Object} [options] Optional options object
+ */
+function RAISocket(socket, options){
+ EventEmitter.call(this);
+
+ this.socket = socket;
+ this.options = options || {};
+
+ this.remoteAddress = socket.remoteAddress;
+
+ this._dataMode = false;
+ this._endDataModeSequence = "\r\n.\r\n";
+ this._endDataModeSequenceRegEx = /\r\n\.\r\n|^\.\r\n/;
+
+ this.secureConnection = !!this.options.secureConnection;
+ this._destroyed = false;
+ this._remainder = "";
+
+ this._ignore_data = false;
+
+ if(this.options.timeout){
+ socket.setTimeout(this.options.timeout);
+ }
+}
+utillib.inherits(RAISocket, EventEmitter);
+
+/**
+ * <p>Sends some data to the client. <code>&lt;CR&gt;&lt;LF&gt;</code> is automatically appended to
+ * the data</p>
+ *
+ * @param {String|Buffer} data Data to be sent to the client
+ */
+RAISocket.prototype.send = function(data){
+ var buffer;
+ if(data instanceof Buffer || (typeof SlowBuffer != "undefined" && data instanceof SlowBuffer)){
+ buffer = new Buffer(data.length+2);
+ buffer[buffer.length-2] = 0xD;
+ buffer[buffer.length-1] = 0xA;
+ data.copy(buffer);
+ }else{
+ buffer = new Buffer((data || "").toString()+"\r\n", "binary");
+ }
+
+ if(this.options.debug){
+ console.log("OUT: \"" +buffer.toString("utf-8").trim()+"\"");
+ }
+
+ if(this.socket && this.socket.writable){
+ this.socket.write(buffer);
+ }else{
+ this.socket.end();
+ }
+};
+
+/**
+ * <p>Instructs the server to be listening for mixed data instead of line based
+ * commands</p>
+ *
+ * @param {String} [sequence="."] - optional sequence on separate line for
+ * matching the data end
+ */
+RAISocket.prototype.startDataMode = function(sequence){
+ this._dataMode = true;
+ if(sequence){
+ sequence = sequence.replace(/\.\=\(\)\-\?\*\\\[\]\^\+\:\|\,/g, "\\$1");
+ this._endDataModeSequence = "\r\n"+sequence+"\r\n";
+ this._endDataModeSequenceRegEx = new RegExp("/\r\n"+sequence+"\r\n|^"+sequence+"\r\n/");
+ }
+};
+
+/**
+ * <p>Instructs the server to upgrade the connection to secure TLS connection</p>
+ *
+ * <p>Fires <code>callback</code> on successful connection upgrade if set,
+ * otherwise emits <code>'tls'</code></p>
+ *
+ * @param {Object} [credentials] An object with PEM encoded key and
+ * certificate <code>{key:"---BEGIN...", cert:"---BEGIN..."}</code>,
+ * if not set autogenerated values will be used.
+ * @param {Function} [callback] If calback is set fire it after successful connection
+ * upgrade, otherwise <code>'tls'</code> is emitted
+ */
+RAISocket.prototype.startTLS = function(credentials, callback){
+
+ if(this.secureConnection){
+ return this._onError(new Error("Secure connection already established"));
+ }
+
+ if(!callback && typeof credentials == "function"){
+ callback = credentials;
+ credentials = undefined;
+ }
+
+ credentials = credentials || this.options.credentials || defaultCredentials;
+
+ this._ignore_data = true;
+
+ var secure_connector = starttls(this.socket, credentials, (function(ssl_socket){
+
+ if(this.options.debug && !ssl_socket.authorized){
+ console.log("WARNING: TLS ERROR ("+ssl_socket.authorizationError+")");
+ }
+
+ this._remainder = "";
+ this._ignore_data = false;
+
+ this.secureConnection = true;
+
+ this.socket = ssl_socket;
+ this.socket.on("data", this._onReceiveData.bind(this));
+
+ if(this.options.debug){
+ console.log("TLS CONNECTION STARTED");
+ }
+
+ if(callback){
+ callback();
+ }else{
+ this.emit("tls");
+ }
+
+ }).bind(this));
+
+ secure_connector.on("error", (function(err){
+ this._onError(err);
+ }).bind(this));
+};
+
+/**
+ * <p>Closes the connection to the client</p>
+ */
+RAISocket.prototype.end = function(){
+ this.socket.end();
+};
+
+/**
+ * <p>Called when a chunk of data arrives from the client. If currently in data
+ * mode, transmit the data otherwise send it to <code>_processData</code></p>
+ *
+ * @event
+ * @param {Buffer|String} chunk Data sent by the client
+ */
+RAISocket.prototype._onReceiveData = function(chunk){
+
+ if(this._ignore_data){ // if currently setting up TLS connection
+ return;
+ }
+
+ var str = typeof chunk=="string"?chunk:chunk.toString("binary"),
+ dataEndMatch, dataRemainderMatch, data, match;
+
+ if(this._dataMode){
+
+ str = this._remainder + str;
+ if((dataEndMatch = str.match(/\r\n.*?$/))){
+ // if ther's a line that is not ended, keep it for later
+ this._remainder = str.substr(dataEndMatch.index);
+ str = str.substr(0, dataEndMatch.index);
+ }else{
+ this._remainder = "";
+ }
+
+ // check if a data end sequence is found from the data
+ if((dataRemainderMatch = (str+this._remainder).match(this._endDataModeSequenceRegEx))){
+ str = str + this._remainder;
+ // if the sequence is not on byte 0 emit remaining data
+ if(dataRemainderMatch.index){
+ data = new Buffer(str.substr(0, dataRemainderMatch.index), "binary");
+ if(this.options.debug){
+ console.log("DATA:", data.toString("utf-8"));
+ }
+ this.emit("data", data);
+ }
+ // emit data ready
+ this._remainder = "";
+ this.emit("ready");
+ this._dataMode = false;
+ // send the remaining data for processing
+ this._processData(str.substr(dataRemainderMatch.index + dataRemainderMatch[0].length)+"\r\n");
+ }else{
+ // check if there's not something in the end of the data that resembles
+ // end sequence - if so, cut it off and save it to the remainder
+ str = str + this._remainder;
+ this._remainder= "";
+ for(var i = Math.min(this._endDataModeSequence.length-1, str.length); i>0; i--){
+ match = this._endDataModeSequence.substr(0, i);
+ if(str.substr(-match.length) == match){
+ this._remainder = str.substr(-match.length);
+ str = str.substr(0, str.length - match.length);
+ }
+ }
+
+ // if there's some data leht, emit it
+ if(str.length){
+ data = new Buffer(str, "binary");
+ if(this.options.debug){
+ console.log("DATA:", data.toString("utf-8"));
+ }
+ this.emit("data", data);
+ }
+ }
+ }else{
+ // Not in data mode, process as command
+ this._processData(str);
+ }
+};
+
+/**
+ * <p>Processed incoming command lines and emits found data as
+ * <code>'command'</code> with the command name as the first param and the rest
+ * of the data as second (Buffer)</p>
+ *
+ * @param {String} str Binary string to be processed
+ */
+RAISocket.prototype._processData = function(str){
+ if(!str.length){
+ return;
+ }
+ var lines = (this._remainder+str).split("\r\n"),
+ match, command;
+
+ this._remainder = lines.pop();
+
+ for(var i=0, len = lines.length; i<len; i++){
+ if(this._ignore_data){
+ // If TLS upgrade is initiated do not process current buffer
+ this._remainder = "";
+ break;
+ }
+ if(!this._dataMode){
+ if((match = lines[i].match(/\s*[\S]+\s?/))){
+ command = (match[0] || "").trim();
+ if(this.options.debug){
+ console.log("COMMAND:", lines[i]);
+ }
+ this.emit("command", command, new Buffer(lines[i].substr(match.index + match[0].length), "binary"));
+ }
+ }else{
+ if(this._remainder){
+ this._remainder += "\r\n";
+ }
+ this._onReceiveData(lines.slice(i).join("\r\n"));
+ break;
+ }
+ }
+};
+
+/**
+ * <p>Called when the connection is or is going to be ended</p>
+ */
+RAISocket.prototype._destroy = function(){
+ if(this._destroyed)return;
+ this._destroyed = true;
+
+ this.removeAllListeners();
+};
+
+/**
+ * <p>Called when the connection is ended. Emits <code>'end'</code></p>
+ *
+ * @event
+ */
+RAISocket.prototype._onEnd = function(){
+ this.emit("end");
+ this._destroy();
+};
+
+/**
+ * <p>Called when an error has appeared. Emits <code>'error'</code> with
+ * the error object as a parameter.</p>
+ *
+ * @event
+ * @param {Object} err Error object
+ */
+RAISocket.prototype._onError = function(err){
+ this.emit("error", err);
+ this._destroy();
+};
+
+/**
+ * <p>Called when a timeout has occured. Connection will be closed and
+ * <code>'timeout'</code> is emitted.</p>
+ *
+ * @event
+ */
+RAISocket.prototype._onTimeout = function(){
+ if(this.options.disconnectOnTimeout){
+ if(this.socket && !this.socket.destroyed){
+ this.socket.end();
+ }
+ this.emit("timeout");
+ this._destroy();
+ }else{
+ this.emit("timeout");
+ }
+};
+
+/**
+ * <p>Called when the connection is closed</p>
+ *
+ * @event
+ * @param {Boolean} hadError did the connection end because of an error?
+ */
+RAISocket.prototype._onClose = function(hadError){
+ this._destroy();
+}; \ No newline at end of file
diff --git a/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/starttls.js b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/starttls.js
new file mode 100644
index 0000000..b17dc65
--- /dev/null
+++ b/tools/node_modules/nodemailer/node_modules/simplesmtp/node_modules/rai/lib/starttls.js
@@ -0,0 +1,113 @@
+// SOURCE: https://gist.github.com/848444
+
+// Target API:
+//
+// var s = require('net').createStream(25, 'smtp.example.com');
+// s.on('connect', function() {
+// require('starttls')(s, options, function() {
+// if (!s.authorized) {
+// s.destroy();
+// return;
+// }
+//
+// s.end("hello world\n");
+// });
+// });
+//
+//
+
+/**
+ * @namespace STARTTLS module
+ * @name starttls
+ */
+module.exports.starttls = starttls;
+
+/**
+ * <p>Upgrades a socket to a secure TLS connection</p>
+ *
+ * @memberOf starttls
+ * @param {Object} socket Plaintext socket to be upgraded
+ * @param {Object} options Certificate data for the server
+ * @param {Function} callback Callback function to be run after upgrade
+ */
+function starttls(socket, options, callback) {
+ var sslcontext, pair, cleartext;
+
+ socket.removeAllListeners("data");
+ sslcontext = require('crypto').createCredentials(options);
+ pair = require('tls').createSecurePair(sslcontext, true);
+ cleartext = pipe(pair, socket);
+
+ pair.on('secure', function() {
+ var verifyError = (pair._ssl || pair.ssl).verifyError();
+
+ if (verifyError) {
+ cleartext.authorized = false;
+ cleartext.authorizationError = verifyError;
+ } else {
+ cleartext.authorized = true;
+ }
+
+ callback(cleartext);
+ });
+
+ cleartext._controlReleased = true;
+ return pair;
+}
+
+function forwardEvents(events, emitterSource, emitterDestination) {
+ var map = [], name, handler;
+
+ for(var i = 0, len = events.length; i < len; i++) {
+ name = events[i];
+
+ handler = forwardEvent.bind(emitterDestination, name);
+
+ map.push(name);
+ emitterSource.on(name, handler);
+ }
+
+ return map;
+}
+
+function forwardEvent() {
+ this.emit.apply(this, arguments);
+}
+
+function removeEvents(map, emitterSource) {
+ for(var i = 0, len = map.length; i < len; i++){
+ emitterSource.removeAllListeners(map[i]);
+ }
+}
+
+function pipe(pair, socket) {
+ pair.encrypted.pipe(socket);
+ socket.pipe(pair.encrypted);
+
+ pair.fd = socket.fd;
+
+ var cleartext = pair.cleartext;
+
+ cleartext.socket = socket;
+ cleartext.encrypted = pair.encrypted;
+ cleartext.authorized = false;
+
+ function onerror(e) {
+ if (cleartext._controlReleased) {
+ cleartext.emit('error', e);
+ }
+ }
+
+ var map = forwardEvents(["timeout", "end", "close", "drain", "error"], socket, cleartext);
+
+ function onclose() {
+ socket.removeListener('error', onerror);
+ socket.removeListener('close', onclose);
+ removeEvents(map,socket);
+ }
+
+ socket.on('error', onerror);
+ socket.on('close', onclose);
+
+ return cleartext;
+} \ No newline at end of file