mirror of
https://github.com/dawidd6/action-send-mail.git
synced 2026-05-06 17:30:35 +07:00
node_modules: update (#290)
Co-authored-by: dawidd6 <9713907+dawidd6@users.noreply.github.com>
This commit is contained in:
332
node_modules/nodemailer/lib/smtp-connection/index.js
generated
vendored
332
node_modules/nodemailer/lib/smtp-connection/index.js
generated
vendored
@@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
|
||||
const packageInfo = require('../../package.json');
|
||||
const EventEmitter = require('events').EventEmitter;
|
||||
const { EventEmitter } = require('events');
|
||||
const net = require('net');
|
||||
const tls = require('tls');
|
||||
const os = require('os');
|
||||
const crypto = require('crypto');
|
||||
const DataStream = require('./data-stream');
|
||||
const PassThrough = require('stream').PassThrough;
|
||||
const { PassThrough } = require('stream');
|
||||
const shared = require('../shared');
|
||||
|
||||
// default timeout values in ms
|
||||
@@ -17,6 +17,28 @@ const GREETING_TIMEOUT = 30 * 1000; // how much to wait after connection is esta
|
||||
const DNS_TIMEOUT = 30 * 1000; // how much to wait for resolveHostname
|
||||
const TEARDOWN_NOOP = () => {}; // reusable no-op handler for absorbing errors during socket teardown
|
||||
|
||||
/**
|
||||
* Re-interpret a server response stored in fake 8-bit byte-container form
|
||||
* (the result of chunk.toString('binary') in _onData) as UTF-8.
|
||||
*
|
||||
* Server reply text has no formally defined charset (RFC 5321 §4.2.1), but
|
||||
* modern MTAs commonly use UTF-8. The byte-container plumbing in _onData is
|
||||
* required to reassemble multi-byte sequences split across socket chunks;
|
||||
* this helper performs the actual decode at the line boundary, falling back
|
||||
* to the byte-container form when the bytes are not valid UTF-8 so that
|
||||
* legacy 8-bit replies are still recoverable byte-for-byte.
|
||||
*/
|
||||
function decodeServerResponse(str) {
|
||||
if (!str) {
|
||||
return str;
|
||||
}
|
||||
const utf8 = Buffer.from(str, 'binary').toString('utf8');
|
||||
// The input is a byte container (each char is in U+0000..U+00FF) so it can never
|
||||
// already contain U+FFFD; any \uFFFD in the result was inserted by Node's UTF-8
|
||||
// decoder for invalid bytes, which means we should return the original bytes intact.
|
||||
return utf8.includes('\uFFFD') ? str : utf8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a SMTP connection object
|
||||
*
|
||||
@@ -68,7 +90,7 @@ class SMTPConnection extends EventEmitter {
|
||||
this.secureConnection = true;
|
||||
}
|
||||
|
||||
this.name = this.options.name || this._getHostname();
|
||||
this.name = (this.options.name || this._getHostname()).toString().replace(/[\r\n]+/g, '');
|
||||
|
||||
this.logger = shared.getLogger(this.options, {
|
||||
component: this.options.component || 'smtp-connection',
|
||||
@@ -76,13 +98,12 @@ class SMTPConnection extends EventEmitter {
|
||||
});
|
||||
|
||||
this.customAuth = new Map();
|
||||
Object.keys(this.options.customAuth || {}).forEach(key => {
|
||||
let mapKey = (key || '').toString().trim().toUpperCase();
|
||||
if (!mapKey) {
|
||||
return;
|
||||
for (const key of Object.keys(this.options.customAuth || {})) {
|
||||
const mapKey = (key || '').toString().trim().toUpperCase();
|
||||
if (mapKey) {
|
||||
this.customAuth.set(mapKey, this.options.customAuth[key]);
|
||||
}
|
||||
this.customAuth.set(mapKey, this.options.customAuth[key]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose version nr, just for the reference
|
||||
@@ -266,27 +287,7 @@ class SMTPConnection extends EventEmitter {
|
||||
} else if (this.options.socket) {
|
||||
// socket object is set up but not yet connected
|
||||
this._socket = this.options.socket;
|
||||
return shared.resolveHostname(opts, (err, resolved) => {
|
||||
if (err) {
|
||||
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
|
||||
}
|
||||
this.logger.debug(
|
||||
{
|
||||
tnx: 'dns',
|
||||
source: opts.host,
|
||||
resolved: resolved.host,
|
||||
cached: !!resolved.cached
|
||||
},
|
||||
'Resolved %s as %s [cache %s]',
|
||||
opts.host,
|
||||
resolved.host,
|
||||
resolved.cached ? 'hit' : 'miss'
|
||||
);
|
||||
Object.keys(resolved).forEach(key => {
|
||||
if (key.charAt(0) !== '_' && resolved[key]) {
|
||||
opts[key] = resolved[key];
|
||||
}
|
||||
});
|
||||
return this._resolveAndConnect(opts, _resolved => {
|
||||
try {
|
||||
this._socket.connect(this.port, this.host, () => {
|
||||
this._socket.setKeepAlive(true);
|
||||
@@ -297,80 +298,59 @@ class SMTPConnection extends EventEmitter {
|
||||
return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
|
||||
}
|
||||
});
|
||||
} else if (this.secureConnection) {
|
||||
// connect using tls
|
||||
if (this.options.tls) {
|
||||
Object.keys(this.options.tls).forEach(key => {
|
||||
opts[key] = this.options.tls[key];
|
||||
});
|
||||
}
|
||||
|
||||
// ensure servername for SNI
|
||||
if (this.servername && !opts.servername) {
|
||||
opts.servername = this.servername;
|
||||
}
|
||||
|
||||
return shared.resolveHostname(opts, (err, resolved) => {
|
||||
if (err) {
|
||||
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
|
||||
}
|
||||
this.logger.debug(
|
||||
{
|
||||
tnx: 'dns',
|
||||
source: opts.host,
|
||||
resolved: resolved.host,
|
||||
cached: !!resolved.cached
|
||||
},
|
||||
'Resolved %s as %s [cache %s]',
|
||||
opts.host,
|
||||
resolved.host,
|
||||
resolved.cached ? 'hit' : 'miss'
|
||||
);
|
||||
Object.keys(resolved).forEach(key => {
|
||||
if (key.charAt(0) !== '_' && resolved[key]) {
|
||||
opts[key] = resolved[key];
|
||||
}
|
||||
});
|
||||
|
||||
// Store fallback addresses for retry on connection failure
|
||||
this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
|
||||
this._connectOpts = Object.assign({}, opts);
|
||||
|
||||
this._connectToHost(opts, true);
|
||||
});
|
||||
} else {
|
||||
// connect using plaintext
|
||||
return shared.resolveHostname(opts, (err, resolved) => {
|
||||
if (err) {
|
||||
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
|
||||
}
|
||||
this.logger.debug(
|
||||
{
|
||||
tnx: 'dns',
|
||||
source: opts.host,
|
||||
resolved: resolved.host,
|
||||
cached: !!resolved.cached
|
||||
},
|
||||
'Resolved %s as %s [cache %s]',
|
||||
opts.host,
|
||||
resolved.host,
|
||||
resolved.cached ? 'hit' : 'miss'
|
||||
);
|
||||
Object.keys(resolved).forEach(key => {
|
||||
if (key.charAt(0) !== '_' && resolved[key]) {
|
||||
opts[key] = resolved[key];
|
||||
}
|
||||
});
|
||||
if (this.secureConnection) {
|
||||
Object.assign(opts, this.options.tls || {});
|
||||
|
||||
// ensure servername for SNI
|
||||
if (this.servername && !opts.servername) {
|
||||
opts.servername = this.servername;
|
||||
}
|
||||
}
|
||||
|
||||
return this._resolveAndConnect(opts, resolved => {
|
||||
// Store fallback addresses for retry on connection failure
|
||||
this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
|
||||
this._connectOpts = Object.assign({}, opts);
|
||||
|
||||
this._connectToHost(opts, false);
|
||||
this._connectToHost(opts, this.secureConnection);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolves the hostname and applies resolved values to opts,
|
||||
* then calls the provided callback with the resolved data
|
||||
*
|
||||
* @param {Object} opts Connection options (modified in place)
|
||||
* @param {Function} callback Called with resolved data on success
|
||||
*/
|
||||
_resolveAndConnect(opts, callback) {
|
||||
return shared.resolveHostname(opts, (err, resolved) => {
|
||||
if (err) {
|
||||
return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
|
||||
}
|
||||
this.logger.debug(
|
||||
{
|
||||
tnx: 'dns',
|
||||
source: opts.host,
|
||||
resolved: resolved.host,
|
||||
cached: !!resolved.cached
|
||||
},
|
||||
'Resolved %s as %s [cache %s]',
|
||||
opts.host,
|
||||
resolved.host,
|
||||
resolved.cached ? 'hit' : 'miss'
|
||||
);
|
||||
for (const key of Object.keys(resolved)) {
|
||||
if (key.charAt(0) !== '_' && resolved[key]) {
|
||||
opts[key] = resolved[key];
|
||||
}
|
||||
}
|
||||
callback(resolved);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to connect to the specified host address
|
||||
*
|
||||
@@ -381,7 +361,7 @@ class SMTPConnection extends EventEmitter {
|
||||
this._connectionAttemptId++;
|
||||
const currentAttemptId = this._connectionAttemptId;
|
||||
|
||||
let connectFn = secure ? tls.connect : net.connect;
|
||||
const connectFn = secure ? tls.connect : net.connect;
|
||||
try {
|
||||
this._socket = connectFn(opts, () => {
|
||||
// Ignore callback if this is a stale connection attempt
|
||||
@@ -418,7 +398,7 @@ class SMTPConnection extends EventEmitter {
|
||||
clearTimeout(this._connectionTimeout);
|
||||
|
||||
// Check if we have fallback addresses to try
|
||||
let canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
|
||||
const canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
|
||||
|
||||
if (!canFallback) {
|
||||
// No more fallback addresses, report the error
|
||||
@@ -426,7 +406,7 @@ class SMTPConnection extends EventEmitter {
|
||||
return;
|
||||
}
|
||||
|
||||
let nextHost = this._fallbackAddresses.shift();
|
||||
const nextHost = this._fallbackAddresses.shift();
|
||||
|
||||
this.logger.info(
|
||||
{
|
||||
@@ -478,12 +458,7 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
this._closing = true;
|
||||
|
||||
let closeMethod = 'end';
|
||||
|
||||
if (this.stage === 'init') {
|
||||
// Close the socket immediately when connection timed out
|
||||
closeMethod = 'destroy';
|
||||
}
|
||||
const closeMethod = this.stage === 'init' ? 'destroy' : 'end';
|
||||
|
||||
this.logger.debug(
|
||||
{
|
||||
@@ -493,7 +468,7 @@ class SMTPConnection extends EventEmitter {
|
||||
closeMethod
|
||||
);
|
||||
|
||||
let socket = (this._socket && this._socket.socket) || this._socket;
|
||||
const socket = (this._socket && this._socket.socket) || this._socket;
|
||||
|
||||
if (socket && !socket.destroyed) {
|
||||
try {
|
||||
@@ -551,11 +526,11 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this.customAuth.has(this._authMethod)) {
|
||||
let handler = this.customAuth.get(this._authMethod);
|
||||
const handler = this.customAuth.get(this._authMethod);
|
||||
let lastResponse;
|
||||
let returned = false;
|
||||
|
||||
let resolve = () => {
|
||||
const resolve = () => {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
@@ -574,7 +549,7 @@ class SMTPConnection extends EventEmitter {
|
||||
callback(null, true);
|
||||
};
|
||||
|
||||
let reject = err => {
|
||||
const reject = err => {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
@@ -582,7 +557,7 @@ class SMTPConnection extends EventEmitter {
|
||||
callback(this._formatError(err, 'EAUTH', lastResponse, 'AUTH ' + this._authMethod));
|
||||
};
|
||||
|
||||
let handlerResponse = handler({
|
||||
const handlerResponse = handler({
|
||||
auth: this._auth,
|
||||
method: this._authMethod,
|
||||
|
||||
@@ -709,7 +684,7 @@ class SMTPConnection extends EventEmitter {
|
||||
|
||||
// ensure that callback is only called once
|
||||
let returned = false;
|
||||
let callback = function () {
|
||||
const callback = function () {
|
||||
if (returned) {
|
||||
return;
|
||||
}
|
||||
@@ -722,11 +697,11 @@ class SMTPConnection extends EventEmitter {
|
||||
message.on('error', err => callback(this._formatError(err, 'ESTREAM', false, 'API')));
|
||||
}
|
||||
|
||||
let startTime = Date.now();
|
||||
const startTime = Date.now();
|
||||
this._setEnvelope(envelope, (err, info) => {
|
||||
if (err) {
|
||||
// create passthrough stream to consume to prevent OOM
|
||||
let stream = new PassThrough();
|
||||
const stream = new PassThrough();
|
||||
if (typeof message.pipe === 'function') {
|
||||
message.pipe(stream);
|
||||
} else {
|
||||
@@ -736,8 +711,8 @@ class SMTPConnection extends EventEmitter {
|
||||
|
||||
return callback(err);
|
||||
}
|
||||
let envelopeTime = Date.now();
|
||||
let stream = this._createSendStream((err, str) => {
|
||||
const envelopeTime = Date.now();
|
||||
const stream = this._createSendStream((err, str) => {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
@@ -921,7 +896,7 @@ class SMTPConnection extends EventEmitter {
|
||||
err.message += ': ' + response;
|
||||
}
|
||||
|
||||
let responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
|
||||
const responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
|
||||
if (responseCode) {
|
||||
err.responseCode = responseCode;
|
||||
}
|
||||
@@ -942,15 +917,15 @@ class SMTPConnection extends EventEmitter {
|
||||
let serverResponse = false;
|
||||
|
||||
if (this._remainder && this._remainder.trim()) {
|
||||
this.lastServerResponse = serverResponse = decodeServerResponse(this._remainder.trim());
|
||||
if (this.options.debug || this.options.transactionLog) {
|
||||
this.logger.debug(
|
||||
{
|
||||
tnx: 'server'
|
||||
},
|
||||
this._remainder.replace(/\r?\n$/, '')
|
||||
serverResponse
|
||||
);
|
||||
}
|
||||
this.lastServerResponse = serverResponse = this._remainder.trim();
|
||||
}
|
||||
|
||||
this.logger.info(
|
||||
@@ -1016,15 +991,14 @@ class SMTPConnection extends EventEmitter {
|
||||
this._socket.removeListener('data', this._onSocketData); // incoming data is going to be gibberish from this point onwards
|
||||
this._socket.removeListener('timeout', this._onSocketTimeout); // timeout will be re-set for the new socket object
|
||||
|
||||
let socketPlain = this._socket;
|
||||
let opts = {
|
||||
socket: this._socket,
|
||||
host: this.host
|
||||
};
|
||||
|
||||
Object.keys(this.options.tls || {}).forEach(key => {
|
||||
opts[key] = this.options.tls[key];
|
||||
});
|
||||
const socketPlain = this._socket;
|
||||
const opts = Object.assign(
|
||||
{
|
||||
socket: this._socket,
|
||||
host: this.host
|
||||
},
|
||||
this.options.tls || {}
|
||||
);
|
||||
|
||||
// ensure servername for SNI
|
||||
if (this.servername && !opts.servername) {
|
||||
@@ -1071,7 +1045,7 @@ class SMTPConnection extends EventEmitter {
|
||||
return false;
|
||||
}
|
||||
|
||||
let str = (this.lastServerResponse = (this._responseQueue.shift() || '').toString());
|
||||
let str = (this.lastServerResponse = decodeServerResponse((this._responseQueue.shift() || '').toString()));
|
||||
|
||||
if (/^\d+-/.test(str.split('\n').pop())) {
|
||||
// keep waiting for the final part of multiline response
|
||||
@@ -1092,7 +1066,7 @@ class SMTPConnection extends EventEmitter {
|
||||
setImmediate(() => this._processResponse());
|
||||
}
|
||||
|
||||
let action = this._responseActions.shift();
|
||||
const action = this._responseActions.shift();
|
||||
|
||||
if (typeof action === 'function') {
|
||||
action.call(this, str);
|
||||
@@ -1140,7 +1114,7 @@ class SMTPConnection extends EventEmitter {
|
||||
* {from:{address:'...',name:'...'}, to:[address:'...',name:'...']}
|
||||
*/
|
||||
_setEnvelope(envelope, callback) {
|
||||
let args = [];
|
||||
const args = [];
|
||||
let useSmtpUtf8 = false;
|
||||
|
||||
this._envelope = envelope || {};
|
||||
@@ -1175,7 +1149,7 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
// clone the recipients array for latter manipulation
|
||||
this._envelope.rcptQueue = JSON.parse(JSON.stringify(this._envelope.to || []));
|
||||
this._envelope.rcptQueue = [].concat(this._envelope.to || []);
|
||||
this._envelope.rejected = [];
|
||||
this._envelope.rejectedErrors = [];
|
||||
this._envelope.accepted = [];
|
||||
@@ -1207,7 +1181,10 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
if (this._envelope.size && this._supportedExtensions.includes('SIZE')) {
|
||||
args.push('SIZE=' + this._envelope.size);
|
||||
const sizeValue = Number(this._envelope.size) || 0;
|
||||
if (sizeValue > 0) {
|
||||
args.push('SIZE=' + sizeValue);
|
||||
}
|
||||
}
|
||||
|
||||
// If the server supports DSN and the envelope includes an DSN prop
|
||||
@@ -1260,7 +1237,7 @@ class SMTPConnection extends EventEmitter {
|
||||
throw new Error('ret: ' + JSON.stringify(ret));
|
||||
}
|
||||
|
||||
let envid = (params.envid || params.id || '').toString() || null;
|
||||
const envid = (params.envid || params.id || '').toString() || null;
|
||||
|
||||
let notify = params.notify || null;
|
||||
if (notify) {
|
||||
@@ -1268,8 +1245,8 @@ class SMTPConnection extends EventEmitter {
|
||||
notify = notify.split(',');
|
||||
}
|
||||
notify = notify.map(n => n.trim().toUpperCase());
|
||||
let validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
|
||||
let invalidNotify = notify.filter(n => !validNotify.includes(n));
|
||||
const validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
|
||||
const invalidNotify = notify.filter(n => !validNotify.includes(n));
|
||||
if (invalidNotify.length || (notify.length > 1 && notify.includes('NEVER'))) {
|
||||
throw new Error('notify: ' + JSON.stringify(notify.join(',')));
|
||||
}
|
||||
@@ -1290,7 +1267,7 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
_getDsnRcptToArgs() {
|
||||
let args = [];
|
||||
const args = [];
|
||||
// If the server supports DSN and the envelope includes an DSN prop
|
||||
// then append DSN params to the RCPT TO command
|
||||
if (this._envelope.dsn && this._supportedExtensions.includes('DSN')) {
|
||||
@@ -1305,12 +1282,11 @@ class SMTPConnection extends EventEmitter {
|
||||
}
|
||||
|
||||
_createSendStream(callback) {
|
||||
let dataStream = new DataStream();
|
||||
let logStream;
|
||||
const dataStream = new DataStream();
|
||||
|
||||
if (this.options.lmtp) {
|
||||
this._envelope.accepted.forEach((recipient, i) => {
|
||||
let final = i === this._envelope.accepted.length - 1;
|
||||
const final = i === this._envelope.accepted.length - 1;
|
||||
this._responseActions.push(str => {
|
||||
this._actionLMTPStream(recipient, final, str, callback);
|
||||
});
|
||||
@@ -1326,7 +1302,7 @@ class SMTPConnection extends EventEmitter {
|
||||
});
|
||||
|
||||
if (this.options.debug) {
|
||||
logStream = new PassThrough();
|
||||
const logStream = new PassThrough();
|
||||
logStream.on('readable', () => {
|
||||
let chunk;
|
||||
while ((chunk = logStream.read())) {
|
||||
@@ -1604,24 +1580,21 @@ class SMTPConnection extends EventEmitter {
|
||||
* @param {String} str Message from the server
|
||||
*/
|
||||
_actionAUTH_CRAM_MD5(str, callback) {
|
||||
let challengeMatch = str.match(/^334\s+(.+)$/);
|
||||
let challengeString = '';
|
||||
const challengeMatch = str.match(/^334\s+(.+)$/);
|
||||
|
||||
if (!challengeMatch) {
|
||||
return callback(
|
||||
this._formatError('Invalid login sequence while waiting for server challenge string', 'EAUTH', str, 'AUTH CRAM-MD5')
|
||||
);
|
||||
} else {
|
||||
challengeString = challengeMatch[1];
|
||||
}
|
||||
|
||||
// Decode from base64
|
||||
let base64decoded = Buffer.from(challengeString, 'base64').toString('ascii'),
|
||||
hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
|
||||
const base64decoded = Buffer.from(challengeMatch[1], 'base64').toString('ascii');
|
||||
const hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
|
||||
|
||||
hmacMD5.update(base64decoded);
|
||||
|
||||
let prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
|
||||
const prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
|
||||
|
||||
this._responseActions.push(str => {
|
||||
this._actionAUTH_CRAM_MD5_PASS(str, callback);
|
||||
@@ -1742,39 +1715,29 @@ class SMTPConnection extends EventEmitter {
|
||||
* @param {String} str Message from the server
|
||||
*/
|
||||
_actionMAIL(str, callback) {
|
||||
let message, curRecipient;
|
||||
if (Number(str.charAt(0)) !== 2) {
|
||||
if (this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)) {
|
||||
message = 'Internationalized mailbox name not allowed';
|
||||
} else {
|
||||
message = 'Mail command failed';
|
||||
}
|
||||
const message =
|
||||
this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)
|
||||
? 'Internationalized mailbox name not allowed'
|
||||
: 'Mail command failed';
|
||||
return callback(this._formatError(message, 'EENVELOPE', str, 'MAIL FROM'));
|
||||
}
|
||||
|
||||
if (!this._envelope.rcptQueue.length) {
|
||||
return callback(this._formatError("Can't send mail - no recipients defined", 'EENVELOPE', false, 'API'));
|
||||
} else {
|
||||
this._recipientQueue = [];
|
||||
|
||||
if (this._supportedExtensions.includes('PIPELINING')) {
|
||||
while (this._envelope.rcptQueue.length) {
|
||||
curRecipient = this._envelope.rcptQueue.shift();
|
||||
this._recipientQueue.push(curRecipient);
|
||||
this._responseActions.push(str => {
|
||||
this._actionRCPT(str, callback);
|
||||
});
|
||||
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
|
||||
}
|
||||
} else {
|
||||
curRecipient = this._envelope.rcptQueue.shift();
|
||||
this._recipientQueue.push(curRecipient);
|
||||
this._responseActions.push(str => {
|
||||
this._actionRCPT(str, callback);
|
||||
});
|
||||
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
|
||||
}
|
||||
}
|
||||
|
||||
this._recipientQueue = [];
|
||||
const usePipelining = this._supportedExtensions.includes('PIPELINING');
|
||||
|
||||
do {
|
||||
const curRecipient = this._envelope.rcptQueue.shift();
|
||||
this._recipientQueue.push(curRecipient);
|
||||
this._responseActions.push(str => {
|
||||
this._actionRCPT(str, callback);
|
||||
});
|
||||
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
|
||||
} while (usePipelining && this._envelope.rcptQueue.length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1783,16 +1746,14 @@ class SMTPConnection extends EventEmitter {
|
||||
* @param {String} str Message from the server
|
||||
*/
|
||||
_actionRCPT(str, callback) {
|
||||
let message,
|
||||
err,
|
||||
curRecipient = this._recipientQueue.shift();
|
||||
let err;
|
||||
const curRecipient = this._recipientQueue.shift();
|
||||
if (Number(str.charAt(0)) !== 2) {
|
||||
// this is a soft error
|
||||
if (this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)) {
|
||||
message = 'Internationalized mailbox name not allowed';
|
||||
} else {
|
||||
message = 'Recipient command failed';
|
||||
}
|
||||
const message =
|
||||
this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)
|
||||
? 'Internationalized mailbox name not allowed'
|
||||
: 'Recipient command failed';
|
||||
this._envelope.rejected.push(curRecipient);
|
||||
// store error for the failed recipient
|
||||
err = this._formatError(message, 'EENVELOPE', str, 'RCPT TO');
|
||||
@@ -1815,12 +1776,12 @@ class SMTPConnection extends EventEmitter {
|
||||
return callback(err);
|
||||
}
|
||||
} else if (this._envelope.rcptQueue.length) {
|
||||
curRecipient = this._envelope.rcptQueue.shift();
|
||||
this._recipientQueue.push(curRecipient);
|
||||
const nextRecipient = this._envelope.rcptQueue.shift();
|
||||
this._recipientQueue.push(nextRecipient);
|
||||
this._responseActions.push(str => {
|
||||
this._actionRCPT(str, callback);
|
||||
});
|
||||
this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
|
||||
this._sendCommand('RCPT TO:<' + nextRecipient + '>' + this._getDsnRcptToArgs());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1836,7 +1797,7 @@ class SMTPConnection extends EventEmitter {
|
||||
return callback(this._formatError('Data command failed', 'EENVELOPE', str, 'DATA'));
|
||||
}
|
||||
|
||||
let response = {
|
||||
const response = {
|
||||
accepted: this._envelope.accepted,
|
||||
rejected: this._envelope.rejected
|
||||
};
|
||||
@@ -1860,12 +1821,9 @@ class SMTPConnection extends EventEmitter {
|
||||
*/
|
||||
_actionSMTPStream(str, callback) {
|
||||
if (Number(str.charAt(0)) !== 2) {
|
||||
// Message failed
|
||||
return callback(this._formatError('Message failed', 'EMESSAGE', str, 'DATA'));
|
||||
} else {
|
||||
// Message sent succesfully
|
||||
return callback(null, str);
|
||||
}
|
||||
return callback(null, str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user