diff options
Diffstat (limited to 'tools/node_modules/nodemailer/lib/engines/ses.js')
-rw-r--r-- | tools/node_modules/nodemailer/lib/engines/ses.js | 217 |
1 files changed, 217 insertions, 0 deletions
diff --git a/tools/node_modules/nodemailer/lib/engines/ses.js b/tools/node_modules/nodemailer/lib/engines/ses.js new file mode 100644 index 0000000..4766c4f --- /dev/null +++ b/tools/node_modules/nodemailer/lib/engines/ses.js @@ -0,0 +1,217 @@ +/* + * This file is based on the original SES module for Nodemailer by dfellis + * https://github.com/andris9/Nodemailer/blob/11fb3ef560b87e1c25e8bc15c2179df5647ea6f5/lib/engines/SES.js + */ + +// NB! Amazon SES does not allow unicode filenames on attachments! + +var http = require('http'), + https = require('https'), + crypto = require('crypto'), + urllib = require("url"); + +// Expose to the world +module.exports = SESTransport; + +/** + * <p>Generates a Transport object for Amazon SES</p> + * + * <p>Possible options can be the following:</p> + * + * <ul> + * <li><b>AWSAccessKeyID</b> - AWS access key (required)</li> + * <li><b>AWSSecretKey</b> - AWS secret (required)</li> + * <li><b>ServiceUrl</b> - optional API endpoint URL (defaults to <code>"https://email.us-east-1.amazonaws.com"</code>) + * </ul> + * + * @constructor + * @param {Object} options Options object for the SES transport + */ +function SESTransport(options){ + this.options = options || {}; + + //Set defaults if necessary + this.options.ServiceUrl = this.options.ServiceUrl || "https://email.us-east-1.amazonaws.com"; +} + +/** + * <p>Compiles a mailcomposer message and forwards it to handler that sends it.</p> + * + * @param {Object} emailMessage MailComposer object + * @param {Function} callback Callback function to run when the sending is completed + */ +SESTransport.prototype.sendMail = function(emailMessage, callback) { + + // SES strips this header line by itself + emailMessage.options.keepBcc = true; + + //Check if required config settings set + if(!this.options.AWSAccessKeyID || !this.options.AWSSecretKey) { + return callback(new Error("Missing AWS Credentials")); + } + + this.generateMessage(emailMessage, (function(err, email){ + if(err){ + return typeof callback == "function" && callback(err); + } + this.handleMessage(email, callback); + }).bind(this)); +}; + +/** + * <p>Compiles and sends the request to SES with e-mail data</p> + * + * @param {String} email Compiled raw e-mail as a string + * @param {Function} callback Callback function to run once the message has been sent + */ +SESTransport.prototype.handleMessage = function(email, callback) { + var request, + + date = new Date(), + + urlparts = urllib.parse(this.options.ServiceUrl), + + params = this.buildKeyValPairs({ + 'Action': 'SendRawEmail', + 'RawMessage.Data': (new Buffer(email, "utf-8")).toString('base64'), + 'Version': '2010-12-01', + 'Timestamp': this.ISODateString(date) + }), + + reqObj = { + host: urlparts.hostname, + path: urlparts.path || "/", + method: "POST", + headers: { + 'Content-Type': 'application/x-www-form-urlencoded', + 'Content-Length': params.length, + 'Date': date.toUTCString(), + 'X-Amzn-Authorization': + ['AWS3-HTTPS AWSAccessKeyID='+this.options.AWSAccessKeyID, + "Signature="+this.buildSignature(date.toUTCString(), this.options.AWSSecretKey), + "Algorithm=HmacSHA256"].join(",") + } + }; + + //Execute the request on the correct protocol + if(urlparts.protocol.substr() == "https:") { + request = https.request(reqObj, this.responseHandler.bind(this, callback)); + } else { + request = http.request(reqObj, this.responseHandler.bind(this, callback)); + } + request.end(params); +}; + +/** + * <p>Handles the response for the HTTP request to SES</p> + * + * @param {Function} callback Callback function to run on end (binded) + * @param {Object} response HTTP Response object + */ +SESTransport.prototype.responseHandler = function(callback, response) { + var body = ""; + response.setEncoding('utf8'); + + //Re-assembles response data + response.on('data', function(d) { + body += d.toString(); + }); + + //Performs error handling and executes callback, if it exists + response.on('end', function(err) { + if(err instanceof Error) { + return typeof callback == "function" && callback(err, null); + } + if(response.statusCode != 200) { + return typeof callback == "function" && + callback(new Error('Email failed: ' + response.statusCode + '\n' + body), null); + } + return typeof callback == "function" && callback(null, { + message: body + }); + }); +}; + +/** + * <p>Compiles the messagecomposer object to a string.</p> + * + * <p>It really sucks but I don't know a good way to stream a POST request with + * unknown legth, so the message needs to be fully composed as a string.</p> + * + * @param {Object} emailMessage MailComposer object + * @param {Function} callback Callback function to run once the message has been compiled + */ + +SESTransport.prototype.generateMessage = function(emailMessage, callback) { + var email = ""; + + emailMessage.on("data", function(chunk){ + email += (chunk || "").toString("utf-8"); + }); + + emailMessage.on("end", function(chunk){ + email += (chunk || "").toString("utf-8"); + callback(null, email); + }); + + emailMessage.streamMessage(); +}; + +/** + * <p>Converts an object into a Array with "key=value" values</p> + * + * @param {Object} config Object with keys and values + * @return {Array} Array of key-value pairs + */ +SESTransport.prototype.buildKeyValPairs = function(config){ + var keys = Object.keys(config).sort(), + keyValPairs = [], + key, i, len; + + for(i=0, len = keys.length; i < len; i++) { + key = keys[i]; + if(key != "ServiceUrl") { + keyValPairs.push((encodeURIComponent(key) + "=" + encodeURIComponent(config[key]))); + } + } + + return keyValPairs.join("&"); +}; + +/** + * <p>Uses SHA-256 HMAC with AWS key on date string to generate a signature</p> + * + * @param {String} date ISO UTC date string + * @param {String} AWSSecretKey ASW secret key + */ +SESTransport.prototype.buildSignature = function(date, AWSSecretKey) { + var sha256 = crypto.createHmac('sha256', AWSSecretKey); + sha256.update(date); + return sha256.digest('base64'); +}; + +/** + * <p>Generates an UTC string in the format of YYY-MM-DDTHH:MM:SSZ</p> + * + * @param {Date} d Date object + * @return {String} Date string + */ +SESTransport.prototype.ISODateString = function(d){ + return d.getUTCFullYear() + '-' + + this.strPad(d.getUTCMonth()+1) + '-' + + this.strPad(d.getUTCDate()) + 'T' + + this.strPad(d.getUTCHours()) + ':' + + this.strPad(d.getUTCMinutes()) + ':' + + this.strPad(d.getUTCSeconds()) + 'Z'; +}; + +/** + * <p>Simple padding function. If the number is below 10, add a zero</p> + * + * @param {Number} n Number to pad with 0 + * @return {String} 0 padded number + */ +SESTransport.prototype.strPad = function(n){ + return n<10 ? '0'+n : n; +}; + |