ngn-idk-core
Version:
490 lines (436 loc) • 12.1 kB
JavaScript
var Base = require('./RemoteServer'),
nodemailer = require('nodemailer');
/**
* @class NGN.core.MailTransport
* This is a wrapper around [nodemailer module](https://github.com/andris9/Nodemailer/) transports,
* which provide a wide set of features for interacting with different kinds of mail servers.
* @extends NGN.core.RemoteServer
* @private
*/
var Class = Base.extend({
/**
* @constructor
* Create a new mail server.
* @param {Object} config
*/
constructor: function(config){
config = config || {};
/**
* @cfg {String} type
* Automatically set to MAIL.
* @readonly
* @static
* @private
*/
config.type = 'SMTP';
config.purpose = 'Transport';
Class.super.constructor.call(this,config);
Object.defineProperties(this,{
/**
* @cfg {String} [service=null]
* The type of transport/server to create. Using this will ignore
* the #host and #port configuration properties.
*
* Several well-known mail services are supported. The
* [list of supported services](https://github.com/andris9/Nodemailer/#well-known-services-for-smtp)
* may change from time to time, but should only grow unless a service is discontinued.
* @private
*/
_service: {
value: config.service || null,
enumerable: false,
writable: true,
configurable:true
},
service: {
enumerable: true,
get: function(){return this._service;}
},
_mailtype: {
value: config.serverType || 'SMTP',
enumerable: false,
writable: true
},
/**
* @cfg {String} [serverType=SMTP]
* The type of mail server to use. Valid types include:
*
* * SMTP
* * SES (Amazon)
* * Sendmail (local)
*
*/
serverType: {
enumerable: true,
get: function(){ return this._mailtype; },
set: function(value){
switch(value.toString().trim().toLowerCase()){
case 'smtp':
case 'ses':
this._mailtype = value.trim().toUpperCase();
break;
case 'sendmail':
this._mailtype = value.trim().toLowerCase();
break;
default:
throw Error(value+' is not a valid Mail Transport. Only SMTP, SES, and Sendmail are supported.');
break;
}
}
},
/**
* @cfg {String} [host=localhost]
* Hostname or IP address of the server. This can be an SMTP server or a URL (ex: Amazon SES endpoint).
* Unnecessary if #_service is defined.
*/
/**
* @cfg {Number} [port=25]
* The port of the host server.
* Unnecessary if #service is defined.
*/
port: {
value: config.port || 25,
enumerable: true,
writable: true
},
/**
* @cfg {Boolean} [secureConnection=false]
* Use SSL. If #port is configured to `587`, this is ignored and forced to `false`.
* SMTP connections established on port 587 start in insecure plain text mode and
* are later upgraded with STARTTLS.
*/
secureConnection: {
value: this.port !== 587 ? NGN.coalesce(config.secureConnection,false) : false,
enumerable: true,
writable: true
},
/**
* @cfg {String} [name=<machine_name>]
* The name of the client server.
*/
name: {
value: config.name || null,
enumerable: true,
writable: true
},
/**
* @cfg {Object} [auth=null]
* An object like:
*
* {
* user: 'jdoe',
* pass: 'pwd1'
* }
* Alternatively, #XOAuth supports XOAuth tokens.
*/
auth: {
value: config.auth || null,
enumerable: true,
writable: true
},
_xoauth: {
value: {XOAuthToken:config.XOAuth || null},
enumerable: true,
writable: true
},
/**
* @cfg {String/Object}
* The XOAuth token string or a configuration oject for #generateXOAuthToken.
*/
xOAuth: {
enumerable: true,
//get: function(){return this._xoauth.XOAuthToken;},
set: function(value){
this._xoauth.XOAuthToken = (typeof value === 'object' ? this.generateXOAuthToken(value) : value);
}
},
/**
* @cfg {Boolean} [ignoreTLS=false]
* Ignore server support for STARTTLS.
*/
ignoreTLS: {
value: NGN.coalesce(config.ignoreTLS,false),
enumerable: true,
writable: true
},
/**
* @cfg {Boolean} [debug=false]
* Output client and server messages to the console.
*/
debug: {
value: NGN.coalesce(config.debug,false),
enumerable: true,
writable: true
},
/**
* @cfg {Number} [maxConnections=5]
* The number of connections to keep in the pool.
*/
maxConnections: {
value: config.maxConnections || 5,
enumerable: true,
writable: true
},
/**
* @cfg {String}
* AWS Access Key.
* Required when #type is `SES`.
*/
awsAccessKey: {
value: config.AWSAccessKey || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* AWS Secret Key.
* Required when #type is `SES`.
*/
awsSecret: {
value: config.AWSSecret || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String} [path=sendmail]
* Path to the `sendmail` command. Only used when #type is `Sendmail`.
*/
path: {
value: config.path || 'sendmail',
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {Array} [args=[]]
* An optional array of extra command line options to pass the #path. Only used when #type is `Sendmail`.
*
* **Example:** `["-f sender@example.com"]`
*/
args: {
value: config.args || [],
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* The domain name used for signing.
* Required when using DKIM.
*/
dkimDomainName: {
value: config.dkimDomainName || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* If you have set up a TXT record with DKIM public key at **zzz**._domainkey.example.com then `zzz` is the selector.
* If this is left blank, NGN will attempt to lookup the record using #dkimDomainName.
* Required when using DKIM.
*/
dkimKeySelector: {
value: config.dkimKeySelector || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* The path to the private key.
*
* For example, `/path/to/key.pem`.
*
* Required when using DKIM.
*/
dkimPrivateKey: {
value: config.dkimPrivateKey || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* Optional colon separated list of header fields to sign.
* By default all fields suggested by RFC4871 #5.5 are used.
*/
dkimHeaderFieldNames: {
value: config.dkimHeaderFieldNames || null,
enumerable: true,
writable: true,
configurable:true
},
/**
* @cfg {String}
* The X_MAILER_NAME attribute.
* Defaults to the nodemailer author.
*/
mailerName: {
value: config.mailerName || null,
enumerable: true,
writable: true
},
/**
* @cfg {String}
* The X_MAILER_HOMEPAGE attribute.
* Defaults to the nodemailer author's website.
*/
mailerUrl: {
value: config.mailerUrl || null,
enumerable: true,
writable: true
},
/**
* @property {Object}
* The raw transport object that is created when the object is constructed.
* @private
*/
_transport: {
value: null,
enumerable: true,
writable: true
}
});
},
/**
* @method
* Create the transport object & connect to the appropriate mail server.
* @private
*/
init: function(){
var options = {};
switch (this.serverType.toUpperCase()){
case 'SMTP':
// Configure Service-based Transport Options
if (this._service !== null){
options.service = this._service;
if (this.auth !== null)
options.auth = this.auth;
else if (this._xoauth !== null)
options.auth = this._xoauth;
} else {
options.host = this.host;
options.port = this.port;
options.secureConnection = this.secureConnection;
options.ignoreTLS = this.ignoreTLS;
options.debug = this.debug;
options.maxConnections = this.maxConnections;
if (this.name !== null)
options.name = this.name;
if (this.auth !== null)
options.auth = this.auth;
else if (this._xoauth !== null)
options.auth = this._xoauth;
}
break;
case 'SES':
options.AWSAccessKeyID = this.awsAccessKey;
options.AWSSecretKey = this.awsSecret;
if (this.host !== 'localhost')
options.ServiceUrl = this.host;
break;
case 'SENDMAIL':
options.path = this.path;
if (this.args.length > 0)
options.args = this.args;
break;
}
this._transport = nodemailer.createTransport(this.serverType,options);
// Support DKIM
if (this.dkimDomainName !== null){
if (this.dkimKeySelector !== null){
NGN.DNS.resolveTxt(this.dkimKeySelector+'._domainkey.'+this.dkimDomainName,function(err, addresses){
if (err) this.fireError(err);
if (addresses.length == 0)
this.fireError('Could not resolve DKIM key at '+this.dkimKeySelector+'._domainkey.'+this.dkimDomainName);
this.useDKIM();
});
} else {
console.log('DKIM lookup has not been implemented yet. Bug the author on Github to add it if you need it. Suggest the ndns module.');
this.useDKIM();
}
}
this.running = true;
},
/**
* @method
* Enable DKIM signing.
* @private
*/
useDKIM: function(){
var opt = {
domainName: this.dkimDomainName,
keySelector: this.dkimKeySelector,
privateKey: require('fs').readFileSync(this.dkimPrivateKey) //TODO: Make it possible to use a relative path.
};
if (this.dkimHeaderFieldNames !== null)
opt.headerFieldNames = this.dkimHeaderFieldNames;
this._transport.useDKIM(opt);
},
/**
* @method
* Generate an XOAuth token.
*
* **Gmail Example** (Other options commented out)
*
* {
* user: "test.nodemailer@gmail.com",
* // requestUrl: "https://oauth.access.point",
* // consumerKey: "anonymous",
* // consumerSecret: "anonymous",
* token: "1/O_HgoO4h2uOUfpus0V--7mygICXrQQ0ZajB3ZH52KqM",
* tokenSecret: "_mUBkIwNPnfQBUIWrJrpXJ0c"
* }
*
* @param {Object} config
* A JSON object containing configuration data.
*/
generateXOAuthToken: function(config){
config = config || {};
return nodemailer.createXOAuthGenerator(config);
},
/**
* @method
* Terminate pooled connections. Only used when #mailtype is `SMTP`.
*/
close: function(){
if (this.running && this.serverType == 'SMTP') {
this._transport.close();
}
},
/**
* @method
* Send an email message
* @param {NGN.mail.Message/Object} message
* This can be a valid NGN.mail.Message or a direct JSON object.
* @param {Function} [callback]
* Optional callback.
*/
send: function(message,callback){
message = message || {};
var me = this;
//TODO: extract the attributes from a message object if that's what is passed in.
this._transport.sendMail(message,function(error, response){
//TODO: Wrap this in a proper logger.
if(error){
me.emit('error',error);
}else{
me.onSent(response.message);
callback(response.message);
}
});
},
/**
* @event sent
* Fired when a message has been sent.
* @param {Object} id
* The ID of the message sent.
*/
onSent: function(id){
this.emit('sent',id);
}
});
module.exports = Class;