aboutsummaryrefslogtreecommitdiff
path: root/node_modules/nodemailer/lib
diff options
context:
space:
mode:
authornanalelfe <nargiza.nosirova@mail.utoronto.ca>2016-08-24 08:52:06 +0000
committernanalelfe <nargiza.nosirova@mail.utoronto.ca>2016-08-24 08:52:06 +0000
commitd96e93e81f98d18aa84f701b578f4239020652c3 (patch)
tree836059ba6a7c45ba549f7c860b741382c6ec1015 /node_modules/nodemailer/lib
parentba43bb22a514a5100ae65ebbdd2d9c5581b2c01d (diff)
Implemented the password reset function
Diffstat (limited to 'node_modules/nodemailer/lib')
-rw-r--r--node_modules/nodemailer/lib/http-proxy.js128
-rw-r--r--node_modules/nodemailer/lib/nodemailer.js409
-rw-r--r--node_modules/nodemailer/lib/template-sender.js129
3 files changed, 666 insertions, 0 deletions
diff --git a/node_modules/nodemailer/lib/http-proxy.js b/node_modules/nodemailer/lib/http-proxy.js
new file mode 100644
index 0000000..24279ba
--- /dev/null
+++ b/node_modules/nodemailer/lib/http-proxy.js
@@ -0,0 +1,128 @@
+'use strict';
+
+/**
+ * Minimal HTTP/S proxy client
+ */
+
+var net = require('net');
+var tls = require('tls');
+var urllib = require('url');
+
+module.exports = proxyConnect;
+
+/**
+ * Establishes proxied connection to destinationPort
+ *
+ * proxyConnect("http://localhost:3128/", 80, "google.com", function(err, socket){
+ * socket.write("GET / HTTP/1.0\r\n\r\n");
+ * });
+ *
+ * @param {String} proxyUrl proxy configuration, etg "http://proxy.host:3128/"
+ * @param {Number} destinationPort Port to open in destination host
+ * @param {String} destinationHost Destination hostname
+ * @param {Function} callback Callback to run with the rocket object once connection is established
+ */
+function proxyConnect(proxyUrl, destinationPort, destinationHost, callback) {
+ var proxy = urllib.parse(proxyUrl);
+
+ // create a socket connection to the proxy server
+ var options;
+ var connect;
+ var socket;
+
+ options = {
+ host: proxy.hostname,
+ port: Number(proxy.port) ? Number(proxy.port) : (proxy.protocol === 'https:' ? 443 : 80)
+ };
+
+ if (proxy.protocol === 'https:') {
+ // we can use untrusted proxies as long as we verify actual SMTP certificates
+ options.rejectUnauthorized = false;
+ connect = tls.connect.bind(tls);
+ } else {
+ connect = net.connect.bind(net);
+ }
+
+ // Error harness for initial connection. Once connection is established, the responsibility
+ // to handle errors is passed to whoever uses this socket
+ var finished = false;
+ var tempSocketErr = function (err) {
+ if (finished) {
+ return;
+ }
+ finished = true;
+ try {
+ socket.destroy();
+ } catch (E) {
+ // ignore
+ }
+ callback(err);
+ };
+
+ socket = connect(options, function () {
+ if (finished) {
+ return;
+ }
+
+ var reqHeaders = {
+ Host: destinationHost + ':' + destinationPort,
+ Connection: 'close'
+ };
+ if (proxy.auth) {
+ reqHeaders['Proxy-Authorization'] = 'Basic ' + new Buffer(proxy.auth).toString('base64');
+ }
+
+ socket.write(
+ // HTTP method
+ 'CONNECT ' + destinationHost + ':' + destinationPort + ' HTTP/1.1\r\n' +
+
+ // HTTP request headers
+ Object.keys(reqHeaders).map(function (key) {
+ return key + ': ' + reqHeaders[key];
+ }).join('\r\n') +
+
+ // End request
+ '\r\n\r\n');
+
+
+ var headers = '';
+ var onSocketData = function (chunk) {
+ var match;
+ var remainder;
+
+ if (finished) {
+ return;
+ }
+
+ headers += chunk.toString('binary');
+ if ((match = headers.match(/\r\n\r\n/))) {
+ socket.removeListener('data', onSocketData);
+ remainder = headers.substr(match.index + match[0].length);
+ headers = headers.substr(0, match.index);
+ if (remainder) {
+ socket.unshift(new Buffer(remainder, 'binary'));
+ }
+ // proxy connection is now established
+ finished = true;
+
+ socket.removeListener('error', tempSocketErr);
+
+ // check response code
+ match = headers.match(/^HTTP\/\d+\.\d+ (\d+)/i);
+ if (!match || (match[1] || '').charAt(0) !== '2') {
+ try {
+ socket.destroy();
+ } catch (E) {
+ // ignore
+ }
+ return callback(new Error('Invalid response from proxy' + (match && ': ' + match[1] || '')));
+ }
+
+ return callback(null, socket);
+ }
+ };
+ socket.on('data', onSocketData);
+ });
+
+ socket.once('error', tempSocketErr);
+}
diff --git a/node_modules/nodemailer/lib/nodemailer.js b/node_modules/nodemailer/lib/nodemailer.js
new file mode 100644
index 0000000..9f0267b
--- /dev/null
+++ b/node_modules/nodemailer/lib/nodemailer.js
@@ -0,0 +1,409 @@
+'use strict';
+
+var mailcomposer = require('mailcomposer');
+var EventEmitter = require('events').EventEmitter;
+var util = require('util');
+var shared = require('nodemailer-shared');
+var directTransport = require('nodemailer-direct-transport');
+var smtpTransport = require('nodemailer-smtp-transport');
+var smtpPoolTransport = require('nodemailer-smtp-pool');
+var templateSender = require('./template-sender');
+var packageData = require('../package.json');
+var httpProxy = require('./http-proxy');
+var Socks = require('socks');
+var urllib = require('url');
+
+// Export createTransport method
+module.exports.createTransport = function (transporter, defaults) {
+ var urlConfig;
+ var options;
+ var mailer;
+ var proxyUrl;
+
+ // if no transporter configuration is provided use direct as default
+ transporter = transporter || directTransport({
+ debug: true
+ });
+
+ if (
+ // provided transporter is a configuration object, not transporter plugin
+ (typeof transporter === 'object' && typeof transporter.send !== 'function') ||
+ // provided transporter looks like a connection url
+ (typeof transporter === 'string' && /^(smtps?|direct):/i.test(transporter))
+ ) {
+
+ if ((urlConfig = typeof transporter === 'string' ? transporter : transporter.url)) {
+ // parse a configuration URL into configuration options
+ options = shared.parseConnectionUrl(urlConfig);
+ } else {
+ options = transporter;
+ }
+
+ if (options.proxy && typeof options.proxy === 'string') {
+ proxyUrl = options.proxy;
+ }
+
+ if (options.transport && typeof options.transport === 'string') {
+ try {
+ transporter = require('nodemailer-' + (options.transport).toLowerCase() + '-transport')(options);
+ } catch (E) {
+ // if transporter loader fails, return an error when sending mail
+ transporter = {
+ send: function (mail, callback) {
+ var errmsg = 'Requested transport plugin "nodemailer-' + (options.transport).toLowerCase() + '-transport" could not be initiated';
+ var err = new Error(errmsg);
+ err.code = 'EINIT';
+ setImmediate(function () {
+ return callback(err);
+ });
+ }
+ };
+ }
+ } else if (options.direct) {
+ transporter = directTransport(options);
+ } else if (options.pool) {
+ transporter = smtpPoolTransport(options);
+ } else {
+ transporter = smtpTransport(options);
+ }
+ }
+
+ mailer = new Nodemailer(transporter, defaults);
+
+ if (proxyUrl) {
+ setupProxy(mailer, proxyUrl);
+ }
+
+ return mailer;
+};
+
+/**
+ * Sets up proxy handler for a Nodemailer object
+ *
+ * @param {Object} mailer Nodemailer instance to modify
+ * @param {String} proxyUrl Proxy configuration url
+ */
+function setupProxy(mailer, proxyUrl) {
+ var proxy = urllib.parse(proxyUrl);
+
+ // setup socket handler for the mailer object
+ mailer.getSocket = function (options, callback) {
+ switch (proxy.protocol) {
+
+ // Connect using a HTTP CONNECT method
+ case 'http:':
+ case 'https:':
+ httpProxy(proxy.href, options.port, options.host, function (err, socket) {
+ if (err) {
+ return callback(err);
+ }
+ return callback(null, {
+ connection: socket
+ });
+ });
+ return;
+
+ // Connect using a SOCKS4/5 proxy
+ case 'socks:':
+ case 'socks5:':
+ case 'socks4:':
+ case 'socks4a:':
+ Socks.createConnection({
+ proxy: {
+ ipaddress: proxy.hostname,
+ port: proxy.port,
+ type: Number(proxy.protocol.replace(/\D/g, '')) || 5
+ },
+ target: {
+ host: options.host,
+ port: options.port
+ },
+ command: 'connect',
+ authentication: !proxy.auth ? false : {
+ username: decodeURIComponent(proxy.auth.split(':').shift()),
+ password: decodeURIComponent(proxy.auth.split(':').pop())
+ }
+ }, function (err, socket) {
+ if (err) {
+ return callback(err);
+ }
+ return callback(null, {
+ connection: socket
+ });
+ });
+ return;
+ }
+
+ callback(new Error('Unknown proxy configuration'));
+ };
+}
+
+/**
+ * Creates an object for exposing the Nodemailer API
+ *
+ * @constructor
+ * @param {Object} transporter Transport object instance to pass the mails to
+ */
+function Nodemailer(transporter, defaults) {
+ EventEmitter.call(this);
+
+ this._defaults = defaults || {};
+
+ this._plugins = {
+ compile: [],
+ stream: []
+ };
+
+ this.transporter = transporter;
+ this.logger = this.transporter.logger || shared.getLogger({
+ logger: false
+ });
+
+ // setup emit handlers for the transporter
+ if (typeof transporter.on === 'function') {
+
+ // deprecated log interface
+ this.transporter.on('log', function (log) {
+ this.logger.debug('%s: %s', log.type, log.message);
+ }.bind(this));
+
+ // transporter errors
+ this.transporter.on('error', function (err) {
+ this.logger.error('Transport Error: %s', err.message);
+ this.emit('error', err);
+ }.bind(this));
+
+ // indicates if the sender has became idle
+ this.transporter.on('idle', function () {
+ var args = Array.prototype.slice.call(arguments);
+ args.unshift('idle');
+ this.emit.apply(this, args);
+ }.bind(this));
+ }
+}
+util.inherits(Nodemailer, EventEmitter);
+
+/**
+ * Creates a template based sender function
+ *
+ * @param {Object} templates Object with string values where key is a message field and value is a template
+ * @param {Object} defaults Optional default message fields
+ * @return {Function} E-mail sender
+ */
+Nodemailer.prototype.templateSender = function (templates, defaults) {
+ return templateSender(this, templates, defaults);
+};
+
+Nodemailer.prototype.use = function (step, plugin) {
+ step = (step || '').toString();
+ if (!this._plugins.hasOwnProperty(step)) {
+ this._plugins[step] = [plugin];
+ } else {
+ this._plugins[step].push(plugin);
+ }
+};
+
+/**
+ * Optional methods passed to the underlying transport object
+ */
+['close', 'isIdle', 'verify'].forEach(function (method) {
+ Nodemailer.prototype[method] = function ( /* possible arguments */ ) {
+ var args = Array.prototype.slice.call(arguments);
+ if (typeof this.transporter[method] === 'function') {
+ return this.transporter[method].apply(this.transporter, args);
+ } else {
+ return false;
+ }
+ };
+});
+
+/**
+ * Sends an email using the preselected transport object
+ *
+ * @param {Object} data E-data description
+ * @param {Function} callback Callback to run once the sending succeeded or failed
+ */
+Nodemailer.prototype.sendMail = function (data, callback) {
+ var promise;
+
+ if (!callback && typeof Promise === 'function') {
+ promise = new Promise(function (resolve, reject) {
+ callback = shared.callbackPromise(resolve, reject);
+ });
+ }
+
+ if (typeof this.getSocket === 'function') {
+ this.transporter.getSocket = this.getSocket.bind(this);
+ this.getSocket = false;
+ }
+
+ data = data || {};
+ data.headers = data.headers || {};
+ callback = callback || function () {};
+
+ // apply defaults
+ Object.keys(this._defaults).forEach(function (key) {
+ if (!(key in data)) {
+ data[key] = this._defaults[key];
+ } else if (key === 'headers') {
+ // headers is a special case. Allow setting individual default headers
+ Object.keys(this._defaults.headers || {}).forEach(function (key) {
+ if (!(key in data.headers)) {
+ data.headers[key] = this._defaults.headers[key];
+ }
+ }.bind(this));
+ }
+ }.bind(this));
+
+ var mail = {
+ data: data,
+ message: null,
+ resolveContent: shared.resolveContent
+ };
+
+ if (typeof this.transporter === 'string') {
+ callback(new Error('Unsupported configuration, downgrade Nodemailer to v0.7.1 to use it'));
+ return promise;
+ }
+
+ this.logger.info('Sending mail using %s/%s', this.transporter.name, this.transporter.version);
+
+ this._processPlugins('compile', mail, function (err) {
+ if (err) {
+ this.logger.error('PluginCompile Error: %s', err.message);
+ return callback(err);
+ }
+
+ mail.message = mailcomposer(mail.data);
+
+ if (mail.data.xMailer !== false) {
+ mail.message.setHeader('X-Mailer', mail.data.xMailer || this._getVersionString());
+ }
+
+ if (mail.data.priority) {
+ switch ((mail.data.priority || '').toString().toLowerCase()) {
+ case 'high':
+ mail.message.setHeader('X-Priority', '1 (Highest)');
+ mail.message.setHeader('X-MSMail-Priority', 'High');
+ mail.message.setHeader('Importance', 'High');
+ break;
+ case 'low':
+ mail.message.setHeader('X-Priority', '5 (Lowest)');
+ mail.message.setHeader('X-MSMail-Priority', 'Low');
+ mail.message.setHeader('Importance', 'Low');
+ break;
+ default:
+ // do not add anything, since all messages are 'Normal' by default
+ }
+ }
+
+ // add optional List-* headers
+ if (mail.data.list && typeof mail.data.list === 'object') {
+ this._getListHeaders(mail.data.list).forEach(function (listHeader) {
+ listHeader.value.forEach(function (value) {
+ mail.message.addHeader(listHeader.key, value);
+ });
+ });
+ }
+
+ this._processPlugins('stream', mail, function (err) {
+ if (err) {
+ this.logger.error('PluginStream Error: %s', err.message);
+ return callback(err);
+ }
+
+ this.transporter.send(mail, function () {
+ var args = Array.prototype.slice.call(arguments);
+ if (args[0]) {
+ this.logger.error('Send Error: %s', args[0].message);
+ }
+ callback.apply(null, args);
+ }.bind(this));
+ }.bind(this));
+ }.bind(this));
+
+ return promise;
+};
+
+Nodemailer.prototype._getVersionString = function () {
+ return util.format(
+ '%s (%s; +%s; %s/%s)',
+ packageData.name,
+ packageData.version,
+ packageData.homepage,
+ this.transporter.name,
+ this.transporter.version
+ );
+};
+
+Nodemailer.prototype._processPlugins = function (step, mail, callback) {
+ step = (step || '').toString();
+
+ if (!this._plugins.hasOwnProperty(step) || !this._plugins[step].length) {
+ return callback(null);
+ }
+
+ var plugins = Array.prototype.slice.call(this._plugins[step]);
+
+ this.logger.debug('Using %s plugins for %s', plugins.length, step);
+
+ var processPlugins = function () {
+ if (!plugins.length) {
+ return callback(null);
+ }
+ var plugin = plugins.shift();
+ plugin(mail, function (err) {
+ if (err) {
+ return callback(err);
+ }
+ processPlugins();
+ });
+ }.bind(this);
+
+ processPlugins();
+};
+
+/**
+ * This method takes list headers structure and converts it into
+ * header list with key-value pairs
+ *
+ * @param {Object} listData Structured List-* headers
+ * @return {Array} An array of headers
+ */
+Nodemailer.prototype._getListHeaders = function (listData) {
+ // make sure an url looks like <protocol:url>
+ var formatListUrl = function (url) {
+ url = url.replace(/[\s<]+|[\s>]+/g, '');
+ if (/^(https?|mailto|ftp):/.test(url)) {
+ return '<' + url + '>';
+ }
+ if (/^[^@]+@[^@]+$/.test(url)) {
+ return '<mailto:' + url + '>';
+ }
+
+ return '<http://' + url + '>';
+ };
+
+ return Object.keys(listData).map(function (key) {
+ return {
+ key: 'list-' + key.toLowerCase().trim(),
+ value: [].concat(listData[key] || []).map(function (value) {
+ if (typeof value === 'string') {
+ return formatListUrl(value);
+ }
+ return {
+ prepared: true,
+ value: [].concat(value || []).map(function (value) {
+ if (typeof value === 'string') {
+ return formatListUrl(value);
+ }
+ if (value && value.url) {
+ return formatListUrl(value.url) + (value.comment ? ' (' + value.comment + ')' : '');
+ }
+ return '';
+ }).join(', ')
+ };
+ })
+ };
+ });
+};
diff --git a/node_modules/nodemailer/lib/template-sender.js b/node_modules/nodemailer/lib/template-sender.js
new file mode 100644
index 0000000..8963ea0
--- /dev/null
+++ b/node_modules/nodemailer/lib/template-sender.js
@@ -0,0 +1,129 @@
+'use strict';
+
+var shared = require('nodemailer-shared');
+
+module.exports = templateSender;
+
+// expose for testing
+module.exports.render = render;
+
+/**
+ * Create template based e-mail sender
+ *
+ * @param {Object} transport Nodemailer transport object to use for actual sending
+ * @param {Object} templates Either an object with template strings or an external renderer
+ * @param {Object} [defaults] Default fields set for every mail sent using this sender
+ * @return {Function} Template based sender
+ */
+function templateSender(transport, templates, defaults) {
+ templates = templates || {};
+ defaults = defaults || {};
+
+ // built in renderer
+ var defaultRenderer = function (context, callback) {
+ var rendered = {};
+ Object.keys(templates).forEach(function (key) {
+ rendered[key] = render(templates[key], {
+ escapeHtml: key === 'html'
+ }, context);
+ });
+ callback(null, rendered);
+ };
+
+ // actual renderer
+ var renderer = (typeof templates.render === 'function' ? templates.render.bind(templates) : defaultRenderer);
+
+ return function (fields, context, callback) {
+
+ var promise;
+
+ if (!callback && typeof Promise === 'function') {
+ promise = new Promise(function (resolve, reject) {
+ callback = shared.callbackPromise(resolve, reject);
+ });
+ }
+
+ // render data
+ renderer(context, function (err, rendered) {
+ if (err) {
+ return callback(err);
+ }
+ var mailData = mix(defaults, fields, rendered);
+ setImmediate(function () {
+ transport.sendMail(mailData, callback);
+ });
+ });
+
+ return promise;
+ };
+}
+
+/**
+ * Merges multiple objects into one. Assumes single level, except 'headers'
+ */
+function mix( /* obj1, obj2, ..., objN */ ) {
+ var args = Array.prototype.slice.call(arguments);
+ var result = {};
+
+ args.forEach(function (arg) {
+ Object.keys(arg || {}).forEach(function (key) {
+ if (key === 'headers') {
+ if (!result.headers) {
+ result.headers = {};
+ }
+ Object.keys(arg[key]).forEach(function (hKey) {
+ if (!(hKey in result.headers)) {
+ result.headers[hKey] = arg[key][hKey];
+ }
+ });
+ } else if (!(key in result)) {
+ result[key] = arg[key];
+ }
+ });
+ });
+
+ return result;
+}
+
+/**
+ * Renders a template string using provided context. Values are marked as {{key}} in the template.
+ *
+ * @param {String} str Template string
+ * @param {Object} options Render options. options.escapeHtml=true escapes html specific characters
+ * @param {Object} context Key-value pairs for the template, eg {name: 'User Name'}
+ * @return {String} Rendered template
+ */
+function render(str, options, context) {
+ str = (str || '').toString();
+ context = context || {};
+ options = options || {};
+
+ var re = /\{\{[ ]*([^{}\s]+)[ ]*\}\}/g;
+
+ return str.replace(re, function (match, key) {
+ var value;
+ if (context.hasOwnProperty(key)) {
+ value = context[key].toString();
+ if (options.escapeHtml) {
+ value = value.replace(/["'&<>]/g, function (char) {
+ switch (char) {
+ case '&':
+ return '&amp;';
+ case '<':
+ return '&lt;';
+ case '>':
+ return '&gt;';
+ case '"':
+ return '&quot;';
+ case '\'':
+ return '&#039;';
+ default:
+ return char;
+ }
+ });
+ }
+ return value;
+ }
+ return match;
+ });
+}