@nocobase/plugin-notification-email 2.3.0-beta.11 → 2.3.0-beta.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/index.js +1 -1
- package/dist/client-v2/locale.d.ts +1 -1
- package/dist/externalVersion.js +7 -7
- package/dist/node_modules/nodemailer/.ncurc.js +2 -0
- package/dist/node_modules/nodemailer/.prettierignore +8 -0
- package/dist/node_modules/nodemailer/.prettierrc +12 -0
- package/dist/node_modules/nodemailer/.prettierrc.js +2 -0
- package/dist/node_modules/nodemailer/.release-please-config.json +9 -0
- package/dist/node_modules/nodemailer/eslint.config.js +88 -0
- package/dist/node_modules/nodemailer/lib/addressparser/index.js +314 -71
- package/dist/node_modules/nodemailer/lib/base64/index.js +9 -13
- package/dist/node_modules/nodemailer/lib/dkim/index.js +17 -18
- package/dist/node_modules/nodemailer/lib/dkim/message-parser.js +12 -13
- package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +3 -3
- package/dist/node_modules/nodemailer/lib/dkim/sign.js +23 -19
- package/dist/node_modules/nodemailer/lib/errors.js +59 -0
- package/dist/node_modules/nodemailer/lib/fetch/cookies.js +14 -19
- package/dist/node_modules/nodemailer/lib/fetch/index.js +164 -27
- package/dist/node_modules/nodemailer/lib/json-transport/index.js +4 -4
- package/dist/node_modules/nodemailer/lib/mail-composer/index.js +235 -161
- package/dist/node_modules/nodemailer/lib/mailer/index.js +106 -52
- package/dist/node_modules/nodemailer/lib/mailer/mail-message.js +106 -68
- package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +105 -59
- package/dist/node_modules/nodemailer/lib/mime-funcs/mime-types.js +21 -14
- package/dist/node_modules/nodemailer/lib/mime-node/index.js +399 -138
- package/dist/node_modules/nodemailer/lib/mime-node/last-newline.js +1 -1
- package/dist/node_modules/nodemailer/lib/mime-node/le-unix.js +4 -7
- package/dist/node_modules/nodemailer/lib/mime-node/le-windows.js +1 -4
- package/dist/node_modules/nodemailer/lib/nodemailer.js +1 -1
- package/dist/node_modules/nodemailer/lib/punycode/index.js +460 -0
- package/dist/node_modules/nodemailer/lib/qp/index.js +33 -22
- package/dist/node_modules/nodemailer/lib/sendmail-transport/index.js +54 -44
- package/dist/node_modules/nodemailer/lib/ses-transport/index.js +110 -193
- package/dist/node_modules/nodemailer/lib/shared/index.js +316 -207
- package/dist/node_modules/nodemailer/lib/shared/objects.js +42 -0
- package/dist/node_modules/nodemailer/lib/shared/url.js +151 -0
- package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +3 -6
- package/dist/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +50 -19
- package/dist/node_modules/nodemailer/lib/smtp-connection/index.js +423 -218
- package/dist/node_modules/nodemailer/lib/smtp-pool/index.js +27 -34
- package/dist/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +19 -16
- package/dist/node_modules/nodemailer/lib/smtp-transport/index.js +66 -53
- package/dist/node_modules/nodemailer/lib/stream-transport/index.js +16 -7
- package/dist/node_modules/nodemailer/lib/well-known/index.js +7 -7
- package/dist/node_modules/nodemailer/lib/well-known/services.json +333 -38
- package/dist/node_modules/nodemailer/lib/xoauth2/index.js +105 -33
- package/dist/node_modules/nodemailer/package.json +1 -1
- package/package.json +3 -3
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
const MimeNode = require('../mime-node');
|
|
6
6
|
const mimeFuncs = require('../mime-funcs');
|
|
7
|
+
const { parseDataURI, copyOwnKeys } = require('../shared');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Creates the object for composing a MimeNode instance out from the mail options
|
|
@@ -31,7 +32,11 @@ class MailComposer {
|
|
|
31
32
|
|
|
32
33
|
// Compose MIME tree
|
|
33
34
|
if (this.mail.raw) {
|
|
34
|
-
this.message = new MimeNode('message/rfc822', {
|
|
35
|
+
this.message = new MimeNode('message/rfc822', {
|
|
36
|
+
newline: this.mail.newline,
|
|
37
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
38
|
+
disableFileAccess: this.mail.disableFileAccess
|
|
39
|
+
}).setRaw(this.mail.raw);
|
|
35
40
|
} else if (this._useMixed) {
|
|
36
41
|
this.message = this._createMixed();
|
|
37
42
|
} else if (this._useAlternative) {
|
|
@@ -58,7 +63,7 @@ class MailComposer {
|
|
|
58
63
|
|
|
59
64
|
// Add headers to the root node, always overrides custom headers
|
|
60
65
|
['from', 'sender', 'to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'message-id', 'date'].forEach(header => {
|
|
61
|
-
|
|
66
|
+
const key = header.replace(/-(\w)/g, (o, c) => c.toUpperCase());
|
|
62
67
|
if (this.mail[key]) {
|
|
63
68
|
this.message.setHeader(header, this.mail[key]);
|
|
64
69
|
}
|
|
@@ -82,23 +87,36 @@ class MailComposer {
|
|
|
82
87
|
* @returns {Object} An object of arrays (`related` and `attached`)
|
|
83
88
|
*/
|
|
84
89
|
getAttachments(findRelated) {
|
|
85
|
-
let
|
|
86
|
-
|
|
87
|
-
let data;
|
|
88
|
-
let isMessageNode = /^message\//i.test(attachment.contentType);
|
|
89
|
-
|
|
90
|
+
let eventObject;
|
|
91
|
+
const attachments = [].concat(this.mail.attachments || []).map((attachment, i) => {
|
|
90
92
|
if (/^data:/i.test(attachment.path || attachment.href)) {
|
|
91
93
|
attachment = this._processDataUrl(attachment);
|
|
92
94
|
}
|
|
93
95
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const contentType =
|
|
97
|
+
attachment.contentType || mimeFuncs.detectMimeType(attachment.filename || attachment.path || attachment.href || 'bin');
|
|
98
|
+
|
|
99
|
+
const isImage = /^image\//i.test(contentType);
|
|
100
|
+
const isMessageNode = /^message\//i.test(contentType);
|
|
101
|
+
|
|
102
|
+
const contentDisposition =
|
|
103
|
+
attachment.contentDisposition || (isMessageNode || (isImage && attachment.cid) ? 'inline' : 'attachment');
|
|
97
104
|
|
|
98
|
-
|
|
105
|
+
let contentTransferEncoding;
|
|
106
|
+
if ('contentTransferEncoding' in attachment) {
|
|
107
|
+
// also contains `false`, to set
|
|
108
|
+
contentTransferEncoding = attachment.contentTransferEncoding;
|
|
109
|
+
} else if (isMessageNode) {
|
|
110
|
+
// the content might include non-ASCII bytes but at this point we do not know it yet
|
|
111
|
+
contentTransferEncoding = '8bit';
|
|
112
|
+
} else {
|
|
113
|
+
contentTransferEncoding = 'base64'; // the default
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const data = {
|
|
99
117
|
contentType,
|
|
100
118
|
contentDisposition,
|
|
101
|
-
contentTransferEncoding
|
|
119
|
+
contentTransferEncoding
|
|
102
120
|
};
|
|
103
121
|
|
|
104
122
|
if (attachment.filename) {
|
|
@@ -128,7 +146,8 @@ class MailComposer {
|
|
|
128
146
|
} else if (attachment.href) {
|
|
129
147
|
data.content = {
|
|
130
148
|
href: attachment.href,
|
|
131
|
-
httpHeaders: attachment.httpHeaders
|
|
149
|
+
httpHeaders: attachment.httpHeaders,
|
|
150
|
+
tls: attachment.tls
|
|
132
151
|
};
|
|
133
152
|
} else {
|
|
134
153
|
data.content = attachment.content || '';
|
|
@@ -146,21 +165,7 @@ class MailComposer {
|
|
|
146
165
|
});
|
|
147
166
|
|
|
148
167
|
if (this.mail.icalEvent) {
|
|
149
|
-
|
|
150
|
-
typeof this.mail.icalEvent === 'object' &&
|
|
151
|
-
(this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
|
|
152
|
-
) {
|
|
153
|
-
icalEvent = this.mail.icalEvent;
|
|
154
|
-
} else {
|
|
155
|
-
icalEvent = {
|
|
156
|
-
content: this.mail.icalEvent
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
eventObject = {};
|
|
161
|
-
Object.keys(icalEvent).forEach(key => {
|
|
162
|
-
eventObject[key] = icalEvent[key];
|
|
163
|
-
});
|
|
168
|
+
eventObject = Object.assign({}, this._getIcalEvent());
|
|
164
169
|
|
|
165
170
|
eventObject.contentType = 'application/ics';
|
|
166
171
|
if (!eventObject.headers) {
|
|
@@ -176,12 +181,75 @@ class MailComposer {
|
|
|
176
181
|
attached: attachments.concat(eventObject || []),
|
|
177
182
|
related: []
|
|
178
183
|
};
|
|
179
|
-
} else {
|
|
180
|
-
return {
|
|
181
|
-
attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
|
|
182
|
-
related: attachments.filter(attachment => !!attachment.cid)
|
|
183
|
-
};
|
|
184
184
|
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
|
|
188
|
+
related: attachments.filter(attachment => !!attachment.cid)
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Returns the icalEvent value with `path`/`href`/data uri input normalized into
|
|
194
|
+
* a `content` entry, the same way as for regular attachments. The same event is
|
|
195
|
+
* included twice (as a text/calendar alternative and as an application/ics
|
|
196
|
+
* attachment), so the shared content object is marked to be resolved just once
|
|
197
|
+
* and the buffered result is reused by the second node.
|
|
198
|
+
*
|
|
199
|
+
* @returns {Object} Normalized icalEvent data
|
|
200
|
+
*/
|
|
201
|
+
_getIcalEvent() {
|
|
202
|
+
if (!this._icalEvent) {
|
|
203
|
+
let icalEvent;
|
|
204
|
+
if (
|
|
205
|
+
typeof this.mail.icalEvent === 'object' &&
|
|
206
|
+
(this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
|
|
207
|
+
) {
|
|
208
|
+
// an own "__proto__" key would make the copy inherit path/href from caller
|
|
209
|
+
// data, and the mapping below then replaces the content the caller did set
|
|
210
|
+
icalEvent = copyOwnKeys({}, this.mail.icalEvent);
|
|
211
|
+
} else {
|
|
212
|
+
icalEvent = {
|
|
213
|
+
content: this.mail.icalEvent
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (/^data:/i.test(icalEvent.path || icalEvent.href)) {
|
|
218
|
+
icalEvent = this._processDataUrl(icalEvent);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (/^https?:\/\//i.test(icalEvent.path)) {
|
|
222
|
+
icalEvent.href = icalEvent.path;
|
|
223
|
+
icalEvent.path = undefined;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!icalEvent.raw) {
|
|
227
|
+
// map file path and URL values into `content`, otherwise the content
|
|
228
|
+
// nodes would render an empty body
|
|
229
|
+
if (icalEvent.path) {
|
|
230
|
+
icalEvent.content = {
|
|
231
|
+
path: icalEvent.path
|
|
232
|
+
};
|
|
233
|
+
icalEvent.path = undefined;
|
|
234
|
+
} else if (icalEvent.href) {
|
|
235
|
+
icalEvent.content = {
|
|
236
|
+
href: icalEvent.href,
|
|
237
|
+
httpHeaders: icalEvent.httpHeaders
|
|
238
|
+
};
|
|
239
|
+
icalEvent.href = undefined;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
if (icalEvent.content && typeof icalEvent.content === 'object') {
|
|
244
|
+
// we are going to have the same attachment twice, so mark this to be
|
|
245
|
+
// resolved just once
|
|
246
|
+
icalEvent.content._resolve = true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
this._icalEvent = icalEvent;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
return this._icalEvent;
|
|
185
253
|
}
|
|
186
254
|
|
|
187
255
|
/**
|
|
@@ -190,16 +258,14 @@ class MailComposer {
|
|
|
190
258
|
* @returns {Array} An array of alternative elements. Includes the `text` and `html` values as well
|
|
191
259
|
*/
|
|
192
260
|
getAlternatives() {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
html,
|
|
196
|
-
watchHtml,
|
|
197
|
-
amp,
|
|
198
|
-
icalEvent,
|
|
199
|
-
eventObject;
|
|
261
|
+
const alternatives = [];
|
|
262
|
+
let text, html, watchHtml, amp, eventObject;
|
|
200
263
|
|
|
201
264
|
if (this.mail.text) {
|
|
202
|
-
if (
|
|
265
|
+
if (
|
|
266
|
+
typeof this.mail.text === 'object' &&
|
|
267
|
+
(this.mail.text.content || this.mail.text.path || this.mail.text.href || this.mail.text.raw)
|
|
268
|
+
) {
|
|
203
269
|
text = this.mail.text;
|
|
204
270
|
} else {
|
|
205
271
|
text = {
|
|
@@ -224,7 +290,10 @@ class MailComposer {
|
|
|
224
290
|
}
|
|
225
291
|
|
|
226
292
|
if (this.mail.amp) {
|
|
227
|
-
if (
|
|
293
|
+
if (
|
|
294
|
+
typeof this.mail.amp === 'object' &&
|
|
295
|
+
(this.mail.amp.content || this.mail.amp.path || this.mail.amp.href || this.mail.amp.raw)
|
|
296
|
+
) {
|
|
228
297
|
amp = this.mail.amp;
|
|
229
298
|
} else {
|
|
230
299
|
amp = {
|
|
@@ -236,37 +305,21 @@ class MailComposer {
|
|
|
236
305
|
|
|
237
306
|
// NB! when including attachments with a calendar alternative you might end up in a blank screen on some clients
|
|
238
307
|
if (this.mail.icalEvent) {
|
|
239
|
-
|
|
240
|
-
typeof this.mail.icalEvent === 'object' &&
|
|
241
|
-
(this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
|
|
242
|
-
) {
|
|
243
|
-
icalEvent = this.mail.icalEvent;
|
|
244
|
-
} else {
|
|
245
|
-
icalEvent = {
|
|
246
|
-
content: this.mail.icalEvent
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
eventObject = {};
|
|
251
|
-
Object.keys(icalEvent).forEach(key => {
|
|
252
|
-
eventObject[key] = icalEvent[key];
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
if (eventObject.content && typeof eventObject.content === 'object') {
|
|
256
|
-
// we are going to have the same attachment twice, so mark this to be
|
|
257
|
-
// resolved just once
|
|
258
|
-
eventObject.content._resolve = true;
|
|
259
|
-
}
|
|
308
|
+
eventObject = Object.assign({}, this._getIcalEvent());
|
|
260
309
|
|
|
261
310
|
eventObject.filename = false;
|
|
262
|
-
eventObject.contentType =
|
|
311
|
+
eventObject.contentType =
|
|
312
|
+
'text/calendar; charset=utf-8; method=' + (eventObject.method || 'PUBLISH').toString().trim().toUpperCase();
|
|
263
313
|
if (!eventObject.headers) {
|
|
264
314
|
eventObject.headers = {};
|
|
265
315
|
}
|
|
266
316
|
}
|
|
267
317
|
|
|
268
318
|
if (this.mail.html) {
|
|
269
|
-
if (
|
|
319
|
+
if (
|
|
320
|
+
typeof this.mail.html === 'object' &&
|
|
321
|
+
(this.mail.html.content || this.mail.html.path || this.mail.html.href || this.mail.html.raw)
|
|
322
|
+
) {
|
|
270
323
|
html = this.mail.html;
|
|
271
324
|
} else {
|
|
272
325
|
html = {
|
|
@@ -284,14 +337,14 @@ class MailComposer {
|
|
|
284
337
|
.concat(eventObject || [])
|
|
285
338
|
.concat(this.mail.alternatives || [])
|
|
286
339
|
.forEach(alternative => {
|
|
287
|
-
let data;
|
|
288
|
-
|
|
289
340
|
if (/^data:/i.test(alternative.path || alternative.href)) {
|
|
290
341
|
alternative = this._processDataUrl(alternative);
|
|
291
342
|
}
|
|
292
343
|
|
|
293
|
-
data = {
|
|
294
|
-
contentType:
|
|
344
|
+
const data = {
|
|
345
|
+
contentType:
|
|
346
|
+
alternative.contentType ||
|
|
347
|
+
mimeFuncs.detectMimeType(alternative.filename || alternative.path || alternative.href || 'txt'),
|
|
295
348
|
contentTransferEncoding: alternative.contentTransferEncoding
|
|
296
349
|
};
|
|
297
350
|
|
|
@@ -340,26 +393,22 @@ class MailComposer {
|
|
|
340
393
|
* @returns {Object} MimeNode node element
|
|
341
394
|
*/
|
|
342
395
|
_createMixed(parentNode) {
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
360
|
-
newline: this.mail.newline
|
|
361
|
-
});
|
|
362
|
-
}
|
|
396
|
+
const node = parentNode
|
|
397
|
+
? parentNode.createChild('multipart/mixed', {
|
|
398
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
399
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
400
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
401
|
+
newline: this.mail.newline
|
|
402
|
+
})
|
|
403
|
+
: new MimeNode('multipart/mixed', {
|
|
404
|
+
baseBoundary: this.mail.baseBoundary,
|
|
405
|
+
textEncoding: this.mail.textEncoding,
|
|
406
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
407
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
408
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
409
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
410
|
+
newline: this.mail.newline
|
|
411
|
+
});
|
|
363
412
|
|
|
364
413
|
if (this._useAlternative) {
|
|
365
414
|
this._createAlternative(node);
|
|
@@ -388,26 +437,22 @@ class MailComposer {
|
|
|
388
437
|
* @returns {Object} MimeNode node element
|
|
389
438
|
*/
|
|
390
439
|
_createAlternative(parentNode) {
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
408
|
-
newline: this.mail.newline
|
|
409
|
-
});
|
|
410
|
-
}
|
|
440
|
+
const node = parentNode
|
|
441
|
+
? parentNode.createChild('multipart/alternative', {
|
|
442
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
443
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
444
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
445
|
+
newline: this.mail.newline
|
|
446
|
+
})
|
|
447
|
+
: new MimeNode('multipart/alternative', {
|
|
448
|
+
baseBoundary: this.mail.baseBoundary,
|
|
449
|
+
textEncoding: this.mail.textEncoding,
|
|
450
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
451
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
452
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
453
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
454
|
+
newline: this.mail.newline
|
|
455
|
+
});
|
|
411
456
|
|
|
412
457
|
this._alternatives.forEach(alternative => {
|
|
413
458
|
if (this._useRelated && this._htmlNode === alternative) {
|
|
@@ -427,26 +472,22 @@ class MailComposer {
|
|
|
427
472
|
* @returns {Object} MimeNode node element
|
|
428
473
|
*/
|
|
429
474
|
_createRelated(parentNode) {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
447
|
-
newline: this.mail.newline
|
|
448
|
-
});
|
|
449
|
-
}
|
|
475
|
+
const node = parentNode
|
|
476
|
+
? parentNode.createChild('multipart/related; type="text/html"', {
|
|
477
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
478
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
479
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
480
|
+
newline: this.mail.newline
|
|
481
|
+
})
|
|
482
|
+
: new MimeNode('multipart/related; type="text/html"', {
|
|
483
|
+
baseBoundary: this.mail.baseBoundary,
|
|
484
|
+
textEncoding: this.mail.textEncoding,
|
|
485
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
486
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
487
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
488
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
489
|
+
newline: this.mail.newline
|
|
490
|
+
});
|
|
450
491
|
|
|
451
492
|
this._createContentNode(node, this._htmlNode);
|
|
452
493
|
|
|
@@ -466,33 +507,30 @@ class MailComposer {
|
|
|
466
507
|
element = element || {};
|
|
467
508
|
element.content = element.content || '';
|
|
468
509
|
|
|
469
|
-
|
|
470
|
-
let encoding = (element.encoding || 'utf8')
|
|
510
|
+
const encoding = (element.encoding || 'utf8')
|
|
471
511
|
.toString()
|
|
472
512
|
.toLowerCase()
|
|
473
513
|
.replace(/[-_\s]/g, '');
|
|
474
514
|
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
});
|
|
495
|
-
}
|
|
515
|
+
const node = parentNode
|
|
516
|
+
? parentNode.createChild(element.contentType, {
|
|
517
|
+
filename: element.filename,
|
|
518
|
+
textEncoding: this.mail.textEncoding,
|
|
519
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
520
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
521
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
522
|
+
newline: this.mail.newline
|
|
523
|
+
})
|
|
524
|
+
: new MimeNode(element.contentType, {
|
|
525
|
+
filename: element.filename,
|
|
526
|
+
baseBoundary: this.mail.baseBoundary,
|
|
527
|
+
textEncoding: this.mail.textEncoding,
|
|
528
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
529
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
530
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
531
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
532
|
+
newline: this.mail.newline
|
|
533
|
+
});
|
|
496
534
|
|
|
497
535
|
// add custom headers
|
|
498
536
|
if (element.headers) {
|
|
@@ -537,12 +575,54 @@ class MailComposer {
|
|
|
537
575
|
* @return {Object} Parsed element
|
|
538
576
|
*/
|
|
539
577
|
_processDataUrl(element) {
|
|
540
|
-
|
|
541
|
-
|
|
578
|
+
const dataUrl = element.path || element.href;
|
|
579
|
+
|
|
580
|
+
// Early validation to prevent ReDoS
|
|
581
|
+
if (!dataUrl || typeof dataUrl !== 'string') {
|
|
582
|
+
return element;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
if (!dataUrl.startsWith('data:')) {
|
|
542
586
|
return element;
|
|
543
587
|
}
|
|
544
588
|
|
|
545
|
-
|
|
589
|
+
if (dataUrl.length > 52428800) {
|
|
590
|
+
// 52428800 chars = 50MB limit for data URL string (~37.5MB decoded image)
|
|
591
|
+
// Extract content type before rejecting to preserve MIME type
|
|
592
|
+
let detectedType = 'application/octet-stream';
|
|
593
|
+
const commaPos = dataUrl.indexOf(',');
|
|
594
|
+
|
|
595
|
+
if (commaPos > 0 && commaPos < 200) {
|
|
596
|
+
// Parse header safely with size limit
|
|
597
|
+
const header = dataUrl.substring(5, commaPos); // skip 'data:'
|
|
598
|
+
const parts = header.split(';');
|
|
599
|
+
if (parts[0] && parts[0].includes('/')) {
|
|
600
|
+
detectedType = parts[0].trim();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// Return empty content for excessively long data URLs
|
|
605
|
+
return Object.assign(copyOwnKeys({}, element), {
|
|
606
|
+
path: false,
|
|
607
|
+
href: false,
|
|
608
|
+
content: Buffer.alloc(0),
|
|
609
|
+
contentType: element.contentType || detectedType
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
let parsedDataUri;
|
|
614
|
+
try {
|
|
615
|
+
parsedDataUri = parseDataURI(dataUrl);
|
|
616
|
+
} catch (_err) {
|
|
617
|
+
return element;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
if (!parsedDataUri) {
|
|
621
|
+
return element;
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
element.content = parsedDataUri.data;
|
|
625
|
+
element.contentType = element.contentType || parsedDataUri.contentType;
|
|
546
626
|
|
|
547
627
|
if ('path' in element) {
|
|
548
628
|
element.path = false;
|
|
@@ -552,12 +632,6 @@ class MailComposer {
|
|
|
552
632
|
element.href = false;
|
|
553
633
|
}
|
|
554
634
|
|
|
555
|
-
parts[1].split(';').forEach(item => {
|
|
556
|
-
if (/^\w+\/[^/]+$/i.test(item)) {
|
|
557
|
-
element.contentType = element.contentType || item.toLowerCase();
|
|
558
|
-
}
|
|
559
|
-
});
|
|
560
|
-
|
|
561
635
|
return element;
|
|
562
636
|
}
|
|
563
637
|
}
|