nodemailer 10.0.13 → 10.0.14
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 +12 -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/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/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 +5 -5
|
@@ -39,6 +39,7 @@ export function quoteString(value) {
|
|
|
39
39
|
* no need to encode the values in any way. If the value is plaintext but has
|
|
40
40
|
* longer lines then allowed, then use format=flowed
|
|
41
41
|
*
|
|
42
|
+
* @param str Multi line string to check
|
|
42
43
|
* @param lineLength Max line length to check for
|
|
43
44
|
* @returns Returns true if there is at least one line longer than lineLength chars
|
|
44
45
|
*/
|
|
@@ -185,7 +186,10 @@ export function buildHeaderValue(structured) {
|
|
|
185
186
|
}
|
|
186
187
|
});
|
|
187
188
|
}
|
|
188
|
-
else if (/[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
|
|
189
|
+
else if (!value.length || /[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
|
|
190
|
+
// a parameter value is a token or a quoted-string and a token is never empty, so
|
|
191
|
+
// a valueless parameter such as the 'flag' of 'multipart/mixed; flag; boundary=b'
|
|
192
|
+
// goes out as 'flag=""' rather than as the 'flag=' that parses as neither
|
|
189
193
|
paramsArray.push(param + '=' + JSON.stringify(value));
|
|
190
194
|
}
|
|
191
195
|
else {
|
|
@@ -207,9 +211,9 @@ export function buildHeaderValue(structured) {
|
|
|
207
211
|
* title*0*=utf-8''unicode
|
|
208
212
|
* title*1*=%20string
|
|
209
213
|
*
|
|
214
|
+
* @param key Parameter name the generated keys are built from, for example title
|
|
210
215
|
* @param data String to be encoded
|
|
211
216
|
* @param [maxLength=50] Max length for generated chunks
|
|
212
|
-
* @param [fromCharset='UTF-8'] Source sharacter set
|
|
213
217
|
* @return A list of encoded keys and headers
|
|
214
218
|
*/
|
|
215
219
|
export function buildHeaderParam(key, data, maxLength) {
|
|
@@ -336,6 +340,26 @@ export function buildHeaderParam(key, data, maxLength) {
|
|
|
336
340
|
value: item.line
|
|
337
341
|
}));
|
|
338
342
|
}
|
|
343
|
+
/**
|
|
344
|
+
* An RFC 2045 token: printable ASCII without SPACE, the control characters, DEL and the
|
|
345
|
+
* tspecials. A charset name is one, and the name a continuation carries is written into
|
|
346
|
+
* the encoded word the parameter value becomes, so it is checked against this before it
|
|
347
|
+
* goes in. Whitespace used to come off it only because the value was trimmed first.
|
|
348
|
+
*/
|
|
349
|
+
const TOKEN = /^[^\x00-\x20\x7f()<>@,;:\\"/[\]?=]+$/;
|
|
350
|
+
/**
|
|
351
|
+
* Whether a string can be a header parameter name.
|
|
352
|
+
*
|
|
353
|
+
* A parameter name is a token, so it is never empty. A "__proto__" name would target the
|
|
354
|
+
* prototype chain of the params object instead of an own property of it and read back as
|
|
355
|
+
* Object.prototype, so it is no name either.
|
|
356
|
+
*
|
|
357
|
+
* @param name Candidate parameter name, already lowercased
|
|
358
|
+
* @return true when the name can be used
|
|
359
|
+
*/
|
|
360
|
+
function _isParamName(name) {
|
|
361
|
+
return !!name && !isProtoKey(name);
|
|
362
|
+
}
|
|
339
363
|
/**
|
|
340
364
|
* Parses a header value with key=value arguments into a structured
|
|
341
365
|
* object.
|
|
@@ -356,134 +380,224 @@ export function parseHeaderValue(str) {
|
|
|
356
380
|
value: '',
|
|
357
381
|
params: {}
|
|
358
382
|
};
|
|
359
|
-
//
|
|
360
|
-
//
|
|
361
|
-
//
|
|
383
|
+
// A duplicated parameter resolves to its first occurrence, the way a duplicated header
|
|
384
|
+
// does. Letting the last one win disagrees with the receivers that take the first, and
|
|
385
|
+
// the two readings of 'boundary="b"; boundary="c"' name different delimiters. The
|
|
386
|
+
// continuation join below is the only writer of the name it builds, so it tests the
|
|
387
|
+
// name with _isParamName directly rather than taking that rule along from here.
|
|
362
388
|
const setParam = (name, value) => {
|
|
363
|
-
|
|
389
|
+
name = name.toLowerCase();
|
|
390
|
+
if (_isParamName(name) && !Object.prototype.hasOwnProperty.call(response.params, name)) {
|
|
364
391
|
response.params[name] = value;
|
|
365
392
|
}
|
|
366
393
|
};
|
|
367
394
|
let key = false;
|
|
368
395
|
let value = '';
|
|
369
|
-
let
|
|
396
|
+
let stage = 'value';
|
|
370
397
|
let quote = false;
|
|
371
398
|
let escaped = false;
|
|
372
399
|
let chr;
|
|
400
|
+
// Whitespace seen outside a quoted string is held back until a significant character
|
|
401
|
+
// follows it, so the whitespace around a value is dropped without trimming spaces the
|
|
402
|
+
// sender quoted on purpose. Trimming the stored value instead loses the trailing space
|
|
403
|
+
// of 'filename*0="Annual Report "', which the next continuation section is appended to.
|
|
404
|
+
let pendingSpace = '';
|
|
405
|
+
let quoteClosed = false;
|
|
406
|
+
// Whitespace ahead of the first character of a value is padding and is dropped, the
|
|
407
|
+
// whitespace between two characters of it is content
|
|
408
|
+
const flushSpace = () => {
|
|
409
|
+
if (value.length) {
|
|
410
|
+
value += pendingSpace;
|
|
411
|
+
}
|
|
412
|
+
pendingSpace = '';
|
|
413
|
+
};
|
|
414
|
+
const addChr = (c) => {
|
|
415
|
+
flushSpace();
|
|
416
|
+
value += c;
|
|
417
|
+
};
|
|
418
|
+
const takeValue = () => {
|
|
419
|
+
const taken = value;
|
|
420
|
+
value = '';
|
|
421
|
+
pendingSpace = '';
|
|
422
|
+
quoteClosed = false;
|
|
423
|
+
return taken;
|
|
424
|
+
};
|
|
425
|
+
const storeValue = () => {
|
|
426
|
+
const taken = takeValue();
|
|
427
|
+
if (key === false) {
|
|
428
|
+
response.value = taken;
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
setParam(key, taken);
|
|
432
|
+
}
|
|
433
|
+
};
|
|
434
|
+
// A parameter name with no '=' is a valueless parameter, not the start of the next one.
|
|
435
|
+
// Without this the name keeps growing across the ';' and swallows whatever follows, which
|
|
436
|
+
// is how 'multipart/mixed; flag; boundary="AAA"' lost its boundary to a parameter named
|
|
437
|
+
// 'flag; boundary' and left the node declaring the generated boundary beside the asked
|
|
438
|
+
// for one, so a receiver reading the first of the two found no delimiter it matched.
|
|
439
|
+
const storeEmptyKey = () => {
|
|
440
|
+
setParam(takeValue().trim(), '');
|
|
441
|
+
};
|
|
373
442
|
for (let i = 0, len = str.length; i < len; i++) {
|
|
374
443
|
chr = str.charAt(i);
|
|
375
|
-
if (
|
|
444
|
+
if (stage === 'key') {
|
|
376
445
|
if (chr === '=') {
|
|
377
|
-
key =
|
|
378
|
-
|
|
379
|
-
|
|
446
|
+
key = takeValue().trim();
|
|
447
|
+
stage = 'value';
|
|
448
|
+
continue;
|
|
449
|
+
}
|
|
450
|
+
if (chr === ';') {
|
|
451
|
+
storeEmptyKey();
|
|
380
452
|
continue;
|
|
381
453
|
}
|
|
382
454
|
value += chr;
|
|
383
455
|
}
|
|
384
456
|
else {
|
|
457
|
+
if (quoteClosed && chr !== ';') {
|
|
458
|
+
// Nothing behind a closed quoted string reaches the value. RFC 2045 says a
|
|
459
|
+
// parameter value is a token or a quoted string and not both, so what follows
|
|
460
|
+
// one is junk and only the ';' that ends the parameter still counts. Tested
|
|
461
|
+
// ahead of the branches rather than beside the append at the foot of them,
|
|
462
|
+
// where each branch above was a way around it: a second '"' reopened quoting
|
|
463
|
+
// and swallowed the rest of the header, so 'boundary="AAA" "; boundary=BBB"'
|
|
464
|
+
// read as a single boundary of 'AAA ', and the junk of
|
|
465
|
+
// 'boundary="AAA" (unterminated comment' still joined the declared boundary
|
|
466
|
+
// through the escape branch. quoteClosed is only ever set while no quote is
|
|
467
|
+
// open, and this is what keeps one from being opened afterwards.
|
|
468
|
+
escaped = false;
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
385
471
|
if (escaped) {
|
|
386
|
-
|
|
472
|
+
addChr(chr);
|
|
387
473
|
}
|
|
388
|
-
else if (chr === '\\') {
|
|
474
|
+
else if (quote && chr === '\\') {
|
|
475
|
+
// a backslash only escapes inside a quoted string, everywhere else it is an
|
|
476
|
+
// ordinary character. Treating it as an escape turns the parameter value
|
|
477
|
+
// 'C:\Users\me\report.txt' into 'C:Usersmereport.txt'
|
|
389
478
|
escaped = true;
|
|
390
479
|
continue;
|
|
391
480
|
}
|
|
392
481
|
else if (quote && chr === quote) {
|
|
393
482
|
quote = false;
|
|
483
|
+
quoteClosed = true;
|
|
394
484
|
}
|
|
395
485
|
else if (!quote && chr === '"') {
|
|
396
486
|
quote = chr;
|
|
487
|
+
flushSpace();
|
|
397
488
|
}
|
|
398
489
|
else if (!quote && chr === ';') {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
}
|
|
405
|
-
type = 'key';
|
|
406
|
-
value = '';
|
|
490
|
+
storeValue();
|
|
491
|
+
stage = 'key';
|
|
492
|
+
}
|
|
493
|
+
else if (!quote && (chr === ' ' || chr === '\t')) {
|
|
494
|
+
pendingSpace += chr;
|
|
407
495
|
}
|
|
408
496
|
else {
|
|
409
|
-
|
|
497
|
+
addChr(chr);
|
|
410
498
|
}
|
|
411
499
|
escaped = false;
|
|
412
500
|
}
|
|
413
501
|
}
|
|
414
|
-
if (
|
|
415
|
-
|
|
416
|
-
response.value = value.trim();
|
|
417
|
-
}
|
|
418
|
-
else {
|
|
419
|
-
setParam(key, value.trim());
|
|
420
|
-
}
|
|
502
|
+
if (stage === 'value') {
|
|
503
|
+
storeValue();
|
|
421
504
|
}
|
|
422
|
-
else
|
|
423
|
-
|
|
505
|
+
else {
|
|
506
|
+
// a key with no value, as in 'Header-Key: somevalue; key=value; emptykey'
|
|
507
|
+
storeEmptyKey();
|
|
424
508
|
}
|
|
425
509
|
// handle parameter value continuations
|
|
426
510
|
// https://tools.ietf.org/html/rfc2231#section-3
|
|
427
|
-
//
|
|
511
|
+
// Sections are collected in a list and ordered below rather than written into an array
|
|
512
|
+
// at their own section number. An index write makes the array as long as the number the
|
|
513
|
+
// header asked for, and the join that follows walks all of it, so the 55 byte value
|
|
514
|
+
// "attachment; filename*0*=utf-8''a; filename*4000000000=b" held a core for over two
|
|
515
|
+
// minutes.
|
|
516
|
+
const continuations = new Map();
|
|
428
517
|
Object.keys(response.params).forEach(key => {
|
|
429
|
-
|
|
430
|
-
if (
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
518
|
+
const match = key.match(/(\*(\d+)|\*(\d+)\*|\*)$/);
|
|
519
|
+
if (!match) {
|
|
520
|
+
// not a continuation parameter, there is nothing to join
|
|
521
|
+
return;
|
|
522
|
+
}
|
|
523
|
+
const actualKey = key.substr(0, match.index);
|
|
524
|
+
const nr = Number(match[2] || match[3]) || 0;
|
|
525
|
+
// RFC 2231 section 4.1: only a section whose name ends in '*' is percent encoded
|
|
526
|
+
const encoded = match[0].substr(-1) === '*';
|
|
527
|
+
// remove the old reference
|
|
528
|
+
let value = response.params[key];
|
|
529
|
+
delete response.params[key];
|
|
530
|
+
if (!_isParamName(actualKey)) {
|
|
531
|
+
// the joined value can not be written back under this name. It is empty when the
|
|
532
|
+
// continuation suffix was all there was of it, as in the bare '*' of 'text/plain; a;*'
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
535
|
+
let continuation = continuations.get(actualKey);
|
|
536
|
+
if (!continuation) {
|
|
537
|
+
continuation = { charset: false, sections: [] };
|
|
538
|
+
continuations.set(actualKey, continuation);
|
|
539
|
+
}
|
|
540
|
+
const charsetMatch = nr === 0 && encoded ? value.match(/^([^']*)'[^']*'(.*)$/) : null;
|
|
541
|
+
if (charsetMatch) {
|
|
542
|
+
// the charset is a token, and anything else named as one is no charset a consumer
|
|
543
|
+
// could resolve, so it reads as the unnamed case rather than being carried into
|
|
544
|
+
// the encoded word below. A "\r\n" of a prefix would otherwise reach a consumer
|
|
545
|
+
// of the parsed value as the charset of a word that no decoder can act on
|
|
546
|
+
continuation.charset = TOKEN.test(charsetMatch[1]) ? charsetMatch[1] : 'iso-8859-1';
|
|
547
|
+
value = charsetMatch[2];
|
|
454
548
|
}
|
|
549
|
+
continuation.sections.push({ nr, value, encoded });
|
|
455
550
|
});
|
|
456
551
|
// concatenate split rfc2231 strings and convert encoded strings to mime encoded words
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
return '_';
|
|
473
|
-
}
|
|
474
|
-
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
475
|
-
})
|
|
476
|
-
// change from urlencoding to percent encoding
|
|
477
|
-
.replace(/%/g, '=') +
|
|
478
|
-
'?=';
|
|
479
|
-
}
|
|
480
|
-
else {
|
|
481
|
-
response.params[key] = value;
|
|
482
|
-
}
|
|
552
|
+
continuations.forEach((continuation, key) => {
|
|
553
|
+
if (Object.prototype.hasOwnProperty.call(response.params, key)) {
|
|
554
|
+
// The same name was also given as a plain parameter, which the starred keys were
|
|
555
|
+
// just deleted from around, so this write would be the only one in the function
|
|
556
|
+
// to override a name already taken. 'filename=plain.txt; filename*0=evil.txt'
|
|
557
|
+
// resolves to the plain parameter either way round, so the reading does not come
|
|
558
|
+
// down to which of the two spellings the sender put first
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
continuation.sections.sort((a, b) => a.nr - b.nr);
|
|
562
|
+
if (!continuation.charset) {
|
|
563
|
+
// nothing said which charset the percent escapes of an encoded section are in,
|
|
564
|
+
// so every section is passed on as the text it already is
|
|
565
|
+
response.params[key] = continuation.sections.map(section => section.value).join('');
|
|
566
|
+
return;
|
|
483
567
|
}
|
|
568
|
+
// convert "%AB" to "=?charset?Q?=AB?="
|
|
569
|
+
response.params[key] = '=?' + continuation.charset + '?Q?' + continuation.sections.map(_encodeContinuationSection).join('') + '?=';
|
|
484
570
|
});
|
|
485
571
|
return response;
|
|
486
572
|
}
|
|
573
|
+
/**
|
|
574
|
+
* Renders one parameter value continuation section as the payload of a Q encoded word.
|
|
575
|
+
*
|
|
576
|
+
* A section whose name ends in '*' is percent encoded and its escapes carry the bytes of
|
|
577
|
+
* the value, so they only have to be rewritten into the "=AB" spelling a Q encoded word
|
|
578
|
+
* uses. A section without the '*' is literal text (RFC 2231 section 4.1), so its '%' is a
|
|
579
|
+
* '%' and is escaped along with the characters a Q encoded word can not carry bare.
|
|
580
|
+
* Decoding a literal section invents bytes that never appeared on the wire: it is how the
|
|
581
|
+
* value 'filename*0*=utf-8''safe; filename*1=%2F..%2F..%2Fetc%2Fpasswd' was emitted as a
|
|
582
|
+
* filename every receiving client reads back as 'safe/../../etc/passwd'.
|
|
583
|
+
*
|
|
584
|
+
* @param section One collected continuation section
|
|
585
|
+
* @return The section as Q encoded word payload
|
|
586
|
+
*/
|
|
587
|
+
function _encodeContinuationSection(section) {
|
|
588
|
+
const specials = section.encoded ? /[=?_\s]/g : /[=?_\s%]/g;
|
|
589
|
+
return (section.value
|
|
590
|
+
// fix invalidly encoded chars
|
|
591
|
+
.replace(specials, s => {
|
|
592
|
+
const c = s.charCodeAt(0).toString(16);
|
|
593
|
+
if (s === ' ') {
|
|
594
|
+
return '_';
|
|
595
|
+
}
|
|
596
|
+
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
597
|
+
})
|
|
598
|
+
// change from urlencoding to percent encoding
|
|
599
|
+
.replace(/%/g, '='));
|
|
600
|
+
}
|
|
487
601
|
/**
|
|
488
602
|
* Returns file extension for a content type string. If no suitable extensions
|
|
489
603
|
* are found, 'bin' is used as the default extension
|
|
@@ -1256,6 +1256,7 @@ class MimeNode {
|
|
|
1256
1256
|
*
|
|
1257
1257
|
* @param addresses An array of address objects
|
|
1258
1258
|
* @param [uniqueList] An array to be populated with addresses
|
|
1259
|
+
* @param [seenAddresses] Addresses already added to uniqueList, shared with recursive calls to keep deduplication linear
|
|
1259
1260
|
* @return address string
|
|
1260
1261
|
* @internal
|
|
1261
1262
|
*/
|
|
@@ -1393,7 +1394,7 @@ class MimeNode {
|
|
|
1393
1394
|
/**
|
|
1394
1395
|
* If needed, mime encodes the name part
|
|
1395
1396
|
*
|
|
1396
|
-
* @param
|
|
1397
|
+
* @param value Name part of an address
|
|
1397
1398
|
* @returns Mime word encoded string if needed
|
|
1398
1399
|
* @internal
|
|
1399
1400
|
*/
|
package/dist/esm/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/esm/package-info.js
CHANGED
|
@@ -175,6 +175,7 @@ const basicToDigit = function (codePoint) {
|
|
|
175
175
|
* @see `basicToDigit()`
|
|
176
176
|
* @private
|
|
177
177
|
* @param digit The numeric value of a basic code point.
|
|
178
|
+
* @param flag Non-zero to use the uppercase form of the code point.
|
|
178
179
|
* @returns The basic code point whose value (when used for
|
|
179
180
|
* representing integers) is `digit`, which needs to be in the range
|
|
180
181
|
* `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/esm/shared/index.js
CHANGED
|
@@ -318,6 +318,7 @@ export const _logFunc = (logger, level, defaults, data, message, ...args) => {
|
|
|
318
318
|
* creates a default console logger
|
|
319
319
|
*
|
|
320
320
|
* @param [options] Options object that might include 'logger' value
|
|
321
|
+
* @param [defaults] Fields merged into every log entry, overridden by the fields of the entry itself
|
|
321
322
|
* @return bunyan compatible logger
|
|
322
323
|
*/
|
|
323
324
|
export 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.
|
|
@@ -88,18 +88,28 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
|
|
|
88
88
|
.join('\r\n') +
|
|
89
89
|
// End request
|
|
90
90
|
'\r\n\r\n');
|
|
91
|
-
|
|
91
|
+
// The response is collected as chunks and only the bytes that just arrived, together
|
|
92
|
+
// with the three before them, are searched for the end of the headers. Appending to a
|
|
93
|
+
// string and searching all of it again re-read the whole response on every chunk.
|
|
94
|
+
const chunks = [];
|
|
95
|
+
let received = 0;
|
|
96
|
+
let tail = '';
|
|
92
97
|
const onSocketData = (chunk) => {
|
|
93
98
|
let match;
|
|
94
|
-
let remainder;
|
|
95
99
|
if (finished) {
|
|
96
100
|
return;
|
|
97
101
|
}
|
|
98
|
-
|
|
99
|
-
|
|
102
|
+
const window = tail + chunk.toString('binary');
|
|
103
|
+
const windowEnd = window.indexOf('\r\n\r\n');
|
|
104
|
+
chunks.push(chunk);
|
|
105
|
+
received += chunk.length;
|
|
106
|
+
tail = window.slice(-3);
|
|
107
|
+
if (windowEnd >= 0) {
|
|
100
108
|
socket.removeListener('data', onSocketData);
|
|
101
|
-
|
|
102
|
-
|
|
109
|
+
const headerEnd = received - window.length + windowEnd;
|
|
110
|
+
const response = Buffer.concat(chunks, received).toString('binary');
|
|
111
|
+
const headers = response.substr(0, headerEnd);
|
|
112
|
+
const remainder = response.substr(headerEnd + 4);
|
|
103
113
|
if (remainder) {
|
|
104
114
|
socket.unshift(Buffer.from(remainder, 'binary'));
|
|
105
115
|
}
|
|
@@ -123,7 +133,7 @@ function httpProxyClient(proxyUrl, destinationPort, destinationHost, tlsOptions,
|
|
|
123
133
|
socket.setTimeout(0);
|
|
124
134
|
return done(null, socket);
|
|
125
135
|
}
|
|
126
|
-
if (
|
|
136
|
+
if (received > MAX_RESPONSE_HEADER_BYTES) {
|
|
127
137
|
socket.removeListener('data', onSocketData);
|
|
128
138
|
const err = new Error('Proxy response headers too large');
|
|
129
139
|
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
|
/**
|