@nocobase/plugin-workflow-mailer 2.3.0-beta.13 → 2.3.0-beta.15

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 (173) hide show
  1. package/dist/externalVersion.js +6 -6
  2. package/dist/node_modules/joi/package.json +1 -1
  3. package/dist/node_modules/nodemailer/dist/cjs/addressparser/index.d.ts +59 -0
  4. package/dist/node_modules/nodemailer/dist/cjs/addressparser/index.js +790 -0
  5. package/dist/node_modules/nodemailer/dist/cjs/base64/index.d.ts +36 -0
  6. package/dist/node_modules/nodemailer/dist/cjs/base64/index.js +119 -0
  7. package/dist/node_modules/nodemailer/dist/cjs/dkim/index.d.ts +52 -0
  8. package/dist/node_modules/nodemailer/dist/cjs/dkim/index.js +228 -0
  9. package/dist/node_modules/nodemailer/dist/cjs/dkim/message-parser.d.ts +48 -0
  10. package/dist/node_modules/nodemailer/dist/cjs/dkim/message-parser.js +180 -0
  11. package/dist/node_modules/nodemailer/dist/cjs/dkim/relaxed-body.d.ts +36 -0
  12. package/dist/node_modules/nodemailer/dist/cjs/dkim/relaxed-body.js +148 -0
  13. package/dist/node_modules/nodemailer/dist/cjs/dkim/sign.d.ts +52 -0
  14. package/dist/node_modules/nodemailer/dist/cjs/dkim/sign.js +165 -0
  15. package/dist/node_modules/nodemailer/dist/cjs/errors.d.ts +96 -0
  16. package/dist/node_modules/nodemailer/dist/cjs/errors.js +70 -0
  17. package/dist/node_modules/nodemailer/dist/cjs/fetch/cookies.d.ts +107 -0
  18. package/dist/node_modules/nodemailer/dist/cjs/fetch/cookies.js +282 -0
  19. package/dist/node_modules/nodemailer/dist/cjs/fetch/index.d.ts +65 -0
  20. package/dist/node_modules/nodemailer/dist/cjs/fetch/index.js +384 -0
  21. package/dist/node_modules/nodemailer/dist/cjs/json-transport/index.d.ts +64 -0
  22. package/dist/node_modules/nodemailer/dist/cjs/json-transport/index.js +99 -0
  23. package/dist/node_modules/nodemailer/dist/cjs/mail-composer/index.d.ts +174 -0
  24. package/dist/node_modules/nodemailer/dist/cjs/mail-composer/index.js +613 -0
  25. package/dist/node_modules/nodemailer/dist/cjs/mailer/index.d.ts +237 -0
  26. package/dist/node_modules/nodemailer/dist/cjs/mailer/index.js +415 -0
  27. package/dist/node_modules/nodemailer/dist/cjs/mailer/mail-message.d.ts +97 -0
  28. package/dist/node_modules/nodemailer/dist/cjs/mailer/mail-message.js +342 -0
  29. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/index.d.ts +161 -0
  30. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/index.js +787 -0
  31. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/mime-types.d.ts +2 -0
  32. package/dist/node_modules/nodemailer/dist/cjs/mime-funcs/mime-types.js +2103 -0
  33. package/dist/node_modules/nodemailer/dist/cjs/mime-node/index.d.ts +333 -0
  34. package/dist/node_modules/nodemailer/dist/cjs/mime-node/index.js +1493 -0
  35. package/dist/node_modules/nodemailer/dist/cjs/mime-node/last-newline.d.ts +5 -0
  36. package/dist/node_modules/nodemailer/dist/cjs/mime-node/last-newline.js +32 -0
  37. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-unix.d.ts +9 -0
  38. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-unix.js +40 -0
  39. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-windows.d.ts +10 -0
  40. package/dist/node_modules/nodemailer/dist/cjs/mime-node/le-windows.js +47 -0
  41. package/dist/node_modules/nodemailer/dist/cjs/nodemailer.d.ts +124 -0
  42. package/dist/node_modules/nodemailer/dist/cjs/nodemailer.js +1 -0
  43. package/dist/node_modules/nodemailer/dist/cjs/package-info.d.ts +3 -0
  44. package/dist/node_modules/nodemailer/dist/cjs/package-info.js +7 -0
  45. package/dist/node_modules/nodemailer/dist/cjs/package.json +3 -0
  46. package/dist/node_modules/nodemailer/dist/cjs/punycode/index.d.ts +73 -0
  47. package/dist/node_modules/nodemailer/{lib → dist/cjs}/punycode/index.js +62 -111
  48. package/dist/node_modules/nodemailer/dist/cjs/qp/index.d.ts +32 -0
  49. package/dist/node_modules/nodemailer/dist/cjs/qp/index.js +207 -0
  50. package/dist/node_modules/nodemailer/dist/cjs/sendmail-transport/index.d.ts +69 -0
  51. package/dist/node_modules/nodemailer/dist/cjs/sendmail-transport/index.js +210 -0
  52. package/dist/node_modules/nodemailer/dist/cjs/ses-transport/index.d.ts +112 -0
  53. package/dist/node_modules/nodemailer/dist/cjs/ses-transport/index.js +252 -0
  54. package/dist/node_modules/nodemailer/dist/cjs/shared/index.d.ts +260 -0
  55. package/dist/node_modules/nodemailer/dist/cjs/shared/index.js +680 -0
  56. package/dist/node_modules/nodemailer/dist/cjs/shared/objects.d.ts +23 -0
  57. package/dist/node_modules/nodemailer/dist/cjs/shared/objects.js +43 -0
  58. package/dist/node_modules/nodemailer/dist/cjs/shared/url.d.ts +21 -0
  59. package/dist/node_modules/nodemailer/dist/cjs/shared/url.js +255 -0
  60. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.d.ts +15 -0
  61. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/data-stream.js +108 -0
  62. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.d.ts +47 -0
  63. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/http-proxy-client.js +189 -0
  64. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/index.d.ts +416 -0
  65. package/dist/node_modules/nodemailer/dist/cjs/smtp-connection/index.js +1837 -0
  66. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/index.d.ts +133 -0
  67. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/index.js +569 -0
  68. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.d.ts +62 -0
  69. package/dist/node_modules/nodemailer/dist/cjs/smtp-pool/pool-resource.js +257 -0
  70. package/dist/node_modules/nodemailer/dist/cjs/smtp-transport/index.d.ts +156 -0
  71. package/dist/node_modules/nodemailer/dist/cjs/smtp-transport/index.js +371 -0
  72. package/dist/node_modules/nodemailer/dist/cjs/stream-transport/index.d.ts +65 -0
  73. package/dist/node_modules/nodemailer/dist/cjs/stream-transport/index.js +146 -0
  74. package/dist/node_modules/nodemailer/dist/cjs/well-known/index.d.ts +35 -0
  75. package/dist/node_modules/nodemailer/dist/cjs/well-known/index.js +41 -0
  76. package/dist/node_modules/nodemailer/dist/cjs/well-known/services.d.ts +2 -0
  77. package/dist/node_modules/nodemailer/dist/cjs/well-known/services.js +674 -0
  78. package/dist/node_modules/nodemailer/dist/cjs/xoauth2/index.d.ts +201 -0
  79. package/dist/node_modules/nodemailer/dist/cjs/xoauth2/index.js +426 -0
  80. package/dist/node_modules/nodemailer/dist/esm/addressparser/index.d.ts +59 -0
  81. package/dist/node_modules/nodemailer/{lib → dist/esm}/addressparser/index.js +361 -131
  82. package/dist/node_modules/nodemailer/dist/esm/base64/index.d.ts +36 -0
  83. package/dist/node_modules/nodemailer/{lib → dist/esm}/base64/index.js +31 -56
  84. package/dist/node_modules/nodemailer/dist/esm/dkim/index.d.ts +52 -0
  85. package/dist/node_modules/nodemailer/{lib → dist/esm}/dkim/index.js +49 -78
  86. package/dist/node_modules/nodemailer/dist/esm/dkim/message-parser.d.ts +48 -0
  87. package/dist/node_modules/nodemailer/{lib → dist/esm}/dkim/message-parser.js +64 -42
  88. package/dist/node_modules/nodemailer/dist/esm/dkim/relaxed-body.d.ts +36 -0
  89. package/dist/node_modules/nodemailer/dist/esm/dkim/relaxed-body.js +141 -0
  90. package/dist/node_modules/nodemailer/dist/esm/dkim/sign.d.ts +52 -0
  91. package/dist/node_modules/nodemailer/{lib → dist/esm}/dkim/sign.js +50 -46
  92. package/dist/node_modules/nodemailer/dist/esm/errors.d.ts +96 -0
  93. package/dist/node_modules/nodemailer/{lib → dist/esm}/errors.js +26 -18
  94. package/dist/node_modules/nodemailer/dist/esm/fetch/cookies.d.ts +107 -0
  95. package/dist/node_modules/nodemailer/dist/esm/fetch/cookies.js +242 -0
  96. package/dist/node_modules/nodemailer/dist/esm/fetch/index.d.ts +65 -0
  97. package/dist/node_modules/nodemailer/{lib → dist/esm}/fetch/index.js +80 -147
  98. package/dist/node_modules/nodemailer/dist/esm/json-transport/index.d.ts +64 -0
  99. package/dist/node_modules/nodemailer/{lib → dist/esm}/json-transport/index.js +19 -39
  100. package/dist/node_modules/nodemailer/dist/esm/mail-composer/index.d.ts +174 -0
  101. package/dist/node_modules/nodemailer/{lib → dist/esm}/mail-composer/index.js +199 -265
  102. package/dist/node_modules/nodemailer/dist/esm/mailer/index.d.ts +237 -0
  103. package/dist/node_modules/nodemailer/{lib → dist/esm}/mailer/index.js +128 -234
  104. package/dist/node_modules/nodemailer/dist/esm/mailer/mail-message.d.ts +97 -0
  105. package/dist/node_modules/nodemailer/{lib → dist/esm}/mailer/mail-message.js +62 -114
  106. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/index.d.ts +161 -0
  107. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/index.js +738 -0
  108. package/dist/node_modules/nodemailer/dist/esm/mime-funcs/mime-types.d.ts +2 -0
  109. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-funcs/mime-types.js +29 -42
  110. package/dist/node_modules/nodemailer/dist/esm/mime-node/index.d.ts +333 -0
  111. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/index.js +259 -372
  112. package/dist/node_modules/nodemailer/dist/esm/mime-node/last-newline.d.ts +5 -0
  113. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/last-newline.js +4 -10
  114. package/dist/node_modules/nodemailer/dist/esm/mime-node/le-unix.d.ts +9 -0
  115. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/le-unix.js +6 -11
  116. package/dist/node_modules/nodemailer/dist/esm/mime-node/le-windows.d.ts +10 -0
  117. package/dist/node_modules/nodemailer/{lib → dist/esm}/mime-node/le-windows.js +6 -13
  118. package/dist/node_modules/nodemailer/dist/esm/nodemailer.d.ts +124 -0
  119. package/dist/node_modules/nodemailer/dist/esm/nodemailer.js +166 -0
  120. package/dist/node_modules/nodemailer/dist/esm/package-info.d.ts +3 -0
  121. package/dist/node_modules/nodemailer/dist/esm/package-info.js +4 -0
  122. package/dist/node_modules/nodemailer/dist/esm/package.json +3 -0
  123. package/dist/node_modules/nodemailer/dist/esm/punycode/index.d.ts +73 -0
  124. package/dist/node_modules/nodemailer/dist/esm/punycode/index.js +405 -0
  125. package/dist/node_modules/nodemailer/dist/esm/qp/index.d.ts +32 -0
  126. package/dist/node_modules/nodemailer/{lib → dist/esm}/qp/index.js +38 -67
  127. package/dist/node_modules/nodemailer/dist/esm/sendmail-transport/index.d.ts +69 -0
  128. package/dist/node_modules/nodemailer/{lib → dist/esm}/sendmail-transport/index.js +51 -101
  129. package/dist/node_modules/nodemailer/dist/esm/ses-transport/index.d.ts +112 -0
  130. package/dist/node_modules/nodemailer/dist/esm/ses-transport/index.js +212 -0
  131. package/dist/node_modules/nodemailer/dist/esm/shared/index.d.ts +260 -0
  132. package/dist/node_modules/nodemailer/{lib → dist/esm}/shared/index.js +156 -272
  133. package/dist/node_modules/nodemailer/dist/esm/shared/objects.d.ts +23 -0
  134. package/dist/node_modules/nodemailer/{lib → dist/esm}/shared/objects.js +13 -17
  135. package/dist/node_modules/nodemailer/dist/esm/shared/url.d.ts +21 -0
  136. package/dist/node_modules/nodemailer/dist/esm/shared/url.js +214 -0
  137. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.d.ts +15 -0
  138. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/data-stream.js +103 -0
  139. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/http-proxy-client.d.ts +47 -0
  140. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-connection/http-proxy-client.js +49 -74
  141. package/dist/node_modules/nodemailer/dist/esm/smtp-connection/index.d.ts +416 -0
  142. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-connection/index.js +574 -794
  143. package/dist/node_modules/nodemailer/dist/esm/smtp-pool/index.d.ts +133 -0
  144. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-pool/index.js +172 -284
  145. package/dist/node_modules/nodemailer/dist/esm/smtp-pool/pool-resource.d.ts +62 -0
  146. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-pool/pool-resource.js +57 -97
  147. package/dist/node_modules/nodemailer/dist/esm/smtp-transport/index.d.ts +156 -0
  148. package/dist/node_modules/nodemailer/{lib → dist/esm}/smtp-transport/index.js +81 -179
  149. package/dist/node_modules/nodemailer/dist/esm/stream-transport/index.d.ts +65 -0
  150. package/dist/node_modules/nodemailer/{lib → dist/esm}/stream-transport/index.js +36 -74
  151. package/dist/node_modules/nodemailer/dist/esm/well-known/index.d.ts +35 -0
  152. package/dist/node_modules/nodemailer/{lib → dist/esm}/well-known/index.js +5 -16
  153. package/dist/node_modules/nodemailer/dist/esm/well-known/services.d.ts +2 -0
  154. package/dist/node_modules/nodemailer/dist/esm/well-known/services.js +671 -0
  155. package/dist/node_modules/nodemailer/dist/esm/xoauth2/index.d.ts +201 -0
  156. package/dist/node_modules/nodemailer/{lib → dist/esm}/xoauth2/index.js +125 -187
  157. package/dist/node_modules/nodemailer/{lib → dist}/well-known/services.json +9 -2
  158. package/dist/node_modules/nodemailer/package.json +1 -1
  159. package/package.json +3 -3
  160. package/dist/node_modules/nodemailer/.gitattributes +0 -6
  161. package/dist/node_modules/nodemailer/.ncurc.js +0 -9
  162. package/dist/node_modules/nodemailer/.prettierignore +0 -8
  163. package/dist/node_modules/nodemailer/.prettierrc +0 -12
  164. package/dist/node_modules/nodemailer/.prettierrc.js +0 -10
  165. package/dist/node_modules/nodemailer/.release-please-config.json +0 -9
  166. package/dist/node_modules/nodemailer/eslint.config.js +0 -88
  167. package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +0 -154
  168. package/dist/node_modules/nodemailer/lib/fetch/cookies.js +0 -276
  169. package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +0 -671
  170. package/dist/node_modules/nodemailer/lib/nodemailer.js +0 -1
  171. package/dist/node_modules/nodemailer/lib/ses-transport/index.js +0 -266
  172. package/dist/node_modules/nodemailer/lib/shared/url.js +0 -151
  173. package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +0 -105
