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.
- package/CHANGELOG.md +17 -0
- package/lib/addressparser/index.js +68 -83
- package/lib/base64/index.js +6 -5
- package/lib/dkim/index.js +8 -16
- package/lib/dkim/message-parser.js +12 -13
- package/lib/dkim/relaxed-body.js +2 -2
- package/lib/dkim/sign.js +13 -14
- package/lib/errors.js +4 -7
- package/lib/fetch/cookies.js +13 -18
- package/lib/fetch/index.js +12 -15
- package/lib/json-transport/index.js +4 -4
- package/lib/mail-composer/index.js +86 -116
- package/lib/mailer/index.js +26 -26
- package/lib/mailer/mail-message.js +17 -21
- package/lib/mime-funcs/index.js +25 -40
- package/lib/mime-funcs/mime-types.js +7 -11
- package/lib/mime-node/index.js +81 -72
- package/lib/mime-node/last-newline.js +1 -1
- package/lib/mime-node/le-unix.js +4 -7
- package/lib/mime-node/le-windows.js +1 -4
- package/lib/nodemailer.js +11 -18
- package/lib/qp/index.js +24 -21
- package/lib/sendmail-transport/index.js +30 -41
- package/lib/ses-transport/index.js +23 -34
- package/lib/shared/index.js +94 -140
- package/lib/smtp-connection/data-stream.js +3 -6
- package/lib/smtp-connection/http-proxy-client.js +11 -13
- package/lib/smtp-connection/index.js +118 -182
- package/lib/smtp-pool/index.js +20 -32
- package/lib/smtp-pool/pool-resource.js +8 -12
- package/lib/smtp-transport/index.js +22 -42
- package/lib/stream-transport/index.js +7 -7
- package/lib/well-known/index.js +7 -7
- package/lib/xoauth2/index.js +22 -28
- package/package.json +4 -3
package/lib/smtp-pool/index.js
CHANGED
|
@@ -51,11 +51,8 @@ class SMTPPool extends EventEmitter {
|
|
|
51
51
|
component: this.options.component || 'smtp-pool'
|
|
52
52
|
});
|
|
53
53
|
|
|
54
|
-
// temporary object
|
|
55
|
-
let connection = new SMTPConnection(this.options);
|
|
56
|
-
|
|
57
54
|
this.name = 'SMTP (pool)';
|
|
58
|
-
this.version = packageData.version + '[client:' +
|
|
55
|
+
this.version = packageData.version + '[client:' + packageData.version + ']';
|
|
59
56
|
|
|
60
57
|
this._rateLimit = {
|
|
61
58
|
counter: 0,
|
|
@@ -123,7 +120,7 @@ class SMTPPool extends EventEmitter {
|
|
|
123
120
|
*/
|
|
124
121
|
close() {
|
|
125
122
|
let connection;
|
|
126
|
-
|
|
123
|
+
const len = this._connections.length;
|
|
127
124
|
this._closed = true;
|
|
128
125
|
|
|
129
126
|
// clear rate limit timer if it exists
|
|
@@ -164,7 +161,7 @@ class SMTPPool extends EventEmitter {
|
|
|
164
161
|
}
|
|
165
162
|
|
|
166
163
|
// make sure that entire queue would be cleaned
|
|
167
|
-
|
|
164
|
+
const invokeCallbacks = () => {
|
|
168
165
|
if (!this._queue.length) {
|
|
169
166
|
this.logger.debug(
|
|
170
167
|
{
|
|
@@ -174,7 +171,7 @@ class SMTPPool extends EventEmitter {
|
|
|
174
171
|
);
|
|
175
172
|
return;
|
|
176
173
|
}
|
|
177
|
-
|
|
174
|
+
const entry = this._queue.shift();
|
|
178
175
|
if (entry && typeof entry.callback === 'function') {
|
|
179
176
|
try {
|
|
180
177
|
entry.callback(new Error('Connection pool was closed'));
|
|
@@ -201,9 +198,6 @@ class SMTPPool extends EventEmitter {
|
|
|
201
198
|
* an available connection, then use this connection to send the mail
|
|
202
199
|
*/
|
|
203
200
|
_processMessages() {
|
|
204
|
-
let connection;
|
|
205
|
-
let i, len;
|
|
206
|
-
|
|
207
201
|
// do nothing if already closed
|
|
208
202
|
if (this._closed) {
|
|
209
203
|
return;
|
|
@@ -220,12 +214,7 @@ class SMTPPool extends EventEmitter {
|
|
|
220
214
|
}
|
|
221
215
|
|
|
222
216
|
// find first available connection
|
|
223
|
-
|
|
224
|
-
if (this._connections[i].available) {
|
|
225
|
-
connection = this._connections[i];
|
|
226
|
-
break;
|
|
227
|
-
}
|
|
228
|
-
}
|
|
217
|
+
let connection = this._connections.find(c => c.available);
|
|
229
218
|
|
|
230
219
|
if (!connection && this._connections.length < this.options.maxConnections) {
|
|
231
220
|
connection = this._createConnection();
|
|
@@ -243,7 +232,7 @@ class SMTPPool extends EventEmitter {
|
|
|
243
232
|
this.emit('idle');
|
|
244
233
|
}
|
|
245
234
|
|
|
246
|
-
|
|
235
|
+
const entry = (connection.queueEntry = this._queue.shift());
|
|
247
236
|
entry.messageId = (connection.queueEntry.mail.message.getHeader('message-id') || '').replace(/[<>\s]/g, '');
|
|
248
237
|
|
|
249
238
|
connection.available = false;
|
|
@@ -294,7 +283,7 @@ class SMTPPool extends EventEmitter {
|
|
|
294
283
|
* Creates a new pool resource
|
|
295
284
|
*/
|
|
296
285
|
_createConnection() {
|
|
297
|
-
|
|
286
|
+
const connection = new PoolResource(this);
|
|
298
287
|
|
|
299
288
|
connection.id = ++this._connectionCounter;
|
|
300
289
|
|
|
@@ -331,7 +320,7 @@ class SMTPPool extends EventEmitter {
|
|
|
331
320
|
|
|
332
321
|
// resource is terminated with an error
|
|
333
322
|
connection.once('error', err => {
|
|
334
|
-
if (err.code !==
|
|
323
|
+
if (err.code !== errors.EMAXLIMIT) {
|
|
335
324
|
this.logger.warn(
|
|
336
325
|
{
|
|
337
326
|
err,
|
|
@@ -450,7 +439,7 @@ class SMTPPool extends EventEmitter {
|
|
|
450
439
|
}
|
|
451
440
|
|
|
452
441
|
_requeueEntryOnConnectionClose(connection) {
|
|
453
|
-
connection.queueEntry.requeueAttempts
|
|
442
|
+
connection.queueEntry.requeueAttempts += 1;
|
|
454
443
|
this.logger.debug(
|
|
455
444
|
{
|
|
456
445
|
tnx: 'pool',
|
|
@@ -484,7 +473,7 @@ class SMTPPool extends EventEmitter {
|
|
|
484
473
|
* @param {Object} connection The PoolResource to remove
|
|
485
474
|
*/
|
|
486
475
|
_removeConnection(connection) {
|
|
487
|
-
|
|
476
|
+
const index = this._connections.indexOf(connection);
|
|
488
477
|
|
|
489
478
|
if (index !== -1) {
|
|
490
479
|
this._connections.splice(index, 1);
|
|
@@ -501,7 +490,7 @@ class SMTPPool extends EventEmitter {
|
|
|
501
490
|
return callback();
|
|
502
491
|
}
|
|
503
492
|
|
|
504
|
-
|
|
493
|
+
const now = Date.now();
|
|
505
494
|
|
|
506
495
|
if (this._rateLimit.counter < this._rateLimit.limit) {
|
|
507
496
|
return callback();
|
|
@@ -511,7 +500,9 @@ class SMTPPool extends EventEmitter {
|
|
|
511
500
|
|
|
512
501
|
if (this._rateLimit.checkpoint <= now - this._rateLimit.delta) {
|
|
513
502
|
return this._clearRateLimit();
|
|
514
|
-
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
if (!this._rateLimit.timeout) {
|
|
515
506
|
this._rateLimit.timeout = setTimeout(() => this._clearRateLimit(), this._rateLimit.delta - (now - this._rateLimit.checkpoint));
|
|
516
507
|
this._rateLimit.checkpoint = now;
|
|
517
508
|
}
|
|
@@ -528,7 +519,7 @@ class SMTPPool extends EventEmitter {
|
|
|
528
519
|
|
|
529
520
|
// resume all paused connections
|
|
530
521
|
while (this._rateLimit.waiting.length) {
|
|
531
|
-
|
|
522
|
+
const cb = this._rateLimit.waiting.shift();
|
|
532
523
|
setImmediate(cb);
|
|
533
524
|
}
|
|
534
525
|
}
|
|
@@ -554,7 +545,7 @@ class SMTPPool extends EventEmitter {
|
|
|
554
545
|
});
|
|
555
546
|
}
|
|
556
547
|
|
|
557
|
-
|
|
548
|
+
const auth = new PoolResource(this).auth;
|
|
558
549
|
|
|
559
550
|
this.getSocket(this.options, (err, socketOptions) => {
|
|
560
551
|
if (err) {
|
|
@@ -578,13 +569,10 @@ class SMTPPool extends EventEmitter {
|
|
|
578
569
|
options.host || '',
|
|
579
570
|
options.port || ''
|
|
580
571
|
);
|
|
581
|
-
options = shared.assign(false, options);
|
|
582
|
-
Object.keys(socketOptions).forEach(key => {
|
|
583
|
-
options[key] = socketOptions[key];
|
|
584
|
-
});
|
|
572
|
+
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
585
573
|
}
|
|
586
574
|
|
|
587
|
-
|
|
575
|
+
const connection = new SMTPConnection(options);
|
|
588
576
|
let returned = false;
|
|
589
577
|
|
|
590
578
|
connection.once('error', err => {
|
|
@@ -604,7 +592,7 @@ class SMTPPool extends EventEmitter {
|
|
|
604
592
|
return callback(new Error('Connection closed'));
|
|
605
593
|
});
|
|
606
594
|
|
|
607
|
-
|
|
595
|
+
const finalize = () => {
|
|
608
596
|
if (returned) {
|
|
609
597
|
return;
|
|
610
598
|
}
|
|
@@ -633,7 +621,7 @@ class SMTPPool extends EventEmitter {
|
|
|
633
621
|
finalize();
|
|
634
622
|
});
|
|
635
623
|
} else if (!auth && connection.allowsAuth && options.forceAuth) {
|
|
636
|
-
|
|
624
|
+
const err = new Error('Authentication info was not provided');
|
|
637
625
|
err.code = errors.ENOAUTH;
|
|
638
626
|
|
|
639
627
|
returned = true;
|
|
@@ -23,7 +23,7 @@ class PoolResource extends EventEmitter {
|
|
|
23
23
|
if (this.options.auth) {
|
|
24
24
|
switch ((this.options.auth.type || '').toString().toUpperCase()) {
|
|
25
25
|
case 'OAUTH2': {
|
|
26
|
-
|
|
26
|
+
const oauth2 = new XOAuth2(this.options.auth, this.logger);
|
|
27
27
|
oauth2.provisionCallback =
|
|
28
28
|
(this.pool.mailer && this.pool.mailer.get('oauth2_provision_cb')) || oauth2.provisionCallback;
|
|
29
29
|
this.auth = {
|
|
@@ -90,10 +90,7 @@ class PoolResource extends EventEmitter {
|
|
|
90
90
|
options.port || ''
|
|
91
91
|
);
|
|
92
92
|
|
|
93
|
-
options = assign(false, options);
|
|
94
|
-
Object.keys(socketOptions).forEach(key => {
|
|
95
|
-
options[key] = socketOptions[key];
|
|
96
|
-
});
|
|
93
|
+
options = Object.assign(assign(false, options), socketOptions);
|
|
97
94
|
}
|
|
98
95
|
|
|
99
96
|
this.connection = new SMTPConnection(options);
|
|
@@ -114,12 +111,12 @@ class PoolResource extends EventEmitter {
|
|
|
114
111
|
}
|
|
115
112
|
returned = true;
|
|
116
113
|
|
|
117
|
-
|
|
114
|
+
const timer = setTimeout(() => {
|
|
118
115
|
if (returned) {
|
|
119
116
|
return;
|
|
120
117
|
}
|
|
121
118
|
// still have not returned, this means we have an unexpected connection close
|
|
122
|
-
|
|
119
|
+
const err = new Error('Unexpected socket close');
|
|
123
120
|
if (this.connection && this.connection._socket && this.connection._socket.upgrading) {
|
|
124
121
|
// starttls connection errors
|
|
125
122
|
err.code = errors.ETLS;
|
|
@@ -180,10 +177,10 @@ class PoolResource extends EventEmitter {
|
|
|
180
177
|
});
|
|
181
178
|
}
|
|
182
179
|
|
|
183
|
-
|
|
184
|
-
|
|
180
|
+
const envelope = mail.message.getEnvelope();
|
|
181
|
+
const messageId = mail.message.messageId();
|
|
185
182
|
|
|
186
|
-
|
|
183
|
+
const recipients = [].concat(envelope.to || []);
|
|
187
184
|
if (recipients.length > 3) {
|
|
188
185
|
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
189
186
|
}
|
|
@@ -224,9 +221,8 @@ class PoolResource extends EventEmitter {
|
|
|
224
221
|
info.messageId = messageId;
|
|
225
222
|
|
|
226
223
|
setImmediate(() => {
|
|
227
|
-
let err;
|
|
228
224
|
if (this.messages >= this.options.maxMessages) {
|
|
229
|
-
err = new Error('Resource exhausted');
|
|
225
|
+
const err = new Error('Resource exhausted');
|
|
230
226
|
err.code = errors.EMAXLIMIT;
|
|
231
227
|
this.connection.close();
|
|
232
228
|
this.emit('error', err);
|
|
@@ -49,11 +49,8 @@ class SMTPTransport extends EventEmitter {
|
|
|
49
49
|
component: this.options.component || 'smtp-transport'
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
// temporary object
|
|
53
|
-
let connection = new SMTPConnection(this.options);
|
|
54
|
-
|
|
55
52
|
this.name = 'SMTP';
|
|
56
|
-
this.version = packageData.version + '[client:' +
|
|
53
|
+
this.version = packageData.version + '[client:' + packageData.version + ']';
|
|
57
54
|
|
|
58
55
|
if (this.options.auth) {
|
|
59
56
|
this.auth = this.getAuth({});
|
|
@@ -77,24 +74,13 @@ class SMTPTransport extends EventEmitter {
|
|
|
77
74
|
return this.auth;
|
|
78
75
|
}
|
|
79
76
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
hasAuth = true;
|
|
86
|
-
authData[key] = this.options.auth[key];
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (authOpts && typeof authOpts === 'object') {
|
|
91
|
-
Object.keys(authOpts).forEach(key => {
|
|
92
|
-
hasAuth = true;
|
|
93
|
-
authData[key] = authOpts[key];
|
|
94
|
-
});
|
|
95
|
-
}
|
|
77
|
+
const authData = Object.assign(
|
|
78
|
+
{},
|
|
79
|
+
this.options.auth && typeof this.options.auth === 'object' ? this.options.auth : {},
|
|
80
|
+
authOpts && typeof authOpts === 'object' ? authOpts : {}
|
|
81
|
+
);
|
|
96
82
|
|
|
97
|
-
if (
|
|
83
|
+
if (Object.keys(authData).length === 0) {
|
|
98
84
|
return false;
|
|
99
85
|
}
|
|
100
86
|
|
|
@@ -103,7 +89,7 @@ class SMTPTransport extends EventEmitter {
|
|
|
103
89
|
if (!authData.service && !authData.user) {
|
|
104
90
|
return false;
|
|
105
91
|
}
|
|
106
|
-
|
|
92
|
+
const oauth2 = new XOAuth2(authData, this.logger);
|
|
107
93
|
oauth2.provisionCallback = (this.mailer && this.mailer.get('oauth2_provision_cb')) || oauth2.provisionCallback;
|
|
108
94
|
oauth2.on('token', token => this.mailer.emit('token', token));
|
|
109
95
|
oauth2.on('error', err => this.emit('error', err));
|
|
@@ -160,13 +146,10 @@ class SMTPTransport extends EventEmitter {
|
|
|
160
146
|
);
|
|
161
147
|
|
|
162
148
|
// only copy options if we need to modify it
|
|
163
|
-
options = shared.assign(false, options);
|
|
164
|
-
Object.keys(socketOptions).forEach(key => {
|
|
165
|
-
options[key] = socketOptions[key];
|
|
166
|
-
});
|
|
149
|
+
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
167
150
|
}
|
|
168
151
|
|
|
169
|
-
|
|
152
|
+
const connection = new SMTPConnection(options);
|
|
170
153
|
|
|
171
154
|
connection.once('error', err => {
|
|
172
155
|
if (returned) {
|
|
@@ -182,13 +165,13 @@ class SMTPTransport extends EventEmitter {
|
|
|
182
165
|
return;
|
|
183
166
|
}
|
|
184
167
|
|
|
185
|
-
|
|
168
|
+
const timer = setTimeout(() => {
|
|
186
169
|
if (returned) {
|
|
187
170
|
return;
|
|
188
171
|
}
|
|
189
172
|
returned = true;
|
|
190
173
|
// still have not returned, this means we have an unexpected connection close
|
|
191
|
-
|
|
174
|
+
const err = new Error('Unexpected socket close');
|
|
192
175
|
if (connection && connection._socket && connection._socket.upgrading) {
|
|
193
176
|
// starttls connection errors
|
|
194
177
|
err.code = errors.ETLS;
|
|
@@ -203,11 +186,11 @@ class SMTPTransport extends EventEmitter {
|
|
|
203
186
|
}
|
|
204
187
|
});
|
|
205
188
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
189
|
+
const sendMessage = () => {
|
|
190
|
+
const envelope = mail.message.getEnvelope();
|
|
191
|
+
const messageId = mail.message.messageId();
|
|
209
192
|
|
|
210
|
-
|
|
193
|
+
const recipients = [].concat(envelope.to || []);
|
|
211
194
|
if (recipients.length > 3) {
|
|
212
195
|
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
213
196
|
}
|
|
@@ -272,7 +255,7 @@ class SMTPTransport extends EventEmitter {
|
|
|
272
255
|
return;
|
|
273
256
|
}
|
|
274
257
|
|
|
275
|
-
|
|
258
|
+
const auth = this.getAuth(mail.data.auth);
|
|
276
259
|
|
|
277
260
|
if (auth && (connection.allowsAuth || options.forceAuth)) {
|
|
278
261
|
connection.login(auth, err => {
|
|
@@ -335,13 +318,10 @@ class SMTPTransport extends EventEmitter {
|
|
|
335
318
|
options.port || ''
|
|
336
319
|
);
|
|
337
320
|
|
|
338
|
-
options = shared.assign(false, options);
|
|
339
|
-
Object.keys(socketOptions).forEach(key => {
|
|
340
|
-
options[key] = socketOptions[key];
|
|
341
|
-
});
|
|
321
|
+
options = Object.assign(shared.assign(false, options), socketOptions);
|
|
342
322
|
}
|
|
343
323
|
|
|
344
|
-
|
|
324
|
+
const connection = new SMTPConnection(options);
|
|
345
325
|
let returned = false;
|
|
346
326
|
|
|
347
327
|
connection.once('error', err => {
|
|
@@ -361,7 +341,7 @@ class SMTPTransport extends EventEmitter {
|
|
|
361
341
|
return callback(new Error('Connection closed'));
|
|
362
342
|
});
|
|
363
343
|
|
|
364
|
-
|
|
344
|
+
const finalize = () => {
|
|
365
345
|
if (returned) {
|
|
366
346
|
return;
|
|
367
347
|
}
|
|
@@ -375,7 +355,7 @@ class SMTPTransport extends EventEmitter {
|
|
|
375
355
|
return;
|
|
376
356
|
}
|
|
377
357
|
|
|
378
|
-
|
|
358
|
+
const authData = this.getAuth({});
|
|
379
359
|
|
|
380
360
|
if (authData && (connection.allowsAuth || options.forceAuth)) {
|
|
381
361
|
connection.login(authData, err => {
|
|
@@ -392,7 +372,7 @@ class SMTPTransport extends EventEmitter {
|
|
|
392
372
|
finalize();
|
|
393
373
|
});
|
|
394
374
|
} else if (!authData && connection.allowsAuth && options.forceAuth) {
|
|
395
|
-
|
|
375
|
+
const err = new Error('Authentication info was not provided');
|
|
396
376
|
err.code = errors.ENOAUTH;
|
|
397
377
|
|
|
398
378
|
returned = true;
|
|
@@ -18,7 +18,7 @@ class StreamTransport {
|
|
|
18
18
|
constructor(options) {
|
|
19
19
|
options = options || {};
|
|
20
20
|
|
|
21
|
-
this.options = options
|
|
21
|
+
this.options = options;
|
|
22
22
|
|
|
23
23
|
this.name = 'StreamTransport';
|
|
24
24
|
this.version = packageData.version;
|
|
@@ -40,10 +40,10 @@ class StreamTransport {
|
|
|
40
40
|
// We probably need this in the output
|
|
41
41
|
mail.message.keepBcc = true;
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
const envelope = mail.data.envelope || mail.message.getEnvelope();
|
|
44
|
+
const messageId = mail.message.messageId();
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
const recipients = [].concat(envelope.to || []);
|
|
47
47
|
if (recipients.length > 3) {
|
|
48
48
|
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
|
49
49
|
}
|
|
@@ -91,13 +91,13 @@ class StreamTransport {
|
|
|
91
91
|
);
|
|
92
92
|
});
|
|
93
93
|
return done(null, {
|
|
94
|
-
envelope
|
|
94
|
+
envelope,
|
|
95
95
|
messageId,
|
|
96
96
|
message: stream
|
|
97
97
|
});
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
const chunks = [];
|
|
101
101
|
let chunklen = 0;
|
|
102
102
|
stream.on('readable', () => {
|
|
103
103
|
let chunk;
|
|
@@ -123,7 +123,7 @@ class StreamTransport {
|
|
|
123
123
|
|
|
124
124
|
stream.on('end', () =>
|
|
125
125
|
done(null, {
|
|
126
|
-
envelope
|
|
126
|
+
envelope,
|
|
127
127
|
messageId,
|
|
128
128
|
message: Buffer.concat(chunks, chunklen)
|
|
129
129
|
})
|
package/lib/well-known/index.js
CHANGED
|
@@ -4,16 +4,17 @@ const services = require('./services.json');
|
|
|
4
4
|
const normalized = {};
|
|
5
5
|
|
|
6
6
|
Object.keys(services).forEach(key => {
|
|
7
|
-
|
|
7
|
+
const service = services[key];
|
|
8
|
+
const normalizedService = normalizeService(service);
|
|
8
9
|
|
|
9
|
-
normalized[normalizeKey(key)] =
|
|
10
|
+
normalized[normalizeKey(key)] = normalizedService;
|
|
10
11
|
|
|
11
12
|
[].concat(service.aliases || []).forEach(alias => {
|
|
12
|
-
normalized[normalizeKey(alias)] =
|
|
13
|
+
normalized[normalizeKey(alias)] = normalizedService;
|
|
13
14
|
});
|
|
14
15
|
|
|
15
16
|
[].concat(service.domains || []).forEach(domain => {
|
|
16
|
-
normalized[normalizeKey(domain)] =
|
|
17
|
+
normalized[normalizeKey(domain)] = normalizedService;
|
|
17
18
|
});
|
|
18
19
|
});
|
|
19
20
|
|
|
@@ -22,11 +23,10 @@ function normalizeKey(key) {
|
|
|
22
23
|
}
|
|
23
24
|
|
|
24
25
|
function normalizeService(service) {
|
|
25
|
-
|
|
26
|
-
let response = {};
|
|
26
|
+
const response = {};
|
|
27
27
|
|
|
28
28
|
Object.keys(service).forEach(key => {
|
|
29
|
-
if (
|
|
29
|
+
if (!['domains', 'aliases'].includes(key)) {
|
|
30
30
|
response[key] = service[key];
|
|
31
31
|
}
|
|
32
32
|
});
|
package/lib/xoauth2/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const Stream = require('stream')
|
|
3
|
+
const { Stream } = require('stream');
|
|
4
4
|
const nmfetch = require('../fetch');
|
|
5
5
|
const crypto = require('crypto');
|
|
6
6
|
const shared = require('../shared');
|
|
@@ -42,13 +42,13 @@ class XOAuth2 extends Stream {
|
|
|
42
42
|
|
|
43
43
|
if (options && options.serviceClient) {
|
|
44
44
|
if (!options.privateKey || !options.user) {
|
|
45
|
-
|
|
45
|
+
const err = new Error('Options "privateKey" and "user" are required for service account!');
|
|
46
46
|
err.code = errors.EOAUTH2;
|
|
47
47
|
setImmediate(() => this.emit('error', err));
|
|
48
48
|
return;
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
|
|
51
|
+
const serviceRequestTimeout = Math.min(Math.max(Number(this.options.serviceRequestTimeout) || 0, 0), 3600);
|
|
52
52
|
this.options.serviceRequestTimeout = serviceRequestTimeout || 5 * 60;
|
|
53
53
|
}
|
|
54
54
|
|
|
@@ -72,7 +72,7 @@ class XOAuth2 extends Stream {
|
|
|
72
72
|
if (this.options.expires && Number(this.options.expires)) {
|
|
73
73
|
this.expires = this.options.expires;
|
|
74
74
|
} else {
|
|
75
|
-
|
|
75
|
+
const timeout = Math.max(Number(this.options.timeout) || 0, 0);
|
|
76
76
|
this.expires = (timeout && Date.now() + timeout * 1000) || 0;
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -123,7 +123,7 @@ class XOAuth2 extends Stream {
|
|
|
123
123
|
'Cannot renew access token for %s: No refresh mechanism available',
|
|
124
124
|
this.options.user
|
|
125
125
|
);
|
|
126
|
-
|
|
126
|
+
const err = new Error("Can't create new access token for user");
|
|
127
127
|
err.code = errors.EOAUTH2;
|
|
128
128
|
return callback(err);
|
|
129
129
|
}
|
|
@@ -210,8 +210,8 @@ class XOAuth2 extends Stream {
|
|
|
210
210
|
let loggedUrlOptions;
|
|
211
211
|
if (this.options.serviceClient) {
|
|
212
212
|
// service account - https://developers.google.com/identity/protocols/OAuth2ServiceAccount
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
const iat = Math.floor(Date.now() / 1000); // unix time
|
|
214
|
+
const tokenData = {
|
|
215
215
|
iss: this.options.serviceClient,
|
|
216
216
|
scope: this.options.scope || 'https://mail.google.com/',
|
|
217
217
|
sub: this.options.user,
|
|
@@ -223,7 +223,7 @@ class XOAuth2 extends Stream {
|
|
|
223
223
|
try {
|
|
224
224
|
token = this.jwtSignRS256(tokenData);
|
|
225
225
|
} catch (_err) {
|
|
226
|
-
|
|
226
|
+
const err = new Error("Can't generate token. Check your auth options");
|
|
227
227
|
err.code = errors.EOAUTH2;
|
|
228
228
|
return callback(err);
|
|
229
229
|
}
|
|
@@ -239,7 +239,7 @@ class XOAuth2 extends Stream {
|
|
|
239
239
|
};
|
|
240
240
|
} else {
|
|
241
241
|
if (!this.options.refreshToken) {
|
|
242
|
-
|
|
242
|
+
const err = new Error("Can't create new access token for user");
|
|
243
243
|
err.code = errors.EOAUTH2;
|
|
244
244
|
return callback(err);
|
|
245
245
|
}
|
|
@@ -260,10 +260,8 @@ class XOAuth2 extends Stream {
|
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
-
Object.
|
|
264
|
-
|
|
265
|
-
loggedUrlOptions[key] = this.options.customParams[key];
|
|
266
|
-
});
|
|
263
|
+
Object.assign(urlOptions, this.options.customParams);
|
|
264
|
+
Object.assign(loggedUrlOptions, this.options.customParams);
|
|
267
265
|
|
|
268
266
|
this.logger.debug(
|
|
269
267
|
{
|
|
@@ -298,19 +296,15 @@ class XOAuth2 extends Stream {
|
|
|
298
296
|
'Response: %s',
|
|
299
297
|
(body || '').toString()
|
|
300
298
|
);
|
|
301
|
-
|
|
299
|
+
const err = new Error('Invalid authentication response');
|
|
302
300
|
err.code = errors.EOAUTH2;
|
|
303
301
|
return callback(err);
|
|
304
302
|
}
|
|
305
303
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
} else {
|
|
311
|
-
logData[key] = (data[key] || '').toString().substr(0, 6) + '...';
|
|
312
|
-
}
|
|
313
|
-
});
|
|
304
|
+
const logData = Object.assign({}, data);
|
|
305
|
+
if (logData.access_token) {
|
|
306
|
+
logData.access_token = (logData.access_token || '').toString().substr(0, 6) + '...';
|
|
307
|
+
}
|
|
314
308
|
|
|
315
309
|
this.logger.debug(
|
|
316
310
|
{
|
|
@@ -331,7 +325,7 @@ class XOAuth2 extends Stream {
|
|
|
331
325
|
if (data.error_uri) {
|
|
332
326
|
errorMessage += ' (' + data.error_uri + ')';
|
|
333
327
|
}
|
|
334
|
-
|
|
328
|
+
const err = new Error(errorMessage);
|
|
335
329
|
err.code = errors.EOAUTH2;
|
|
336
330
|
return callback(err);
|
|
337
331
|
}
|
|
@@ -341,7 +335,7 @@ class XOAuth2 extends Stream {
|
|
|
341
335
|
return callback(null, this.accessToken);
|
|
342
336
|
}
|
|
343
337
|
|
|
344
|
-
|
|
338
|
+
const err = new Error('No access token');
|
|
345
339
|
err.code = errors.EOAUTH2;
|
|
346
340
|
return callback(err);
|
|
347
341
|
});
|
|
@@ -354,7 +348,7 @@ class XOAuth2 extends Stream {
|
|
|
354
348
|
* @return {String} Base64 encoded token for IMAP or SMTP login
|
|
355
349
|
*/
|
|
356
350
|
buildXOAuth2Token(accessToken) {
|
|
357
|
-
|
|
351
|
+
const authData = ['user=' + (this.options.user || ''), 'auth=Bearer ' + (accessToken || this.accessToken), '', ''];
|
|
358
352
|
return Buffer.from(authData.join('\x01'), 'utf-8').toString('base64');
|
|
359
353
|
}
|
|
360
354
|
|
|
@@ -373,10 +367,10 @@ class XOAuth2 extends Stream {
|
|
|
373
367
|
postRequest(url, payload, params, callback) {
|
|
374
368
|
let returned = false;
|
|
375
369
|
|
|
376
|
-
|
|
370
|
+
const chunks = [];
|
|
377
371
|
let chunklen = 0;
|
|
378
372
|
|
|
379
|
-
|
|
373
|
+
const req = nmfetch(url, {
|
|
380
374
|
method: 'post',
|
|
381
375
|
headers: params.customHeaders,
|
|
382
376
|
body: payload,
|
|
@@ -434,7 +428,7 @@ class XOAuth2 extends Stream {
|
|
|
434
428
|
*/
|
|
435
429
|
jwtSignRS256(payload) {
|
|
436
430
|
payload = ['{"alg":"RS256","typ":"JWT"}', JSON.stringify(payload)].map(val => this.toBase64URL(val)).join('.');
|
|
437
|
-
|
|
431
|
+
const signature = crypto.createSign('RSA-SHA256').update(payload).sign(this.options.privateKey);
|
|
438
432
|
return payload + '.' + this.toBase64URL(signature);
|
|
439
433
|
}
|
|
440
434
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nodemailer",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.4",
|
|
4
4
|
"description": "Easy as cake e-mail sending from your Node.js applications",
|
|
5
5
|
"main": "lib/nodemailer.js",
|
|
6
6
|
"scripts": {
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
"format:check": "prettier --check \"**/*.{js,json,md}\"",
|
|
11
11
|
"lint": "eslint .",
|
|
12
12
|
"lint:fix": "eslint . --fix",
|
|
13
|
-
"update": "rm -rf node_modules/ package-lock.json && ncu -u && npm install"
|
|
13
|
+
"update": "rm -rf node_modules/ package-lock.json && ncu -u && npm install",
|
|
14
|
+
"test:syntax": "docker run --rm -v \"$PWD:/app:ro\" -w /app node:6-alpine node test/syntax-compat.js"
|
|
14
15
|
},
|
|
15
16
|
"repository": {
|
|
16
17
|
"type": "git",
|
|
@@ -26,7 +27,7 @@
|
|
|
26
27
|
},
|
|
27
28
|
"homepage": "https://nodemailer.com/",
|
|
28
29
|
"devDependencies": {
|
|
29
|
-
"@aws-sdk/client-sesv2": "3.
|
|
30
|
+
"@aws-sdk/client-sesv2": "3.1011.0",
|
|
30
31
|
"bunyan": "1.8.15",
|
|
31
32
|
"c8": "11.0.0",
|
|
32
33
|
"eslint": "10.0.3",
|