@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.
Files changed (252) hide show
  1. package/.jsii +9 -9
  2. package/lib/rootmail.js +1 -1
  3. package/lib/ses-receive.js +1 -1
  4. package/lib/ses-receive.ops-santa-handler.js +2 -2
  5. package/node_modules/@aws-sdk/checksums/package.json +3 -3
  6. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-cjs/index.js +9 -7
  7. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-es/schemas/schemas_0.js +11 -6
  8. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveriesCommand.d.ts +3 -0
  9. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliveryDestinationsCommand.d.ts +3 -0
  10. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/DescribeDeliverySourcesCommand.d.ts +3 -0
  11. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/commands/PutResourcePolicyCommand.d.ts +1 -1
  12. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/models/models_0.d.ts +27 -1
  13. package/node_modules/@aws-sdk/client-cloudwatch-logs/dist-types/ts3.4/models/models_0.d.ts +3 -0
  14. package/node_modules/@aws-sdk/client-cloudwatch-logs/package.json +4 -4
  15. package/node_modules/@aws-sdk/client-route-53/dist-cjs/index.js +1 -1
  16. package/node_modules/@aws-sdk/client-route-53/package.json +4 -4
  17. package/node_modules/@aws-sdk/client-s3/dist-cjs/index.js +1 -1
  18. package/node_modules/@aws-sdk/client-s3/package.json +7 -7
  19. package/node_modules/@aws-sdk/client-ses/dist-cjs/index.js +1 -1
  20. package/node_modules/@aws-sdk/client-ses/package.json +4 -4
  21. package/node_modules/@aws-sdk/client-ssm/dist-cjs/index.js +1 -1
  22. package/node_modules/@aws-sdk/client-ssm/package.json +4 -4
  23. package/node_modules/@aws-sdk/core/dist-cjs/submodules/protocols/index.js +94 -1589
  24. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/index.js +1 -7
  25. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsJsonRpcProtocol.js +1 -3
  26. package/node_modules/@aws-sdk/core/dist-es/submodules/protocols/json/AwsRestJsonProtocol.js +1 -3
  27. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/index.d.ts +2 -8
  28. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
  29. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
  30. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
  31. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
  32. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/index.d.ts +10 -10
  33. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_0Protocol.d.ts +1 -2
  34. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJson1_1Protocol.d.ts +1 -2
  35. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsJsonRpcProtocol.d.ts +2 -3
  36. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/AwsRestJsonProtocol.d.ts +2 -3
  37. package/node_modules/@aws-sdk/core/package.json +2 -2
  38. package/node_modules/@aws-sdk/credential-provider-env/package.json +3 -3
  39. package/node_modules/@aws-sdk/credential-provider-http/package.json +3 -3
  40. package/node_modules/@aws-sdk/credential-provider-ini/package.json +10 -10
  41. package/node_modules/@aws-sdk/credential-provider-login/package.json +4 -4
  42. package/node_modules/@aws-sdk/credential-provider-node/package.json +8 -8
  43. package/node_modules/@aws-sdk/credential-provider-process/package.json +3 -3
  44. package/node_modules/@aws-sdk/credential-provider-sso/package.json +5 -5
  45. package/node_modules/@aws-sdk/credential-provider-web-identity/package.json +4 -4
  46. package/node_modules/@aws-sdk/middleware-sdk-s3/package.json +3 -3
  47. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/cognito-identity/index.js +1 -1
  48. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/signin/index.js +1 -1
  49. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso/index.js +1 -1
  50. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sso-oidc/index.js +1 -1
  51. package/node_modules/@aws-sdk/nested-clients/dist-cjs/submodules/sts/index.js +1 -1
  52. package/node_modules/@aws-sdk/nested-clients/package.json +3 -3
  53. package/node_modules/@aws-sdk/token-providers/dist-cjs/index.js +37 -19
  54. package/node_modules/@aws-sdk/token-providers/dist-es/fromSso.js +37 -19
  55. package/node_modules/@aws-sdk/token-providers/package.json +4 -4
  56. package/node_modules/@smithy/core/dist-cjs/submodules/protocols/index.js +1695 -3
  57. package/node_modules/@smithy/core/dist-es/submodules/protocols/ConfigurableSerdeContext.js +6 -0
  58. package/node_modules/@smithy/core/dist-es/submodules/protocols/UnionSerde.js +26 -0
  59. package/node_modules/@smithy/core/dist-es/submodules/protocols/common.js +3 -0
  60. package/node_modules/@smithy/core/dist-es/submodules/protocols/index.js +8 -0
  61. package/node_modules/@smithy/core/dist-es/submodules/protocols/json/SmithyRpcV2JsonProtocol.js +102 -0
  62. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeDeserializer.js +8 -1
  63. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonShapeSerializer.js +8 -1
  64. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.js +13 -2
  65. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonShapeSerializer2.js +11 -1
  66. package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/parseJsonBody.js +16 -1
  67. package/node_modules/@smithy/core/dist-es/submodules/protocols/writeKey.js +3 -0
  68. package/node_modules/@smithy/core/dist-types/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
  69. package/node_modules/@smithy/core/dist-types/submodules/protocols/UnionSerde.d.ts +24 -0
  70. package/node_modules/@smithy/core/dist-types/submodules/protocols/common.d.ts +2 -0
  71. package/node_modules/@smithy/core/dist-types/submodules/protocols/index.d.ts +9 -0
  72. package/node_modules/@smithy/core/dist-types/submodules/protocols/json/JsonSettings.d.ts +13 -0
  73. package/node_modules/@smithy/core/dist-types/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
  74. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/parseJsonBody.d.ts +16 -0
  75. package/node_modules/@smithy/core/dist-types/submodules/protocols/writeKey.d.ts +4 -0
  76. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/ConfigurableSerdeContext.d.ts +8 -0
  77. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/UnionSerde.d.ts +24 -0
  78. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/common.d.ts +2 -0
  79. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/index.d.ts +9 -0
  80. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/JsonSettings.d.ts +13 -0
  81. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/SmithyRpcV2JsonProtocol.d.ts +28 -0
  82. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonCodec.d.ts +15 -11
  83. 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
  84. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +14 -0
  85. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +29 -0
  86. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +51 -0
  87. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +13 -9
  88. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +30 -0
  89. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +60 -0
  90. package/node_modules/{@aws-sdk → @smithy}/core/dist-types/ts3.4/submodules/protocols/json/detectBufferParsing.d.ts +1 -1
  91. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +21 -0
  92. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +15 -0
  93. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +11 -0
  94. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +38 -0
  95. package/node_modules/@smithy/core/dist-types/ts3.4/submodules/protocols/writeKey.d.ts +4 -0
  96. package/node_modules/@smithy/core/package.json +2 -1
  97. package/node_modules/@zone-eu/mailsplit/README.md +43 -54
  98. package/node_modules/@zone-eu/mailsplit/lib/headers.js +10 -4
  99. package/node_modules/@zone-eu/mailsplit/lib/message-splitter.js +5 -4
  100. package/node_modules/@zone-eu/mailsplit/lib/mime-node.d.ts +4 -1
  101. package/node_modules/@zone-eu/mailsplit/lib/mime-node.js +30 -5
  102. package/node_modules/@zone-eu/mailsplit/lib/types.d.ts +3 -1
  103. package/node_modules/@zone-eu/mailsplit/package.json +22 -17
  104. package/node_modules/libbase64/CHANGELOG.md +20 -0
  105. package/node_modules/libbase64/README.md +40 -13
  106. package/node_modules/libbase64/dist/cjs/libbase64.d.ts +80 -0
  107. package/node_modules/libbase64/dist/cjs/libbase64.js +310 -0
  108. package/node_modules/libbase64/dist/cjs/package.json +3 -0
  109. package/node_modules/libbase64/dist/esm/libbase64.d.ts +80 -0
  110. package/node_modules/libbase64/{lib → dist/esm}/libbase64.js +74 -117
  111. package/node_modules/libbase64/dist/esm/package.json +3 -0
  112. package/node_modules/libbase64/package.json +59 -13
  113. package/node_modules/libmime/CHANGELOG.md +38 -0
  114. package/node_modules/libmime/README.md +55 -26
  115. package/node_modules/libmime/SECURITY.md +152 -0
  116. package/node_modules/libmime/dist/cjs/charset.d.ts +43 -0
  117. package/node_modules/libmime/dist/cjs/charset.js +135 -0
  118. package/node_modules/libmime/dist/cjs/charsets.d.ts +2 -0
  119. package/node_modules/libmime/dist/cjs/charsets.js +213 -0
  120. package/node_modules/libmime/dist/cjs/get-charset-name.d.ts +1 -0
  121. package/node_modules/libmime/dist/cjs/get-charset-name.js +224 -0
  122. package/node_modules/libmime/dist/cjs/libmime.d.ts +218 -0
  123. package/node_modules/libmime/dist/cjs/libmime.js +1027 -0
  124. package/node_modules/libmime/dist/cjs/mimetypes.d.ts +7 -0
  125. package/node_modules/libmime/dist/cjs/mimetypes.js +2072 -0
  126. package/node_modules/libmime/dist/cjs/package.json +3 -0
  127. package/node_modules/libmime/dist/cjs/types.d.ts +59 -0
  128. package/node_modules/libmime/dist/cjs/types.js +4 -0
  129. package/node_modules/libmime/dist/esm/charset.d.ts +43 -0
  130. package/node_modules/libmime/dist/esm/charset.js +125 -0
  131. package/node_modules/libmime/dist/esm/charsets.d.ts +2 -0
  132. package/node_modules/libmime/{lib → dist/esm}/charsets.js +3 -5
  133. package/node_modules/libmime/dist/esm/get-charset-name.d.ts +1 -0
  134. package/node_modules/libmime/{lib → dist/esm}/get-charset-name.js +1 -8
  135. package/node_modules/libmime/dist/esm/libmime.d.ts +219 -0
  136. package/node_modules/libmime/{lib → dist/esm}/libmime.js +275 -334
  137. package/node_modules/libmime/dist/esm/mimetypes.d.ts +7 -0
  138. package/node_modules/libmime/dist/esm/mimetypes.js +2068 -0
  139. package/node_modules/libmime/dist/esm/package.json +3 -0
  140. package/node_modules/libmime/dist/esm/types.d.ts +59 -0
  141. package/node_modules/libmime/dist/esm/types.js +3 -0
  142. package/node_modules/libmime/package.json +60 -15
  143. package/node_modules/libqp/CHANGELOG.md +18 -0
  144. package/node_modules/libqp/README.md +50 -19
  145. package/node_modules/libqp/dist/cjs/libqp.d.ts +74 -0
  146. package/node_modules/libqp/dist/cjs/libqp.js +378 -0
  147. package/node_modules/libqp/dist/cjs/package.json +3 -0
  148. package/node_modules/libqp/dist/esm/libqp.d.ts +74 -0
  149. package/node_modules/libqp/dist/esm/libqp.js +369 -0
  150. package/node_modules/libqp/dist/esm/package.json +3 -0
  151. package/node_modules/libqp/lib/libqp.js +4 -383
  152. package/node_modules/libqp/lib/package.json +3 -0
  153. package/node_modules/libqp/package.json +62 -6
  154. package/node_modules/mailparser/CHANGELOG.md +56 -0
  155. package/node_modules/mailparser/README.md +6 -0
  156. package/node_modules/mailparser/dist/cjs/index.d.ts +10 -0
  157. package/node_modules/mailparser/dist/cjs/index.js +12 -0
  158. package/node_modules/mailparser/dist/cjs/mail-parser.d.ts +98 -0
  159. package/node_modules/mailparser/dist/cjs/mail-parser.js +1419 -0
  160. package/node_modules/mailparser/dist/cjs/package.json +3 -0
  161. package/node_modules/mailparser/dist/cjs/simple-parser.d.ts +17 -0
  162. package/node_modules/mailparser/dist/cjs/simple-parser.js +133 -0
  163. package/node_modules/mailparser/dist/cjs/stream-hash.d.ts +24 -0
  164. package/node_modules/mailparser/dist/cjs/stream-hash.js +33 -0
  165. package/node_modules/mailparser/dist/cjs/tlds.cjs +8 -0
  166. package/node_modules/mailparser/dist/cjs/types.d.ts +309 -0
  167. package/node_modules/mailparser/dist/cjs/types.js +2 -0
  168. package/node_modules/mailparser/dist/esm/index.d.ts +10 -0
  169. package/node_modules/mailparser/dist/esm/index.js +4 -0
  170. package/node_modules/mailparser/dist/esm/mail-parser.d.ts +98 -0
  171. package/node_modules/mailparser/{lib → dist/esm}/mail-parser.js +302 -368
  172. package/node_modules/mailparser/dist/esm/package.json +3 -0
  173. package/node_modules/mailparser/dist/esm/simple-parser.d.ts +17 -0
  174. package/node_modules/mailparser/{lib → dist/esm}/simple-parser.js +45 -68
  175. package/node_modules/mailparser/dist/esm/stream-hash.d.ts +24 -0
  176. package/node_modules/mailparser/{lib → dist/esm}/stream-hash.js +7 -9
  177. package/node_modules/mailparser/dist/esm/tlds.cjs +8 -0
  178. package/node_modules/mailparser/dist/esm/types.d.ts +309 -0
  179. package/node_modules/mailparser/dist/esm/types.js +1 -0
  180. package/node_modules/mailparser/package.json +88 -17
  181. package/node_modules/nodemailer/CHANGELOG.md +44 -0
  182. package/node_modules/nodemailer/dist/cjs/addressparser/index.js +695 -267
  183. package/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +4 -1
  184. package/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +8 -2
  185. package/node_modules/nodemailer/dist/cjs/mime-node/index.js +48 -16
  186. package/node_modules/nodemailer/dist/cjs/package-info.d.ts +1 -1
  187. package/node_modules/nodemailer/dist/cjs/package-info.js +1 -1
  188. package/node_modules/nodemailer/dist/cjs/shared/url.js +22 -5
  189. package/node_modules/nodemailer/dist/esm/addressparser/index.js +695 -267
  190. package/node_modules/nodemailer/dist/esm/mailer/mail-message.js +4 -1
  191. package/node_modules/nodemailer/dist/esm/mime-funcs/index.js +8 -2
  192. package/node_modules/nodemailer/dist/esm/mime-node/index.js +48 -16
  193. package/node_modules/nodemailer/dist/esm/package-info.d.ts +1 -1
  194. package/node_modules/nodemailer/dist/esm/package-info.js +1 -1
  195. package/node_modules/nodemailer/dist/esm/shared/url.js +22 -5
  196. package/node_modules/nodemailer/package.json +1 -1
  197. package/package.json +9 -9
  198. package/node_modules/@aws-sdk/core/dist-types/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -7
  199. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonSettings.d.ts +0 -4
  200. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -13
  201. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -18
  202. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -27
  203. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -15
  204. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -39
  205. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReplacer.d.ts +0 -7
  206. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/jsonReviver.d.ts +0 -7
  207. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/needsReviver.d.ts +0 -2
  208. package/node_modules/@aws-sdk/core/dist-types/ts3.4/submodules/protocols/json/parseJsonBody.d.ts +0 -14
  209. package/node_modules/libbase64/.eslintrc +0 -11
  210. package/node_modules/libbase64/.github/FUNDING.yml +0 -4
  211. package/node_modules/libbase64/.github/VULNERABILITY_REPORT.yml +0 -75
  212. package/node_modules/libbase64/.github/workflows/release.yaml +0 -39
  213. package/node_modules/libbase64/.github/workflows/test.yml +0 -21
  214. package/node_modules/libbase64/.ncurc.js +0 -13
  215. package/node_modules/libbase64/.prettierrc.js +0 -8
  216. package/node_modules/libmime/lib/charset.js +0 -131
  217. package/node_modules/libmime/lib/mimetypes.js +0 -2049
  218. package/node_modules/libqp/.eslintrc +0 -15
  219. package/node_modules/libqp/.github/FUNDING.yml +0 -4
  220. package/node_modules/libqp/.github/VULNERABILITY_REPORT.yml +0 -75
  221. package/node_modules/libqp/.github/workflows/release.yaml +0 -39
  222. package/node_modules/libqp/.github/workflows/test.yml +0 -21
  223. package/node_modules/libqp/.prettierrc.js +0 -8
  224. package/node_modules/libqp/test/libqp-test.js +0 -366
  225. package/node_modules/mailparser/.gitattributes +0 -1
  226. package/node_modules/mailparser/.prettierignore +0 -3
  227. package/node_modules/mailparser/.prettierrc.js +0 -10
  228. package/node_modules/mailparser/.release-please-manifest.json +0 -3
  229. package/node_modules/mailparser/eslint.config.js +0 -33
  230. package/node_modules/mailparser/index.js +0 -9
  231. package/node_modules/mailparser/release-please-config.json +0 -10
  232. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/JsonSettings.js +0 -0
  233. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonCodec.js +0 -0
  234. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v1/JsonSettings.js +0 -0
  235. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.js +0 -0
  236. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/codec-v2/JsonCodec2.js +0 -0
  237. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/detectBufferParsing.js +0 -0
  238. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReplacer.js +0 -0
  239. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/jsonReviver.js +0 -0
  240. /package/node_modules/{@aws-sdk → @smithy}/core/dist-es/submodules/protocols/json/needsReviver.js +0 -0
  241. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonCodec.d.ts +0 -0
  242. /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
  243. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeDeserializer.d.ts +0 -0
  244. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v1/JsonShapeSerializer.d.ts +0 -0
  245. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonBytesStringAdapter.d.ts +0 -0
  246. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonCodec2.d.ts +0 -0
  247. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeDeserializer2.d.ts +0 -0
  248. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/codec-v2/JsonShapeSerializer2.d.ts +0 -0
  249. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/detectBufferParsing.d.ts +0 -0
  250. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReplacer.d.ts +0 -0
  251. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/jsonReviver.d.ts +0 -0
  252. /package/node_modules/{@aws-sdk → @smithy}/core/dist-types/submodules/protocols/json/needsReviver.d.ts +0 -0
