@mavogel/awscdk-rootmail 0.1.24 → 0.1.26
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 +9 -9
- package/lib/rootmail.js +1 -1
- package/lib/ses-receive.js +1 -1
- package/lib/ses-receive.ops-santa-handler.js +2 -2
- package/node_modules/@aws-sdk/checksums/package.json +3 -3
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +9 -7
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/schemas/schemas_0.js +11 -6
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveriesCommand.d.ts +3 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveryDestinationsCommand.d.ts +3 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliverySourcesCommand.d.ts +3 -0
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/PutResourcePolicyCommand.d.ts +1 -1
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/models_0.d.ts +27 -1
- package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/models_0.d.ts +3 -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 +1 -1
- package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
- package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +1 -1
- package/node_modules/@aws-sdk/client-s3/package.json +7 -7
- package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +1 -1
- package/node_modules/@aws-sdk/client-ses/package.json +4 -4
- package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +1 -1
- package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
- package/node_modules/@aws-sdk/core/dist-cjs/submodules/protocols/index.js +94 -1589
- package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/index.js +1 -7
- package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsJsonRpcProtocol.js +1 -3
- package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsRestJsonProtocol.js +1 -3
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/index.d.ts +2 -8
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/index.d.ts +10 -10
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
- package/node_modules/@aws-sdk/core/package.json +2 -2
- 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 +3 -3
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
- package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
- package/node_modules/@aws-sdk/token-providers/dist-cjs/index.js +37 -19
- package/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js +37 -19
- package/node_modules/@aws-sdk/token-providers/package.json +4 -4
- package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +1695 -3
- package/node_modules/@smithy/core/dist-es/submodules/protocols/ConfigurableSerdeContext.js +6 -0
- package/node_modules/@smithy/core/dist-es/submodules/protocols/UnionSerde.js +26 -0
- package/node_modules/@smithy/core/dist-es/submodules/protocols/common.js +3 -0
- package/node_modules/@smithy/core/dist-es/submodules/protocols/index.js +8 -0
- package/node_modules/@smithy/core/dist-es/submodules/protocols/json/SmithyRpcV2JsonProtocol.js +102 -0
- package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeDeserializer.js +8 -1
- package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeSerializer.js +8 -1
- package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.js +13 -2
- package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeSerializer2.js +11 -1
- package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/parseJsonBody.js +16 -1
- package/node_modules/@smithy/core/dist-es/submodules/protocols/writeKey.js +3 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/UnionSerde.d.ts +24 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/common.d.ts +2 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/index.d.ts +9 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/json/JsonSettings.d.ts +13 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
- package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/parseJsonBody.d.ts +16 -0
- package/node_modules/@smithy/core/dist-types/submodules/protocols/writeKey.d.ts +4 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/UnionSerde.d.ts +24 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/common.d.ts +2 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/index.d.ts +9 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/JsonSettings.d.ts +13 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
- package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonCodec.d.ts +15 -11
- package/node_modules/{@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json → @smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1}/JsonSettings.d.ts +7 -4
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +14 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +29 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +51 -0
- package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +13 -9
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +30 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +60 -0
- package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/detectBufferParsing.d.ts +1 -1
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +21 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +15 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +11 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +38 -0
- package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/writeKey.d.ts +4 -0
- package/node_modules/@smithy/core/package.json +2 -1
- package/node_modules/@zone-eu/mailsplit/README.md +43 -54
- package/node_modules/@zone-eu/mailsplit/lib/headers.js +10 -4
- package/node_modules/@zone-eu/mailsplit/lib/message-splitter.js +5 -4
- package/node_modules/@zone-eu/mailsplit/lib/mime-node.d.ts +4 -1
- package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +30 -5
- package/node_modules/@zone-eu/mailsplit/lib/types.d.ts +3 -1
- package/node_modules/@zone-eu/mailsplit/package.json +22 -17
- package/node_modules/libbase64/CHANGELOG.md +20 -0
- package/node_modules/libbase64/README.md +40 -13
- package/node_modules/libbase64/dist/cjs/libbase64.d.ts +80 -0
- package/node_modules/libbase64/dist/cjs/libbase64.js +310 -0
- package/node_modules/libbase64/dist/cjs/package.json +3 -0
- package/node_modules/libbase64/dist/esm/libbase64.d.ts +80 -0
- package/node_modules/libbase64/{lib → dist/esm}/libbase64.js +74 -117
- package/node_modules/libbase64/dist/esm/package.json +3 -0
- package/node_modules/libbase64/package.json +59 -13
- package/node_modules/libmime/CHANGELOG.md +38 -0
- package/node_modules/libmime/README.md +55 -26
- package/node_modules/libmime/SECURITY.md +152 -0
- package/node_modules/libmime/dist/cjs/charset.d.ts +43 -0
- package/node_modules/libmime/dist/cjs/charset.js +135 -0
- package/node_modules/libmime/dist/cjs/charsets.d.ts +2 -0
- package/node_modules/libmime/dist/cjs/charsets.js +213 -0
- package/node_modules/libmime/dist/cjs/get-charset-name.d.ts +1 -0
- package/node_modules/libmime/dist/cjs/get-charset-name.js +224 -0
- package/node_modules/libmime/dist/cjs/libmime.d.ts +218 -0
- package/node_modules/libmime/dist/cjs/libmime.js +1027 -0
- package/node_modules/libmime/dist/cjs/mimetypes.d.ts +7 -0
- package/node_modules/libmime/dist/cjs/mimetypes.js +2072 -0
- package/node_modules/libmime/dist/cjs/package.json +3 -0
- package/node_modules/libmime/dist/cjs/types.d.ts +59 -0
- package/node_modules/libmime/dist/cjs/types.js +4 -0
- package/node_modules/libmime/dist/esm/charset.d.ts +43 -0
- package/node_modules/libmime/dist/esm/charset.js +125 -0
- package/node_modules/libmime/dist/esm/charsets.d.ts +2 -0
- package/node_modules/libmime/{lib → dist/esm}/charsets.js +3 -5
- package/node_modules/libmime/dist/esm/get-charset-name.d.ts +1 -0
- package/node_modules/libmime/{lib → dist/esm}/get-charset-name.js +1 -8
- package/node_modules/libmime/dist/esm/libmime.d.ts +219 -0
- package/node_modules/libmime/{lib → dist/esm}/libmime.js +275 -334
- package/node_modules/libmime/dist/esm/mimetypes.d.ts +7 -0
- package/node_modules/libmime/dist/esm/mimetypes.js +2068 -0
- package/node_modules/libmime/dist/esm/package.json +3 -0
- package/node_modules/libmime/dist/esm/types.d.ts +59 -0
- package/node_modules/libmime/dist/esm/types.js +3 -0
- package/node_modules/libmime/package.json +60 -15
- package/node_modules/libqp/CHANGELOG.md +18 -0
- package/node_modules/libqp/README.md +50 -19
- package/node_modules/libqp/dist/cjs/libqp.d.ts +74 -0
- package/node_modules/libqp/dist/cjs/libqp.js +378 -0
- package/node_modules/libqp/dist/cjs/package.json +3 -0
- package/node_modules/libqp/dist/esm/libqp.d.ts +74 -0
- package/node_modules/libqp/dist/esm/libqp.js +369 -0
- package/node_modules/libqp/dist/esm/package.json +3 -0
- package/node_modules/libqp/lib/libqp.js +4 -383
- package/node_modules/libqp/lib/package.json +3 -0
- package/node_modules/libqp/package.json +62 -6
- package/node_modules/mailparser/CHANGELOG.md +56 -0
- package/node_modules/mailparser/README.md +6 -0
- package/node_modules/mailparser/dist/cjs/index.d.ts +10 -0
- package/node_modules/mailparser/dist/cjs/index.js +12 -0
- package/node_modules/mailparser/dist/cjs/mail-parser.d.ts +98 -0
- package/node_modules/mailparser/dist/cjs/mail-parser.js +1419 -0
- package/node_modules/mailparser/dist/cjs/package.json +3 -0
- package/node_modules/mailparser/dist/cjs/simple-parser.d.ts +17 -0
- package/node_modules/mailparser/dist/cjs/simple-parser.js +133 -0
- package/node_modules/mailparser/dist/cjs/stream-hash.d.ts +24 -0
- package/node_modules/mailparser/dist/cjs/stream-hash.js +33 -0
- package/node_modules/mailparser/dist/cjs/tlds.cjs +8 -0
- package/node_modules/mailparser/dist/cjs/types.d.ts +309 -0
- package/node_modules/mailparser/dist/cjs/types.js +2 -0
- package/node_modules/mailparser/dist/esm/index.d.ts +10 -0
- package/node_modules/mailparser/dist/esm/index.js +4 -0
- package/node_modules/mailparser/dist/esm/mail-parser.d.ts +98 -0
- package/node_modules/mailparser/{lib → dist/esm}/mail-parser.js +302 -368
- package/node_modules/mailparser/dist/esm/package.json +3 -0
- package/node_modules/mailparser/dist/esm/simple-parser.d.ts +17 -0
- package/node_modules/mailparser/{lib → dist/esm}/simple-parser.js +45 -68
- package/node_modules/mailparser/dist/esm/stream-hash.d.ts +24 -0
- package/node_modules/mailparser/{lib → dist/esm}/stream-hash.js +7 -9
- package/node_modules/mailparser/dist/esm/tlds.cjs +8 -0
- package/node_modules/mailparser/dist/esm/types.d.ts +309 -0
- package/node_modules/mailparser/dist/esm/types.js +1 -0
- package/node_modules/mailparser/package.json +88 -17
- package/node_modules/nodemailer/CHANGELOG.md +44 -0
- package/node_modules/nodemailer/dist/cjs/addressparser/index.js +695 -267
- package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +4 -1
- package/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +8 -2
- package/node_modules/nodemailer/dist/cjs/mime-node/index.js +48 -16
- 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/shared/url.js +22 -5
- package/node_modules/nodemailer/dist/esm/addressparser/index.js +695 -267
- package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +4 -1
- package/node_modules/nodemailer/dist/esm/mime-funcs/index.js +8 -2
- package/node_modules/nodemailer/dist/esm/mime-node/index.js +48 -16
- 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/shared/url.js +22 -5
- package/node_modules/nodemailer/package.json +1 -1
- package/package.json +9 -9
- package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -7
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -4
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -13
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -18
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -27
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -15
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -39
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +0 -7
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +0 -7
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +0 -2
- package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +0 -14
- package/node_modules/libbase64/.eslintrc +0 -11
- package/node_modules/libbase64/.github/FUNDING.yml +0 -4
- package/node_modules/libbase64/.github/VULNERABILITY_REPORT.yml +0 -75
- package/node_modules/libbase64/.github/workflows/release.yaml +0 -39
- package/node_modules/libbase64/.github/workflows/test.yml +0 -21
- package/node_modules/libbase64/.ncurc.js +0 -13
- package/node_modules/libbase64/.prettierrc.js +0 -8
- package/node_modules/libmime/lib/charset.js +0 -131
- package/node_modules/libmime/lib/mimetypes.js +0 -2049
- package/node_modules/libqp/.eslintrc +0 -15
- package/node_modules/libqp/.github/FUNDING.yml +0 -4
- package/node_modules/libqp/.github/VULNERABILITY_REPORT.yml +0 -75
- package/node_modules/libqp/.github/workflows/release.yaml +0 -39
- package/node_modules/libqp/.github/workflows/test.yml +0 -21
- package/node_modules/libqp/.prettierrc.js +0 -8
- package/node_modules/libqp/test/libqp-test.js +0 -366
- package/node_modules/mailparser/.gitattributes +0 -1
- package/node_modules/mailparser/.prettierignore +0 -3
- package/node_modules/mailparser/.prettierrc.js +0 -10
- package/node_modules/mailparser/.release-please-manifest.json +0 -3
- package/node_modules/mailparser/eslint.config.js +0 -33
- package/node_modules/mailparser/index.js +0 -9
- package/node_modules/mailparser/release-please-config.json +0 -10
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/JsonSettings.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonCodec.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonSettings.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonCodec2.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/detectBufferParsing.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReplacer.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReviver.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/needsReviver.js +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonCodec.d.ts +0 -0
- /package/node_modules/{@aws-sdk/core/dist-types/submodules/protocols/json → @smithy/core/dist-types/submodules/protocols/json/codec-v1}/JsonSettings.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/detectBufferParsing.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReplacer.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReviver.d.ts +0 -0
- /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/needsReviver.d.ts +0 -0
|
@@ -0,0 +1,1027 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* eslint no-div-regex: 0 */
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
20
|
+
var ownKeys = function(o) {
|
|
21
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
22
|
+
var ar = [];
|
|
23
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
24
|
+
return ar;
|
|
25
|
+
};
|
|
26
|
+
return ownKeys(o);
|
|
27
|
+
};
|
|
28
|
+
return function (mod) {
|
|
29
|
+
if (mod && mod.__esModule) return mod;
|
|
30
|
+
var result = {};
|
|
31
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
32
|
+
__setModuleDefault(result, mod);
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
})();
|
|
36
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
37
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
38
|
+
};
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.Libmime = void 0;
|
|
41
|
+
const node_buffer_1 = require("node:buffer");
|
|
42
|
+
const libbase64_1 = __importDefault(require("libbase64"));
|
|
43
|
+
const libqp_1 = __importDefault(require("libqp"));
|
|
44
|
+
const libcharset = __importStar(require("./charset.js"));
|
|
45
|
+
const mimetypes = __importStar(require("./mimetypes.js"));
|
|
46
|
+
const STAGE_KEY = 0x1001;
|
|
47
|
+
const STAGE_VALUE = 0x1002;
|
|
48
|
+
// Sets an own property on a plain object. A regular assignment with a
|
|
49
|
+
// '__proto__' key would silently modify the object's prototype instead of
|
|
50
|
+
// creating an own property, so it needs special handling.
|
|
51
|
+
const setOwnProperty = (obj, key, value) => {
|
|
52
|
+
if (key === '__proto__') {
|
|
53
|
+
Object.defineProperty(obj, key, {
|
|
54
|
+
value,
|
|
55
|
+
writable: true,
|
|
56
|
+
enumerable: true,
|
|
57
|
+
configurable: true
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
obj[key] = value;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
// Whitespace that separates the tokens of a header value. Kept as a char comparison,
|
|
65
|
+
// a regex test would be run for every single char of every parsed header
|
|
66
|
+
const isWSP = (chr) => chr === ' ' || chr === '\t' || chr === '\r' || chr === '\n' || chr === '\f' || chr === '\v';
|
|
67
|
+
class Libmime {
|
|
68
|
+
constructor(config) {
|
|
69
|
+
this.config = config || {};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Checks if a value is plaintext string (uses only printable 7bit chars)
|
|
73
|
+
*
|
|
74
|
+
* When isParam is set the value is destined for a header parameter, so HT, CR and LF
|
|
75
|
+
* are not plaintext either: a header parameter has no way to carry them. HT is a valid
|
|
76
|
+
* fold point, so folding and unfolding a header would rewrite it as a space, and CR/LF
|
|
77
|
+
* cannot appear in a header value at all. DEL is neither a token character nor qtext,
|
|
78
|
+
* so it can not be carried bare or quoted. Such values have to go through the rfc2231
|
|
79
|
+
* parameter continuation encoding instead, the same way non-ASCII text does.
|
|
80
|
+
*
|
|
81
|
+
* @param {String} value String to be tested
|
|
82
|
+
* @param {Boolean} [isParam] Set to true if the value is a header parameter value
|
|
83
|
+
* @returns {Boolean} true if it is a plaintext string
|
|
84
|
+
*/
|
|
85
|
+
isPlainText(value, isParam) {
|
|
86
|
+
let re = isParam ? /[\x00-\x1f\x7f\u0080-\uFFFF]/ : /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/;
|
|
87
|
+
return typeof value === 'string' && !re.test(value);
|
|
88
|
+
}
|
|
89
|
+
// A parameter value goes out as is only when it is plain text that does not look like an encoded
|
|
90
|
+
// word. Receivers decode encoded words found in parameter values (and libmime itself does so for
|
|
91
|
+
// unencoded continuations), so as plain text it would come back as something else
|
|
92
|
+
isPlainParamValue(value) {
|
|
93
|
+
return this.isPlainText(value, true) && !value.includes('=?');
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Checks if a multi line string containes lines longer than the selected value.
|
|
97
|
+
*
|
|
98
|
+
* Useful when detecting if a mail message needs any processing at all:
|
|
99
|
+
* if only plaintext characters are used and lines are short, then there is
|
|
100
|
+
* no need to encode the values in any way. If the value is plaintext but has
|
|
101
|
+
* longer lines then allowed, then use format=flowed
|
|
102
|
+
*
|
|
103
|
+
* @param {String} str String to be tested
|
|
104
|
+
* @param {Number} lineLength Max line length to check for
|
|
105
|
+
* @returns {Boolean} Returns true if there is at least one line longer than lineLength chars
|
|
106
|
+
*/
|
|
107
|
+
hasLongerLines(str, lineLength) {
|
|
108
|
+
return new RegExp('^.{' + (lineLength + 1) + ',}', 'm').test(str);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Decodes a string from a format=flowed soft wrapping.
|
|
112
|
+
*
|
|
113
|
+
* @param {String} str Plaintext string with format=flowed to decode
|
|
114
|
+
* @param {Boolean} [delSp] If true, delete leading spaces (delsp=yes)
|
|
115
|
+
* @return {String} Mime decoded string
|
|
116
|
+
*/
|
|
117
|
+
decodeFlowed(input, delSp) {
|
|
118
|
+
let str = (input || '').toString();
|
|
119
|
+
let lines = str.split(/\r?\n/);
|
|
120
|
+
let result = [];
|
|
121
|
+
// The current paragraph is kept as a list of non-empty pieces and joined once when it
|
|
122
|
+
// ends. Appending to one string and testing it with a regex on every line re-scans (and
|
|
123
|
+
// flattens) the whole paragraph each time, which is quadratic on long soft-wrapped bodies.
|
|
124
|
+
let parts = [], partsLength = 0,
|
|
125
|
+
// quote depth and the original quote prefix ("> ", ">>" ...) of the current paragraph
|
|
126
|
+
depth = 0, prefix = '';
|
|
127
|
+
let pushPart = (part) => {
|
|
128
|
+
if (part) {
|
|
129
|
+
parts.push(part);
|
|
130
|
+
partsLength += part.length;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
// remove soft linebreaks
|
|
134
|
+
// soft linebreaks are added after space symbols
|
|
135
|
+
for (let i = 0; i < lines.length; i++) {
|
|
136
|
+
let line = lines[i];
|
|
137
|
+
// RFC 3676 section 4.5: a quoted line starts with one or more ">" characters and the one
|
|
138
|
+
// space directly after them is space-stuffing. The quote marker is not content, so a soft
|
|
139
|
+
// line break only joins lines of the same quote depth and drops the continuation's marker.
|
|
140
|
+
let lineDepth = 0;
|
|
141
|
+
while (line.charCodeAt(lineDepth) === 0x3e /* > */) {
|
|
142
|
+
lineDepth++;
|
|
143
|
+
}
|
|
144
|
+
let prefixLength = lineDepth && line.charCodeAt(lineDepth) === 0x20 /* space */ ? lineDepth + 1 : lineDepth;
|
|
145
|
+
let content = line.slice(prefixLength);
|
|
146
|
+
// the last piece is never empty, so it holds the last character of the paragraph
|
|
147
|
+
let last = parts.length ? parts[parts.length - 1] : '';
|
|
148
|
+
// Lines never contain a line break, so a signature separator can only be the whole
|
|
149
|
+
// paragraph (without its quote marker) being exactly "-- "
|
|
150
|
+
let isSoftBreak = lineDepth === depth && last.endsWith(' ') && !(partsLength === 3 && parts.join('') === '-- ');
|
|
151
|
+
if (isSoftBreak) {
|
|
152
|
+
if (delSp) {
|
|
153
|
+
// delsp adds space to text to be able to fold it
|
|
154
|
+
// these spaces can be removed once the text is unfolded
|
|
155
|
+
parts.pop();
|
|
156
|
+
partsLength -= last.length;
|
|
157
|
+
pushPart(last.slice(0, -1));
|
|
158
|
+
pushPart(content);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
// remove space stuffing of the continuation line, the stuffing of a paragraph's first
|
|
162
|
+
// line is removed below. Without delsp the text is only wrapped after spaces, so a
|
|
163
|
+
// leading space can only be stuffing (http://tools.ietf.org/html/rfc3676#section-4.4).
|
|
164
|
+
// The stuffing of a quoted line is already part of its prefix.
|
|
165
|
+
pushPart(lineDepth ? content : content.replace(/^ /, ''));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
if (i > 0) {
|
|
170
|
+
result.push(prefix + parts.join(''));
|
|
171
|
+
}
|
|
172
|
+
parts = [];
|
|
173
|
+
partsLength = 0;
|
|
174
|
+
depth = lineDepth;
|
|
175
|
+
prefix = line.slice(0, prefixLength);
|
|
176
|
+
pushPart(content);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (partsLength || prefix) {
|
|
180
|
+
result.push(prefix + parts.join(''));
|
|
181
|
+
}
|
|
182
|
+
// remove whitespace stuffing
|
|
183
|
+
// http://tools.ietf.org/html/rfc3676#section-4.4
|
|
184
|
+
return result.join('\n').replace(/^ /gm, '');
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Adds soft line breaks to content marked with format=flowed to
|
|
188
|
+
* ensure that no line in the message is never longer than lineLength
|
|
189
|
+
*
|
|
190
|
+
* @param {String} str Plaintext string that requires wrapping
|
|
191
|
+
* @param {Number} [lineLength=76] Maximum length of a line
|
|
192
|
+
* @return {String} String with forced line breaks
|
|
193
|
+
*/
|
|
194
|
+
encodeFlowed(str, lineLength) {
|
|
195
|
+
lineLength = lineLength || 76;
|
|
196
|
+
// space stuffing http://tools.ietf.org/html/rfc3676#section-4.2
|
|
197
|
+
let stuff = (line) => line.replace(/^( |From|>)/i, ' $1');
|
|
198
|
+
let flowed = [];
|
|
199
|
+
str.split(/\r?\n/).forEach(line => {
|
|
200
|
+
// A space in front of a hard line break would turn it into a soft one and the decoder would
|
|
201
|
+
// join the next line to this one, so trailing spaces are trimmed (rfc3676 section 4.2). The
|
|
202
|
+
// signature separator "-- " is the one line that keeps its trailing space
|
|
203
|
+
if (line !== '-- ') {
|
|
204
|
+
line = line.replace(/ +$/, '');
|
|
205
|
+
}
|
|
206
|
+
let folded = this.foldLines(stuff(line), lineLength, true).split('\r\n');
|
|
207
|
+
// the lines that folding starts need stuffing as much as the first one does,
|
|
208
|
+
// a continuation line that starts with ">" would be read as a quote
|
|
209
|
+
for (let i = 1; i < folded.length; i++) {
|
|
210
|
+
folded[i] = stuff(folded[i]);
|
|
211
|
+
}
|
|
212
|
+
flowed.push(folded.join('\r\n'));
|
|
213
|
+
});
|
|
214
|
+
return flowed.join('\r\n');
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Encodes a string or an Buffer to an UTF-8 MIME Word (rfc2047)
|
|
218
|
+
*
|
|
219
|
+
* @param {String|Buffer} data String to be encoded
|
|
220
|
+
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
|
|
221
|
+
* @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
|
|
222
|
+
* @return {String} Single or several mime words joined together
|
|
223
|
+
*/
|
|
224
|
+
encodeWord(data, mimeWordEncoding, maxLength) {
|
|
225
|
+
mimeWordEncoding = (mimeWordEncoding || 'Q').toString().toUpperCase().trim().charAt(0);
|
|
226
|
+
maxLength = maxLength || 0;
|
|
227
|
+
let encodedStr = '';
|
|
228
|
+
let toCharset = 'UTF-8';
|
|
229
|
+
if (maxLength && maxLength > 7 + toCharset.length) {
|
|
230
|
+
maxLength -= 7 + toCharset.length;
|
|
231
|
+
}
|
|
232
|
+
if (mimeWordEncoding === 'Q') {
|
|
233
|
+
// https://tools.ietf.org/html/rfc2047#section-5 rule (3)
|
|
234
|
+
encodedStr = libqp_1.default.encode(data).replace(/[^a-z0-9!*+\-/=]/gi, chr => {
|
|
235
|
+
let ord = chr.charCodeAt(0).toString(16).toUpperCase();
|
|
236
|
+
if (chr === ' ') {
|
|
237
|
+
return '_';
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
return '=' + (ord.length === 1 ? '0' + ord : ord);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
else if (mimeWordEncoding === 'B') {
|
|
245
|
+
// the chunking loop below splits raw text and base64 encodes each part, so a
|
|
246
|
+
// Buffer goes in as its UTF-8 string: handing it the base64 of the whole input
|
|
247
|
+
// would encode the encoding itself and decode back to base64 text
|
|
248
|
+
encodedStr = typeof data === 'string' ? data : data.toString('utf-8');
|
|
249
|
+
maxLength = maxLength ? Math.max(3, ((maxLength - (maxLength % 4)) / 4) * 3) : 0;
|
|
250
|
+
}
|
|
251
|
+
if (maxLength && (mimeWordEncoding !== 'B' ? encodedStr : libbase64_1.default.encode(data)).length > maxLength) {
|
|
252
|
+
if (mimeWordEncoding === 'Q') {
|
|
253
|
+
encodedStr = this.splitMimeEncodedString(encodedStr, maxLength).join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
// RFC2047 6.3 (2) states that encoded-word must include an integral number of characters, so no chopping unicode sequences
|
|
257
|
+
let parts = [];
|
|
258
|
+
let lpart = '';
|
|
259
|
+
// iterating a string yields whole code points, a surrogate pair stays together and an
|
|
260
|
+
// unpaired surrogate comes on its own without swallowing the unit that follows it
|
|
261
|
+
for (let chr of encodedStr) {
|
|
262
|
+
// check if we can add this character to the existing string
|
|
263
|
+
// without breaking byte length limit
|
|
264
|
+
if (node_buffer_1.Buffer.byteLength(lpart + chr) <= maxLength || !lpart) {
|
|
265
|
+
lpart += chr;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
// we hit the length limit, so push the existing string and start over
|
|
269
|
+
parts.push(libbase64_1.default.encode(lpart));
|
|
270
|
+
lpart = chr;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if (lpart) {
|
|
274
|
+
parts.push(libbase64_1.default.encode(lpart));
|
|
275
|
+
}
|
|
276
|
+
if (parts.length > 1) {
|
|
277
|
+
encodedStr = parts.join('?= =?' + toCharset + '?' + mimeWordEncoding + '?');
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
encodedStr = parts.join('');
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (mimeWordEncoding === 'B') {
|
|
285
|
+
encodedStr = libbase64_1.default.encode(data);
|
|
286
|
+
}
|
|
287
|
+
return '=?' + toCharset + '?' + mimeWordEncoding + '?' + encodedStr + (encodedStr.slice(-2) === '?=' ? '' : '?=');
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Normalizes a charset name to its canonical form, resolving common aliases and
|
|
291
|
+
* invalid names. For example `iso-8859-8-i` and `iso-8859-8-e` become `ISO-8859-8`
|
|
292
|
+
* and `win-1257` becomes `windows-1257`. Unknown charsets are returned uppercased.
|
|
293
|
+
* Missing or non-string input defaults to `UTF-8`.
|
|
294
|
+
*
|
|
295
|
+
* @param {String} charset Charset name to normalize
|
|
296
|
+
* @return {String} Canonical charset name
|
|
297
|
+
*/
|
|
298
|
+
normalizeCharset(charset) {
|
|
299
|
+
return libcharset.normalizeCharset(charset);
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Decodes the inner payload of a single mime encoded-word into a unicode string.
|
|
303
|
+
* Expects the three components of `=?charset?encoding?text?=` already separated.
|
|
304
|
+
*
|
|
305
|
+
* @param {String} charset Charset name from the encoded-word (may include an RFC 2231 language tag, which is ignored)
|
|
306
|
+
* @param {String} encoding Encoding indicator, either 'Q' or 'B' (case insensitive)
|
|
307
|
+
* @param {String} str Encoded payload between the second `?` and the trailing `?=`
|
|
308
|
+
* @return {String} Decoded unicode string
|
|
309
|
+
*/
|
|
310
|
+
decodeWord(charset, encoding, str) {
|
|
311
|
+
// RFC2231 added language tag to the encoding
|
|
312
|
+
// see: https://tools.ietf.org/html/rfc2231#section-5
|
|
313
|
+
// this implementation silently ignores this tag
|
|
314
|
+
let splitPos = charset.indexOf('*');
|
|
315
|
+
if (splitPos >= 0) {
|
|
316
|
+
charset = charset.substring(0, splitPos);
|
|
317
|
+
}
|
|
318
|
+
charset = libcharset.normalizeCharset(charset);
|
|
319
|
+
encoding = encoding.toUpperCase();
|
|
320
|
+
let decoded;
|
|
321
|
+
if (encoding === 'Q') {
|
|
322
|
+
str = str
|
|
323
|
+
// remove spaces between = and hex char, this might indicate invalidly applied line splitting
|
|
324
|
+
.replace(/=\s+([0-9a-fA-F])/g, '=$1')
|
|
325
|
+
// convert all underscores to spaces
|
|
326
|
+
.replace(/[_\s]/g, ' ');
|
|
327
|
+
let buf = node_buffer_1.Buffer.from(str);
|
|
328
|
+
let bytes = [];
|
|
329
|
+
for (let i = 0, len = buf.length; i < len; i++) {
|
|
330
|
+
let c = buf[i];
|
|
331
|
+
if (i <= len - 2 && c === 0x3d /* = */) {
|
|
332
|
+
let c1 = this.getHex(buf[i + 1]);
|
|
333
|
+
let c2 = this.getHex(buf[i + 2]);
|
|
334
|
+
if (c1 && c2) {
|
|
335
|
+
let c = parseInt(c1 + c2, 16);
|
|
336
|
+
bytes.push(c);
|
|
337
|
+
i += 2;
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
bytes.push(c);
|
|
342
|
+
}
|
|
343
|
+
decoded = node_buffer_1.Buffer.from(bytes);
|
|
344
|
+
}
|
|
345
|
+
else if (encoding === 'B') {
|
|
346
|
+
// libbase64 decodes input made of several padded segments segment by segment
|
|
347
|
+
decoded = libbase64_1.default.decode(str);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
// keep as is, convert Buffer to unicode string, assume utf8
|
|
351
|
+
decoded = node_buffer_1.Buffer.from(str);
|
|
352
|
+
}
|
|
353
|
+
return libcharset.decode(decoded, charset);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Finds word sequences with non ascii text and converts these to mime words
|
|
357
|
+
*
|
|
358
|
+
* @param {String|Buffer} data String to be encoded
|
|
359
|
+
* @param {String} mimeWordEncoding='Q' Encoding for the mime word, either Q or B
|
|
360
|
+
* @param {Number} [maxLength=0] If set, split mime words into several chunks if needed
|
|
361
|
+
* @param {String} [fromCharset='UTF-8'] Source character set
|
|
362
|
+
* @return {String} String with possible mime words
|
|
363
|
+
*/
|
|
364
|
+
encodeWords(data, mimeWordEncoding, maxLength, fromCharset) {
|
|
365
|
+
if (!fromCharset && typeof maxLength === 'string' && !maxLength.match(/^[0-9]+$/)) {
|
|
366
|
+
fromCharset = maxLength;
|
|
367
|
+
maxLength = undefined;
|
|
368
|
+
}
|
|
369
|
+
let maxLineLength = Number(maxLength) || 0;
|
|
370
|
+
let decodedValue = libcharset.decode(libcharset.convert(data || '', fromCharset));
|
|
371
|
+
let encodedValue;
|
|
372
|
+
let firstMatch = decodedValue.match(/(?:^|\s)([^\s]*[\u0080-\uFFFF])/);
|
|
373
|
+
if (!firstMatch) {
|
|
374
|
+
return decodedValue;
|
|
375
|
+
}
|
|
376
|
+
let startIndex = firstMatch.index +
|
|
377
|
+
(firstMatch[0].match(/[^\s]/) || {
|
|
378
|
+
index: 0
|
|
379
|
+
}).index;
|
|
380
|
+
// The encoded part ends with the word that holds the last non ascii char. Found with a scan,
|
|
381
|
+
// the regex that used to do this rescanned the rest of the word from every non ascii char in
|
|
382
|
+
// it, which made a long non ascii word quadratic
|
|
383
|
+
let lastIndex = decodedValue.length - 1;
|
|
384
|
+
while (lastIndex >= 0 && decodedValue.charCodeAt(lastIndex) < 0x80) {
|
|
385
|
+
lastIndex--;
|
|
386
|
+
}
|
|
387
|
+
let wordEnd = decodedValue.slice(lastIndex + 1).search(/\s/);
|
|
388
|
+
let endIndex = wordEnd < 0 ? decodedValue.length : lastIndex + 1 + wordEnd;
|
|
389
|
+
encodedValue =
|
|
390
|
+
(startIndex ? decodedValue.substring(0, startIndex) : '') +
|
|
391
|
+
this.encodeWord(decodedValue.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLineLength) +
|
|
392
|
+
(endIndex < decodedValue.length ? decodedValue.slice(endIndex) : '');
|
|
393
|
+
return encodedValue;
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* Decode a string that might include one or several mime words
|
|
397
|
+
*
|
|
398
|
+
* @param {String} str String including some mime words that will be decoded
|
|
399
|
+
* @return {String} Decoded unicode string
|
|
400
|
+
*/
|
|
401
|
+
decodeWords(str) {
|
|
402
|
+
return ((str || '')
|
|
403
|
+
.toString()
|
|
404
|
+
// find base64 words that can be joined
|
|
405
|
+
.replace(/(=\?([^?]+)\?[Bb]\?[^?]*\?=)\s*(?==\?([^?]+)\?[Bb]\?[^?]*\?=)/g, (match, left, chLeft, chRight) => {
|
|
406
|
+
// only mark b64 chunks to be joined if charsets match
|
|
407
|
+
if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {
|
|
408
|
+
// set a joiner marker
|
|
409
|
+
return left + '__\x00JOIN\x00__';
|
|
410
|
+
}
|
|
411
|
+
return match;
|
|
412
|
+
})
|
|
413
|
+
// find QP words that can be joined
|
|
414
|
+
.replace(/(=\?([^?]+)\?[Qq]\?[^?]*\?=)\s*(?==\?([^?]+)\?[Qq]\?[^?]*\?=)/g, (match, left, chLeft, chRight) => {
|
|
415
|
+
// only mark QP chunks to be joined if charsets match
|
|
416
|
+
if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {
|
|
417
|
+
// set a joiner marker
|
|
418
|
+
return left + '__\x00JOIN\x00__';
|
|
419
|
+
}
|
|
420
|
+
return match;
|
|
421
|
+
})
|
|
422
|
+
// join base64 encoded words
|
|
423
|
+
.replace(/(\?=)?__\x00JOIN\x00__(=\?([^?]+)\?[QqBb]\?)?/g, '')
|
|
424
|
+
// remove spaces between mime encoded words
|
|
425
|
+
.replace(/(=\?[^?]+\?[QqBb]\?[^?]*\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]*\?=)/g, '$1')
|
|
426
|
+
// decode words
|
|
427
|
+
.replace(/=\?([\w_\-*]+)\?([QqBb])\?([^?]*)\?=/g, (_m, charset, encoding, text) => this.decodeWord(charset, encoding, text)));
|
|
428
|
+
}
|
|
429
|
+
getHex(c) {
|
|
430
|
+
if (c !== undefined &&
|
|
431
|
+
((c >= 0x30 /* 0 */ && c <= 0x39) /* 9 */ || (c >= 0x61 /* a */ && c <= 0x66) /* f */ || (c >= 0x41 /* A */ && c <= 0x46)) /* F */) {
|
|
432
|
+
return String.fromCharCode(c);
|
|
433
|
+
}
|
|
434
|
+
return false;
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* Splits a string by :
|
|
438
|
+
* The result is not mime word decoded, you need to do your own decoding based
|
|
439
|
+
* on the rules for the specific header key
|
|
440
|
+
*
|
|
441
|
+
* @param {String} headerLine Single header line, might include linebreaks as well if folded
|
|
442
|
+
* @return {Object} An object of {key, value}
|
|
443
|
+
*/
|
|
444
|
+
decodeHeader(headerLine) {
|
|
445
|
+
let line = (headerLine || '')
|
|
446
|
+
.toString()
|
|
447
|
+
.replace(/(?:\r?\n|\r)[ \t]*/g, ' ')
|
|
448
|
+
.trim(), match = line.match(/^\s*([^:]+):(.*)$/), key = ((match && match[1]) || '').trim().toLowerCase(), value = ((match && match[2]) || '').trim();
|
|
449
|
+
return {
|
|
450
|
+
key,
|
|
451
|
+
value
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* Parses a block of header lines. Does not decode mime words as every
|
|
456
|
+
* header might have its own rules (eg. formatted email addresses and such)
|
|
457
|
+
*
|
|
458
|
+
* @param {String} headers Headers string
|
|
459
|
+
* @return {Object} An object of headers, where header keys are object keys and every value is an Array of the values for that key
|
|
460
|
+
*/
|
|
461
|
+
decodeHeaders(headers) {
|
|
462
|
+
let lines = headers.split(/\r?\n|\r/), headersObj = {}, header, i, len;
|
|
463
|
+
// Empty lines in front of the block are not part of any header, a caller might
|
|
464
|
+
// include the line break that separates a mime part boundary from its headers
|
|
465
|
+
let headersPos = 0;
|
|
466
|
+
while (headersPos < lines.length && lines[headersPos] === '') {
|
|
467
|
+
headersPos++;
|
|
468
|
+
}
|
|
469
|
+
// An empty line terminates the header block, anything after it is the message body.
|
|
470
|
+
// Without this a body line that starts with whitespace would be folded into the last header
|
|
471
|
+
let bodyPos = lines.indexOf('', headersPos);
|
|
472
|
+
lines = lines.slice(headersPos, bodyPos >= 0 ? bodyPos : lines.length);
|
|
473
|
+
// unfold folded lines, a continuation line always starts with a space or a tab
|
|
474
|
+
// Built in one forward pass, removing continuation lines with splice() is quadratic
|
|
475
|
+
let unfolded = [];
|
|
476
|
+
for (i = 0, len = lines.length; i < len; i++) {
|
|
477
|
+
if (unfolded.length && /^[ \t]/.test(lines[i])) {
|
|
478
|
+
unfolded[unfolded.length - 1] += '\r\n' + lines[i];
|
|
479
|
+
}
|
|
480
|
+
else {
|
|
481
|
+
unfolded.push(lines[i]);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
lines = unfolded;
|
|
485
|
+
for (i = 0, len = lines.length; i < len; i++) {
|
|
486
|
+
header = this.decodeHeader(lines[i]);
|
|
487
|
+
// own properties only, an attacker controlled key such as '__proto__' or 'toString' would
|
|
488
|
+
// otherwise resolve to an inherited Object.prototype member
|
|
489
|
+
if (!Object.hasOwn(headersObj, header.key)) {
|
|
490
|
+
setOwnProperty(headersObj, header.key, [header.value]);
|
|
491
|
+
}
|
|
492
|
+
else {
|
|
493
|
+
headersObj[header.key].push(header.value);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return headersObj;
|
|
497
|
+
}
|
|
498
|
+
/**
|
|
499
|
+
* Joins parsed header value together as 'value; param1=value1; param2=value2'
|
|
500
|
+
* PS: We are following RFC 822 for the list of special characters that we need to keep in quotes.
|
|
501
|
+
* Refer: https://www.w3.org/Protocols/rfc1341/4_Content-Type.html
|
|
502
|
+
* @param {Object} structured Parsed header value
|
|
503
|
+
* @return {String} joined header value
|
|
504
|
+
*/
|
|
505
|
+
buildHeaderValue(structured) {
|
|
506
|
+
let paramsArray = [];
|
|
507
|
+
let params = structured.params || {};
|
|
508
|
+
Object.keys(params).forEach(key => {
|
|
509
|
+
// filename might include unicode characters so it is a special case
|
|
510
|
+
let value = params[key];
|
|
511
|
+
// a parameter name is a token too and it is emitted without any quoting around it
|
|
512
|
+
let param = key.replace(/[\x00-\x1f\x7f]/g, '');
|
|
513
|
+
if (!this.isPlainParamValue(value) || value.length >= 75) {
|
|
514
|
+
this.buildHeaderParam(param, value, 50).forEach(encodedParam => {
|
|
515
|
+
if (!/[\s"\\;:/=(),<>@[\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.endsWith('*')) {
|
|
516
|
+
paramsArray.push(encodedParam.key + '=' + encodedParam.value);
|
|
517
|
+
}
|
|
518
|
+
else {
|
|
519
|
+
paramsArray.push(encodedParam.key + '=' + JSON.stringify(encodedParam.value));
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
else if (!value.length || /[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
|
|
524
|
+
// a parameter value is a token or a quoted-string and a token is never empty, so
|
|
525
|
+
// a valueless parameter such as the 'flag' of 'multipart/mixed; flag; boundary=b'
|
|
526
|
+
// goes out as 'flag=""' rather than as the 'flag=' that parses as neither
|
|
527
|
+
paramsArray.push(param + '=' + JSON.stringify(value));
|
|
528
|
+
}
|
|
529
|
+
else {
|
|
530
|
+
paramsArray.push(param + '=' + value);
|
|
531
|
+
}
|
|
532
|
+
});
|
|
533
|
+
// the value ahead of the parameters is a token, it has no way to carry a control
|
|
534
|
+
// char or DEL and there is no quoting construct around it to escape one into
|
|
535
|
+
let value = typeof structured.value === 'string' ? structured.value.replace(/[\x00-\x1f\x7f]/g, '') : structured.value;
|
|
536
|
+
return value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');
|
|
537
|
+
}
|
|
538
|
+
/**
|
|
539
|
+
* Parses a header value with key=value arguments into a structured
|
|
540
|
+
* object.
|
|
541
|
+
*
|
|
542
|
+
* parseHeaderValue('text/plain; CHARSET=UTF-8') ->
|
|
543
|
+
* {
|
|
544
|
+
* 'value': 'text/plain',
|
|
545
|
+
* 'params': {
|
|
546
|
+
* 'charset': 'UTF-8'
|
|
547
|
+
* }
|
|
548
|
+
* }
|
|
549
|
+
*
|
|
550
|
+
* The result also carries a non-enumerable `decodedParams` array that names the params
|
|
551
|
+
* assembled from rfc2231 segments (title*=, title*0*=, title*0=). Those values are already
|
|
552
|
+
* decoded, so a caller that runs decodeWords() over the params has to leave them out
|
|
553
|
+
*
|
|
554
|
+
* @param {String} str Header value
|
|
555
|
+
* @return {Object} Header value as a parsed structure
|
|
556
|
+
*/
|
|
557
|
+
parseHeaderValue(str) {
|
|
558
|
+
let response = {
|
|
559
|
+
value: '',
|
|
560
|
+
params: {}
|
|
561
|
+
};
|
|
562
|
+
// names of the params assembled from rfc2231 segments, see the continuation handling below.
|
|
563
|
+
// Non-enumerable, so the {value, params} shape callers serialize and compare stays as it is
|
|
564
|
+
Object.defineProperty(response, 'decodedParams', {
|
|
565
|
+
value: [],
|
|
566
|
+
writable: true,
|
|
567
|
+
enumerable: false,
|
|
568
|
+
configurable: true
|
|
569
|
+
});
|
|
570
|
+
let key = false;
|
|
571
|
+
let value = '';
|
|
572
|
+
// Length of the significant part of `value`. Whitespace outside of quotes is
|
|
573
|
+
// not part of the value, while whitespace inside quotes is (rfc2045 section 5.1)
|
|
574
|
+
let valueEnd = 0;
|
|
575
|
+
let stage = STAGE_VALUE;
|
|
576
|
+
let quote = false;
|
|
577
|
+
let escaped = false;
|
|
578
|
+
let chr;
|
|
579
|
+
// Stores the collected value, either as the default value, as the value for the
|
|
580
|
+
// current key, or as a key that has no value at all. Runs once per parameter
|
|
581
|
+
let commit = () => {
|
|
582
|
+
let collected = value.substring(0, valueEnd);
|
|
583
|
+
value = '';
|
|
584
|
+
valueEnd = 0;
|
|
585
|
+
if (stage === STAGE_KEY) {
|
|
586
|
+
// key without a value, see emptykey:
|
|
587
|
+
// Header-Key: somevalue; key=value; emptykey
|
|
588
|
+
if (collected) {
|
|
589
|
+
setOwnProperty(response.params, collected.toLowerCase(), '');
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
else if (key === false) {
|
|
593
|
+
// default value
|
|
594
|
+
response.value = collected;
|
|
595
|
+
}
|
|
596
|
+
else {
|
|
597
|
+
// subkey value
|
|
598
|
+
setOwnProperty(response.params, key, collected);
|
|
599
|
+
}
|
|
600
|
+
};
|
|
601
|
+
for (let i = 0, len = str.length; i < len; i++) {
|
|
602
|
+
chr = str.charAt(i);
|
|
603
|
+
switch (stage) {
|
|
604
|
+
case STAGE_KEY:
|
|
605
|
+
if (chr === '=') {
|
|
606
|
+
key = value.substring(0, valueEnd).toLowerCase();
|
|
607
|
+
value = '';
|
|
608
|
+
valueEnd = 0;
|
|
609
|
+
stage = STAGE_VALUE;
|
|
610
|
+
}
|
|
611
|
+
else if (chr === ';') {
|
|
612
|
+
// key without a value, eg. the "inline" in "attachment; inline; filename=a.txt"
|
|
613
|
+
commit();
|
|
614
|
+
}
|
|
615
|
+
else if (isWSP(chr)) {
|
|
616
|
+
if (value.length) {
|
|
617
|
+
value += chr;
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
value += chr;
|
|
622
|
+
valueEnd = value.length;
|
|
623
|
+
}
|
|
624
|
+
break;
|
|
625
|
+
case STAGE_VALUE:
|
|
626
|
+
if (escaped) {
|
|
627
|
+
value += chr;
|
|
628
|
+
valueEnd = value.length;
|
|
629
|
+
}
|
|
630
|
+
else if (chr === '\\') {
|
|
631
|
+
escaped = true;
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
else if (chr === '"') {
|
|
635
|
+
// every char between the quotes is significant, including whitespace
|
|
636
|
+
quote = !quote;
|
|
637
|
+
}
|
|
638
|
+
else if (!quote && chr === ';') {
|
|
639
|
+
commit();
|
|
640
|
+
stage = STAGE_KEY;
|
|
641
|
+
}
|
|
642
|
+
else if (!quote && isWSP(chr)) {
|
|
643
|
+
if (value.length) {
|
|
644
|
+
value += chr;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
value += chr;
|
|
649
|
+
valueEnd = value.length;
|
|
650
|
+
}
|
|
651
|
+
escaped = false;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
// finalize remainder
|
|
656
|
+
commit();
|
|
657
|
+
// handle parameter value continuations
|
|
658
|
+
// https://tools.ietf.org/html/rfc2231#section-3
|
|
659
|
+
// Collect the segments of every continuation parameter. These are kept out of
|
|
660
|
+
// response.params so that a half assembled value can not leak into the result
|
|
661
|
+
let continuations = new Map();
|
|
662
|
+
for (let key of Object.keys(response.params)) {
|
|
663
|
+
let match = key.match(/\*((\d+)\*?)?$/);
|
|
664
|
+
if (!match) {
|
|
665
|
+
// nothing to do here, does not seem like a continuation param
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
let actualKey = key.substring(0, match.index).toLowerCase();
|
|
669
|
+
let nr = Number(match[2]) || 0;
|
|
670
|
+
let value = response.params[key];
|
|
671
|
+
// remove the old reference
|
|
672
|
+
delete response.params[key];
|
|
673
|
+
let continuation = continuations.get(actualKey);
|
|
674
|
+
if (!continuation) {
|
|
675
|
+
continuation = {
|
|
676
|
+
charset: false,
|
|
677
|
+
values: []
|
|
678
|
+
};
|
|
679
|
+
continuations.set(actualKey, continuation);
|
|
680
|
+
}
|
|
681
|
+
if (nr === 0 && match[0].charAt(match[0].length - 1) === '*' && (match = value.match(/^([^']*)'[^']*'(.*)$/))) {
|
|
682
|
+
continuation.charset = match[1] || 'utf-8';
|
|
683
|
+
value = match[2];
|
|
684
|
+
}
|
|
685
|
+
continuation.values.push({ nr, value });
|
|
686
|
+
}
|
|
687
|
+
// concatenate the split rfc2231 strings and decode the encoded ones
|
|
688
|
+
for (let [key, continuation] of continuations) {
|
|
689
|
+
// NB! An assembled continuation always overrides a plain parameter with the same
|
|
690
|
+
// name, in either source order. The plain form is the legacy ascii fallback of the
|
|
691
|
+
// extended one, so rfc6266 section 4.3 requires picking the extended value. Letting
|
|
692
|
+
// the fallback win would make libmime disagree with the mail clients that follow it
|
|
693
|
+
let value = continuation.values
|
|
694
|
+
.sort((a, b) => a.nr - b.nr)
|
|
695
|
+
.map(val => val.value)
|
|
696
|
+
.join('');
|
|
697
|
+
// Both branches below store a decoded value, so a caller that runs decodeWords() over the
|
|
698
|
+
// params has to leave this one out. A value that percent-encodes an encoded word would
|
|
699
|
+
// otherwise be decoded twice: filename*=utf-8''invoice%3D%3Futf-8%3FQ%3F%3D2Eexe%3F%3D
|
|
700
|
+
// names the file "invoice=?utf-8?Q?=2Eexe?=", not "invoice.exe"
|
|
701
|
+
response.decodedParams.push(key);
|
|
702
|
+
if (!continuation.charset) {
|
|
703
|
+
setOwnProperty(response.params, key, this.decodeWords(value));
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
// convert "%AB" to the quoted printable "=AB" and decode it as the charset of the parameter.
|
|
707
|
+
// NB! The charset is passed in as an argument instead of building a "=?charset?Q?=AB?=" string,
|
|
708
|
+
// a charset that contains "?" would otherwise inject an encoded word of its own
|
|
709
|
+
let qpValue = value
|
|
710
|
+
// fix invalidly encoded chars
|
|
711
|
+
.replace(/[=_\s]/g, s => {
|
|
712
|
+
if (s === ' ') {
|
|
713
|
+
return '_';
|
|
714
|
+
}
|
|
715
|
+
let c = s.charCodeAt(0).toString(16);
|
|
716
|
+
return '%' + (c.length < 2 ? '0' : '') + c;
|
|
717
|
+
})
|
|
718
|
+
// change from urlencoding to percent encoding
|
|
719
|
+
.replace(/%/g, '=');
|
|
720
|
+
setOwnProperty(response.params, key, this.decodeWord(continuation.charset, 'Q', qpValue));
|
|
721
|
+
}
|
|
722
|
+
return response;
|
|
723
|
+
}
|
|
724
|
+
/**
|
|
725
|
+
* Encodes a string or an Buffer to an UTF-8 Parameter Value Continuation encoding (rfc2231)
|
|
726
|
+
* Useful for splitting long parameter values.
|
|
727
|
+
*
|
|
728
|
+
* For example
|
|
729
|
+
* title="unicode string"
|
|
730
|
+
* becomes
|
|
731
|
+
* title*0*=utf-8''unicode
|
|
732
|
+
* title*1*=%20string
|
|
733
|
+
*
|
|
734
|
+
* @param {String} key Parameter name (eg. 'filename')
|
|
735
|
+
* @param {String|Buffer} data String to be encoded
|
|
736
|
+
* @param {Number} [maxLength=50] Max length for generated chunks
|
|
737
|
+
* @param {String} [fromCharset='UTF-8'] Source character set
|
|
738
|
+
* @return {Array} A list of encoded keys and headers
|
|
739
|
+
*/
|
|
740
|
+
buildHeaderParam(key, data, maxLength, fromCharset) {
|
|
741
|
+
let list = [];
|
|
742
|
+
if (typeof data !== 'string' && !node_buffer_1.Buffer.isBuffer(data)) {
|
|
743
|
+
// documented input is a string or a Buffer, normalize anything else
|
|
744
|
+
data = data === null || data === undefined ? '' : data.toString();
|
|
745
|
+
}
|
|
746
|
+
let encodedStr = typeof data === 'string' ? data : libcharset.decode(data, fromCharset);
|
|
747
|
+
let chr;
|
|
748
|
+
let line;
|
|
749
|
+
let startPos = 0;
|
|
750
|
+
let isEncoded = false;
|
|
751
|
+
let i, len;
|
|
752
|
+
maxLength = maxLength || 50;
|
|
753
|
+
// process ascii only text
|
|
754
|
+
if (this.isPlainParamValue(data)) {
|
|
755
|
+
// check if conversion is even needed
|
|
756
|
+
if (encodedStr.length <= maxLength) {
|
|
757
|
+
return [
|
|
758
|
+
{
|
|
759
|
+
key,
|
|
760
|
+
value: encodedStr
|
|
761
|
+
}
|
|
762
|
+
];
|
|
763
|
+
}
|
|
764
|
+
encodedStr = encodedStr.replace(new RegExp('.{' + maxLength + '}', 'g'), str => {
|
|
765
|
+
list.push({
|
|
766
|
+
line: str
|
|
767
|
+
});
|
|
768
|
+
return '';
|
|
769
|
+
});
|
|
770
|
+
if (encodedStr) {
|
|
771
|
+
list.push({
|
|
772
|
+
line: encodedStr
|
|
773
|
+
});
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
else {
|
|
777
|
+
if (/[\uD800-\uDBFF]/.test(encodedStr)) {
|
|
778
|
+
// string contains surrogate pairs, so split it into code points. An unpaired
|
|
779
|
+
// surrogate comes on its own and does not tear apart a valid pair following it
|
|
780
|
+
encodedStr = Array.from(encodedStr);
|
|
781
|
+
}
|
|
782
|
+
// first line includes the charset and language info and needs to be encoded
|
|
783
|
+
// even if it does not contain any unicode characters
|
|
784
|
+
line = "utf-8''";
|
|
785
|
+
isEncoded = true;
|
|
786
|
+
startPos = 0;
|
|
787
|
+
// process text with unicode or special chars
|
|
788
|
+
for (i = 0, len = encodedStr.length; i < len; i++) {
|
|
789
|
+
chr = encodedStr[i];
|
|
790
|
+
if (isEncoded) {
|
|
791
|
+
chr = this.safeEncodeURIComponent(chr);
|
|
792
|
+
}
|
|
793
|
+
else {
|
|
794
|
+
// try to urlencode current char
|
|
795
|
+
chr = chr === ' ' ? chr : this.safeEncodeURIComponent(chr);
|
|
796
|
+
// By default it is not required to encode a line, the need
|
|
797
|
+
// only appears when the string contains unicode or special chars
|
|
798
|
+
// in this case we start processing the line over and encode all chars
|
|
799
|
+
if (chr !== encodedStr[i]) {
|
|
800
|
+
// Check if it is even possible to add the encoded char to the line
|
|
801
|
+
// If not, there is no reason to use this line, just push it to the list
|
|
802
|
+
// and start a new line with the char that needs encoding
|
|
803
|
+
if ((this.safeEncodeURIComponent(line) + chr).length >= maxLength) {
|
|
804
|
+
list.push({
|
|
805
|
+
line,
|
|
806
|
+
encoded: isEncoded
|
|
807
|
+
});
|
|
808
|
+
// the line we start here holds an encoded char, so it has to be
|
|
809
|
+
// flagged as one. Otherwise it gets an unstarred continuation key
|
|
810
|
+
// and a receiver reads the percent escapes as literal text
|
|
811
|
+
line = '';
|
|
812
|
+
isEncoded = true;
|
|
813
|
+
}
|
|
814
|
+
else {
|
|
815
|
+
isEncoded = true;
|
|
816
|
+
i = startPos;
|
|
817
|
+
line = '';
|
|
818
|
+
continue;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
// if the line is already too long, push it to the list and start a new one
|
|
823
|
+
if ((line + chr).length >= maxLength) {
|
|
824
|
+
list.push({
|
|
825
|
+
line,
|
|
826
|
+
encoded: isEncoded
|
|
827
|
+
});
|
|
828
|
+
line = chr = encodedStr[i] === ' ' ? ' ' : this.safeEncodeURIComponent(encodedStr[i]);
|
|
829
|
+
if (chr === encodedStr[i]) {
|
|
830
|
+
isEncoded = false;
|
|
831
|
+
startPos = i - 1;
|
|
832
|
+
}
|
|
833
|
+
else {
|
|
834
|
+
isEncoded = true;
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
else {
|
|
838
|
+
line += chr;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
if (line) {
|
|
842
|
+
list.push({
|
|
843
|
+
line,
|
|
844
|
+
encoded: isEncoded
|
|
845
|
+
});
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
return list.map((item, i) => ({
|
|
849
|
+
// encoded lines: {name}*{part}*
|
|
850
|
+
// unencoded lines: {name}*{part}
|
|
851
|
+
// if any line needs to be encoded then the first line (part==0) is always encoded
|
|
852
|
+
key: key + '*' + i + (item.encoded ? '*' : ''),
|
|
853
|
+
value: item.line
|
|
854
|
+
}));
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* Returns file extension for a content type string. If no suitable extensions
|
|
858
|
+
* are found, 'bin' is used as the default extension
|
|
859
|
+
*
|
|
860
|
+
* @param {String} mimeType Content type to be checked for
|
|
861
|
+
* @return {String} File extension
|
|
862
|
+
*/
|
|
863
|
+
detectExtension(input) {
|
|
864
|
+
let mimeType = (input || '').toString().toLowerCase().replace(/\s/g, '');
|
|
865
|
+
if (!Object.hasOwn(mimetypes.list, mimeType)) {
|
|
866
|
+
return 'bin';
|
|
867
|
+
}
|
|
868
|
+
if (typeof mimetypes.list[mimeType] === 'string') {
|
|
869
|
+
return mimetypes.list[mimeType];
|
|
870
|
+
}
|
|
871
|
+
let mimeParts = mimeType.split('/');
|
|
872
|
+
// search for name match
|
|
873
|
+
for (let i = 0, len = mimetypes.list[mimeType].length; i < len; i++) {
|
|
874
|
+
if (mimeParts[1] === mimetypes.list[mimeType][i]) {
|
|
875
|
+
return mimetypes.list[mimeType][i];
|
|
876
|
+
}
|
|
877
|
+
}
|
|
878
|
+
// use the first one
|
|
879
|
+
return mimetypes.list[mimeType][0] !== '*' ? mimetypes.list[mimeType][0] : 'bin';
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* Returns content type for a file extension. If no suitable content types
|
|
883
|
+
* are found, 'application/octet-stream' is used as the default content type
|
|
884
|
+
*
|
|
885
|
+
* @param {String} extension Extension (or filename) to be checked for
|
|
886
|
+
* @return {String} Content type
|
|
887
|
+
*/
|
|
888
|
+
detectMimeType(input) {
|
|
889
|
+
let extension = (input || '').toString().toLowerCase().replace(/\s/g, '').replace(/^\./g, '').split('.').pop() || '';
|
|
890
|
+
if (!Object.hasOwn(mimetypes.extensions, extension)) {
|
|
891
|
+
return 'application/octet-stream';
|
|
892
|
+
}
|
|
893
|
+
if (typeof mimetypes.extensions[extension] === 'string') {
|
|
894
|
+
return mimetypes.extensions[extension];
|
|
895
|
+
}
|
|
896
|
+
let mimeParts;
|
|
897
|
+
// search for name match
|
|
898
|
+
for (let i = 0, len = mimetypes.extensions[extension].length; i < len; i++) {
|
|
899
|
+
mimeParts = mimetypes.extensions[extension][i].split('/');
|
|
900
|
+
if (mimeParts[1] === extension) {
|
|
901
|
+
return mimetypes.extensions[extension][i];
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
// use the first one
|
|
905
|
+
return mimetypes.extensions[extension][0];
|
|
906
|
+
}
|
|
907
|
+
/**
|
|
908
|
+
* Folds long lines, useful for folding header lines (afterSpace=false) and
|
|
909
|
+
* flowed text (afterSpace=true)
|
|
910
|
+
*
|
|
911
|
+
* @param {String} str String to be folded
|
|
912
|
+
* @param {Number} [lineLength=76] Maximum length of a line
|
|
913
|
+
* @param {Boolean} afterSpace If true, leave a space in the end of a line
|
|
914
|
+
* @return {String} String with folded lines
|
|
915
|
+
*/
|
|
916
|
+
foldLines(input, lineLength, afterSpace) {
|
|
917
|
+
let str = (input || '').toString();
|
|
918
|
+
lineLength = lineLength || 76;
|
|
919
|
+
let pos = 0, len = str.length, result = '', line, match;
|
|
920
|
+
while (pos < len) {
|
|
921
|
+
line = str.substring(pos, pos + Math.max(lineLength, 0));
|
|
922
|
+
if (line.length < lineLength) {
|
|
923
|
+
result += line;
|
|
924
|
+
break;
|
|
925
|
+
}
|
|
926
|
+
if ((match = line.match(/^[^\n\r]*(\r?\n|\r)/))) {
|
|
927
|
+
line = match[0];
|
|
928
|
+
result += line;
|
|
929
|
+
pos += line.length;
|
|
930
|
+
continue;
|
|
931
|
+
}
|
|
932
|
+
else if ((match = line.match(/(\s+)[^\s]*$/)) && match[0].length - (afterSpace ? (match[1] || '').length : 0) < line.length) {
|
|
933
|
+
line = line.substring(0, line.length - (match[0].length - (afterSpace ? (match[1] || '').length : 0)));
|
|
934
|
+
}
|
|
935
|
+
else if ((match = str.slice(pos + line.length).match(/^[^\s]+(\s*)/))) {
|
|
936
|
+
line = line + match[0].substring(0, match[0].length - (!afterSpace ? (match[1] || '').length : 0));
|
|
937
|
+
}
|
|
938
|
+
result += line;
|
|
939
|
+
pos += line.length;
|
|
940
|
+
if (pos < len) {
|
|
941
|
+
result += '\r\n';
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
return result;
|
|
945
|
+
}
|
|
946
|
+
/**
|
|
947
|
+
* Splits a mime encoded string. Needed for dividing mime words into smaller chunks
|
|
948
|
+
*
|
|
949
|
+
* @param {String} str Mime encoded string to be split up
|
|
950
|
+
* @param {Number} maxlen Maximum length of characters for one part (minimum 12)
|
|
951
|
+
* @return {Array} Split string
|
|
952
|
+
*/
|
|
953
|
+
splitMimeEncodedString(str, maxlen) {
|
|
954
|
+
let curLine, fallbackLine, match, chr, done, lines = [];
|
|
955
|
+
// require at least 12 symbols to fit possible 4 octet UTF-8 sequences
|
|
956
|
+
maxlen = Math.max(maxlen || 0, 12);
|
|
957
|
+
while (str.length) {
|
|
958
|
+
curLine = str.substring(0, maxlen);
|
|
959
|
+
// move incomplete escaped char back to main
|
|
960
|
+
if ((match = curLine.match(/[=][0-9A-F]?$/i))) {
|
|
961
|
+
curLine = curLine.substring(0, match.index);
|
|
962
|
+
}
|
|
963
|
+
// Malformed input (a run of stray UTF-8 continuation bytes, or a dangling "=" at the
|
|
964
|
+
// end) has no split point that keeps a character sequence whole, so the loop below
|
|
965
|
+
// walks back to an empty line looking for one. An empty line never consumes anything
|
|
966
|
+
// and would loop forever, so keep the widest chunk that at least does not cut a "=XX"
|
|
967
|
+
// escape in half, or the rest of the string when there is not even that
|
|
968
|
+
fallbackLine = curLine.length ? curLine : str.substring(0, maxlen);
|
|
969
|
+
done = false;
|
|
970
|
+
while (!done && curLine.length) {
|
|
971
|
+
done = true;
|
|
972
|
+
// check if not middle of a unicode char sequence
|
|
973
|
+
if ((match = str.slice(curLine.length).match(/^[=]([0-9A-F]{2})/i))) {
|
|
974
|
+
chr = parseInt(match[1], 16);
|
|
975
|
+
// invalid sequence, move one escaped char back and recheck. Only over a whole
|
|
976
|
+
// "=XX" escape, a plain char in front of a stray continuation byte ends the walk
|
|
977
|
+
if (chr < 0xc2 && chr > 0x7f && /=[0-9A-F]{2}$/i.test(curLine)) {
|
|
978
|
+
curLine = curLine.substring(0, curLine.length - 3);
|
|
979
|
+
done = false;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (!curLine.length) {
|
|
984
|
+
curLine = fallbackLine;
|
|
985
|
+
}
|
|
986
|
+
lines.push(curLine);
|
|
987
|
+
str = str.slice(curLine.length);
|
|
988
|
+
}
|
|
989
|
+
return lines;
|
|
990
|
+
}
|
|
991
|
+
encodeURICharComponent(chr) {
|
|
992
|
+
let res = '';
|
|
993
|
+
// percent encoding is defined over bytes, so encode the char as UTF-8 first
|
|
994
|
+
let buf = node_buffer_1.Buffer.from(chr, 'utf-8');
|
|
995
|
+
for (let i = 0, len = buf.length; i < len; i++) {
|
|
996
|
+
let ord = buf[i].toString(16).toUpperCase();
|
|
997
|
+
res += '%' + (ord.length < 2 ? '0' : '') + ord;
|
|
998
|
+
}
|
|
999
|
+
return res;
|
|
1000
|
+
}
|
|
1001
|
+
safeEncodeURIComponent(input) {
|
|
1002
|
+
let str = (input || '').toString();
|
|
1003
|
+
try {
|
|
1004
|
+
// might throw if we try to encode invalid sequences, eg. partial emoji
|
|
1005
|
+
str = encodeURIComponent(str);
|
|
1006
|
+
}
|
|
1007
|
+
catch (_err) {
|
|
1008
|
+
// an unpaired surrogate has no utf-8 representation, so run the value through a
|
|
1009
|
+
// utf-8 roundtrip to get the same U+FFFD every other encoder here produces and
|
|
1010
|
+
// retry. The value must never come back unencoded, it goes into a header parameter
|
|
1011
|
+
// where a bare quote or semicolon would break it out into a parameter of its own
|
|
1012
|
+
str = encodeURIComponent(node_buffer_1.Buffer.from(str, 'utf-8').toString('utf-8'));
|
|
1013
|
+
}
|
|
1014
|
+
// ensure chars that are not handled by encodeURICompent are converted as well
|
|
1015
|
+
return str.replace(/[\x00-\x1F *'()<>@,;:\\"[\]?=\u007F-\uFFFF]/g, chr => this.encodeURICharComponent(chr));
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
exports.Libmime = Libmime;
|
|
1019
|
+
/**
|
|
1020
|
+
* The shared instance, the default export of the package. The class is attached to it as `Libmime`,
|
|
1021
|
+
* so `require('libmime').Libmime` keeps working
|
|
1022
|
+
*/
|
|
1023
|
+
const libmime = new Libmime();
|
|
1024
|
+
libmime.Libmime = Libmime;
|
|
1025
|
+
exports.default = libmime;
|
|
1026
|
+
module.exports = exports.default;
|
|
1027
|
+
Object.defineProperty(module.exports, 'default', { value: exports.default, enumerable: false, writable: true, configurable: true });
|