@@ -1,7 +1,31 @@
1
- 'use strict';
2
-
3
- const { Transform } = require('stream');
4
-
1
+ import { Transform } from 'node:stream';
2
+ /**
3
+ * Drops the SP and HTAB from both ends of a field name.
4
+ *
5
+ * An index scan rather than `/^[ \t]+|[ \t]+$/g`, which retries the trailing branch at
6
+ * every offset of a blank run that is followed by other text: the run is consumed greedily,
7
+ * `$` fails, and the engine gives the characters back one at a time before moving on to
8
+ * start the same walk one offset further in. A field name carrying a 128 KiB run of spaces
9
+ * between two atoms took about 6.7 seconds to trim, and the cost grows with its square.
10
+ *
11
+ * Not `.trim()`, which takes the other Unicode spaces along: an NBSP in front of a field
12
+ * name would come off and select the name into the signed set, where the relaxed
13
+ * canonicalization in sign.ts deliberately leaves everything but SP and HTAB alone.
14
+ *
15
+ * @param str Field name to trim
16
+ * @return The field name without the surrounding SP and HTAB
17
+ */
18
+ function _trimFieldName(str) {
19
+ let start = 0;
20
+ let end = str.length;
21
+ while (start < end && (str.charCodeAt(start) === 0x20 || str.charCodeAt(start) === 0x09)) {
22
+ start++;
23
+ }
24
+ while (end > start && (str.charCodeAt(end - 1) === 0x20 || str.charCodeAt(end - 1) === 0x09)) {
25
+ end--;
26
+ }
27
+ return str.slice(start, end);
28
+ }
5
29
  /**
6
30
  * MessageParser instance is a transform stream that separates message headers
7
31
  * from the rest of the body. Headers are emitted with the 'headers' event. Message
@@ -17,46 +41,42 @@ class MessageParser extends Transform {
17
41
  this.rawHeaders = false;
18
42
  this.bodySize = 0;
19
43
  }
20
-
21
44
  /**
22
45
  * Keeps count of the last 4 bytes in order to detect line breaks on chunk boundaries
23
46
  *
24
- * @param {Buffer} data Next data chunk from the stream
47
+ * @param data Next data chunk from the stream
25
48
  */
