@mavogel/awscdk-rootmail 0.1.14 → 0.1.16
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/.jsii +10 -10
- package/lib/rootmail.js +1 -1
- package/lib/ses-receive.js +1 -1
- package/node_modules/@aws-sdk/checksums/package.json +3 -3
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +23 -1
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/CloudWatchLogsServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/models/errors.js +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/CloudWatchLogsServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/CloudWatchLogsServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +4 -4
- package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +71 -1
- package/node_modules/@aws-sdk/client-route-53/dist-es/models/Route53ServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-es/models/errors.js +69 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/models/Route53ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/models/errors.d.ts +69 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/Route53ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-route-53/dist-types/ts3.4/models/errors.d.ts +69 -0
- package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
- package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +23 -1
- package/node_modules/@aws-sdk/client-s3/dist-es/models/S3ServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-es/models/errors.js +21 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/models/S3ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/S3ServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-s3/dist-types/ts3.4/models/errors.d.ts +21 -0
- package/node_modules/@aws-sdk/client-s3/package.json +8 -8
- package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +36 -1
- package/node_modules/@aws-sdk/client-ses/dist-es/models/SESServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-es/models/errors.js +34 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/models/SESServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/models/errors.d.ts +34 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/SESServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ses/dist-types/ts3.4/models/errors.d.ts +34 -0
- package/node_modules/@aws-sdk/client-ses/package.json +4 -4
- package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +142 -1
- package/node_modules/@aws-sdk/client-ssm/dist-es/models/SSMServiceException.js +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-es/models/errors.js +140 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/models/SSMServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/models/errors.d.ts +140 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/SSMServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/client-ssm/dist-types/ts3.4/models/errors.d.ts +140 -0
- package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
- package/node_modules/@aws-sdk/core/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-env/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-ini/package.json +10 -10
- package/node_modules/@aws-sdk/credential-provider-login/package.json +4 -4
- package/node_modules/@aws-sdk/credential-provider-node/package.json +8 -8
- package/node_modules/@aws-sdk/credential-provider-process/package.json +3 -3
- package/node_modules/@aws-sdk/credential-provider-sso/package.json +5 -5
- package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +4 -4
- package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +4 -4
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +11 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +6 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +6 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +13 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +9 -1
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/CognitoIdentityServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/cognito-identity/models/errors.js +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/SigninServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/signin/models/errors.js +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/SSOServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso/models/errors.js +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/SSOOIDCServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sso-oidc/models/errors.js +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/STSServiceException.js +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-es/submodules/sts/models/errors.js +7 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/cognito-identity/models/errors.d.ts +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/SigninServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/signin/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/SSOServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sso-oidc/models/errors.d.ts +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/STSServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/submodules/sts/models/errors.d.ts +7 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/CognitoIdentityServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/cognito-identity/models/errors.d.ts +9 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/SigninServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/signin/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/SSOServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso/models/errors.d.ts +4 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/SSOOIDCServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sso-oidc/models/errors.d.ts +11 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/STSServiceException.d.ts +1 -0
- package/node_modules/@aws-sdk/nested-clients/dist-types/ts3.4/submodules/sts/models/errors.d.ts +7 -0
- package/node_modules/@aws-sdk/nested-clients/package.json +4 -4
- package/node_modules/@aws-sdk/signature-v4-multi-region/package.json +2 -2
- package/node_modules/@aws-sdk/token-providers/package.json +4 -4
- package/node_modules/@smithy/core/dist-cjs/submodules/client/index.js +33 -11
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.browser.js +2 -1
- package/node_modules/@smithy/core/dist-cjs/submodules/endpoints/index.js +2 -1
- package/node_modules/@smithy/core/dist-cjs/submodules/schema/index.js +3 -0
- package/node_modules/@smithy/core/dist-es/submodules/client/smithy-client/exceptions.js +33 -11
- package/node_modules/@smithy/core/dist-es/submodules/endpoints/util-endpoints/cache/EndpointCache.js +2 -1
- package/node_modules/@smithy/core/dist-es/submodules/schema/TypeRegistry.js +3 -0
- package/node_modules/@smithy/core/dist-types/submodules/client/smithy-client/exceptions.d.ts +1 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/client/smithy-client/exceptions.d.ts +1 -0
- package/node_modules/@smithy/core/package.json +1 -1
- package/node_modules/@smithy/signature-v4/dist-cjs/index.js +4 -1
- package/node_modules/@smithy/signature-v4/dist-es/getCanonicalHeaders.js +4 -1
- package/node_modules/@smithy/signature-v4/package.json +3 -3
- package/node_modules/mailparser/.release-please-manifest.json +1 -1
- package/node_modules/mailparser/CHANGELOG.md +7 -0
- package/node_modules/mailparser/package.json +2 -2
- package/node_modules/nodemailer/CHANGELOG.md +27 -0
- package/node_modules/nodemailer/dist/cjs/base64/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/cjs/base64/index.js +80 -41
- package/node_modules/nodemailer/dist/cjs/dkim/index.js +43 -29
- package/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +2 -2
- package/node_modules/nodemailer/dist/cjs/errors.d.ts +17 -0
- package/node_modules/nodemailer/dist/cjs/errors.js +12 -1
- package/node_modules/nodemailer/dist/cjs/fetch/index.js +23 -3
- package/node_modules/nodemailer/dist/cjs/mailer/index.js +23 -7
- package/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +5 -0
- package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +20 -0
- package/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +1 -1
- package/node_modules/nodemailer/dist/cjs/mime-node/index.js +165 -105
- package/node_modules/nodemailer/dist/cjs/nodemailer.js +12 -9
- package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
- package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
- package/node_modules/nodemailer/dist/cjs/qp/index.d.ts +2 -0
- package/node_modules/nodemailer/dist/cjs/qp/index.js +103 -33
- package/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +54 -14
- package/node_modules/nodemailer/dist/cjs/shared/index.d.ts +32 -1
- package/node_modules/nodemailer/dist/cjs/shared/index.js +212 -115
- package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +3 -1
- package/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +88 -68
- package/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +7 -1
- package/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +348 -136
- package/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +7 -1
- package/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +106 -17
- package/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +8 -2
- package/node_modules/nodemailer/dist/esm/base64/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/esm/base64/index.js +80 -41
- package/node_modules/nodemailer/dist/esm/dkim/index.js +43 -29
- package/node_modules/nodemailer/dist/esm/dkim/message-parser.js +2 -2
- package/node_modules/nodemailer/dist/esm/errors.d.ts +17 -0
- package/node_modules/nodemailer/dist/esm/errors.js +10 -0
- package/node_modules/nodemailer/dist/esm/fetch/index.js +23 -3
- package/node_modules/nodemailer/dist/esm/mailer/index.js +23 -7
- package/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +5 -0
- package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +20 -0
- package/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +1 -1
- package/node_modules/nodemailer/dist/esm/mime-node/index.js +166 -106
- package/node_modules/nodemailer/dist/esm/nodemailer.js +12 -9
- package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
- package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
- package/node_modules/nodemailer/dist/esm/qp/index.d.ts +2 -0
- package/node_modules/nodemailer/dist/esm/qp/index.js +103 -33
- package/node_modules/nodemailer/dist/esm/sendmail-transport/index.js +54 -14
- package/node_modules/nodemailer/dist/esm/shared/index.d.ts +32 -1
- package/node_modules/nodemailer/dist/esm/shared/index.js +207 -115
- package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +3 -1
- package/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.js +88 -68
- package/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +7 -1
- package/node_modules/nodemailer/dist/esm/smtp-connection/index.js +348 -136
- package/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +3 -0
- package/node_modules/nodemailer/dist/esm/smtp-pool/index.js +7 -1
- package/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.js +106 -17
- package/node_modules/nodemailer/dist/esm/smtp-transport/index.js +8 -2
- package/node_modules/nodemailer/package.json +1 -1
- package/node_modules/uuid/package.json +17 -17
- package/package.json +9 -9
|
@@ -71,6 +71,14 @@ const PLAIN_ADDRESS = /^[^\s"(),:;<>@[\\\]]+@[^\s"(),:;<>@[\\\]]+$/;
|
|
|
71
71
|
// unroutable garbage into mail for a domain the sender never named. None of these
|
|
72
72
|
// characters are legal in a domain, so keep them away from the mapper.
|
|
73
73
|
const URL_PARSER_UNSAFE = /[/\\?#%\x00-\x20\x7F]/;
|
|
74
|
+
// pipeline() needs a callback to not throw, the errors it sees reach the last stream anyway
|
|
75
|
+
const PIPELINE_NOOP = () => false;
|
|
76
|
+
// The error a node streaming into an output that was destroyed stops with
|
|
77
|
+
function abortedError() {
|
|
78
|
+
const err = new Error('Message stream was closed before the message was generated');
|
|
79
|
+
err.code = errors.ESTREAM;
|
|
80
|
+
return err;
|
|
81
|
+
}
|
|
74
82
|
/**
|
|
75
83
|
* Encodes a domain the way browsers, the WHATWG URL Standard and DNS facing resolvers do,
|
|
76
84
|
* which is with UTS-46 mapping applied before the Punycode step.
|
|
@@ -90,9 +98,14 @@ const URL_PARSER_UNSAFE = /[/\\?#%\x00-\x20\x7F]/;
|
|
|
90
98
|
*/
|
|
91
99
|
function normalizeDomain(domain, toUnicode) {
|
|
92
100
|
// domainToASCII and domainToUnicode landed in Node 7, the bundled codec covers Node 6
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
101
|
+
if (typeof node_url_1.default.domainToASCII === 'function' &&
|
|
102
|
+
typeof node_url_1.default.domainToUnicode === 'function' &&
|
|
103
|
+
!URL_PARSER_UNSAFE.test(domain)) {
|
|
104
|
+
// The U-label form is decoded from the A-label form rather than from the input:
|
|
105
|
+
// Deno's domainToASCII returns an empty string for a label it cannot decode, while
|
|
106
|
+
// its domainToUnicode returns the label with U+FFFD in place of the bad part
|
|
107
|
+
const ascii = node_url_1.default.domainToASCII(domain);
|
|
108
|
+
const mapped = ascii && toUnicode ? node_url_1.default.domainToUnicode(ascii) : ascii;
|
|
96
109
|
if (mapped) {
|
|
97
110
|
return mapped;
|
|
98
111
|
}
|
|
@@ -444,11 +457,7 @@ class MimeNode {
|
|
|
444
457
|
if (typeof this.content.pipe === 'function') {
|
|
445
458
|
// pre-stream handler. might be triggered if a stream is set as content
|
|
446
459
|
// and 'error' fires before anything is done with this stream
|
|
447
|
-
this.
|
|
448
|
-
this.content.removeListener('error', this._contentErrorHandler);
|
|
449
|
-
this.content = err;
|
|
450
|
-
};
|
|
451
|
-
this.content.once('error', this._contentErrorHandler);
|
|
460
|
+
shared.recordStreamErrors(this.content);
|
|
452
461
|
}
|
|
453
462
|
else if (typeof this.content === 'string') {
|
|
454
463
|
this._isPlainText = mimeFuncs.isPlainText(this.content);
|
|
@@ -660,40 +669,45 @@ class MimeNode {
|
|
|
660
669
|
createReadStream(options) {
|
|
661
670
|
options = options || {};
|
|
662
671
|
const stream = new node_stream_1.PassThrough(options);
|
|
663
|
-
let outputStream = stream;
|
|
664
|
-
let transform;
|
|
665
672
|
this.stream(stream, options, err => {
|
|
666
673
|
if (err) {
|
|
667
|
-
|
|
674
|
+
stream.destroy(err);
|
|
668
675
|
return;
|
|
669
676
|
}
|
|
670
677
|
stream.end();
|
|
671
678
|
});
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
+
// the content streams of the nodes the message did not get to are not going to be read
|
|
680
|
+
stream.once('close', () => {
|
|
681
|
+
if (!stream.writableFinished) {
|
|
682
|
+
this._destroyContentStreams();
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
// The stages are joined with pipeline, which destroys all of them when any one fails
|
|
686
|
+
// or is destroyed. An error anywhere reaches the returned stream exactly once, and a
|
|
687
|
+
// consumer that destroys the returned stream stops the tree from reading its sources
|
|
688
|
+
const stages = [stream];
|
|
689
|
+
for (const transform of this._transforms) {
|
|
690
|
+
stages.push(typeof transform === 'function' ? transform() : transform);
|
|
679
691
|
}
|
|
680
692
|
// ensure terminating newline after possible user transforms
|
|
681
|
-
|
|
682
|
-
outputStream.
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
693
|
+
stages.push(new last_newline_js_1.default());
|
|
694
|
+
let outputStream = (0, node_stream_1.pipeline)(stages, PIPELINE_NOOP);
|
|
695
|
+
// dkim and stuff. A process function reads its input itself and reports the errors of
|
|
696
|
+
// that input on its output, so only a consumer abort has to be carried back upstream
|
|
697
|
+
for (const processFunc of this._processFuncs) {
|
|
698
|
+
const input = outputStream;
|
|
699
|
+
outputStream = processFunc(input);
|
|
700
|
+
if (outputStream !== input) {
|
|
701
|
+
(0, node_stream_1.finished)(outputStream, err => {
|
|
702
|
+
if (err) {
|
|
703
|
+
input.destroy();
|
|
704
|
+
}
|
|
705
|
+
});
|
|
706
|
+
}
|
|
690
707
|
}
|
|
691
708
|
if (this.newline) {
|
|
692
709
|
const winbreak = ['win', 'windows', 'dos', '\r\n'].includes(this.newline.toString().toLowerCase());
|
|
693
|
-
|
|
694
|
-
const stream = outputStream.pipe(newlineTransform);
|
|
695
|
-
outputStream.on('error', err => stream.emit('error', err));
|
|
696
|
-
return stream;
|
|
710
|
+
outputStream = (0, node_stream_1.pipeline)(outputStream, winbreak ? new le_windows_js_1.default() : new le_unix_js_1.default(), PIPELINE_NOOP);
|
|
697
711
|
}
|
|
698
712
|
return outputStream;
|
|
699
713
|
}
|
|
@@ -719,8 +733,10 @@ class MimeNode {
|
|
|
719
733
|
}
|
|
720
734
|
stream(outputStream, options, done) {
|
|
721
735
|
const transferEncoding = this.getTransferEncoding();
|
|
722
|
-
|
|
723
|
-
|
|
736
|
+
// the streams this node is reading from. A consumer that goes away mid-message destroys
|
|
737
|
+
// the output, and these are released with it instead of staying paused with a file or a
|
|
738
|
+
// socket open behind them
|
|
739
|
+
let activeStreams = [];
|
|
724
740
|
// protect actual callback against multiple triggering
|
|
725
741
|
let returned = false;
|
|
726
742
|
const callback = (err) => {
|
|
@@ -728,13 +744,39 @@ class MimeNode {
|
|
|
728
744
|
return;
|
|
729
745
|
}
|
|
730
746
|
returned = true;
|
|
747
|
+
outputStream.removeListener('close', onOutputClose);
|
|
731
748
|
done(err);
|
|
732
749
|
};
|
|
750
|
+
function onOutputClose() {
|
|
751
|
+
for (const stream of activeStreams) {
|
|
752
|
+
stream.destroy();
|
|
753
|
+
}
|
|
754
|
+
return callback(abortedError());
|
|
755
|
+
}
|
|
756
|
+
// reads the streams into the output. The listener is only attached while the node reads
|
|
757
|
+
// something, a deeply nested tree would otherwise stack one per level on the output
|
|
758
|
+
const readInto = (...streams) => {
|
|
759
|
+
if (!activeStreams.length) {
|
|
760
|
+
outputStream.once('close', onOutputClose);
|
|
761
|
+
}
|
|
762
|
+
activeStreams = streams;
|
|
763
|
+
};
|
|
764
|
+
// stops here if the output was destroyed in the meantime
|
|
765
|
+
const aborted = () => {
|
|
766
|
+
if (outputStream.destroyed) {
|
|
767
|
+
callback(abortedError());
|
|
768
|
+
return true;
|
|
769
|
+
}
|
|
770
|
+
return false;
|
|
771
|
+
};
|
|
733
772
|
// for multipart nodes, push child nodes
|
|
734
773
|
// for content nodes end the stream
|
|
735
774
|
const finalize = () => {
|
|
736
775
|
let childId = 0;
|
|
737
776
|
const processChildNode = () => {
|
|
777
|
+
if (aborted()) {
|
|
778
|
+
return;
|
|
779
|
+
}
|
|
738
780
|
if (childId >= this.childNodes.length) {
|
|
739
781
|
outputStream.write('\r\n--' + this.boundary + '--\r\n');
|
|
740
782
|
return callback();
|
|
@@ -757,89 +799,80 @@ class MimeNode {
|
|
|
757
799
|
};
|
|
758
800
|
// pushes node content
|
|
759
801
|
const sendContent = () => {
|
|
760
|
-
if (
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
802
|
+
if (aborted()) {
|
|
803
|
+
return;
|
|
804
|
+
}
|
|
805
|
+
if (!this.content) {
|
|
806
|
+
return setImmediate(finalize);
|
|
807
|
+
}
|
|
808
|
+
const createStream = () => {
|
|
809
|
+
if (aborted()) {
|
|
810
|
+
return;
|
|
764
811
|
}
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
this.content.once('error', this._contentErrorHandler);
|
|
812
|
+
const contentError = this._takeStreamContent(this.content);
|
|
813
|
+
if (contentError) {
|
|
814
|
+
return callback(contentError);
|
|
769
815
|
}
|
|
770
|
-
const
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
}
|
|
781
|
-
else {
|
|
782
|
-
// anything that is not QP or Base54 passes as-is
|
|
783
|
-
localStream = this._getStream(this.content);
|
|
784
|
-
localStream.pipe(outputStream, {
|
|
785
|
-
end: false
|
|
786
|
-
});
|
|
787
|
-
localStream.once('end', finalize);
|
|
788
|
-
}
|
|
789
|
-
localStream.once('error', err => callback(err));
|
|
790
|
-
};
|
|
791
|
-
if (this.content._resolve) {
|
|
792
|
-
const chunks = [];
|
|
793
|
-
let chunklen = 0;
|
|
794
|
-
let returned = false;
|
|
795
|
-
const sourceStream = this._getStream(this.content);
|
|
796
|
-
sourceStream.on('error', err => {
|
|
797
|
-
if (returned) {
|
|
798
|
-
return;
|
|
799
|
-
}
|
|
800
|
-
returned = true;
|
|
801
|
-
callback(err);
|
|
802
|
-
});
|
|
803
|
-
sourceStream.on('readable', () => {
|
|
804
|
-
let chunk;
|
|
805
|
-
while ((chunk = sourceStream.read()) !== null) {
|
|
806
|
-
chunks.push(chunk);
|
|
807
|
-
chunklen += chunk.length;
|
|
808
|
-
}
|
|
809
|
-
});
|
|
810
|
-
sourceStream.on('end', () => {
|
|
811
|
-
if (returned) {
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
|
-
returned = true;
|
|
815
|
-
this.content._resolve = false;
|
|
816
|
-
this.content._resolvedValue = Buffer.concat(chunks, chunklen);
|
|
817
|
-
setImmediate(createStream);
|
|
816
|
+
const localStream = this._getStream(this.content);
|
|
817
|
+
if (['quoted-printable', 'base64'].includes(transferEncoding)) {
|
|
818
|
+
const contentStream = transferEncoding === 'base64'
|
|
819
|
+
? new base64.Encoder(options)
|
|
820
|
+
: // outside of text a lone CR or LF is data, encoding it keeps it from being
|
|
821
|
+
// turned into a line break by a newline transform or the receiving side
|
|
822
|
+
new qp.Encoder(Object.assign({}, options, { binary: !/^text\//i.test(this.contentType || '') }));
|
|
823
|
+
readInto(localStream, contentStream);
|
|
824
|
+
contentStream.pipe(outputStream, {
|
|
825
|
+
end: false
|
|
818
826
|
});
|
|
827
|
+
// pipeline reports errors and tears both streams down. It calls back once the
|
|
828
|
+
// encoder took all of its input, the encoded output may still be waiting to
|
|
829
|
+
// be read, so the node is only done once the encoder's readable side ended
|
|
830
|
+
(0, node_stream_1.pipeline)(localStream, contentStream, err => err && callback(err));
|
|
831
|
+
(0, node_stream_1.finished)(contentStream, { writable: false }, err => (err ? callback(err) : finalize()));
|
|
819
832
|
}
|
|
820
833
|
else {
|
|
821
|
-
|
|
834
|
+
// anything that is not QP or Base54 passes as-is
|
|
835
|
+
readInto(localStream);
|
|
836
|
+
localStream.pipe(outputStream, {
|
|
837
|
+
end: false
|
|
838
|
+
});
|
|
839
|
+
(0, node_stream_1.finished)(localStream, { writable: false }, err => (err ? callback(err) : finalize()));
|
|
822
840
|
}
|
|
823
|
-
|
|
841
|
+
};
|
|
842
|
+
if (this.content._resolve) {
|
|
843
|
+
const sourceStream = this._getStream(this.content);
|
|
844
|
+
readInto(sourceStream);
|
|
845
|
+
shared.resolveStream(sourceStream, (err, value) => {
|
|
846
|
+
if (err) {
|
|
847
|
+
return callback(err);
|
|
848
|
+
}
|
|
849
|
+
if (returned) {
|
|
850
|
+
return;
|
|
851
|
+
}
|
|
852
|
+
this.content._resolve = false;
|
|
853
|
+
this.content._resolvedValue = value;
|
|
854
|
+
setImmediate(createStream);
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
else {
|
|
858
|
+
setImmediate(createStream);
|
|
824
859
|
}
|
|
825
|
-
return setImmediate(finalize);
|
|
826
860
|
};
|
|
827
861
|
if (this._raw) {
|
|
828
862
|
setImmediate(() => {
|
|
829
|
-
if (
|
|
830
|
-
|
|
831
|
-
return callback(this._raw);
|
|
863
|
+
if (aborted()) {
|
|
864
|
+
return;
|
|
832
865
|
}
|
|
833
|
-
|
|
834
|
-
if (
|
|
835
|
-
|
|
866
|
+
const rawError = this._takeStreamContent(this._raw);
|
|
867
|
+
if (rawError) {
|
|
868
|
+
return callback(rawError);
|
|
836
869
|
}
|
|
837
870
|
const raw = this._getStream(this._raw);
|
|
871
|
+
readInto(raw);
|
|
838
872
|
raw.pipe(outputStream, {
|
|
839
873
|
end: false
|
|
840
874
|
});
|
|
841
|
-
|
|
842
|
-
raw.on('end', finalize);
|
|
875
|
+
(0, node_stream_1.finished)(raw, { writable: false }, err => (err ? callback(err) : finalize()));
|
|
843
876
|
});
|
|
844
877
|
}
|
|
845
878
|
else {
|
|
@@ -955,11 +988,7 @@ class MimeNode {
|
|
|
955
988
|
if (this._raw && typeof this._raw.pipe === 'function') {
|
|
956
989
|
// pre-stream handler. might be triggered if a stream is set as content
|
|
957
990
|
// and 'error' fires before anything is done with this stream
|
|
958
|
-
this.
|
|
959
|
-
this._raw.removeListener('error', this._contentErrorHandler);
|
|
960
|
-
this._raw = err;
|
|
961
|
-
};
|
|
962
|
-
this._raw.once('error', this._contentErrorHandler);
|
|
991
|
+
shared.recordStreamErrors(this._raw);
|
|
963
992
|
}
|
|
964
993
|
return this;
|
|
965
994
|
}
|
|
@@ -985,6 +1014,37 @@ class MimeNode {
|
|
|
985
1014
|
}
|
|
986
1015
|
return false;
|
|
987
1016
|
}
|
|
1017
|
+
/**
|
|
1018
|
+
* Destroys the content streams of this node and of every node below it
|
|
1019
|
+
*
|
|
1020
|
+
* @internal
|
|
1021
|
+
*/
|
|
1022
|
+
_destroyContentStreams() {
|
|
1023
|
+
shared.destroyStream(this.content);
|
|
1024
|
+
shared.destroyStream(this._raw);
|
|
1025
|
+
for (const child of this.childNodes) {
|
|
1026
|
+
child._destroyContentStreams();
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
/**
|
|
1030
|
+
* Checks that a content value can still be read before the node streams it. A stream is
|
|
1031
|
+
* refused once it errored, ended or was destroyed: piping it would either never finish or
|
|
1032
|
+
* produce an empty body without any error
|
|
1033
|
+
*
|
|
1034
|
+
* @param content Node content or raw value
|
|
1035
|
+
* @returns The error to stop with, or null when the content can be read
|
|
1036
|
+
* @internal
|
|
1037
|
+
*/
|
|
1038
|
+
_takeStreamContent(content) {
|
|
1039
|
+
if (Object.prototype.toString.call(content) === '[object Error]') {
|
|
1040
|
+
return content;
|
|
1041
|
+
}
|
|
1042
|
+
if (!content || typeof content.pipe !== 'function' || content._resolvedValue) {
|
|
1043
|
+
// the value of a resolved stream is read from the buffered copy
|
|
1044
|
+
return null;
|
|
1045
|
+
}
|
|
1046
|
+
return shared.unreadableStreamError(content);
|
|
1047
|
+
}
|
|
988
1048
|
/**
|
|
989
1049
|
* Detects and returns handle to a stream related with the content.
|
|
990
1050
|
*
|
|
@@ -50,10 +50,12 @@ const index_js_7 = __importDefault(require("./ses-transport/index.js"));
|
|
|
50
50
|
const errors = __importStar(require("./errors.js"));
|
|
51
51
|
const index_js_8 = __importDefault(require("./fetch/index.js"));
|
|
52
52
|
const packageData = __importStar(require("./package-info.js"));
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const
|
|
53
|
+
// Read on use rather than at load, so that importing the module does not touch the
|
|
54
|
+
// environment (Deno refuses that without --allow-env, even for apps that never use Ethereal)
|
|
55
|
+
const etherealApi = () => (process.env.ETHEREAL_API || 'https://api.nodemailer.com').replace(/\/+$/, '');
|
|
56
|
+
const etherealWeb = () => (process.env.ETHEREAL_WEB || 'https://ethereal.email').replace(/\/+$/, '');
|
|
57
|
+
const etherealApiKey = () => (process.env.ETHEREAL_API_KEY || '').replace(/\s*/g, '') || null;
|
|
58
|
+
const etherealCache = () => ['true', 'yes', 'y', '1'].includes((process.env.ETHEREAL_CACHE || 'yes').toString().trim().toLowerCase());
|
|
57
59
|
let testAccount = false;
|
|
58
60
|
function createTransport(transporter, defaults) {
|
|
59
61
|
let options;
|
|
@@ -114,11 +116,11 @@ function createTestAccount(apiUrl, callback) {
|
|
|
114
116
|
});
|
|
115
117
|
}
|
|
116
118
|
const done = callback;
|
|
117
|
-
if (
|
|
119
|
+
if (etherealCache() && testAccount) {
|
|
118
120
|
setImmediate(() => done(null, testAccount));
|
|
119
121
|
return promise;
|
|
120
122
|
}
|
|
121
|
-
apiUrl = apiUrl ||
|
|
123
|
+
apiUrl = apiUrl || etherealApi();
|
|
122
124
|
const chunks = [];
|
|
123
125
|
let chunklen = 0;
|
|
124
126
|
const requestHeaders = {};
|
|
@@ -126,8 +128,9 @@ function createTestAccount(apiUrl, callback) {
|
|
|
126
128
|
requestor: packageData.name,
|
|
127
129
|
version: packageData.version
|
|
128
130
|
};
|
|
129
|
-
|
|
130
|
-
|
|
131
|
+
const apiKey = etherealApiKey();
|
|
132
|
+
if (apiKey) {
|
|
133
|
+
requestHeaders.Authorization = 'Bearer ' + apiKey;
|
|
131
134
|
}
|
|
132
135
|
const fetchOptions = {
|
|
133
136
|
contentType: 'application/json',
|
|
@@ -195,7 +198,7 @@ function getTestMessageUrl(info) {
|
|
|
195
198
|
}
|
|
196
199
|
}
|
|
197
200
|
if (infoProps.has('STATUS') && infoProps.has('MSGID')) {
|
|
198
|
-
return ((testAccount && testAccount.web) ||
|
|
201
|
+
return ((testAccount && testAccount.web) || etherealWeb()) + '/message/' + infoProps.get('MSGID');
|
|
199
202
|
}
|
|
200
203
|
return false;
|
|
201
204
|
}
|
|
@@ -14,6 +14,8 @@ export declare function wrap(str: string, lineLength?: number): string;
|
|
|
14
14
|
export interface QPEncoderOptions {
|
|
15
15
|
/** Maximum length for lines, set to false to disable wrapping */
|
|
16
16
|
lineLength?: number | false | undefined;
|
|
17
|
+
/** The input is binary data: a CR or LF that is not part of a CRLF pair is encoded */
|
|
18
|
+
binary?: boolean | undefined;
|
|
17
19
|
}
|
|
18
20
|
/** The name @types/nodemailer used for QPEncoderOptions */
|
|
19
21
|
export type EncoderOptions = QPEncoderOptions;
|
|
@@ -21,23 +21,52 @@ const QP_RANGES = [
|
|
|
21
21
|
[0x20, 0x3c], // <SP>!"#$%&'()*+,-./0123456789:;
|
|
22
22
|
[0x3e, 0x7e] // >?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}
|
|
23
23
|
];
|
|
24
|
+
// 1 for every byte value that is written as is, see QP_RANGES
|
|
25
|
+
const QP_LITERAL = new Uint8Array(256);
|
|
26
|
+
for (let i = 0; i < 256; i++) {
|
|
27
|
+
QP_LITERAL[i] = checkRanges(i, QP_RANGES) ? 1 : 0;
|
|
28
|
+
}
|
|
29
|
+
const HEX_DIGITS = Buffer.from('0123456789ABCDEF', 'latin1');
|
|
30
|
+
const isWhitespace = (c) => c === 0x20 || c === 0x09;
|
|
24
31
|
function encode(buffer) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
+
return encodeBytes(typeof buffer === 'string' ? Buffer.from(buffer, 'utf-8') : buffer);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Encodes bytes that may be followed by more input
|
|
36
|
+
*
|
|
37
|
+
* @param buffer Bytes to encode
|
|
38
|
+
* @param [next] The byte that follows the buffer, whitespace before it is kept literal unless it
|
|
39
|
+
* is a line break. Without it the buffer ends the input and its trailing whitespace is encoded
|
|
40
|
+
* @param [binary] Keep only CRLF pairs literal. A lone CR or LF is data, not a line break, and
|
|
41
|
+
* anything that rewrites line endings on the way would change it
|
|
42
|
+
* @param [previous] The byte before the buffer, for a buffer that starts with LF
|
|
43
|
+
* @returns Quoted-Printable encoded string
|
|
44
|
+
*/
|
|
45
|
+
function encodeBytes(buffer, next, binary, previous) {
|
|
46
|
+
const len = buffer.length;
|
|
47
|
+
// every byte takes three characters at most
|
|
48
|
+
const output = Buffer.allocUnsafe(len * 3);
|
|
49
|
+
let pos = 0;
|
|
50
|
+
for (let i = 0; i < len; i++) {
|
|
51
|
+
const ord = buffer[i];
|
|
52
|
+
const following = i + 1 < len ? buffer[i + 1] : next;
|
|
53
|
+
const lineBreakByte = binary && (ord === 0x0d || ord === 0x0a)
|
|
54
|
+
? ord === 0x0d
|
|
55
|
+
? following === 0x0a
|
|
56
|
+
: (i > 0 ? buffer[i - 1] : previous) === 0x0d
|
|
57
|
+
: true;
|
|
32
58
|
// if the char is in allowed range, then keep as is, unless it is a WS in the end of a line
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
59
|
+
if (QP_LITERAL[ord] &&
|
|
60
|
+
lineBreakByte &&
|
|
61
|
+
!(isWhitespace(ord) && (following === undefined || following === 0x0a || following === 0x0d))) {
|
|
62
|
+
output[pos++] = ord;
|
|
36
63
|
continue;
|
|
37
64
|
}
|
|
38
|
-
|
|
65
|
+
output[pos++] = 0x3d; // =
|
|
66
|
+
output[pos++] = HEX_DIGITS[Math.floor(ord / 16)];
|
|
67
|
+
output[pos++] = HEX_DIGITS[ord % 16];
|
|
39
68
|
}
|
|
40
|
-
return
|
|
69
|
+
return output.toString('latin1', 0, pos);
|
|
41
70
|
}
|
|
42
71
|
/**
|
|
43
72
|
* Adds soft line breaks to a Quoted-Printable string
|
|
@@ -148,6 +177,10 @@ function checkRanges(nr, ranges) {
|
|
|
148
177
|
}
|
|
149
178
|
return false;
|
|
150
179
|
}
|
|
180
|
+
// Input is encoded and wrapped this many bytes at a time. wrap() works on strings, and a large
|
|
181
|
+
// chunk handed over at once, such as a whole Buffer attachment, would be held several times over
|
|
182
|
+
// as intermediate strings
|
|
183
|
+
const ENCODE_SLICE_SIZE = 64 * 1024;
|
|
151
184
|
/**
|
|
152
185
|
* Creates a transform stream for encoding data to Quoted-Printable encoding
|
|
153
186
|
*
|
|
@@ -163,45 +196,82 @@ class Encoder extends node_stream_1.Transform {
|
|
|
163
196
|
this.options.lineLength = this.options.lineLength || 76;
|
|
164
197
|
}
|
|
165
198
|
this._curLine = '';
|
|
199
|
+
this._remainingBytes = false;
|
|
200
|
+
this._lastByte = undefined;
|
|
166
201
|
this.inputBytes = 0;
|
|
167
202
|
this.outputBytes = 0;
|
|
168
203
|
}
|
|
169
204
|
/** @internal */
|
|
170
205
|
_transform(chunk, encoding, done) {
|
|
171
|
-
let
|
|
172
|
-
if (
|
|
173
|
-
chunk = Buffer.from(chunk, encoding);
|
|
174
|
-
}
|
|
175
|
-
if (!chunk || !chunk.length) {
|
|
206
|
+
let buf = encoding !== 'buffer' ? Buffer.from(chunk, encoding) : chunk;
|
|
207
|
+
if (!buf || !buf.length) {
|
|
176
208
|
return done();
|
|
177
209
|
}
|
|
178
|
-
this.inputBytes +=
|
|
179
|
-
if (this.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
qp = qp.replace(/(^|\n)([^\n]*)$/, (match, lineBreak, lastLine) => {
|
|
183
|
-
this._curLine = lastLine;
|
|
184
|
-
return lineBreak;
|
|
185
|
-
});
|
|
186
|
-
if (qp) {
|
|
187
|
-
this.outputBytes += qp.length;
|
|
188
|
-
this.push(qp);
|
|
189
|
-
}
|
|
210
|
+
this.inputBytes += buf.length;
|
|
211
|
+
if (this._remainingBytes) {
|
|
212
|
+
buf = Buffer.concat([this._remainingBytes, buf], this._remainingBytes.length + buf.length);
|
|
213
|
+
this._remainingBytes = false;
|
|
190
214
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
215
|
+
// Whitespace is encoded when it ends a line, and the end of the input counts as one. Hold
|
|
216
|
+
// back the whitespace a chunk ends with until it is known what follows it, so the output
|
|
217
|
+
// does not depend on where the input was split
|
|
218
|
+
let end = buf.length;
|
|
219
|
+
// a binary CR can only be written once it is known whether LF follows
|
|
220
|
+
while (end > 0 && (isWhitespace(buf[end - 1]) || (this.options.binary && buf[end - 1] === 0x0d))) {
|
|
221
|
+
end--;
|
|
222
|
+
}
|
|
223
|
+
if (buf.length - end <= ENCODE_SLICE_SIZE) {
|
|
224
|
+
this._remainingBytes = end < buf.length ? Buffer.from(buf.subarray(end)) : false;
|
|
225
|
+
buf = buf.subarray(0, end);
|
|
195
226
|
}
|
|
227
|
+
this._encodeSlices(buf);
|
|
196
228
|
done();
|
|
197
229
|
}
|
|
198
230
|
/** @internal */
|
|
199
231
|
_flush(done) {
|
|
232
|
+
if (this._remainingBytes) {
|
|
233
|
+
this._encodeSlices(this._remainingBytes);
|
|
234
|
+
this._remainingBytes = false;
|
|
235
|
+
}
|
|
200
236
|
if (this._curLine) {
|
|
201
237
|
this.outputBytes += this._curLine.length;
|
|
202
238
|
this.push(this._curLine, 'ascii');
|
|
239
|
+
this._curLine = '';
|
|
203
240
|
}
|
|
204
241
|
done();
|
|
205
242
|
}
|
|
243
|
+
/**
|
|
244
|
+
* Encodes the input in slices of ENCODE_SLICE_SIZE bytes. Each slice is told the byte that
|
|
245
|
+
* follows it, so a slice that ends in whitespace is encoded as if it was not split
|
|
246
|
+
*
|
|
247
|
+
* @internal
|
|
248
|
+
*/
|
|
249
|
+
_encodeSlices(buf) {
|
|
250
|
+
for (let start = 0; start < buf.length; start += ENCODE_SLICE_SIZE) {
|
|
251
|
+
const end = Math.min(start + ENCODE_SLICE_SIZE, buf.length);
|
|
252
|
+
this._encodeSlice(buf.subarray(start, end), end < buf.length ? buf[end] : undefined);
|
|
253
|
+
this._lastByte = buf[end - 1];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
/** @internal */
|
|
257
|
+
_encodeSlice(buf, next) {
|
|
258
|
+
let qp;
|
|
259
|
+
if (this.options.lineLength) {
|
|
260
|
+
qp = wrap(this._curLine + encodeBytes(buf, next, this.options.binary, this._lastByte), this.options.lineLength);
|
|
261
|
+
// the last line is kept until it is known whether it needs a soft break
|
|
262
|
+
const lastLF = qp.lastIndexOf('\n');
|
|
263
|
+
this._curLine = qp.substring(lastLF + 1);
|
|
264
|
+
qp = qp.substring(0, lastLF + 1);
|
|
265
|
+
if (qp) {
|
|
266
|
+
this.outputBytes += qp.length;
|
|
267
|
+
this.push(qp, 'ascii');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
qp = encodeBytes(buf, next, this.options.binary, this._lastByte);
|
|
272
|
+
this.outputBytes += qp.length;
|
|
273
|
+
this.push(qp, 'ascii');
|
|
274
|
+
}
|
|
275
|
+
}
|
|
206
276
|
}
|
|
207
277
|
exports.Encoder = Encoder;
|