nodemailer 10.0.13 → 10.0.15
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 +20 -0
- package/dist/cjs/addressparser/index.d.ts +0 -1
- package/dist/cjs/addressparser/index.js +55 -17
- package/dist/cjs/dkim/message-parser.js +28 -4
- package/dist/cjs/dkim/sign.d.ts +1 -0
- package/dist/cjs/dkim/sign.js +1 -0
- package/dist/cjs/errors.d.ts +3 -3
- package/dist/cjs/mailer/index.d.ts +7 -1
- package/dist/cjs/mime-funcs/index.d.ts +2 -1
- package/dist/cjs/mime-funcs/index.js +197 -83
- package/dist/cjs/mime-node/index.js +2 -1
- package/dist/cjs/nodemailer.d.ts +13 -2
- package/dist/cjs/package-info.d.ts +1 -1
- package/dist/cjs/package-info.js +1 -1
- package/dist/cjs/punycode/index.js +1 -0
- package/dist/cjs/ses-transport/index.d.ts +6 -1
- package/dist/cjs/shared/index.d.ts +25 -1
- package/dist/cjs/shared/index.js +1 -0
- package/dist/cjs/smtp-connection/http-proxy-client.d.ts +11 -3
- package/dist/cjs/smtp-connection/http-proxy-client.js +17 -7
- package/dist/cjs/smtp-connection/index.d.ts +1 -1
- package/dist/cjs/smtp-connection/index.js +15 -1
- package/dist/cjs/smtp-pool/index.d.ts +6 -1
- package/dist/cjs/smtp-transport/index.d.ts +6 -1
- package/dist/cjs/well-known/services.js +6 -0
- package/dist/esm/addressparser/index.d.ts +0 -1
- package/dist/esm/addressparser/index.js +55 -17
- package/dist/esm/dkim/message-parser.js +28 -4
- package/dist/esm/dkim/sign.d.ts +1 -0
- package/dist/esm/dkim/sign.js +1 -0
- package/dist/esm/errors.d.ts +3 -3
- package/dist/esm/mailer/index.d.ts +7 -1
- package/dist/esm/mime-funcs/index.d.ts +2 -1
- package/dist/esm/mime-funcs/index.js +197 -83
- package/dist/esm/mime-node/index.js +2 -1
- package/dist/esm/nodemailer.d.ts +13 -2
- package/dist/esm/package-info.d.ts +1 -1
- package/dist/esm/package-info.js +1 -1
- package/dist/esm/punycode/index.js +1 -0
- package/dist/esm/ses-transport/index.d.ts +6 -1
- package/dist/esm/shared/index.d.ts +25 -1
- package/dist/esm/shared/index.js +1 -0
- package/dist/esm/smtp-connection/http-proxy-client.d.ts +11 -3
- package/dist/esm/smtp-connection/http-proxy-client.js +17 -7
- package/dist/esm/smtp-connection/index.d.ts +1 -1
- package/dist/esm/smtp-connection/index.js +15 -1
- package/dist/esm/smtp-pool/index.d.ts +6 -1
- package/dist/esm/smtp-transport/index.d.ts +6 -1
- package/dist/esm/well-known/services.js +6 -0
- package/dist/well-known/services.json +7 -0
- package/package.json +8 -8
|
@@ -88,6 +88,7 @@ function quoteString(value) {
|
|
|
88
88
|
* no need to encode the values in any way. If the value is plaintext but has
|
|
89
89
|
* longer lines then allowed, then use format=flowed
|
|
90
90
|
*
|
|
91
|
+
* @param str Multi line string to check
|
|
91
92
|
* @param lineLength Max line length to check for
|
|
92
93
|
* @returns Returns true if there is at least one line longer than lineLength chars
|
|
93
94
|
*/
|
|
@@ -234,7 +235,10 @@ function buildHeaderValue(structured) {
|
|
|
234
235
|
}
|
|
235
236
|
});
|
|
236
237
|
}
|
|
237
|
-
else if (/[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
|
|
238
|
+
else if (!value.length || /[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
|
|
239
|
+
// a parameter value is a token or a quoted-string and a token is never empty, so
|
|
240
|
+
// a valueless parameter such as the 'flag' of 'multipart/mixed; flag; boundary=b'
|
|
241
|
+
// goes out as 'flag=""' rather than as the 'flag=' that parses as neither
|
|
238
242
|
paramsArray.push(param + '=' + JSON.stringify(value));
|
|
239
243
|
}
|
|
240
244
|
else {
|
|
@@ -256,9 +260,9 @@ function buildHeaderValue(structured) {
|
|
|
256
260
|
* title*0*=utf-8''unicode
|
|
257
261
|
* title*1*=%20string
|
|
258
262
|
*
|
|
263
|
+
* @param key Parameter name the generated keys are built from, for example title
|
|
259
264
|
* @param data String to be encoded
|
|
260
265
|
* @param [maxLength=50] Max length for generated chunks
|
|
261
|
-
* @param [fromCharset='UTF-8'] Source sharacter set
|
|
262
266
|
* @return A list of encoded keys and headers
|
|
263
267
|
*/
|
|
264
268
|
function buildHeaderParam(key, data, maxLength) {
|
|
@@ -385,6 +389,26 @@ function buildHeaderParam(key, data, maxLength) {
|
|
|
385
389
|
value: item.line
|
|
386
390
|
}));
|
|
387
391
|
}
|
|
392
|
+
/**
|
|
393
|
+
* An RFC 2045 token: printable ASCII without SPACE, the control characters, DEL and the
|
|
394
|
+
* tspecials. A charset name is one, and the name a continuation carries is written into
|
|
395
|
+
* the encoded word the parameter value becomes, so it is checked against this before it
|
|
396
|
+
* goes in. Whitespace used to come off it only because the value was trimmed first.
|
|
397
|
+
*/
|
|
398
|
+
const TOKEN = /^[^\x00-\x20\x7f()<>@,;:\\"/[\]?=]+$/;
|
|
399
|
+
/**
|
|
400
|
+
* Whether a string can be a header parameter name.
|
|
401
|
+
*
|
|
402
|
+
* A parameter name is a token, so it is never empty. A "__proto__" name would target the
|
|
403
|
+
* prototype chain of the params object instead of an own property of it and read back as
|
|
404
|
+
* Object.prototype, so it is no name either.
|
|
405
|
+
*
|
|
406
|
+
* @param name Candidate parameter name, already lowercased
|
|
407
|
+
* @return true when the name can be used
|
|
408
|
+
*/
|
|
409
|
+
function _isParamName(name) {
|
|
410
|
+
return !!name && !(0, objects_js_1.isProtoKey)(name);
|
|
411
|
+
}
|
|
388
412
|
/**
|
|
389
413
|
* Parses a header value with key=value arguments into a structured
|
|
390
414
|
* object.
|
|
@@ -405,134 +429,224 @@ function parseHeaderValue(str) {
|
|
|
405
429
|
value: '',
|
|
406
430
|
params: {}
|
|
407
431
|
};
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
//
|
|
432
|
+
// A duplicated parameter resolves to its first occurrence, the way a duplicated header
|
|
433
|
+
// does. Letting the last one win disagrees with the receivers that take the first, and
|
|
434
|
+
// the two readings of 'boundary="b"; boundary="c"' name different delimiters. The
|
|
435
|
+
// continuation join below is the only writer of the name it builds, so it tests the
|
|
436
|
+
// name with _isParamName directly rather than taking that rule along from here.
|
|
411
437
|
const setParam = (name, value) => {
|
|
412
|
-
|
|
438
|
+
name = name.toLowerCase();
|
|
439
|
+
if (_isParamName(name) && !Object.prototype.hasOwnProperty.call(response.params, name)) {
|
|
413
440
|
response.params[name] = value;
|
|
414
441
|
}
|
|
415
442
|
};
|
|
416
443
|
let key = false;
|
|
417
444
|
let value = '';
|
|
418
|
-
let
|
|
445
|
+
let stage = 'value';
|
|
419
446
|
let quote = false;
|
|
420
447
|
let escaped = false;
|
|
421
448
|
let chr;
|
|
449
|
+
// Whitespace seen outside a quoted string is held back until a significant character
|
|
450
|
+
// follows it, so the whitespace around a value is dropped without trimming spaces the
|
|
451
|
+
// sender quoted on purpose. Trimming the stored value instead loses the trailing space
|
|
452
|
+
// of 'filename*0="Annual Report "', which the next continuation section is appended to.
|
|
453
|
+
let pendingSpace = '';
|
|
454
|
+
let quoteClosed = false;
|
|
455
|
+
// Whitespace ahead of the first character of a value is padding and is dropped, the
|
|
456
|
+
// whitespace between two characters of it is content
|
|
457
|
+
const flushSpace = () => {
|
|
458
|
+
if (value.length) {
|
|
459
|
+
value += pendingSpace;
|
|
460
|
+
}
|
|
461
|
+
pendingSpace = '';
|
|
462
|
+
};
|
|
463
|
+
const addChr = (c) => {
|
|
464
|
+
flushSpace();
|
|
465
|
+
value += c;
|
|
466
|
+
};
|
|
467
|
+
const takeValue = () => {
|
|
468
|
+
const taken = value;
|
|
469
|
+
value = '';
|
|
470
|
+
pendingSpace = '';
|
|
471
|
+
quoteClosed = false;
|
|
472
|
+
return taken;
|
|
473
|
+
};
|
|
474
|
+
const storeValue = () => {
|
|
475
|
+
const taken = takeValue();
|
|
476
|
+
if (key === false) {
|
|
477
|
+
response.value = taken;
|
|
478
|
+
}
|
|
479
|
+
else {
|
|
480
|
+
setParam(key, taken);
|
|
481
|
+
}
|
|
482
|
+
};
|
|
483
|
+
// A parameter name with no '=' is a valueless parameter, not the start of the next one.
|
|
484
|
+
// Without this the name keeps growing across the ';' and swallows whatever follows, which
|
|
485
|
+
// is how 'multipart/mixed; flag; boundary="AAA"' lost its boundary to a parameter named
|
|
486
|
+
// 'flag; boundary' and left the node declaring the generated boundary beside the asked
|
|
487
|
+
// for one, so a receiver reading the first of the two found no delimiter it matched.
|
|
488
|
+
const storeEmptyKey = () => {
|
|
489
|
+
setParam(takeValue().trim(), '');
|
|
490
|
+
};
|
|
422
491
|
for (let i = 0, len = str.length; i < len; i++) {
|
|
423
492
|
chr = str.charAt(i);
|
|
424
|
-
if (
|
|
493
|
+
if (stage === 'key') {
|
|
425
494
|
if (chr === '=') {
|
|
426
|
-
key =
|
|
427
|
-
|
|
428
|
-
|
|
495
|
+
key = takeValue().trim();
|
|
496
|
+
stage = 'value';
|
|
497
|
+
continue;
|
|
498
|
+
}
|
|
499
|
+
if (chr === ';') {
|
|
500
|
+
storeEmptyKey();
|
|
429
501
|
continue;
|
|
430
502
|
}
|
|
431
503
|
value += chr;
|
|
432
504
|
}
|
|
433
505
|
else {
|
|
506
|
+
if (quoteClosed && chr !== ';') {
|
|
507
|
+
// Nothing behind a closed quoted string reaches the value. RFC 2045 says a
|
|
508
|
+
// parameter value is a token or a quoted string and not both, so what follows
|
|
509
|
+
// one is junk and only the ';' that ends the parameter still counts. Tested
|
|
510
|
+
// ahead of the branches rather than beside the append at the foot of them,
|
|
511
|
+
// where each branch above was a way around it: a second '"' reopened quoting
|
|
512
|
+
// and swallowed the rest of the header, so 'boundary="AAA" "; boundary=BBB"'
|
|
513
|
+
// read as a single boundary of 'AAA ', and the junk of
|
|
514
|
+
// 'boundary="AAA" (unterminated comment' still joined the declared boundary
|
|
515
|
+
// through the escape branch. quoteClosed is only ever set while no quote is
|
|
516
|
+
// open, and this is what keeps one from being opened afterwards.
|
|
517
|
+
escaped = false;
|
|
518
|
+
continue;
|
|
519
|
+
}
|
|
434
520
|
if (escaped) {
|
|
435
|
-
|
|
521
|
+
addChr(chr);
|
|
436
522
|
}
|
|
437
|
-
else if (chr === '\\') {
|
|
523
|
+
else if (quote && chr === '\\') {
|
|
524
|
+
// a backslash only escapes inside a quoted string, everywhere else it is an
|
|
525
|
+
// ordinary character. Treating it as an escape turns the parameter value
|
|
526
|
+
// 'C:\Users\me\report.txt' into 'C:Usersmereport.txt'
|
|
438
527
|
escaped = true;
|
|
439
528
|
continue;
|
|
440
529
|
}
|
|
441
530
|
else if (quote && chr === quote) {
|
|
442
531
|
quote = false;
|
|
532
|
+
quoteClosed = true;
|
|
443
533
|
}
|
|
444
534
|
else if (!quote && chr === '"') {
|
|
445
535
|
quote = chr;
|
|
536
|
+
flushSpace();
|
|
446
537
|
}
|
|
447
538
|
else if (!quote && chr === ';') {
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
type = 'key';
|
|
455
|
-
value = '';
|
|
539
|
+
storeValue();
|
|
540
|
+
stage = 'key';
|
|
541
|
+
}
|
|
542
|
+
else if (!quote && (chr === ' ' || chr === '\t')) {
|
|
543
|
+
pendingSpace += chr;
|
|
456
544
|
}
|
|
457
545
|
else {
|
|
458
|
-
|
|
546
|
+
addChr(chr);
|
|
459
547
|
}
|
|
460
548
|
escaped = false;
|
|
461
549
|
}
|
|
462
550
|
}
|
|
463
|
-
if (
|
|
464
|
-
|
|
465
|
-
response.value = value.trim();
|
|
466
|
-
}
|
|
467
|
-
else {
|
|
468
|
-
setParam(key, value.trim());
|
|
469
|
-
}
|
|
551
|
+
if (stage === 'value') {
|
|
552
|
+
storeValue();
|
|
470
553
|
}
|
|
471
|
-
else
|
|
472
|
-
|
|
554
|
+
else {
|
|
555
|
+
// a key with no value, as in 'Header-Key: somevalue; key=value; emptykey'
|
|
556
|
+
storeEmptyKey();
|
|
473
557
|
}
|
|
474
558
|
// handle parameter value continuations
|
|
475
559
|
// https://tools.ietf.org/html/rfc2231#section-3
|
|
476
|
-
//
|
|
560
|
+
// Sections are collected in a list and ordered below rather than written into an array
|
|
561
|
+
// at their own section number. An index write makes the array as long as the number the
|
|
562
|
+
// header asked for, and the join that follows walks all of it, so the 55 byte value
|
|
563
|
+
// "attachment; filename*0*=utf-8''a; filename*4000000000=b" held a core for over two
|
|
564
|
+
// minutes.
|
|
565
|
+
const continuations = new Map();
|
|
477
566
|
Object.keys(response.params).forEach(key => {
|
|
478
|
-
|
|
479
|
-
if (
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
567
|
+
const match = key.match(/(\*(\d+)|\*(\d+)\*|\*)$/);
|
|
568
|
+
if (!match) {
|
|
569
|
+
// not a continuation parameter, there is nothing to join
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const actualKey = key.substr(0, match.index);
|
|
573
|
+
const nr = Number(match[2] || match[3]) || 0;
|
|
574
|
+
// RFC 2231 section 4.1: only a section whose name ends in '*' is percent encoded
|
|
575
|
+
const encoded = match[0].substr(-1) === '*';
|
|
576
|
+
// remove the old reference
|
|
577
|
+
let value = response.params[key];
|
|
578
|
+
delete response.params[key];
|
|
579
|
+
if (!_isParamName(actualKey)) {
|
|
580
|
+
// the joined value can not be written back under this name. It is empty when the
|
|
581
|
+
// continuation suffix was all there was of it, as in the bare '*' of 'text/plain; a;*'
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
let continuation = continuations.get(actualKey);
|
|
585
|
+
if (!continuation) {
|
|
586
|
+
continuation = { charset: false, sections: [] };
|
|
587
|
+
continuations.set(actualKey, continuation);
|
|
588
|
+
}
|
|
589
|
+
const charsetMatch = nr === 0 && encoded ? value.match(/^([^']*)'[^']*'(.*)$/) : null;
|
|
590
|
+
if (charsetMatch) {
|
|
591
|
+
// the charset is a token, and anything else named as one is no charset a consumer
|
|
592
|
+
// could resolve, so it reads as the unnamed case rather than being carried into
|
|
593
|
+
// the encoded word below. A "\r\n" of a prefix would otherwise reach a consumer
|
|
594
|
+
// of the parsed value as the charset of a word that no decoder can act on
|
|
595
|
+
continuation.charset = TOKEN.test(charsetMatch[1]) ? charsetMatch[1] : 'iso-8859-1';
|
|
596
|
+
value = charsetMatch[2];
|
|
503
597
|
}
|
|
598
|
+
continuation.sections.push({ nr, value, encoded });
|
|
504
599
|
});
|
|
505
600
|
// concatenate split rfc2231 strings and convert encoded strings to mime encoded words
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
return '_';
|
|
522
|
-
}
|
|
523
|
-
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
524
|
-
})
|
|
525
|
-
// change from urlencoding to percent encoding
|
|
526
|
-
.replace(/%/g, '=') +
|
|
527
|
-
'?=';
|
|
528
|
-
}
|
|
529
|
-
else {
|
|
530
|
-
response.params[key] = value;
|
|
531
|
-
}
|
|
601
|
+
continuations.forEach((continuation, key) => {
|
|
602
|
+
if (Object.prototype.hasOwnProperty.call(response.params, key)) {
|
|
603
|
+
// The same name was also given as a plain parameter, which the starred keys were
|
|
604
|
+
// just deleted from around, so this write would be the only one in the function
|
|
605
|
+
// to override a name already taken. 'filename=plain.txt; filename*0=evil.txt'
|
|
606
|
+
// resolves to the plain parameter either way round, so the reading does not come
|
|
607
|
+
// down to which of the two spellings the sender put first
|
|
608
|
+
return;
|
|
609
|
+
}
|
|
610
|
+
continuation.sections.sort((a, b) => a.nr - b.nr);
|
|
611
|
+
if (!continuation.charset) {
|
|
612
|
+
// nothing said which charset the percent escapes of an encoded section are in,
|
|
613
|
+
// so every section is passed on as the text it already is
|
|
614
|
+
response.params[key] = continuation.sections.map(section => section.value).join('');
|
|
615
|
+
return;
|
|
532
616
|
}
|
|
617
|
+
// convert "%AB" to "=?charset?Q?=AB?="
|
|
618
|
+
response.params[key] = '=?' + continuation.charset + '?Q?' + continuation.sections.map(_encodeContinuationSection).join('') + '?=';
|
|
533
619
|
});
|
|
534
620
|
return response;
|
|
535
621
|
}
|
|
622
|
+
/**
|
|
623
|
+
* Renders one parameter value continuation section as the payload of a Q encoded word.
|
|
624
|
+
*
|
|
625
|
+
* A section whose name ends in '*' is percent encoded and its escapes carry the bytes of
|
|
626
|
+
* the value, so they only have to be rewritten into the "=AB" spelling a Q encoded word
|
|
627
|
+
* uses. A section without the '*' is literal text (RFC 2231 section 4.1), so its '%' is a
|
|
628
|
+
* '%' and is escaped along with the characters a Q encoded word can not carry bare.
|
|
629
|
+
* Decoding a literal section invents bytes that never appeared on the wire: it is how the
|
|
630
|
+
* value 'filename*0*=utf-8''safe; filename*1=%2F..%2F..%2Fetc%2Fpasswd' was emitted as a
|
|
631
|
+
* filename every receiving client reads back as 'safe/../../etc/passwd'.
|
|
632
|
+
*
|
|
633
|
+
* @param section One collected continuation section
|
|
634
|
+
* @return The section as Q encoded word payload
|
|
635
|
+
*/
|
|
636
|
+
function _encodeContinuationSection(section) {
|
|
637
|
+
const specials = section.encoded ? /[=?_\s]/g : /[=?_\s%]/g;
|
|
638
|
+
return (section.value
|
|
639
|
+
// fix invalidly encoded chars
|
|
640
|
+
.replace(specials, s => {
|
|
641
|
+
const c = s.charCodeAt(0).toString(16);
|
|
642
|
+
if (s === ' ') {
|
|
643
|
+
return '_';
|
|
644
|
+
}
|
|
645
|
+
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
646
|
+
})
|
|
647
|
+
// change from urlencoding to percent encoding
|
|
648
|
+
.replace(/%/g, '='));
|
|
649
|
+
}
|
|
536
650
|
/**
|
|
537
651
|
* Returns file extension for a content type string. If no suitable extensions
|
|
538
652
|
* are found, 'bin' is used as the default extension
|
|
@@ -1294,6 +1294,7 @@ class MimeNode {
|
|
|
1294
1294
|
*
|
|
1295
1295
|
* @param addresses An array of address objects
|
|
1296
1296
|
* @param [uniqueList] An array to be populated with addresses
|
|
1297
|
+
* @param [seenAddresses] Addresses already added to uniqueList, shared with recursive calls to keep deduplication linear
|
|
1297
1298
|
* @return address string
|
|
1298
1299
|
* @internal
|
|
1299
1300
|
*/
|
|
@@ -1431,7 +1432,7 @@ class MimeNode {
|
|
|
1431
1432
|
/**
|
|
1432
1433
|
* If needed, mime encodes the name part
|
|
1433
1434
|
*
|
|
1434
|
-
* @param
|
|
1435
|
+
* @param value Name part of an address
|
|
1435
1436
|
* @returns Mime word encoded string if needed
|
|
1436
1437
|
* @internal
|
|
1437
1438
|
*/
|
package/dist/cjs/nodemailer.d.ts
CHANGED
|
@@ -75,11 +75,22 @@ export declare function createTransport(transporter?: TransportConfig | Transpor
|
|
|
75
75
|
/**
|
|
76
76
|
* Creates a test account from the Ethereal service (https://ethereal.email)
|
|
77
77
|
*
|
|
78
|
-
* @param
|
|
79
|
-
* @param callback Callback function to run with the account object. If not set, a Promise is returned
|
|
78
|
+
* @param callback Callback function to run with the account object
|
|
80
79
|
*/
|
|
81
80
|
export declare function createTestAccount(callback: TestAccountCallback): void;
|
|
81
|
+
/**
|
|
82
|
+
* Creates a test account from the Ethereal service (https://ethereal.email)
|
|
83
|
+
*
|
|
84
|
+
* @param apiUrl API endpoint, defaults to https://api.nodemailer.com
|
|
85
|
+
* @param callback Callback function to run with the account object
|
|
86
|
+
*/
|
|
82
87
|
export declare function createTestAccount(apiUrl: string | false | null | undefined, callback: TestAccountCallback): void;
|
|
88
|
+
/**
|
|
89
|
+
* Creates a test account from the Ethereal service (https://ethereal.email)
|
|
90
|
+
*
|
|
91
|
+
* @param [apiUrl] API endpoint, defaults to https://api.nodemailer.com
|
|
92
|
+
* @returns Promise that resolves with the account object
|
|
93
|
+
*/
|
|
83
94
|
export declare function createTestAccount(apiUrl?: string | false | null): Promise<TestAccount>;
|
|
84
95
|
/**
|
|
85
96
|
* Resolves the Ethereal web URL for a message sent through an Ethereal test account
|
package/dist/cjs/package-info.js
CHANGED
|
@@ -178,6 +178,7 @@ const basicToDigit = function (codePoint) {
|
|
|
178
178
|
* @see `basicToDigit()`
|
|
179
179
|
* @private
|
|
180
180
|
* @param digit The numeric value of a basic code point.
|
|
181
|
+
* @param flag Non-zero to use the uppercase form of the code point.
|
|
181
182
|
* @returns The basic code point whose value (when used for
|
|
182
183
|
* representing integers) is `digit`, which needs to be in the range
|
|
183
184
|
* `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
|
|
@@ -85,9 +85,14 @@ declare class SESTransport extends EventEmitter {
|
|
|
85
85
|
/**
|
|
86
86
|
* Verifies SES configuration
|
|
87
87
|
*
|
|
88
|
-
* @
|
|
88
|
+
* @returns Promise that resolves to true if the configuration is usable
|
|
89
89
|
*/
|
|
90
90
|
verify(): Promise<true>;
|
|
91
|
+
/**
|
|
92
|
+
* Verifies SES configuration
|
|
93
|
+
*
|
|
94
|
+
* @param callback Callback function
|
|
95
|
+
*/
|
|
91
96
|
verify(callback: VerifyCallback): void;
|
|
92
97
|
}
|
|
93
98
|
/**
|
|
@@ -187,6 +187,7 @@ export declare const parseConnectionUrl: (str?: string | null) => ConnectionUrlO
|
|
|
187
187
|
* creates a default console logger
|
|
188
188
|
*
|
|
189
189
|
* @param [options] Options object that might include 'logger' value
|
|
190
|
+
* @param [defaults] Fields merged into every log entry, overridden by the fields of the entry itself
|
|
190
191
|
* @return bunyan compatible logger
|
|
191
192
|
*/
|
|
192
193
|
export declare const getLogger: (options?: GetLoggerOptions, defaults?: LogEntry) => Logger;
|
|
@@ -210,18 +211,41 @@ export declare const parseDataURI: (uri: unknown) => ParsedDataURI | null;
|
|
|
210
211
|
*
|
|
211
212
|
* @param data An object or an Array you want to resolve an element for, see ContentDescriptor for the values it understands
|
|
212
213
|
* @param key Property name or an Array index
|
|
213
|
-
* @param [options] Optional access policy: { disableFileAccess, disableUrlAccess }
|
|
214
214
|
* @param callback Callback function with (err, value)
|
|
215
215
|
*/
|
|
216
216
|
export declare function resolveContent(data: {
|
|
217
217
|
[key: string]: any;
|
|
218
218
|
}, key: string | number, callback: ResolveContentCallback): void;
|
|
219
|
+
/**
|
|
220
|
+
* Resolves a String or a Buffer value for content value
|
|
221
|
+
*
|
|
222
|
+
* @param data An object or an Array you want to resolve an element for, see ContentDescriptor for the values it understands
|
|
223
|
+
* @param key Property name or an Array index
|
|
224
|
+
* @param options Access policy: { disableFileAccess, disableUrlAccess }
|
|
225
|
+
* @param callback Callback function with (err, value)
|
|
226
|
+
*/
|
|
219
227
|
export declare function resolveContent(data: {
|
|
220
228
|
[key: string]: any;
|
|
221
229
|
}, key: string | number, options: ResolveContentOptions | false | undefined, callback: ResolveContentCallback): void;
|
|
230
|
+
/**
|
|
231
|
+
* Resolves a String or a Buffer value for content value
|
|
232
|
+
*
|
|
233
|
+
* @param data An object or an Array you want to resolve an element for, see ContentDescriptor for the values it understands
|
|
234
|
+
* @param key Property name or an Array index
|
|
235
|
+
* @param [options] Optional access policy: { disableFileAccess, disableUrlAccess }
|
|
236
|
+
* @returns Promise that resolves with the value
|
|
237
|
+
*/
|
|
222
238
|
export declare function resolveContent(data: {
|
|
223
239
|
[key: string]: any;
|
|
224
240
|
}, key: string | number, options?: ResolveContentOptions | false): Promise<any>;
|
|
241
|
+
/**
|
|
242
|
+
* Resolves a String or a Buffer value for content value
|
|
243
|
+
*
|
|
244
|
+
* @param data An object or an Array you want to resolve an element for, see ContentDescriptor for the values it understands
|
|
245
|
+
* @param key Property name or an Array index
|
|
246
|
+
* @param options Access policy: { disableFileAccess, disableUrlAccess }
|
|
247
|
+
* @param callback Callback function with (err, value), a Promise is returned if not set
|
|
248
|
+
*/
|
|
225
249
|
export declare function resolveContent(data: {
|
|
226
250
|
[key: string]: any;
|
|
227
251
|
}, key: string | number, options: ResolveContentOptions | false | undefined, callback: ResolveContentCallback | undefined): Promise<any> | void;
|
package/dist/cjs/shared/index.js
CHANGED
|
@@ -362,6 +362,7 @@ exports._logFunc = _logFunc;
|
|
|
362
362
|
* creates a default console logger
|
|
363
363
|
*
|
|
364
364
|
* @param [options] Options object that might include 'logger' value
|
|
365
|
+
* @param [defaults] Fields merged into every log entry, overridden by the fields of the entry itself
|
|
365
366
|
* @return bunyan compatible logger
|
|
366
367
|
*/
|
|
367
368
|
const getLogger = (options, defaults) => {
|
|
@@ -21,13 +21,21 @@ export type HttpProxyClientCallback = Callback<net.Socket>;
|
|
|
21
21
|
* socket.write("GET / HTTP/1.0\r\n\r\n");
|
|
22
22
|
* });
|
|
23
23
|
*
|
|
24
|
-
* @param proxyUrl proxy configuration,
|
|
24
|
+
* @param proxyUrl proxy configuration, e.g. "http://proxy.host:3128/"
|
|
25
25
|
* @param destinationPort Port to open in destination host
|
|
26
26
|
* @param destinationHost Destination hostname
|
|
27
|
-
* @param
|
|
28
|
-
* @param callback Callback to run with the rocket object once connection is established
|
|
27
|
+
* @param callback Callback to run with the socket object once connection is established
|
|
29
28
|
*/
|
|
30
29
|
declare function httpProxyClient(proxyUrl: string, destinationPort: number | string, destinationHost: string, callback: HttpProxyClientCallback): void;
|
|
30
|
+
/**
|
|
31
|
+
* Establishes proxied connection to destinationPort through an HTTPS proxy
|
|
32
|
+
*
|
|
33
|
+
* @param proxyUrl proxy configuration, e.g. "https://proxy.host:3128/"
|
|
34
|
+
* @param destinationPort Port to open in destination host
|
|
35
|
+
* @param destinationHost Destination hostname
|
|
36
|
+
* @param tlsOptions TLS options for the proxy connection (e.g. { rejectUnauthorized: false })
|
|
37
|
+
* @param callback Callback to run with the socket object once connection is established
|
|
38
|
+
*/
|
|
31
39
|
declare function httpProxyClient(proxyUrl: string, destinationPort: number | string, destinationHost: string, tlsOptions: HttpProxyClientOptions | undefined, callback: HttpProxyClientCallback): void;
|
|
32
40
|
/**
|
|
33
41
|
* Socket timeout in milliseconds while the CONNECT handshake is in progress, defaults to 30 seconds.
|
|
@@ -126,18 +126,28 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
|
|
|
126
126
|
.join('\r\n') +
|
|
127
127
|
// End request
|
|
128
128
|
'\r\n\r\n');
|
|
129
|
-
|
|
129
|
+
// The response is collected as chunks and only the bytes that just arrived, together
|
|
130
|
+
// with the three before them, are searched for the end of the headers. Appending to a
|
|
131
|
+
// string and searching all of it again re-read the whole response on every chunk.
|
|
132
|
+
const chunks = [];
|
|
133
|
+
let received = 0;
|
|
134
|
+
let tail = '';
|
|
130
135
|
const onSocketData = (chunk) => {
|
|
131
136
|
let match;
|
|
132
|
-
let remainder;
|
|
133
137
|
if (finished) {
|
|
134
138
|
return;
|
|
135
139
|
}
|
|
136
|
-
|
|
137
|
-
|
|
140
|
+
const window = tail + chunk.toString('binary');
|
|
141
|
+
const windowEnd = window.indexOf('\r\n\r\n');
|
|
142
|
+
chunks.push(chunk);
|
|
143
|
+
received += chunk.length;
|
|
144
|
+
tail = window.slice(-3);
|
|
145
|
+
if (windowEnd >= 0) {
|
|
138
146
|
socket.removeListener('data', onSocketData);
|
|
139
|
-
|
|
140
|
-
|
|
147
|
+
const headerEnd = received - window.length + windowEnd;
|
|
148
|
+
const response = Buffer.concat(chunks, received).toString('binary');
|
|
149
|
+
const headers = response.substr(0, headerEnd);
|
|
150
|
+
const remainder = response.substr(headerEnd + 4);
|
|
141
151
|
if (remainder) {
|
|
142
152
|
socket.unshift(Buffer.from(remainder, 'binary'));
|
|
143
153
|
}
|
|
@@ -161,7 +171,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
|
|
|
161
171
|
socket.setTimeout(0);
|
|
162
172
|
return done(null, socket);
|
|
163
173
|
}
|
|
164
|
-
if (
|
|
174
|
+
if (received > MAX_RESPONSE_HEADER_BYTES) {
|
|
165
175
|
socket.removeListener('data', onSocketData);
|
|
166
176
|
const err = new Error('Proxy response headers too large');
|
|
167
177
|
err.code = errors.EPROXY;
|
|
@@ -379,7 +379,7 @@ declare class SMTPConnection extends EventEmitter {
|
|
|
379
379
|
*
|
|
380
380
|
* @param envelope Envelope object, {from: addr, to: [addr]}
|
|
381
381
|
* @param message String, Buffer or a Stream
|
|
382
|
-
* @param
|
|
382
|
+
* @param done Callback to return once sending is completed
|
|
383
383
|
*/
|
|
384
384
|
send(envelope: SMTPEnvelope, message: string | Buffer | Readable, done: SMTPConnectionSendCallback): void;
|
|
385
385
|
/**
|