@@ -1,15 +1,11 @@
1
- /* eslint no-control-regex: 0, no-div-regex: 0, quotes: 0 */
2
- 'use strict';
3
-
4
- const { Buffer } = require('node:buffer');
5
- const libcharset = require('./charset');
6
- const libbase64 = require('libbase64');
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
- } else {
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
- if (typeof value !== 'string' || /[\x00-\x08\x0b\x0c\x0e-\x1f\u0080-\uFFFF]/.test(value)) {
51
- return false;
52
- } else {
53
- return true;
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(str, delSp) {
81
- str = (str || '').toString();
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 = null,
91
- partsLength = 0;
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 !== null && parts.length ? parts[parts.length - 1] : '';
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
- pushPart(line);
121
- } else {
122
- if (parts !== null) {
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
- pushPart(line);
135
+ depth = lineDepth;
136
+ prefix = line.slice(0, prefixLength);
137
+ pushPart(content);
129
138
  }
130
139
  }
131
-
132
- if (parts !== null && partsLength) {
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
- flowed.push(
155
- this.foldLines(
156
- line
157
- // space stuffing http://tools.ietf.org/html/rfc3676#section-4.2
158
- .replace(/^( |From|>)/gim, ' $1'),
159
- lineLength,
160
- true
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
- } else {
199
+ }
200
+ else {
193
201
  return '=' + (ord.length === 1 ? '0' + ord : ord);
194
202
  }
195
203
  });
196
- } else if (mimeWordEncoding === 'B') {
197
- encodedStr = typeof data === 'string' ? data : libbase64.encode(data);
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
- } else {
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
- for (let i = 0, len = encodedStr.length; i < len; i++) {
209
- let chr = encodedStr.charAt(i);
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
- } else {
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
- } else {
239
+ }
240
+ else {
233
241
  encodedStr = parts.join('');
234
242
  }
235
243
  }
236
- } else if (mimeWordEncoding === 'B') {
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.substr(0, splitPos);
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
- str = Buffer.from(bytes);
301
- } else if (encoding === 'B') {
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
- str = libbase64.decode(str);
304
- } else {
308
+ decoded = libbase64.decode(str);
309
+ }
310
+ else {
305
311
  // keep as is, convert Buffer to unicode string, assume utf8
306
- str = Buffer.from(str);
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.index +
338
- (
339
- firstMatch[0].match(/[^\s]/) || {
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.substr(0, startIndex) : '') +
356
- this.encodeWord(decodedValue.substring(startIndex, endIndex), mimeWordEncoding || 'Q', maxLength) +
357
- (endIndex < decodedValue.length ? decodedValue.substr(endIndex) : '');
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
- (str || '')
371
- .toString()
372
- // find base64 words that can be joined
373
- .replace(/(=\?([^?]+)\?[Bb]\?[^?]*\?=)\s*(?==\?([^?]+)\?[Bb]\?[^?]*\?=)/g, (match, left, chLeft, chRight) => {
374
- // only mark b64 chunks to be joined if charsets match
375
- if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {
376
- // set a joiner marker
377
- return left + '__\x00JOIN\x00__';
378
- }
379
- return match;
380
- })
381
- // find QP words that can be joined
382
- .replace(/(=\?([^?]+)\?[Qq]\?[^?]*\?=)\s*(?==\?([^?]+)\?[Qq]\?[^?]*\?=)/g, (match, left, chLeft, chRight) => {
383
- // only mark QP chunks to be joined if charsets match
384
- if (libcharset.normalizeCharset(chLeft || '') === libcharset.normalizeCharset(chRight || '')) {
385
- // set a joiner marker
386
- return left + '__\x00JOIN\x00__';
387
- }
388
- return match;
389
- })
390
- // join base64 encoded words
391
- .replace(/(\?=)?__\x00JOIN\x00__(=\?([^?]+)\?[QqBb]\?)?/g, '')
392
- // remove spaces between mime encoded words
393
- .replace(/(=\?[^?]+\?[QqBb]\?[^?]*\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]*\?=)/g, '$1')
394
- // decode words
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 ((c >= 0x30 /* 0 */ && c <= 0x39) /* 9 */ || (c >= 0x61 /* a */ && c <= 0x66) /* f */ || (c >= 0x41 /* A */ && c <= 0x46) /* F */) {
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
- .toString()
417
- .replace(/(?:\r?\n|\r)[ \t]*/g, ' ')
418
- .trim(),
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
- } else {
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
- if (!hasOwn(headersObj, header.key)) {
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
- } else {
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(structured.params || {}).forEach(param => {
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 = structured.params[param];
492
- if (!this.isPlainText(value) || value.length >= 75) {
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.substr(-1) === '*') {
476
+ if (!/[\s"\\;:/=(),<>@[\]?]|^[-']|'$/.test(encodedParam.value) || encodedParam.key.endsWith('*')) {
495
477
  paramsArray.push(encodedParam.key + '=' + encodedParam.value);
496
- } else {
478
+ }
479
+ else {
497
480
  paramsArray.push(encodedParam.key + '=' + JSON.stringify(encodedParam.value));
498
481
  }
499
482
  });
500
- } else if (/[\s'"\\;:/=(),<>@[\]?]|^-/.test(value)) {
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
- } else {
489
+ }
490
+ else {
503
491
  paramsArray.push(param + '=' + value);
504
492
  }
505
493
  });
506
-
507
- return structured.value + (paramsArray.length ? '; ' + paramsArray.join('; ') : '');
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: false,
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
- } else if (key === false) {
552
+ }
553
+ else if (key === false) {
569
554
  // default value
570
555
  response.value = collected;
571
- } else {
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
- } else if (chr === ';') {
571
+ }
572
+ else if (chr === ';') {
587
573
  // key without a value, eg. the "inline" in "attachment; inline; filename=a.txt"
588
574
  commit();
589
- } else if (isWSP(chr)) {
575
+ }
576
+ else if (isWSP(chr)) {
590
577
  if (value.length) {
591
578
  value += chr;
592
579
  }
593
- } else {
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
- } else if (chr === '\\') {
590
+ }
591
+ else if (chr === '\\') {
603
592
  escaped = true;
604
593
  continue;
605
- } else if (chr === '"') {
594
+ }
595
+ else if (chr === '"') {
606
596
  // every char between the quotes is significant, including whitespace
607
597
  quote = !quote;
608
- } else if (!quote && chr === ';') {
598
+ }
599
+ else if (!quote && chr === ';') {
609
600
  commit();
610
601
  stage = STAGE_KEY;
611
- } else if (!quote && isWSP(chr)) {
602
+ }
603
+ else if (!quote && isWSP(chr)) {
612
604
  if (value.length) {
613
605
  value += chr;
614
606
  }
615
- } else {
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
- if (s === ' ') {
695
- return '_';
696
- }
697
- let c = s.charCodeAt(0).toString(16);
698
- return '%' + (c.length < 2 ? '0' : '') + c;
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 encodedStrArr;
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.isPlainText(data)) {
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
- } else {
736
+ }
737
+ else {
768
738
  if (/[\uD800-\uDBFF]/.test(encodedStr)) {
769
- // string containts surrogate pairs, so normalize it to an array of bytes
770
- encodedStrArr = [];
771
- for (i = 0, len = encodedStr.length; i < len; i++) {
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
- } else {
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
- startPos = i - 1;
814
- } else {
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
- } else {
793
+ }
794
+ else {
834
795
  isEncoded = true;
835
796
  }
836
- } else {
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(mimeType) {
866
- mimeType = (mimeType || '').toString().toLowerCase().replace(/\s/g, '');
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(extension) {
896
- extension = (extension || '').toString().toLowerCase().replace(/\s/g, '').replace(/^\./g, '').split('.').pop();
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(str, lineLength, afterSpace) {
930
- str = (str || '').toString();
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.substr(pos, lineLength);
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.substr(0, maxlen);
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.substr(0, match.index);
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.substr(curLine.length).match(/^[=]([0-9A-F]{2})/i))) {
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 anc recheck
998
- if (chr < 0xc2 && chr > 0x7f) {
999
- curLine = curLine.substr(0, curLine.length - 3);
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
- if (curLine.length) {
1006
- lines.push(curLine);
944
+ if (!curLine.length) {
945
+ curLine = fallbackLine;
1007
946
  }
1008
- str = str.substr(curLine.length);
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
- safeEncodeURIComponent(str) {
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
- module.exports = new Libmime();
1044
- module.exports.Libmime = Libmime;
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;