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