nodemailer 8.0.2 → 8.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const stream = require('stream');
4
- const Transform = stream.Transform;
3
+ const { Transform } = require('stream');
5
4
 
6
5
  /**
7
6
  * Escapes dots in the beginning of lines. Ends the stream with <CR><LF>.<CR><LF>
@@ -12,9 +11,7 @@ const Transform = stream.Transform;
12
11
  class DataStream extends Transform {
13
12
  constructor(options) {
14
13
  super(options);
15
- // init Transform
16
14
  this.options = options || {};
17
- this._curLine = '';
18
15
 
19
16
  this.inByteCount = 0;
20
17
  this.outByteCount = 0;
@@ -25,7 +22,7 @@ class DataStream extends Transform {
25
22
  * Escapes dots
26
23
  */
27
24
  _transform(chunk, encoding, done) {
28
- let chunks = [];
25
+ const chunks = [];
29
26
  let chunklen = 0;
30
27
  let i,
31
28
  len,
@@ -53,7 +50,7 @@ class DataStream extends Transform {
53
50
  lastPos = i + 1;
54
51
  }
55
52
  } else if (chunk[i] === 0x0a) {
56
- // .
53
+ // \n
57
54
  if ((i && chunk[i - 1] !== 0x0d) || (!i && this.lastByte !== 0x0d)) {
58
55
  if (i > lastPos) {
59
56
  buf = chunk.slice(lastPos, i);
@@ -22,18 +22,14 @@ const errors = require('../errors');
22
22
  * @param {Function} callback Callback to run with the rocket object once connection is established
23
23
  */
24
24
  function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
25
- let proxy = urllib.parse(proxyUrl);
25
+ const proxy = urllib.parse(proxyUrl);
26
26
 
27
- // create a socket connection to the proxy server
28
- let options;
29
- let connect;
30
- let socket;
31
-
32
- options = {
27
+ const options = {
33
28
  host: proxy.hostname,
34
29
  port: Number(proxy.port) ? Number(proxy.port) : proxy.protocol === 'https:' ? 443 : 80
35
30
  };
36
31
 
32
+ let connect;
37
33
  if (proxy.protocol === 'https:') {
38
34
  // we can use untrusted proxies as long as we verify actual SMTP certificates
39
35
  options.rejectUnauthorized = false;
@@ -42,10 +38,12 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
42
38
  connect = net.connect.bind(net);
43
39
  }
44
40
 
41
+ let socket;
42
+
45
43
  // Error harness for initial connection. Once connection is established, the responsibility
46
44
  // to handle errors is passed to whoever uses this socket
47
45
  let finished = false;
48
- let tempSocketErr = err => {
46
+ const tempSocketErr = err => {
49
47
  if (finished) {
50
48
  return;
51
49
  }
@@ -58,8 +56,8 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
58
56
  callback(err);
59
57
  };
60
58
 
61
- let timeoutErr = () => {
62
- let err = new Error('Proxy socket timed out');
59
+ const timeoutErr = () => {
60
+ const err = new Error('Proxy socket timed out');
63
61
  err.code = 'ETIMEDOUT';
64
62
  tempSocketErr(err);
65
63
  };
@@ -69,7 +67,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
69
67
  return;
70
68
  }
71
69
 
72
- let reqHeaders = {
70
+ const reqHeaders = {
73
71
  Host: destinationHost + ':' + destinationPort,
74
72
  Connection: 'close'
75
73
  };
@@ -93,7 +91,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
93
91
  );
94
92
 
95
93
  let headers = '';
96
- let onSocketData = chunk => {
94
+ const onSocketData = chunk => {
97
95
  let match;
98
96
  let remainder;
99
97
 
@@ -122,7 +120,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, callback) {
122
120
  } catch (_E) {
123
121
  // ignore
124
122
  }
125
- let err = new Error('Invalid response from proxy' + ((match && ': ' + match[1]) || ''));
123
+ const err = new Error('Invalid response from proxy' + ((match && ': ' + match[1]) || ''));
126
124
  err.code = errors.EPROXY;
127
125
  return callback(err);
128
126
  }
@@ -1,13 +1,13 @@
1
1
  'use strict';
2
2
 
3
3
  const packageInfo = require('../../package.json');
4
- const EventEmitter = require('events').EventEmitter;
4
+ const { EventEmitter } = require('events');
5
5
  const net = require('net');
6
6
  const tls = require('tls');
7
7
  const os = require('os');
8
8
  const crypto = require('crypto');
9
9
  const DataStream = require('./data-stream');
10
- const PassThrough = require('stream').PassThrough;
10
+ const { PassThrough } = require('stream');
11
11
  const shared = require('../shared');
12
12
 
13
13
  // default timeout values in ms
@@ -76,13 +76,12 @@ class SMTPConnection extends EventEmitter {
76
76
  });
77
77
 
78
78
  this.customAuth = new Map();
79
- Object.keys(this.options.customAuth || {}).forEach(key => {
80
- let mapKey = (key || '').toString().trim().toUpperCase();
81
- if (!mapKey) {
82
- return;
79
+ for (const key of Object.keys(this.options.customAuth || {})) {
80
+ const mapKey = (key || '').toString().trim().toUpperCase();
81
+ if (mapKey) {
82
+ this.customAuth.set(mapKey, this.options.customAuth[key]);
83
83
  }
84
- this.customAuth.set(mapKey, this.options.customAuth[key]);
85
- });
84
+ }
86
85
 
87
86
  /**
88
87
  * Expose version nr, just for the reference
@@ -266,27 +265,7 @@ class SMTPConnection extends EventEmitter {
266
265
  } else if (this.options.socket) {
267
266
  // socket object is set up but not yet connected
268
267
  this._socket = this.options.socket;
269
- return shared.resolveHostname(opts, (err, resolved) => {
270
- if (err) {
271
- return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
272
- }
273
- this.logger.debug(
274
- {
275
- tnx: 'dns',
276
- source: opts.host,
277
- resolved: resolved.host,
278
- cached: !!resolved.cached
279
- },
280
- 'Resolved %s as %s [cache %s]',
281
- opts.host,
282
- resolved.host,
283
- resolved.cached ? 'hit' : 'miss'
284
- );
285
- Object.keys(resolved).forEach(key => {
286
- if (key.charAt(0) !== '_' && resolved[key]) {
287
- opts[key] = resolved[key];
288
- }
289
- });
268
+ return this._resolveAndConnect(opts, _resolved => {
290
269
  try {
291
270
  this._socket.connect(this.port, this.host, () => {
292
271
  this._socket.setKeepAlive(true);
@@ -297,80 +276,59 @@ class SMTPConnection extends EventEmitter {
297
276
  return setImmediate(() => this._onError(E, 'ECONNECTION', false, 'CONN'));
298
277
  }
299
278
  });
300
- } else if (this.secureConnection) {
301
- // connect using tls
302
- if (this.options.tls) {
303
- Object.keys(this.options.tls).forEach(key => {
304
- opts[key] = this.options.tls[key];
305
- });
306
- }
307
-
308
- // ensure servername for SNI
309
- if (this.servername && !opts.servername) {
310
- opts.servername = this.servername;
311
- }
312
-
313
- return shared.resolveHostname(opts, (err, resolved) => {
314
- if (err) {
315
- return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
316
- }
317
- this.logger.debug(
318
- {
319
- tnx: 'dns',
320
- source: opts.host,
321
- resolved: resolved.host,
322
- cached: !!resolved.cached
323
- },
324
- 'Resolved %s as %s [cache %s]',
325
- opts.host,
326
- resolved.host,
327
- resolved.cached ? 'hit' : 'miss'
328
- );
329
- Object.keys(resolved).forEach(key => {
330
- if (key.charAt(0) !== '_' && resolved[key]) {
331
- opts[key] = resolved[key];
332
- }
333
- });
334
-
335
- // Store fallback addresses for retry on connection failure
336
- this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
337
- this._connectOpts = Object.assign({}, opts);
338
-
339
- this._connectToHost(opts, true);
340
- });
341
279
  } else {
342
- // connect using plaintext
343
- return shared.resolveHostname(opts, (err, resolved) => {
344
- if (err) {
345
- return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
280
+ if (this.secureConnection) {
281
+ Object.assign(opts, this.options.tls || {});
282
+
283
+ // ensure servername for SNI
284
+ if (this.servername && !opts.servername) {
285
+ opts.servername = this.servername;
346
286
  }
347
- this.logger.debug(
348
- {
349
- tnx: 'dns',
350
- source: opts.host,
351
- resolved: resolved.host,
352
- cached: !!resolved.cached
353
- },
354
- 'Resolved %s as %s [cache %s]',
355
- opts.host,
356
- resolved.host,
357
- resolved.cached ? 'hit' : 'miss'
358
- );
359
- Object.keys(resolved).forEach(key => {
360
- if (key.charAt(0) !== '_' && resolved[key]) {
361
- opts[key] = resolved[key];
362
- }
363
- });
287
+ }
364
288
 
289
+ return this._resolveAndConnect(opts, resolved => {
365
290
  // Store fallback addresses for retry on connection failure
366
291
  this._fallbackAddresses = (resolved._addresses || []).filter(addr => addr !== opts.host);
367
292
  this._connectOpts = Object.assign({}, opts);
368
293
 
369
- this._connectToHost(opts, false);
294
+ this._connectToHost(opts, this.secureConnection);
370
295
  });
371
296
  }
372
297
  }
373
298
 
299
+ /**
300
+ * Resolves the hostname and applies resolved values to opts,
301
+ * then calls the provided callback with the resolved data
302
+ *
303
+ * @param {Object} opts Connection options (modified in place)
304
+ * @param {Function} callback Called with resolved data on success
305
+ */
306
+ _resolveAndConnect(opts, callback) {
307
+ return shared.resolveHostname(opts, (err, resolved) => {
308
+ if (err) {
309
+ return setImmediate(() => this._onError(err, 'EDNS', false, 'CONN'));
310
+ }
311
+ this.logger.debug(
312
+ {
313
+ tnx: 'dns',
314
+ source: opts.host,
315
+ resolved: resolved.host,
316
+ cached: !!resolved.cached
317
+ },
318
+ 'Resolved %s as %s [cache %s]',
319
+ opts.host,
320
+ resolved.host,
321
+ resolved.cached ? 'hit' : 'miss'
322
+ );
323
+ for (const key of Object.keys(resolved)) {
324
+ if (key.charAt(0) !== '_' && resolved[key]) {
325
+ opts[key] = resolved[key];
326
+ }
327
+ }
328
+ callback(resolved);
329
+ });
330
+ }
331
+
374
332
  /**
375
333
  * Attempts to connect to the specified host address
376
334
  *
@@ -381,7 +339,7 @@ class SMTPConnection extends EventEmitter {
381
339
  this._connectionAttemptId++;
382
340
  const currentAttemptId = this._connectionAttemptId;
383
341
 
384
- let connectFn = secure ? tls.connect : net.connect;
342
+ const connectFn = secure ? tls.connect : net.connect;
385
343
  try {
386
344
  this._socket = connectFn(opts, () => {
387
345
  // Ignore callback if this is a stale connection attempt
@@ -418,7 +376,7 @@ class SMTPConnection extends EventEmitter {
418
376
  clearTimeout(this._connectionTimeout);
419
377
 
420
378
  // Check if we have fallback addresses to try
421
- let canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
379
+ const canFallback = this._fallbackAddresses && this._fallbackAddresses.length && this.stage === 'init' && !this._destroyed;
422
380
 
423
381
  if (!canFallback) {
424
382
  // No more fallback addresses, report the error
@@ -426,7 +384,7 @@ class SMTPConnection extends EventEmitter {
426
384
  return;
427
385
  }
428
386
 
429
- let nextHost = this._fallbackAddresses.shift();
387
+ const nextHost = this._fallbackAddresses.shift();
430
388
 
431
389
  this.logger.info(
432
390
  {
@@ -478,12 +436,7 @@ class SMTPConnection extends EventEmitter {
478
436
  }
479
437
  this._closing = true;
480
438
 
481
- let closeMethod = 'end';
482
-
483
- if (this.stage === 'init') {
484
- // Close the socket immediately when connection timed out
485
- closeMethod = 'destroy';
486
- }
439
+ const closeMethod = this.stage === 'init' ? 'destroy' : 'end';
487
440
 
488
441
  this.logger.debug(
489
442
  {
@@ -493,7 +446,7 @@ class SMTPConnection extends EventEmitter {
493
446
  closeMethod
494
447
  );
495
448
 
496
- let socket = (this._socket && this._socket.socket) || this._socket;
449
+ const socket = (this._socket && this._socket.socket) || this._socket;
497
450
 
498
451
  if (socket && !socket.destroyed) {
499
452
  try {
@@ -551,11 +504,11 @@ class SMTPConnection extends EventEmitter {
551
504
  }
552
505
 
553
506
  if (this.customAuth.has(this._authMethod)) {
554
- let handler = this.customAuth.get(this._authMethod);
507
+ const handler = this.customAuth.get(this._authMethod);
555
508
  let lastResponse;
556
509
  let returned = false;
557
510
 
558
- let resolve = () => {
511
+ const resolve = () => {
559
512
  if (returned) {
560
513
  return;
561
514
  }
@@ -574,7 +527,7 @@ class SMTPConnection extends EventEmitter {
574
527
  callback(null, true);
575
528
  };
576
529
 
577
- let reject = err => {
530
+ const reject = err => {
578
531
  if (returned) {
579
532
  return;
580
533
  }
@@ -582,7 +535,7 @@ class SMTPConnection extends EventEmitter {
582
535
  callback(this._formatError(err, 'EAUTH', lastResponse, 'AUTH ' + this._authMethod));
583
536
  };
584
537
 
585
- let handlerResponse = handler({
538
+ const handlerResponse = handler({
586
539
  auth: this._auth,
587
540
  method: this._authMethod,
588
541
 
@@ -709,7 +662,7 @@ class SMTPConnection extends EventEmitter {
709
662
 
710
663
  // ensure that callback is only called once
711
664
  let returned = false;
712
- let callback = function () {
665
+ const callback = function () {
713
666
  if (returned) {
714
667
  return;
715
668
  }
@@ -722,11 +675,11 @@ class SMTPConnection extends EventEmitter {
722
675
  message.on('error', err => callback(this._formatError(err, 'ESTREAM', false, 'API')));
723
676
  }
724
677
 
725
- let startTime = Date.now();
678
+ const startTime = Date.now();
726
679
  this._setEnvelope(envelope, (err, info) => {
727
680
  if (err) {
728
681
  // create passthrough stream to consume to prevent OOM
729
- let stream = new PassThrough();
682
+ const stream = new PassThrough();
730
683
  if (typeof message.pipe === 'function') {
731
684
  message.pipe(stream);
732
685
  } else {
@@ -736,8 +689,8 @@ class SMTPConnection extends EventEmitter {
736
689
 
737
690
  return callback(err);
738
691
  }
739
- let envelopeTime = Date.now();
740
- let stream = this._createSendStream((err, str) => {
692
+ const envelopeTime = Date.now();
693
+ const stream = this._createSendStream((err, str) => {
741
694
  if (err) {
742
695
  return callback(err);
743
696
  }
@@ -921,7 +874,7 @@ class SMTPConnection extends EventEmitter {
921
874
  err.message += ': ' + response;
922
875
  }
923
876
 
924
- let responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
877
+ const responseCode = (typeof response === 'string' && Number((response.match(/^\d+/) || [])[0])) || false;
925
878
  if (responseCode) {
926
879
  err.responseCode = responseCode;
927
880
  }
@@ -1016,15 +969,14 @@ class SMTPConnection extends EventEmitter {
1016
969
  this._socket.removeListener('data', this._onSocketData); // incoming data is going to be gibberish from this point onwards
1017
970
  this._socket.removeListener('timeout', this._onSocketTimeout); // timeout will be re-set for the new socket object
1018
971
 
1019
- let socketPlain = this._socket;
1020
- let opts = {
1021
- socket: this._socket,
1022
- host: this.host
1023
- };
1024
-
1025
- Object.keys(this.options.tls || {}).forEach(key => {
1026
- opts[key] = this.options.tls[key];
1027
- });
972
+ const socketPlain = this._socket;
973
+ const opts = Object.assign(
974
+ {
975
+ socket: this._socket,
976
+ host: this.host
977
+ },
978
+ this.options.tls || {}
979
+ );
1028
980
 
1029
981
  // ensure servername for SNI
1030
982
  if (this.servername && !opts.servername) {
@@ -1092,7 +1044,7 @@ class SMTPConnection extends EventEmitter {
1092
1044
  setImmediate(() => this._processResponse());
1093
1045
  }
1094
1046
 
1095
- let action = this._responseActions.shift();
1047
+ const action = this._responseActions.shift();
1096
1048
 
1097
1049
  if (typeof action === 'function') {
1098
1050
  action.call(this, str);
@@ -1140,7 +1092,7 @@ class SMTPConnection extends EventEmitter {
1140
1092
  * {from:{address:'...',name:'...'}, to:[address:'...',name:'...']}
1141
1093
  */
1142
1094
  _setEnvelope(envelope, callback) {
1143
- let args = [];
1095
+ const args = [];
1144
1096
  let useSmtpUtf8 = false;
1145
1097
 
1146
1098
  this._envelope = envelope || {};
@@ -1175,7 +1127,7 @@ class SMTPConnection extends EventEmitter {
1175
1127
  }
1176
1128
 
1177
1129
  // clone the recipients array for latter manipulation
1178
- this._envelope.rcptQueue = JSON.parse(JSON.stringify(this._envelope.to || []));
1130
+ this._envelope.rcptQueue = [].concat(this._envelope.to || []);
1179
1131
  this._envelope.rejected = [];
1180
1132
  this._envelope.rejectedErrors = [];
1181
1133
  this._envelope.accepted = [];
@@ -1207,7 +1159,10 @@ class SMTPConnection extends EventEmitter {
1207
1159
  }
1208
1160
 
1209
1161
  if (this._envelope.size && this._supportedExtensions.includes('SIZE')) {
1210
- args.push('SIZE=' + this._envelope.size);
1162
+ const sizeValue = Number(this._envelope.size) || 0;
1163
+ if (sizeValue > 0) {
1164
+ args.push('SIZE=' + sizeValue);
1165
+ }
1211
1166
  }
1212
1167
 
1213
1168
  // If the server supports DSN and the envelope includes an DSN prop
@@ -1260,7 +1215,7 @@ class SMTPConnection extends EventEmitter {
1260
1215
  throw new Error('ret: ' + JSON.stringify(ret));
1261
1216
  }
1262
1217
 
1263
- let envid = (params.envid || params.id || '').toString() || null;
1218
+ const envid = (params.envid || params.id || '').toString() || null;
1264
1219
 
1265
1220
  let notify = params.notify || null;
1266
1221
  if (notify) {
@@ -1268,8 +1223,8 @@ class SMTPConnection extends EventEmitter {
1268
1223
  notify = notify.split(',');
1269
1224
  }
1270
1225
  notify = notify.map(n => n.trim().toUpperCase());
1271
- let validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
1272
- let invalidNotify = notify.filter(n => !validNotify.includes(n));
1226
+ const validNotify = ['NEVER', 'SUCCESS', 'FAILURE', 'DELAY'];
1227
+ const invalidNotify = notify.filter(n => !validNotify.includes(n));
1273
1228
  if (invalidNotify.length || (notify.length > 1 && notify.includes('NEVER'))) {
1274
1229
  throw new Error('notify: ' + JSON.stringify(notify.join(',')));
1275
1230
  }
@@ -1290,7 +1245,7 @@ class SMTPConnection extends EventEmitter {
1290
1245
  }
1291
1246
 
1292
1247
  _getDsnRcptToArgs() {
1293
- let args = [];
1248
+ const args = [];
1294
1249
  // If the server supports DSN and the envelope includes an DSN prop
1295
1250
  // then append DSN params to the RCPT TO command
1296
1251
  if (this._envelope.dsn && this._supportedExtensions.includes('DSN')) {
@@ -1305,12 +1260,11 @@ class SMTPConnection extends EventEmitter {
1305
1260
  }
1306
1261
 
1307
1262
  _createSendStream(callback) {
1308
- let dataStream = new DataStream();
1309
- let logStream;
1263
+ const dataStream = new DataStream();
1310
1264
 
1311
1265
  if (this.options.lmtp) {
1312
1266
  this._envelope.accepted.forEach((recipient, i) => {
1313
- let final = i === this._envelope.accepted.length - 1;
1267
+ const final = i === this._envelope.accepted.length - 1;
1314
1268
  this._responseActions.push(str => {
1315
1269
  this._actionLMTPStream(recipient, final, str, callback);
1316
1270
  });
@@ -1326,7 +1280,7 @@ class SMTPConnection extends EventEmitter {
1326
1280
  });
1327
1281
 
1328
1282
  if (this.options.debug) {
1329
- logStream = new PassThrough();
1283
+ const logStream = new PassThrough();
1330
1284
  logStream.on('readable', () => {
1331
1285
  let chunk;
1332
1286
  while ((chunk = logStream.read())) {
@@ -1604,24 +1558,21 @@ class SMTPConnection extends EventEmitter {
1604
1558
  * @param {String} str Message from the server
1605
1559
  */
1606
1560
  _actionAUTH_CRAM_MD5(str, callback) {
1607
- let challengeMatch = str.match(/^334\s+(.+)$/);
1608
- let challengeString = '';
1561
+ const challengeMatch = str.match(/^334\s+(.+)$/);
1609
1562
 
1610
1563
  if (!challengeMatch) {
1611
1564
  return callback(
1612
1565
  this._formatError('Invalid login sequence while waiting for server challenge string', 'EAUTH', str, 'AUTH CRAM-MD5')
1613
1566
  );
1614
- } else {
1615
- challengeString = challengeMatch[1];
1616
1567
  }
1617
1568
 
1618
1569
  // Decode from base64
1619
- let base64decoded = Buffer.from(challengeString, 'base64').toString('ascii'),
1620
- hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
1570
+ const base64decoded = Buffer.from(challengeMatch[1], 'base64').toString('ascii');
1571
+ const hmacMD5 = crypto.createHmac('md5', this._auth.credentials.pass);
1621
1572
 
1622
1573
  hmacMD5.update(base64decoded);
1623
1574
 
1624
- let prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
1575
+ const prepended = this._auth.credentials.user + ' ' + hmacMD5.digest('hex');
1625
1576
 
1626
1577
  this._responseActions.push(str => {
1627
1578
  this._actionAUTH_CRAM_MD5_PASS(str, callback);
@@ -1742,39 +1693,29 @@ class SMTPConnection extends EventEmitter {
1742
1693
  * @param {String} str Message from the server
1743
1694
  */
1744
1695
  _actionMAIL(str, callback) {
1745
- let message, curRecipient;
1746
1696
  if (Number(str.charAt(0)) !== 2) {
1747
- if (this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)) {
1748
- message = 'Internationalized mailbox name not allowed';
1749
- } else {
1750
- message = 'Mail command failed';
1751
- }
1697
+ const message =
1698
+ this._usingSmtpUtf8 && /^550 /.test(str) && /[\x80-\uFFFF]/.test(this._envelope.from)
1699
+ ? 'Internationalized mailbox name not allowed'
1700
+ : 'Mail command failed';
1752
1701
  return callback(this._formatError(message, 'EENVELOPE', str, 'MAIL FROM'));
1753
1702
  }
1754
1703
 
1755
1704
  if (!this._envelope.rcptQueue.length) {
1756
1705
  return callback(this._formatError("Can't send mail - no recipients defined", 'EENVELOPE', false, 'API'));
1757
- } else {
1758
- this._recipientQueue = [];
1759
-
1760
- if (this._supportedExtensions.includes('PIPELINING')) {
1761
- while (this._envelope.rcptQueue.length) {
1762
- curRecipient = this._envelope.rcptQueue.shift();
1763
- this._recipientQueue.push(curRecipient);
1764
- this._responseActions.push(str => {
1765
- this._actionRCPT(str, callback);
1766
- });
1767
- this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
1768
- }
1769
- } else {
1770
- curRecipient = this._envelope.rcptQueue.shift();
1771
- this._recipientQueue.push(curRecipient);
1772
- this._responseActions.push(str => {
1773
- this._actionRCPT(str, callback);
1774
- });
1775
- this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
1776
- }
1777
1706
  }
1707
+
1708
+ this._recipientQueue = [];
1709
+ const usePipelining = this._supportedExtensions.includes('PIPELINING');
1710
+
1711
+ do {
1712
+ const curRecipient = this._envelope.rcptQueue.shift();
1713
+ this._recipientQueue.push(curRecipient);
1714
+ this._responseActions.push(str => {
1715
+ this._actionRCPT(str, callback);
1716
+ });
1717
+ this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
1718
+ } while (usePipelining && this._envelope.rcptQueue.length);
1778
1719
  }
1779
1720
 
1780
1721
  /**
@@ -1783,16 +1724,14 @@ class SMTPConnection extends EventEmitter {
1783
1724
  * @param {String} str Message from the server
1784
1725
  */
1785
1726
  _actionRCPT(str, callback) {
1786
- let message,
1787
- err,
1788
- curRecipient = this._recipientQueue.shift();
1727
+ let err;
1728
+ const curRecipient = this._recipientQueue.shift();
1789
1729
  if (Number(str.charAt(0)) !== 2) {
1790
1730
  // this is a soft error
1791
- if (this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)) {
1792
- message = 'Internationalized mailbox name not allowed';
1793
- } else {
1794
- message = 'Recipient command failed';
1795
- }
1731
+ const message =
1732
+ this._usingSmtpUtf8 && /^553 /.test(str) && /[\x80-\uFFFF]/.test(curRecipient)
1733
+ ? 'Internationalized mailbox name not allowed'
1734
+ : 'Recipient command failed';
1796
1735
  this._envelope.rejected.push(curRecipient);
1797
1736
  // store error for the failed recipient
1798
1737
  err = this._formatError(message, 'EENVELOPE', str, 'RCPT TO');
@@ -1815,12 +1754,12 @@ class SMTPConnection extends EventEmitter {
1815
1754
  return callback(err);
1816
1755
  }
1817
1756
  } else if (this._envelope.rcptQueue.length) {
1818
- curRecipient = this._envelope.rcptQueue.shift();
1819
- this._recipientQueue.push(curRecipient);
1757
+ const nextRecipient = this._envelope.rcptQueue.shift();
1758
+ this._recipientQueue.push(nextRecipient);
1820
1759
  this._responseActions.push(str => {
1821
1760
  this._actionRCPT(str, callback);
1822
1761
  });
1823
- this._sendCommand('RCPT TO:<' + curRecipient + '>' + this._getDsnRcptToArgs());
1762
+ this._sendCommand('RCPT TO:<' + nextRecipient + '>' + this._getDsnRcptToArgs());
1824
1763
  }
1825
1764
  }
1826
1765
 
@@ -1836,7 +1775,7 @@ class SMTPConnection extends EventEmitter {
1836
1775
  return callback(this._formatError('Data command failed', 'EENVELOPE', str, 'DATA'));
1837
1776
  }
1838
1777
 
1839
- let response = {
1778
+ const response = {
1840
1779
  accepted: this._envelope.accepted,
1841
1780
  rejected: this._envelope.rejected
1842
1781
  };
@@ -1860,12 +1799,9 @@ class SMTPConnection extends EventEmitter {
1860
1799
  */
1861
1800
  _actionSMTPStream(str, callback) {
1862
1801
  if (Number(str.charAt(0)) !== 2) {
1863
- // Message failed
1864
1802
  return callback(this._formatError('Message failed', 'EMESSAGE', str, 'DATA'));
1865
- } else {
1866
- // Message sent succesfully
1867
- return callback(null, str);
1868
1803
  }
1804
+ return callback(null, str);
1869
1805
  }
1870
1806
 
1871
1807
  /**