26
49
  updateLastBytes(data) {
27
50
  const lblen = this.lastBytes.length;
28
51
  const nblen = Math.min(data.length, lblen);
29
-
30
52
  // shift existing bytes
31
53
  for (let i = 0, len = lblen - nblen; i < len; i++) {
32
54
  this.lastBytes[i] = this.lastBytes[i + nblen];
33
55
  }
34
-
35
56
  // add new bytes
36
57
  for (let i = 1; i <= nblen; i++) {
37
58
  this.lastBytes[lblen - i] = data[data.length - i];
38
59
  }
39
60
  }
40
-
41
61
  /**
42
62
  * Finds and removes message headers from the remaining body. We want to keep
43
63
  * headers separated until final delivery to be able to modify these
44
64
  *
45
- * @param {Buffer} data Next chunk of data
46
- * @return {Boolean} Returns true if headers are already found or false otherwise
65
+ * @param data Next chunk of data
66
+ * @return Returns true if headers are already found or false otherwise
47
67
  */
48
68
  checkHeaders(data) {
49
69
  if (this.headersParsed) {
50
70
  return true;
51
71
  }
52
-
53
72
  const lblen = this.lastBytes.length;
54
73
  let headerPos = 0;
55
74
  for (let i = 0, len = this.lastBytes.length + data.length; i < len; i++) {
56
75
  let chr;
57
76
  if (i < lblen) {
58
77
  chr = this.lastBytes[i];
59
- } else {
78
+ }
79
+ else {
60
80
  chr = data[i - lblen];
61
81
  }
62
82
  if (chr === 0x0a && i) {
@@ -67,7 +87,8 @@ class MessageParser extends Transform {
67
87
  headerPos = i - lblen + 1;
68
88
  this.headerBytes += headerPos;
69
89
  break;
70
- } else if (pr1 === 0x0d && pr2 === 0x0a) {
90
+ }
91
+ else if (pr1 === 0x0d && pr2 === 0x0a) {
71
92
  this.headersParsed = true;
72
93
  headerPos = i - lblen + 1;
73
94
  this.headerBytes += headerPos;
@@ -75,13 +96,12 @@ class MessageParser extends Transform {
75
96
  }
76
97
  }
77
98
  }
78
-
79
99
  if (this.headersParsed) {
80
100
  this.headerChunks.push(data.slice(0, headerPos));
81
101
  this.rawHeaders = Buffer.concat(this.headerChunks, this.headerBytes);
82
102
  this.headerChunks = null;
83
103
  this.emit('headers', this.parseHeaders());
84
- if (data.length - 1 > headerPos) {
104
+ if (data.length > headerPos) {
85
105
  const chunk = data.slice(headerPos);
86
106
  this.bodySize += chunk.length;
87
107
  // this would be the first chunk of data sent downstream
@@ -89,66 +109,68 @@ class MessageParser extends Transform {
89
109
  }
90
110
  return false;
91
111
  }
92
-
93
112
  this.headerBytes += data.length;
94
113
  this.headerChunks.push(data);
95
-
96
114
  // store last 4 bytes to catch header break
97
115
  this.updateLastBytes(data);
98
-
99
116
  return false;
100
117
  }
101
-
118
+ /** @internal */
102
119
  _transform(chunk, encoding, callback) {
103
120
  if (!chunk || !chunk.length) {
104
121
  return callback();
105
122
  }
106
-
107
123
  if (typeof chunk === 'string') {
108
124
  chunk = Buffer.from(chunk, encoding);
109
125
  }
110
-
111
126
  let headersFound;
112
-
113
127
  try {
114
128
  headersFound = this.checkHeaders(chunk);
115
- } catch (E) {
129
+ }
130
+ catch (E) {
116
131
  return callback(E);
117
132
  }
118
-
119
133
  if (headersFound) {
120
134
  this.bodySize += chunk.length;
121
135
  this.push(chunk);
122
136
  }
123
-
124
137
  setImmediate(callback);
125
138
  }
126
-
139
+ /** @internal */
127
140
  _flush(callback) {
128
141
  if (this.headerChunks) {
129
- const chunk = Buffer.concat(this.headerChunks, this.headerBytes);
130
- this.bodySize += chunk.length;
131
- this.push(chunk);
142
+ // no empty line was seen, so the message consists of headers only
143
+ this.rawHeaders = Buffer.concat(this.headerChunks, this.headerBytes);
132
144
  this.headerChunks = null;
145
+ this.emit('headers', this.parseHeaders());
133
146
  }
134
147
  callback();
135
148
  }
136
-
137
149
  parseHeaders() {
138
- const lines = (this.rawHeaders || '').toString().split(/\r?\n/);
139
- for (let i = lines.length - 1; i > 0; i--) {
140
- if (/^\s/.test(lines[i])) {
141
- lines[i - 1] += '\n' + lines[i];
142
- lines.splice(i, 1);
150
+ // the header bytes are kept as they are, one character per byte, so the
151
+ // signature covers exactly the bytes the receiving side canonicalizes
152
+ // Only SP and HTAB fold a line, and only they are trimmed from the field name, the
153
+ // same whitespace the relaxed canonicalization in sign.ts works with
154
+ const rawLines = (this.rawHeaders || Buffer.alloc(0)).toString('binary').split(/\r?\n/);
155
+ // Unfold in a single forward pass and only ever test a freshly split line for the
156
+ // continuation prefix. Testing an already merged line instead would rescan a string
157
+ // that grows with every continuation line, so a header folded into many continuation
158
+ // lines (a large recipient list, for example) would take quadratic time to unfold
159
+ const lines = [];
160
+ for (const rawLine of rawLines) {
161
+ if (lines.length && /^[ \t]/.test(rawLine)) {
162
+ lines[lines.length - 1] += '\n' + rawLine;
163
+ }
164
+ else {
165
+ lines.push(rawLine);
143
166
  }
144
167
  }
145
168
  return lines
146
- .filter(line => line.trim())
169
+ .filter(line => /[^ \t\r]/.test(line))
147
170
  .map(line => ({
148
- key: line.substr(0, line.indexOf(':')).trim().toLowerCase(),
149
- line
150
- }));
171
+ key: _trimFieldName(line.substr(0, line.indexOf(':'))).toLowerCase(),
172
+ line
173
+ }));
151
174
  }
152
175
  }
153
-
154
- module.exports = MessageParser;
176
+ export default MessageParser;
@@ -0,0 +1,36 @@
1
+ import { Transform } from 'node:stream';
2
+ import crypto from 'node:crypto';
3
+ /**
4
+ * Options for the relaxed body hash stream
5
+ */
6
+ export interface RelaxedBodyOptions {
7
+ /** Hash algorithm for the body hash, defaults to sha256 */
8
+ hashAlgo?: string | undefined;
9
+ /** Collect the canonicalized body and emit it with the 'hash' event */
10
+ debug?: boolean | undefined;
11
+ }
12
+ /**
13
+ * Passes the message body through unchanged and hashes its relaxed
14
+ * canonicalization (RFC 6376 section 3.4.4) on the side: whitespace at the end
15
+ * of a line is dropped, runs of whitespace within a line become a single space,
16
+ * every line ends with CRLF, empty lines at the end of the body are ignored and
17
+ * a non-empty body always ends with CRLF. Bytes are canonicalized as they arrive,
18
+ * so a line of any length costs constant memory.
19
+ */
20
+ declare class RelaxedBody extends Transform {
21
+ bodyHash: crypto.Hash;
22
+ /** Bytes of the original body seen so far */
23
+ byteLength: number;
24
+ debug: boolean | undefined;
25
+ constructor(options?: RelaxedBodyOptions);
26
+ updateHash(chunk: Buffer, final?: boolean): void;
27
+ }
28
+ /**
29
+ * Type aliases in the layout of @types/nodemailer, so `RelaxedBody.Options` style references keep working
30
+ */
31
+ declare namespace RelaxedBody {
32
+ type Options = RelaxedBodyOptions;
33
+ }
34
+ /** The same alias as a module level export, for `import * as RelaxedBody` and `import RelaxedBody = require()` */
35
+ export type { RelaxedBodyOptions as Options };
36
+ export default RelaxedBody;
@@ -0,0 +1,141 @@
1
+ // streams through a message body and calculates relaxed body hash
2
+ import { Transform } from 'node:stream';
3
+ import crypto from 'node:crypto';
4
+ const CHAR_CR = 0x0d;
5
+ const CHAR_LF = 0x0a;
6
+ const CHAR_SPACE = 0x20;
7
+ const CHAR_TAB = 0x09;
8
+ const CRLF = Buffer.from('\r\n');
9
+ // a run of empty lines is hashed from this buffer in slices
10
+ const EMPTY_LINES = Buffer.alloc(4096, CRLF);
11
+ /**
12
+ * Passes the message body through unchanged and hashes its relaxed
13
+ * canonicalization (RFC 6376 section 3.4.4) on the side: whitespace at the end
14
+ * of a line is dropped, runs of whitespace within a line become a single space,
15
+ * every line ends with CRLF, empty lines at the end of the body are ignored and
16
+ * a non-empty body always ends with CRLF. Bytes are canonicalized as they arrive,
17
+ * so a line of any length costs constant memory.
18
+ */
19
+ class RelaxedBody extends Transform {
20
+ constructor(options) {
21
+ super();
22
+ options = options || {};
23
+ this.bodyHash = crypto.createHash(options.hashAlgo || 'sha256');
24
+ this.byteLength = 0;
25
+ this.debug = options.debug;
26
+ this._debugBody = options.debug ? [] : false;
27
+ this._lineHasContent = false;
28
+ this._pendingWsp = false;
29
+ this._pendingCr = false;
30
+ this._pendingEmptyLines = 0;
31
+ }
32
+ /** @internal */
33
+ _hashCanonical(data) {
34
+ if (!data.length) {
35
+ return;
36
+ }
37
+ this.bodyHash.update(data);
38
+ if (this._debugBody) {
39
+ this._debugBody.push(Buffer.from(data));
40
+ }
41
+ }
42
+ /** @internal */
43
+ _hashEmptyLines() {
44
+ while (this._pendingEmptyLines > 0) {
45
+ const count = Math.min(this._pendingEmptyLines, EMPTY_LINES.length / 2);
46
+ this._hashCanonical(EMPTY_LINES.subarray(0, count * 2));
47
+ this._pendingEmptyLines -= count;
48
+ }
49
+ }
50
+ /**
51
+ * Writes a content byte, with the space a pending run of whitespace collapses to,
52
+ * into the output buffer and returns the new write position. Kept a method rather
53
+ * than a closure so the write position stays a plain local in the byte loop
54
+ * @internal
55
+ */
56
+ _emitContent(out, outPos, c) {
57
+ if (!this._lineHasContent) {
58
+ if (this._pendingEmptyLines) {
59
+ // the first content byte of a line is where the empty lines before it
60
+ // become part of the body, so hash what is in the buffer before them
61
+ this._hashCanonical(out.subarray(0, outPos));
62
+ outPos = 0;
63
+ this._hashEmptyLines();
64
+ }
65
+ this._lineHasContent = true;
66
+ }
67
+ if (this._pendingWsp) {
68
+ out[outPos++] = CHAR_SPACE;
69
+ this._pendingWsp = false;
70
+ }
71
+ out[outPos++] = c;
72
+ return outPos;
73
+ }
74
+ updateHash(chunk, final) {
75
+ // every byte contributes itself at most once, plus a CR for a bare LF
76
+ // and, once per chunk, a pending space and CR carried over from before
77
+ const out = Buffer.allocUnsafe(chunk.length * 2 + 2);
78
+ let outPos = 0;
79
+ for (let i = 0; i < chunk.length; i++) {
80
+ const c = chunk[i];
81
+ if (c === CHAR_LF) {
82
+ // end of line, a CR right before it and any trailing whitespace are dropped
83
+ if (this._lineHasContent) {
84
+ out[outPos++] = CHAR_CR;
85
+ out[outPos++] = CHAR_LF;
86
+ this._lineHasContent = false;
87
+ }
88
+ else {
89
+ this._pendingEmptyLines++;
90
+ }
91
+ this._pendingWsp = false;
92
+ this._pendingCr = false;
93
+ continue;
94
+ }
95
+ if (this._pendingCr) {
96
+ // not followed by LF, so the CR is content
97
+ outPos = this._emitContent(out, outPos, CHAR_CR);
98
+ this._pendingCr = false;
99
+ }
100
+ if (c === CHAR_CR) {
101
+ this._pendingCr = true;
102
+ }
103
+ else if (c === CHAR_SPACE || c === CHAR_TAB) {
104
+ this._pendingWsp = true;
105
+ }
106
+ else {
107
+ outPos = this._emitContent(out, outPos, c);
108
+ }
109
+ }
110
+ if (final && this._pendingCr) {
111
+ // a CR at the very end of the body is content
112
+ outPos = this._emitContent(out, outPos, CHAR_CR);
113
+ this._pendingCr = false;
114
+ }
115
+ this._hashCanonical(out.subarray(0, outPos));
116
+ }
117
+ /** @internal */
118
+ _transform(chunk, encoding, callback) {
119
+ if (!chunk || !chunk.length) {
120
+ return callback();
121
+ }
122
+ if (typeof chunk === 'string') {
123
+ chunk = Buffer.from(chunk, encoding);
124
+ }
125
+ this.updateHash(chunk);
126
+ this.byteLength += chunk.length;
127
+ this.push(chunk);
128
+ callback();
129
+ }
130
+ /** @internal */
131
+ _flush(callback) {
132
+ this.updateHash(Buffer.alloc(0), true);
133
+ if (this._lineHasContent) {
134
+ // the body does not end with a line break, add one
135
+ this._hashCanonical(CRLF);
136
+ }
137
+ this.emit('hash', this.bodyHash.digest('base64'), this.debug ? Buffer.concat(this._debugBody) : false);
138
+ callback();
139
+ }
140
+ }
141
+ export default RelaxedBody;
@@ -0,0 +1,52 @@
1
+ import crypto from 'node:crypto';
2
+ import type { NodemailerError } from '../errors.js';
3
+ import type { MessageParserHeaderLine } from './message-parser.js';
4
+ /**
5
+ * Private key accepted by crypto.Sign#sign: a PEM string, a Buffer, a KeyObject or an
6
+ * object with the key and its passphrase
7
+ */
8
+ export type DKIMPrivateKey = crypto.KeyLike | crypto.SignKeyObjectInput | crypto.SignPrivateKeyInput;
9
+ /**
10
+ * Options for the DKIM signature header generator
11
+ */
12
+ export interface DKIMKey {
13
+ /** Domain name to be signed for */
14
+ domainName?: string | undefined;
15
+ /** DKIM key selector to use */
16
+ keySelector?: string | undefined;
17
+ /** DKIM private key to use */
18
+ privateKey?: DKIMPrivateKey | undefined;
19
+ }
20
+ export interface DKIMSignOptions extends DKIMKey {
21
+ /** Colon separated list of header field names to sign, defaults to the RFC4871 list */
22
+ headerFieldNames?: string | undefined;
23
+ /** Colon separated list of header field names to leave out of the signature */
24
+ skipFields?: string | undefined;
25
+ }
26
+ /**
27
+ * Canonicalized headers and the list of field names that went into them
28
+ */
29
+ export interface DKIMRelaxedHeaders {
30
+ /** Relaxed header lines, each terminated with CRLF, one character per byte ('binary' encoding) */
31
+ headers: string;
32
+ /** Colon separated list of the field names that were included */
33
+ fieldNames: string;
34
+ }
35
+ /**
36
+ * Returns DKIM signature header line
37
+ *
38
+ * @param headers Parsed headers object from MessageParser
39
+ * @param hashAlgo Hash algorithm the body hash was calculated with, for example "sha256"
40
+ * @param bodyHash Base64 encoded hash of the message
41
+ * @param options DKIM options
42
+ * @param options.domainName Domain name to be signed for
43
+ * @param options.keySelector DKIM key selector to use
44
+ * @param options.privateKey DKIM private key to use
45
+ * @return Complete header line
46
+ */
47
+ declare function sign(headers: MessageParserHeaderLine[], hashAlgo: string, bodyHash: string, options?: DKIMSignOptions): string | false;
48
+ declare namespace sign {
49
+ var relaxedHeaders: (headers: MessageParserHeaderLine[], fieldNames?: string, skipFields?: string) => DKIMRelaxedHeaders;
50
+ var unsupportedHashAlgoError: (hashAlgo: string) => NodemailerError;
51
+ }
52
+ export default sign;
@@ -1,60 +1,66 @@
1
- 'use strict';
2
-
3
- const punycode = require('../punycode');
4
- const mimeFuncs = require('../mime-funcs');
5
- const crypto = require('crypto');
6
-
1
+ import * as punycode from '../punycode/index.js';
2
+ import * as mimeFuncs from '../mime-funcs/index.js';
3
+ import crypto from 'node:crypto';
4
+ import * as errors from '../errors.js';
5
+ /**
6
+ * Error for a hashAlgo value the crypto module does not know
7
+ */
8
+ function unsupportedHashAlgoError(hashAlgo) {
9
+ const err = new Error('Unsupported DKIM hash algorithm "' + hashAlgo + '"');
10
+ err.code = errors.ECONFIG;
11
+ return err;
12
+ }
7
13
  /**
8
14
  * Returns DKIM signature header line
9
15
  *
10
- * @param {Object} headers Parsed headers object from MessageParser
11
- * @param {String} bodyHash Base64 encoded hash of the message
12
- * @param {Object} options DKIM options
13
- * @param {String} options.domainName Domain name to be signed for
14
- * @param {String} options.keySelector DKIM key selector to use
15
- * @param {String} options.privateKey DKIM private key to use
16
- * @return {String} Complete header line
16
+ * @param headers Parsed headers object from MessageParser
17
+ * @param hashAlgo Hash algorithm the body hash was calculated with, for example "sha256"
18
+ * @param bodyHash Base64 encoded hash of the message
19
+ * @param options DKIM options
20
+ * @param options.domainName Domain name to be signed for
21
+ * @param options.keySelector DKIM key selector to use
22
+ * @param options.privateKey DKIM private key to use
23
+ * @return Complete header line
17
24
  */
18
-
19
- module.exports = (headers, hashAlgo, bodyHash, options) => {
25
+ function sign(headers, hashAlgo, bodyHash, options) {
20
26
  options = options || {};
21
-
22
27
  // all listed fields from RFC4871 #5.5
23
- const defaultFieldNames =
24
- 'From:Sender:Reply-To:Subject:Date:Message-ID:To:' +
28
+ const defaultFieldNames = 'From:Sender:Reply-To:Subject:Date:Message-ID:To:' +
25
29
  'Cc:MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID:' +
26
30
  'Content-Description:Resent-Date:Resent-From:Resent-Sender:' +
27
31
  'Resent-To:Resent-Cc:Resent-Message-ID:In-Reply-To:References:' +
28
32
  'List-Id:List-Help:List-Unsubscribe:List-Subscribe:List-Post:' +
29
33
  'List-Owner:List-Archive';
30
-
31
34
  const fieldNames = options.headerFieldNames || defaultFieldNames;
32
-
33
35
  const canonicalizedHeaderData = relaxedHeaders(headers, fieldNames, options.skipFields);
34
36
  const dkimHeader = generateDKIMHeader(options.domainName, options.keySelector, canonicalizedHeaderData.fieldNames, hashAlgo, bodyHash);
35
-
36
37
  canonicalizedHeaderData.headers += 'dkim-signature:' + relaxedHeaderLine(dkimHeader);
37
-
38
- const signer = crypto.createSign(('rsa-' + hashAlgo).toUpperCase());
39
- signer.update(canonicalizedHeaderData.headers);
38
+ let signer;
39
+ try {
40
+ signer = crypto.createSign(('rsa-' + hashAlgo).toUpperCase());
41
+ }
42
+ catch (_E) {
43
+ throw unsupportedHashAlgoError(hashAlgo);
44
+ }
45
+ // the header lines are 'binary' strings, so this reproduces the original header bytes
46
+ signer.update(canonicalizedHeaderData.headers, 'latin1');
40
47
  let signature;
41
48
  try {
42
49
  signature = signer.sign(options.privateKey, 'base64');
43
- } catch (_E) {
50
+ }
51
+ catch (_E) {
44
52
  return false;
45
53
  }
46
-
47
54
  return dkimHeader + signature.replace(/(^.{73}|.{75}(?!\r?\n|\r))/g, '$&\r\n ').trim();
48
- };
49
-
50
- module.exports.relaxedHeaders = relaxedHeaders;
51
-
55
+ }
56
+ sign.relaxedHeaders = relaxedHeaders;
57
+ sign.unsupportedHashAlgoError = unsupportedHashAlgoError;
58
+ export default sign;
52
59
  function generateDKIMHeader(domainName, keySelector, fieldNames, hashAlgo, bodyHash) {
53
60
  // the caller supplied tag values are interpolated straight into the tag list, and none of
54
61
  // them has any way to carry a control char, DEL, or one of the delimiters that would close
55
62
  // the value and open a tag of its own
56
- const cleanTagValue = value => (value || '').toString().replace(/[\x00-\x1f\x7f;=]/g, '');
57
-
63
+ const cleanTagValue = (value) => (value || '').toString().replace(/[\x00-\x1f\x7f;=]/g, '');
58
64
  const dkim = [
59
65
  'v=1',
60
66
  'a=rsa-' + hashAlgo,
@@ -65,30 +71,25 @@ function generateDKIMHeader(domainName, keySelector, fieldNames, hashAlgo, bodyH
65
71
  'bh=' + bodyHash,
66
72
  'h=' + cleanTagValue(fieldNames)
67
73
  ].join('; ');
68
-
69
74
  return mimeFuncs.foldLines('DKIM-Signature: ' + dkim, 76) + ';\r\n b=';
70
75
  }
71
-
72
76
  function relaxedHeaders(headers, fieldNames, skipFields) {
73
77
  const includedFields = new Set();
74
78
  const skip = new Set();
75
79
  const headerFields = new Map();
76
-
77
80
  (skipFields || '')
78
81
  .toLowerCase()
79
82
  .split(':')
80
83
  .forEach(field => {
81
- skip.add(field.trim());
82
- });
83
-
84
+ skip.add(field.trim());
85
+ });
84
86
  (fieldNames || '')
85
87
  .toLowerCase()
86
88
  .split(':')
87
89
  .filter(field => !skip.has(field.trim()))
88
90
  .forEach(field => {
89
- includedFields.add(field.trim());
90
- });
91
-
91
+ includedFields.add(field.trim());
92
+ });
92
93
  for (let i = headers.length - 1; i >= 0; i--) {
93
94
  const line = headers[i];
94
95
  // only include the first value from bottom to top
@@ -96,7 +97,6 @@ function relaxedHeaders(headers, fieldNames, skipFields) {
96
97
  headerFields.set(line.key, relaxedHeaderLine(line.line));
97
98
  }
98
99
  }
