%PDF- %PDF-
Direktori : /home/opti/workflow/node_modules/simplesmtp/lib/ |
Current File : /home/opti/workflow/node_modules/simplesmtp/lib/pool.js |
'use strict'; var simplesmtp = require('../index'), EventEmitter = require('events').EventEmitter, utillib = require('util'), xoauth2 = require('xoauth2'); // expose to the world module.exports = function(port, host, options) { var pool = new SMTPConnectionPool(port, host, options); return pool; }; /** * <p>Creates a SMTP connection pool</p> * * <p>Optional options object takes the following possible properties:</p> * <ul> * <li><b>secureConnection</b> - use SSL</li> * <li><b>name</b> - the name of the client server</li> * <li><b>auth</b> - authentication object <code>{user:'...', pass:'...'}</code> * <li><b>ignoreTLS</b> - ignore server support for STARTTLS</li> * <li><b>tls</b> - options for createCredentials</li> * <li><b>debug</b> - output client and server messages to console</li> * <li><b>maxConnections</b> - how many connections to keep in the pool</li> * </ul> * * @constructor * @namespace SMTP Client Pool module * @param {Number} [port=25] The port number to connecto to * @param {String} [host='localhost'] THe hostname to connect to * @param {Object} [options] optional options object */ function SMTPConnectionPool(port, host, options) { EventEmitter.call(this); /** * Port number to connect to * @public */ this.port = port || 25; /** * Hostname to connect to * @public */ this.host = host || 'localhost'; /** * Options object * @public */ this.options = options || {}; this.options.maxConnections = this.options.maxConnections || 5; this.options.maxMessages = this.options.maxMessages || Infinity; /** * An array of connections that are currently idle * @private */ this._connectionsAvailable = []; /** * An array of connections that are currently in use * @private */ this._connectionsInUse = []; /** * Message queue (FIFO) * @private */ this._messageQueue = []; /** * Counter for generating ID values for debugging * @private */ this._idgen = 0; // Initialize XOAUTH2 if needed if (this.options.auth && typeof this.options.auth.XOAuth2 == 'object') { if (!this.options.auth.XOAuth2.user && this.options.auth.user) { this.options.auth.XOAuth2.user = this.options.auth.user; } this.options.auth.XOAuth2 = xoauth2.createXOAuth2Generator(this.options.auth.XOAuth2); } } utillib.inherits(SMTPConnectionPool, EventEmitter); /** * <p>Sends a message. If there's any idling connections available * use one to send the message immediatelly, otherwise add to queue.</p> * * @param {Object} message MailComposer object * @param {Function} callback Callback function to run on finish, gets an * <code>error</code> object as a parameter if the sending failed * and on success an object with <code>failedRecipients</code> array as * a list of addresses that were rejected (if any) and * <code>message</code> which indicates the last message received from * the server */ SMTPConnectionPool.prototype.sendMail = function(message, callback) { var connection; message.returnCallback = callback; if (this._connectionsAvailable.length) { // if available connections pick one connection = this._connectionsAvailable.pop(); this._connectionsInUse.push(connection); this._processMessage(message, connection); } else { this._messageQueue.push(message); if (this._connectionsAvailable.length + this._connectionsInUse.length < this.options.maxConnections) { this._createConnection(); } } }; /** * <p>Closes all connections</p> */ SMTPConnectionPool.prototype.close = function(callback) { var connection; // for some reason destroying the connections seem to be the only way :S while (this._connectionsAvailable.length) { connection = this._connectionsAvailable.pop(); connection.quit(); } while (this._connectionsInUse.length) { connection = this._connectionsInUse.pop(); connection.quit(); } if (callback) { if (typeof setImmediate == 'function') { setImmediate(callback); } else { process.nextTick(callback); } } }; /** * <p>Initiates a connection to the SMTP server and adds it to the pool</p> */ SMTPConnectionPool.prototype._createConnection = function() { var connectionOptions = { instanceId: ++this._idgen, debug: !! this.options.debug, logFile: this.options.logFile, ignoreTLS: !! this.options.ignoreTLS, tls: this.options.tls || false, auth: this.options.auth || false, authMethod: this.options.authMethod, name: this.options.name || false, secureConnection: !! this.options.secureConnection }, connection; if ('greetingTimeout' in this.options) { connectionOptions.greetingTimeout = this.options.greetingTimeout; } if ('socketTimeout' in this.options) { connectionOptions.socketTimeout = this.options.socketTimeout; } if ('connectionTimeout' in this.options) { connectionOptions.connectionTimeout = this.options.connectionTimeout; } if ('rejectUnathorized' in this.options) { connectionOptions.rejectUnathorized = this.options.rejectUnathorized; } if ('localAddress' in this.options) { connectionOptions.localAddress = this.options.localAddress; } connection = simplesmtp.connect(this.port, this.host, connectionOptions); connection._messagesProcessed = 0; connection.on('idle', this._onConnectionIdle.bind(this, connection)); connection.on('message', this._onConnectionMessage.bind(this, connection)); connection.on('ready', this._onConnectionReady.bind(this, connection)); connection.on('error', this._onConnectionError.bind(this, connection)); connection.on('end', this._onConnectionEnd.bind(this, connection)); connection.on('rcptFailed', this._onConnectionRCPTFailed.bind(this, connection)); this.emit('connectionCreated', connection); // as the connection is not ready yet, add to 'in use' queue this._connectionsInUse.push(connection); }; /** * <p>Processes a message by assigning it to a connection object and initiating * the sending process by setting the envelope</p> * * @param {Object} message MailComposer message object * @param {Object} connection <code>simplesmtp.connect</code> connection */ SMTPConnectionPool.prototype._processMessage = function(message, connection) { connection.currentMessage = message; message.currentConnection = connection; connection._messagesProcessed++; // send envelope connection.useEnvelope(message.getEnvelope()); }; /** * <p>Will be fired on <code>'idle'</code> events by the connection, if * there's a message currently in queue</p> * * @event * @param {Object} connection Connection object that fired the event */ SMTPConnectionPool.prototype._onConnectionIdle = function(connection) { var message = this._messageQueue.shift(); if (message) { this._processMessage(message, connection); } else { for (var i = 0, len = this._connectionsInUse.length; i < len; i++) { if (this._connectionsInUse[i] == connection) { this._connectionsInUse.splice(i, 1); // remove from list break; } } this._connectionsAvailable.push(connection); } }; /** * <p>Will be called when not all recipients were accepted</p> * * @event * @param {Object} connection Connection object that fired the event * @param {Array} addresses Failed addresses as an array of strings */ SMTPConnectionPool.prototype._onConnectionRCPTFailed = function(connection, addresses) { if (connection.currentMessage) { connection.currentMessage.failedRecipients = addresses; } }; /** * <p>Will be called when the client is waiting for a message to deliver</p> * * @event * @param {Object} connection Connection object that fired the event */ SMTPConnectionPool.prototype._onConnectionMessage = function(connection) { if (connection.currentMessage) { connection.currentMessage.streamMessage(); connection.currentMessage.pipe(connection); } }; /** * <p>Will be called when a message has been delivered</p> * * @event * @param {Object} connection Connection object that fired the event * @param {Boolean} success True if the message was queued by the SMTP server * @param {String} message Last message received from the server */ SMTPConnectionPool.prototype._onConnectionReady = function(connection, success, message) { var error, responseObj = {}; if (connection._messagesProcessed >= this.options.maxMessages && connection.socket) { connection.emit('end'); connection.removeAllListeners(); if (connection.socket) { connection.socket.destroy(); } this.emit('released', connection); } if (connection.currentMessage && connection.currentMessage.returnCallback) { if (success) { if (connection.currentMessage.failedRecipients) { responseObj.failedRecipients = connection.currentMessage.failedRecipients; } if (message) { responseObj.message = message; } if (connection.currentMessage._messageId) { responseObj.messageId = connection.currentMessage._messageId; } connection.currentMessage.returnCallback(null, responseObj); } else { error = new Error('Message delivery failed' + (message ? ': ' + message : '')); error.name = 'DeliveryError'; error.data = message; connection.currentMessage.returnCallback(error); } } connection.currentMessage = false; }; /** * <p>Will be called when an error occurs</p> * * @event * @param {Object} connection Connection object that fired the event * @param {Object} error Error object */ SMTPConnectionPool.prototype._onConnectionError = function(connection, error) { var message = connection.currentMessage; connection.currentMessage = false; // clear a first message from the list, otherwise an infinite loop will emerge if (!message) { message = this._messageQueue.shift(); } if (message && message.returnCallback) { message.returnCallback(error); } }; /** * <p>Will be called when a connection to the client is closed</p> * * @event * @param {Object} connection Connection object that fired the event */ SMTPConnectionPool.prototype._onConnectionEnd = function(connection) { var removed = false, i, len; // if in 'available' list, remove for (i = 0, len = this._connectionsAvailable.length; i < len; i++) { if (this._connectionsAvailable[i] == connection) { this._connectionsAvailable.splice(i, 1); // remove from list removed = true; break; } } if (!removed) { // if in 'in use' list, remove for (i = 0, len = this._connectionsInUse.length; i < len; i++) { if (this._connectionsInUse[i] == connection) { this._connectionsInUse.splice(i, 1); // remove from list removed = true; break; } } } // if there's still unprocessed mail and available connection slots, create // a new connection if (this._messageQueue.length && this._connectionsInUse.length + this._connectionsAvailable.length < this.options.maxConnections) { this._createConnection(); } };