mirror of
https://github.com/mgerb/mywebsite
synced 2026-01-13 11:12:47 +00:00
updated bunch of file paths and changed the way posts are loaded
This commit is contained in:
441
node_modules/nodemailer-direct-transport/src/direct-transport.js
generated
vendored
Normal file
441
node_modules/nodemailer-direct-transport/src/direct-transport.js
generated
vendored
Normal file
@@ -0,0 +1,441 @@
|
||||
'use strict';
|
||||
|
||||
var createQueue = require('./message-queue');
|
||||
var SMTPConnection = require('smtp-connection');
|
||||
var dns = require('dns');
|
||||
var net = require('net');
|
||||
var os = require('os');
|
||||
var util = require('util');
|
||||
var packageData = require('../package.json');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
// Expose to the world
|
||||
module.exports = function(options) {
|
||||
return new DirectMailer(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new DirectMailer instance. Provides method 'send' to queue
|
||||
* outgoing e-mails. The queue is processed in the background.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} [options] Optional options object
|
||||
*/
|
||||
function DirectMailer(options) {
|
||||
EventEmitter.call(this);
|
||||
this._options = options || {};
|
||||
this._queue = createQueue();
|
||||
this._started = false;
|
||||
this._lastId = 0;
|
||||
|
||||
// temporary object
|
||||
var connection = new SMTPConnection({});
|
||||
|
||||
this.name = 'SMTP (direct)';
|
||||
this.version = packageData.version + '[client:' + connection.version + ']';
|
||||
}
|
||||
util.inherits(DirectMailer, EventEmitter);
|
||||
|
||||
// Adds a dynamic property 'length'
|
||||
Object.defineProperty(DirectMailer.prototype, 'length', {
|
||||
get: function() {
|
||||
return this._queue._instantQueue.length + this._queue._sortedQueue.length;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Adds an outgoing message to the queue. Recipient addresses are sorted
|
||||
* by the receiving domain and for every domain, a copy of the message is queued.
|
||||
*
|
||||
* If input is deemed invalid, an error is thrown, so be ready to catch these
|
||||
* when calling directmail.send(...)
|
||||
*
|
||||
* @param {Object} mail Mail object
|
||||
* @param {Function} callback Callback function
|
||||
*/
|
||||
DirectMailer.prototype.send = function(mail, callback) {
|
||||
|
||||
var envelope = mail.data.envelope || mail.message.getEnvelope();
|
||||
var domainEnvelopes = {};
|
||||
|
||||
if (!envelope.from) {
|
||||
return callback(new Error('"From" address missing'));
|
||||
}
|
||||
|
||||
envelope.to = [].concat(envelope.to || []);
|
||||
|
||||
if (!envelope.to.length) {
|
||||
return callback('"Recipients" addresses missing');
|
||||
}
|
||||
|
||||
// We cant't run existing streams more than once so we need to change these
|
||||
// to buffers. Filenames, URLs etc are not affected – for every
|
||||
// message copy a new file stream will be created
|
||||
this._clearStreams(mail, function() {
|
||||
|
||||
this._formatMessage(mail.message);
|
||||
|
||||
envelope.to.forEach(function(recipient) {
|
||||
recipient = (recipient || '').toString();
|
||||
|
||||
var domain = (recipient.split('@').pop() || '').toLowerCase().trim();
|
||||
|
||||
if (!domainEnvelopes[domain]) {
|
||||
domainEnvelopes[domain] = {
|
||||
from: envelope.from,
|
||||
to: [recipient]
|
||||
};
|
||||
} else if (domainEnvelopes[domain].to.indexOf(recipient) < 0) {
|
||||
domainEnvelopes[domain].to.push(recipient);
|
||||
}
|
||||
});
|
||||
|
||||
var returned = 0;
|
||||
var domains = Object.keys(domainEnvelopes);
|
||||
var combinedInfo = {
|
||||
accepted: [],
|
||||
rejected: [],
|
||||
pending: [],
|
||||
errors: [],
|
||||
envelope: mail.data.envelope || mail.message.getEnvelope()
|
||||
};
|
||||
|
||||
domains.forEach((function(domain) {
|
||||
var called = false;
|
||||
var id = ++this._lastId;
|
||||
var item = {
|
||||
envelope: domainEnvelopes[domain],
|
||||
data: mail.data,
|
||||
message: mail.message,
|
||||
domain: domain,
|
||||
id: id,
|
||||
callback: function(err, info) {
|
||||
if (called) {
|
||||
this._log('Callback for #%s already called. Updated values: %s', id, JSON.stringify(err || info));
|
||||
return;
|
||||
}
|
||||
|
||||
called = true;
|
||||
returned++;
|
||||
|
||||
if (err) {
|
||||
combinedInfo.errors.push(err);
|
||||
if (err.recipients) {
|
||||
combinedInfo.rejected = combinedInfo.rejected.concat(err.recipients || []);
|
||||
}
|
||||
} else if (info) {
|
||||
combinedInfo.accepted = combinedInfo.accepted.concat(info.accepted || []);
|
||||
combinedInfo.rejected = combinedInfo.rejected.concat(info.rejected || []);
|
||||
combinedInfo.pending = combinedInfo.pending.concat(info.pending || []);
|
||||
combinedInfo.messageId = info.messageId;
|
||||
}
|
||||
|
||||
if (returned >= domains.length) {
|
||||
if (combinedInfo.errors.length === domains.length) {
|
||||
var error = new Error('Sending failed');
|
||||
error.errors = combinedInfo.errors;
|
||||
callback(error);
|
||||
} else {
|
||||
callback(null, combinedInfo);
|
||||
}
|
||||
}
|
||||
}.bind(this)
|
||||
};
|
||||
|
||||
this._queue.insert(item);
|
||||
}).bind(this));
|
||||
|
||||
// start send loop if needed
|
||||
if (!this._started) {
|
||||
this._started = true;
|
||||
|
||||
// do not start the loop before current execution context is finished
|
||||
setImmediate(this._loop.bind(this));
|
||||
}
|
||||
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Emit a log event
|
||||
*/
|
||||
DirectMailer.prototype._log = function( /*str, arg1, arg2, ..., argN*/ ) {
|
||||
if (!this._options.debug) {
|
||||
return;
|
||||
}
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
this.emit('log', {
|
||||
type: 'direct',
|
||||
message: util.format.apply(util, args)
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Looping function to fetch a message from the queue and send it.
|
||||
*/
|
||||
DirectMailer.prototype._loop = function() {
|
||||
|
||||
// callback is fired when a message is added to the queue
|
||||
this._queue.get((function(data) {
|
||||
|
||||
this._log('Retrieved message #%s from the queue, resolving %s', data.id, data.domain);
|
||||
|
||||
// Resolve destination MX server
|
||||
this._resolveMx(data.domain, (function(err, list) {
|
||||
|
||||
if (err) {
|
||||
this._log('Resolving %s for #%s failed', data.domain, data.id);
|
||||
this._log(err);
|
||||
} else if (!list || !list.length) {
|
||||
this._log('Could not resolve any MX servers for %s', data.domain);
|
||||
}
|
||||
if (err || !list || !list.length) {
|
||||
data.callback(err || new Error('Could not resolve MX for ' + data.domain));
|
||||
return setImmediate(this._loop.bind(this));
|
||||
}
|
||||
|
||||
// Sort MX list by priority field
|
||||
list.sort(function(a, b) {
|
||||
return (a && a.priority || 0) - (b && b.priority || 0);
|
||||
});
|
||||
|
||||
// Use the first server on the list
|
||||
var exchange = list[0] && list[0].exchange;
|
||||
|
||||
this._log('%s resolved to %s for #%s', data.domain, exchange, data.id);
|
||||
|
||||
// Try to send the message
|
||||
this._process(exchange, data, (function(err, response) {
|
||||
if (err) {
|
||||
this._log('Failed processing message #%s', data.id);
|
||||
} else {
|
||||
this._log('Server responded for #%s: %s', data.id, JSON.stringify(response));
|
||||
}
|
||||
|
||||
if (err) {
|
||||
if (err.responseCode && err.responseCode >= 500) {
|
||||
err.domain = data.domain;
|
||||
err.exchange = exchange;
|
||||
err.recipients = data.envelope.to;
|
||||
data.callback(err);
|
||||
} else {
|
||||
data.replies = (data.replies || 0) + 1;
|
||||
if (data.replies <= 5) {
|
||||
this._queue.insert(data, this._options.retryDelay || data.replies * 15 * 60 * 1000);
|
||||
this._log('Message #%s requeued', data.id);
|
||||
data.callback(null, {
|
||||
pending: {
|
||||
domain: data.domain,
|
||||
exchange: exchange,
|
||||
recipients: data.envelope.to,
|
||||
response: err.response
|
||||
}
|
||||
});
|
||||
} else {
|
||||
err.domain = data.domain;
|
||||
err.exchange = exchange;
|
||||
err.recipients = data.envelope.to;
|
||||
data.callback(err);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data.callback(null, response);
|
||||
}
|
||||
|
||||
setImmediate(this._loop.bind(this));
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
}).bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Sends a message to provided MX server
|
||||
*
|
||||
* @param {String} exchange MX server
|
||||
* @param {Object} data Message object
|
||||
* @param {Function} callback Callback to run once the message is either sent or sending fails
|
||||
*/
|
||||
DirectMailer.prototype._process = function(exchange, data, callback) {
|
||||
this._log('Connecting to %s:%s for message #%s', exchange, this._options.port || 25, data.id);
|
||||
|
||||
var options = {
|
||||
host: exchange,
|
||||
port: this._options.port || 25,
|
||||
ignoreTLS: true
|
||||
};
|
||||
|
||||
// Add options from DirectMailer options to simplesmtp client
|
||||
Object.keys(this._options).forEach((function(key) {
|
||||
options[key] = this._options[key];
|
||||
}).bind(this));
|
||||
|
||||
var connection = new SMTPConnection(options);
|
||||
var returned = false;
|
||||
|
||||
connection.on('log', function(log) {
|
||||
this.emit('log', log);
|
||||
}.bind(this));
|
||||
|
||||
connection.once('error', function(err) {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
returned = true;
|
||||
return callback(err);
|
||||
});
|
||||
|
||||
var sendMessage = function() {
|
||||
connection.send(data.envelope, data.message.createReadStream(), function(err, info) {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
returned = true;
|
||||
|
||||
connection.close();
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
info.messageId = (data.message.getHeader('message-id') || '').replace(/[<>\s]/g, '');
|
||||
return callback(null, info);
|
||||
});
|
||||
};
|
||||
|
||||
connection.connect(function() {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
sendMessage();
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds additional headers to the outgoing message
|
||||
*
|
||||
* @param {Object} message BuildMail message object
|
||||
*/
|
||||
DirectMailer.prototype._formatMessage = function(message) {
|
||||
var hostname = this._resolveHostname(this._options.name);
|
||||
|
||||
// set the first header as 'Received:'
|
||||
message._headers.unshift({
|
||||
key: 'Received',
|
||||
value: 'from localhost (127.0.0.1) by ' + hostname + ' with SMTP; ' + Date()
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Detects stream objects and resolves these to buffers before sending. File paths,
|
||||
* urls etc. are not affected.
|
||||
*
|
||||
* @param {Object} message BuildMail message object
|
||||
* @param {Function} callback Callback to run
|
||||
*/
|
||||
DirectMailer.prototype._clearStreams = function(mail, callback) {
|
||||
var streamNodes = [];
|
||||
|
||||
function walkNode(node) {
|
||||
if (node.content && typeof node.content.pipe === 'function') {
|
||||
streamNodes.push(node);
|
||||
}
|
||||
if (node.childNodes && node.childNodes.length) {
|
||||
node.childNodes.forEach(walkNode);
|
||||
}
|
||||
}
|
||||
walkNode(mail.message);
|
||||
|
||||
function resolveNodes() {
|
||||
if (!streamNodes.length) {
|
||||
return callback();
|
||||
}
|
||||
var node = streamNodes.shift();
|
||||
|
||||
mail.resolveContent(node, 'content', function(err) {
|
||||
if (err) {
|
||||
node.content = new Buffer('<' + err.message + '>');
|
||||
}
|
||||
setImmediate(resolveNodes);
|
||||
});
|
||||
}
|
||||
|
||||
resolveNodes();
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves MX server for a domain
|
||||
*
|
||||
* @param {String} domain Domain to resolve the MX to
|
||||
* @param {Function} callback Callback function to run
|
||||
*/
|
||||
DirectMailer.prototype._resolveMx = function(domain, callback) {
|
||||
domain = domain.replace(/[\[\]]/g, '');
|
||||
|
||||
// Do not try to resolve the domain name if it is an IP address
|
||||
if (net.isIP(domain)) {
|
||||
return callback(null, [{
|
||||
'priority': 0,
|
||||
'exchange': domain
|
||||
}]);
|
||||
}
|
||||
|
||||
dns.resolveMx(domain, function(err, list) {
|
||||
if (err) {
|
||||
if (err.code === 'ENODATA') {
|
||||
// fallback to A
|
||||
dns.resolve4(domain, function(err, list) {
|
||||
if (err) {
|
||||
if (err.code === 'ENODATA') {
|
||||
// fallback to AAAA
|
||||
dns.resolve6(domain, function(err, list) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
// return the first resolved Ipv6 with priority 0
|
||||
return callback(null, [].concat(list || []).map(function(entry) {
|
||||
return {
|
||||
'priority': 0,
|
||||
'exchange': entry
|
||||
};
|
||||
}).slice(0, 1));
|
||||
});
|
||||
} else {
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// return the first resolved Ipv4 with priority 0
|
||||
return callback(null, [].concat(list || []).map(function(entry) {
|
||||
return {
|
||||
'priority': 0,
|
||||
'exchange': entry
|
||||
};
|
||||
}).slice(0, 1));
|
||||
});
|
||||
} else {
|
||||
callback(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
callback(null, list);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves current hostname. If resolved name is an IP address, uses 'localhost'.
|
||||
*
|
||||
* @param {String} [name] Preferred hostname
|
||||
* @return {String} Resolved hostname
|
||||
*/
|
||||
DirectMailer.prototype._resolveHostname = function(name) {
|
||||
if (!name || net.isIP(name.replace(/[\[\]]/g, '').trim())) {
|
||||
name = (os.hostname && os.hostname()) || '';
|
||||
}
|
||||
|
||||
if (!name || net.isIP(name.replace(/[\[\]]/g, '').trim())) {
|
||||
name = 'localhost';
|
||||
}
|
||||
|
||||
return name.toLowerCase();
|
||||
};
|
||||
118
node_modules/nodemailer-direct-transport/src/message-queue.js
generated
vendored
Normal file
118
node_modules/nodemailer-direct-transport/src/message-queue.js
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
'use strict';
|
||||
|
||||
// expose to the world
|
||||
module.exports = function() {
|
||||
return new MessageQueue();
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a queue object
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function MessageQueue() {
|
||||
this._instantQueue = [];
|
||||
this._sortedQueue = [];
|
||||
this._shiftTimer = null;
|
||||
this._callbackQueue = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a callback to be run when something comes available from the queue
|
||||
*
|
||||
* @param {Function} callback Callback function to run with queue element as an argument
|
||||
*/
|
||||
MessageQueue.prototype.get = function(callback) {
|
||||
if (this._instantQueue.length) {
|
||||
callback(this._instantQueue.pop());
|
||||
} else {
|
||||
this._callbackQueue.unshift(callback);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds an element to the queue. If delay (ms) is set, the data will not be available before
|
||||
* specified delay has passed. Otherwise the data will be available for processing immediatelly.
|
||||
*
|
||||
* @param {Mixed} data Value to be queued
|
||||
* @param {Number} [delay] If set, delay the availability of the data by {delay} milliseconds
|
||||
*/
|
||||
MessageQueue.prototype.insert = function(data, delay) {
|
||||
var container, added = -1;
|
||||
if (typeof delay !== 'number') {
|
||||
this._instantQueue.unshift(data);
|
||||
this._processInsert();
|
||||
return true;
|
||||
} else {
|
||||
container = {
|
||||
data: data,
|
||||
available: Date.now() + delay
|
||||
};
|
||||
for (var i = 0, len = this._sortedQueue.length; i < len; i++) {
|
||||
if (this._sortedQueue[i].available >= container.available) {
|
||||
this._sortedQueue.splice(i, 0, container);
|
||||
added = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (added < 0) {
|
||||
this._sortedQueue.push(container);
|
||||
added = 0;
|
||||
}
|
||||
|
||||
if (added === 0) {
|
||||
this._updateShiftTimer();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clears previous timer and creates a new one (if needed) to process the element
|
||||
* in the queue that needs to be processed first.
|
||||
*/
|
||||
MessageQueue.prototype._updateShiftTimer = function() {
|
||||
var nextShift, now = Date.now();
|
||||
clearTimeout(this._shiftTimer);
|
||||
|
||||
if (!this._sortedQueue.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
nextShift = this._sortedQueue[0].available;
|
||||
|
||||
if (nextShift <= now) {
|
||||
this._shiftSorted();
|
||||
} else {
|
||||
setTimeout(this._shiftSorted.bind(this),
|
||||
// add +15ms to ensure that data is already available when the timer is fired
|
||||
this._sortedQueue[0].available - Date.now() + 15);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Moves an element from the delayed queue to the immediate queue if an elmenet
|
||||
* becomes avilable
|
||||
*/
|
||||
MessageQueue.prototype._shiftSorted = function() {
|
||||
var container;
|
||||
if (!this._sortedQueue.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (this._sortedQueue[0].available <= Date.now()) {
|
||||
container = this._sortedQueue.shift();
|
||||
this.insert(container.data);
|
||||
}
|
||||
|
||||
this._updateShiftTimer();
|
||||
};
|
||||
|
||||
/**
|
||||
* If data from a queue is available and a callback is set, run the callback
|
||||
* with available data
|
||||
*/
|
||||
MessageQueue.prototype._processInsert = function() {
|
||||
if (this._instantQueue.length && this._callbackQueue.length) {
|
||||
this._callbackQueue.pop()(this._instantQueue.pop());
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user