99
-
100
100
  const headersList = [];
101
101
  const fields = [];
102
102
  includedFields.forEach(field => {
@@ -105,17 +105,21 @@ function relaxedHeaders(headers, fieldNames, skipFields) {
105
105
  headersList.push(field + ':' + headerFields.get(field));
106
106
  }
107
107
  });
108
-
109
108
  return {
110
109
  headers: headersList.join('\r\n') + '\r\n',
111
110
  fieldNames: fields.join(':')
112
111
  };
113
112
  }
114
-
113
+ /**
114
+ * Relaxed canonicalization of a header field value (RFC 6376 section 3.4.2): unfold, turn
115
+ * every run of SP and HTAB into a single SP and drop the whitespace next to the colon and
116
+ * at the end. Only SP and HTAB count as whitespace, so bytes that decode to other space
117
+ * characters, such as a non-breaking space in a UTF-8 header, stay as they are
118
+ */
115
119
  function relaxedHeaderLine(line) {
116
120
  return line
117
121
  .substr(line.indexOf(':') + 1)
118
122
  .replace(/\r?\n/g, '')
119
- .replace(/\s+/g, ' ')
120
- .trim();
123
+ .replace(/[ \t]+/g, ' ')
124
+ .replace(/^ | $/g, '');
121
125
  }