@nocobase/plugin-notification-email 1.4.0-alpha.20240928155737
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/LICENSE.txt +123 -0
- package/README.md +1 -0
- package/client.d.ts +2 -0
- package/client.js +1 -0
- package/dist/client/ConfigForm.d.ts +10 -0
- package/dist/client/MessageConfigForm.d.ts +12 -0
- package/dist/client/hooks/useTranslation.d.ts +9 -0
- package/dist/client/index.d.ts +15 -0
- package/dist/client/index.js +16 -0
- package/dist/constant.d.ts +10 -0
- package/dist/constant.js +39 -0
- package/dist/externalVersion.js +17 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +48 -0
- package/dist/locale/en-US.json +22 -0
- package/dist/locale/zh-CN.json +22 -0
- package/dist/node_modules/nodemailer/.gitattributes +6 -0
- package/dist/node_modules/nodemailer/.ncurc.js +7 -0
- package/dist/node_modules/nodemailer/.prettierrc.js +8 -0
- package/dist/node_modules/nodemailer/LICENSE +16 -0
- package/dist/node_modules/nodemailer/SECURITY.txt +22 -0
- package/dist/node_modules/nodemailer/lib/addressparser/index.js +313 -0
- package/dist/node_modules/nodemailer/lib/base64/index.js +142 -0
- package/dist/node_modules/nodemailer/lib/dkim/index.js +251 -0
- package/dist/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
- package/dist/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
- package/dist/node_modules/nodemailer/lib/dkim/sign.js +117 -0
- package/dist/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
- package/dist/node_modules/nodemailer/lib/fetch/index.js +274 -0
- package/dist/node_modules/nodemailer/lib/json-transport/index.js +82 -0
- package/dist/node_modules/nodemailer/lib/mail-composer/index.js +565 -0
- package/dist/node_modules/nodemailer/lib/mailer/index.js +427 -0
- package/dist/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
- package/dist/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
- package/dist/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
- package/dist/node_modules/nodemailer/lib/mime-node/index.js +1305 -0
- package/dist/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
- package/dist/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
- package/dist/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
- package/dist/node_modules/nodemailer/lib/nodemailer.js +1 -0
- package/dist/node_modules/nodemailer/lib/qp/index.js +219 -0
- package/dist/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
- package/dist/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
- package/dist/node_modules/nodemailer/lib/shared/index.js +638 -0
- package/dist/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
- package/dist/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
- package/dist/node_modules/nodemailer/lib/smtp-connection/index.js +1812 -0
- package/dist/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
- package/dist/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
- package/dist/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
- package/dist/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
- package/dist/node_modules/nodemailer/lib/well-known/index.js +47 -0
- package/dist/node_modules/nodemailer/lib/well-known/services.json +338 -0
- package/dist/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
- package/dist/node_modules/nodemailer/package.json +1 -0
- package/dist/server/index.d.ts +9 -0
- package/dist/server/index.js +42 -0
- package/dist/server/mail-server.d.ts +14 -0
- package/dist/server/mail-server.js +78 -0
- package/dist/server/plugin.d.ts +19 -0
- package/dist/server/plugin.js +69 -0
- package/package.json +23 -0
- package/server.d.ts +2 -0
- package/server.js +1 -0
- package/tsconfig.json +7 -0
|
@@ -0,0 +1,565 @@
|
|
|
1
|
+
/* eslint no-undefined: 0 */
|
|
2
|
+
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
const MimeNode = require('../mime-node');
|
|
6
|
+
const mimeFuncs = require('../mime-funcs');
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Creates the object for composing a MimeNode instance out from the mail options
|
|
10
|
+
*
|
|
11
|
+
* @constructor
|
|
12
|
+
* @param {Object} mail Mail options
|
|
13
|
+
*/
|
|
14
|
+
class MailComposer {
|
|
15
|
+
constructor(mail) {
|
|
16
|
+
this.mail = mail || {};
|
|
17
|
+
this.message = false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds MimeNode instance
|
|
22
|
+
*/
|
|
23
|
+
compile() {
|
|
24
|
+
this._alternatives = this.getAlternatives();
|
|
25
|
+
this._htmlNode = this._alternatives.filter(alternative => /^text\/html\b/i.test(alternative.contentType)).pop();
|
|
26
|
+
this._attachments = this.getAttachments(!!this._htmlNode);
|
|
27
|
+
|
|
28
|
+
this._useRelated = !!(this._htmlNode && this._attachments.related.length);
|
|
29
|
+
this._useAlternative = this._alternatives.length > 1;
|
|
30
|
+
this._useMixed = this._attachments.attached.length > 1 || (this._alternatives.length && this._attachments.attached.length === 1);
|
|
31
|
+
|
|
32
|
+
// Compose MIME tree
|
|
33
|
+
if (this.mail.raw) {
|
|
34
|
+
this.message = new MimeNode('message/rfc822', { newline: this.mail.newline }).setRaw(this.mail.raw);
|
|
35
|
+
} else if (this._useMixed) {
|
|
36
|
+
this.message = this._createMixed();
|
|
37
|
+
} else if (this._useAlternative) {
|
|
38
|
+
this.message = this._createAlternative();
|
|
39
|
+
} else if (this._useRelated) {
|
|
40
|
+
this.message = this._createRelated();
|
|
41
|
+
} else {
|
|
42
|
+
this.message = this._createContentNode(
|
|
43
|
+
false,
|
|
44
|
+
[]
|
|
45
|
+
.concat(this._alternatives || [])
|
|
46
|
+
.concat(this._attachments.attached || [])
|
|
47
|
+
.shift() || {
|
|
48
|
+
contentType: 'text/plain',
|
|
49
|
+
content: ''
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Add custom headers
|
|
55
|
+
if (this.mail.headers) {
|
|
56
|
+
this.message.addHeader(this.mail.headers);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Add headers to the root node, always overrides custom headers
|
|
60
|
+
['from', 'sender', 'to', 'cc', 'bcc', 'reply-to', 'in-reply-to', 'references', 'subject', 'message-id', 'date'].forEach(header => {
|
|
61
|
+
let key = header.replace(/-(\w)/g, (o, c) => c.toUpperCase());
|
|
62
|
+
if (this.mail[key]) {
|
|
63
|
+
this.message.setHeader(header, this.mail[key]);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Sets custom envelope
|
|
68
|
+
if (this.mail.envelope) {
|
|
69
|
+
this.message.setEnvelope(this.mail.envelope);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ensure Message-Id value
|
|
73
|
+
this.message.messageId();
|
|
74
|
+
|
|
75
|
+
return this.message;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* List all attachments. Resulting attachment objects can be used as input for MimeNode nodes
|
|
80
|
+
*
|
|
81
|
+
* @param {Boolean} findRelated If true separate related attachments from attached ones
|
|
82
|
+
* @returns {Object} An object of arrays (`related` and `attached`)
|
|
83
|
+
*/
|
|
84
|
+
getAttachments(findRelated) {
|
|
85
|
+
let icalEvent, eventObject;
|
|
86
|
+
let attachments = [].concat(this.mail.attachments || []).map((attachment, i) => {
|
|
87
|
+
let data;
|
|
88
|
+
let isMessageNode = /^message\//i.test(attachment.contentType);
|
|
89
|
+
|
|
90
|
+
if (/^data:/i.test(attachment.path || attachment.href)) {
|
|
91
|
+
attachment = this._processDataUrl(attachment);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let contentType = attachment.contentType || mimeFuncs.detectMimeType(attachment.filename || attachment.path || attachment.href || 'bin');
|
|
95
|
+
let isImage = /^image\//i.test(contentType);
|
|
96
|
+
let contentDisposition = attachment.contentDisposition || (isMessageNode || (isImage && attachment.cid) ? 'inline' : 'attachment');
|
|
97
|
+
|
|
98
|
+
data = {
|
|
99
|
+
contentType,
|
|
100
|
+
contentDisposition,
|
|
101
|
+
contentTransferEncoding: 'contentTransferEncoding' in attachment ? attachment.contentTransferEncoding : 'base64'
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
if (attachment.filename) {
|
|
105
|
+
data.filename = attachment.filename;
|
|
106
|
+
} else if (!isMessageNode && attachment.filename !== false) {
|
|
107
|
+
data.filename = (attachment.path || attachment.href || '').split('/').pop().split('?').shift() || 'attachment-' + (i + 1);
|
|
108
|
+
if (data.filename.indexOf('.') < 0) {
|
|
109
|
+
data.filename += '.' + mimeFuncs.detectExtension(data.contentType);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (/^https?:\/\//i.test(attachment.path)) {
|
|
114
|
+
attachment.href = attachment.path;
|
|
115
|
+
attachment.path = undefined;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (attachment.cid) {
|
|
119
|
+
data.cid = attachment.cid;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (attachment.raw) {
|
|
123
|
+
data.raw = attachment.raw;
|
|
124
|
+
} else if (attachment.path) {
|
|
125
|
+
data.content = {
|
|
126
|
+
path: attachment.path
|
|
127
|
+
};
|
|
128
|
+
} else if (attachment.href) {
|
|
129
|
+
data.content = {
|
|
130
|
+
href: attachment.href,
|
|
131
|
+
httpHeaders: attachment.httpHeaders
|
|
132
|
+
};
|
|
133
|
+
} else {
|
|
134
|
+
data.content = attachment.content || '';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (attachment.encoding) {
|
|
138
|
+
data.encoding = attachment.encoding;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (attachment.headers) {
|
|
142
|
+
data.headers = attachment.headers;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return data;
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (this.mail.icalEvent) {
|
|
149
|
+
if (
|
|
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
|
+
});
|
|
164
|
+
|
|
165
|
+
eventObject.contentType = 'application/ics';
|
|
166
|
+
if (!eventObject.headers) {
|
|
167
|
+
eventObject.headers = {};
|
|
168
|
+
}
|
|
169
|
+
eventObject.filename = eventObject.filename || 'invite.ics';
|
|
170
|
+
eventObject.headers['Content-Disposition'] = 'attachment';
|
|
171
|
+
eventObject.headers['Content-Transfer-Encoding'] = 'base64';
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (!findRelated) {
|
|
175
|
+
return {
|
|
176
|
+
attached: attachments.concat(eventObject || []),
|
|
177
|
+
related: []
|
|
178
|
+
};
|
|
179
|
+
} else {
|
|
180
|
+
return {
|
|
181
|
+
attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
|
|
182
|
+
related: attachments.filter(attachment => !!attachment.cid)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* List alternatives. Resulting objects can be used as input for MimeNode nodes
|
|
189
|
+
*
|
|
190
|
+
* @returns {Array} An array of alternative elements. Includes the `text` and `html` values as well
|
|
191
|
+
*/
|
|
192
|
+
getAlternatives() {
|
|
193
|
+
let alternatives = [],
|
|
194
|
+
text,
|
|
195
|
+
html,
|
|
196
|
+
watchHtml,
|
|
197
|
+
amp,
|
|
198
|
+
icalEvent,
|
|
199
|
+
eventObject;
|
|
200
|
+
|
|
201
|
+
if (this.mail.text) {
|
|
202
|
+
if (typeof this.mail.text === 'object' && (this.mail.text.content || this.mail.text.path || this.mail.text.href || this.mail.text.raw)) {
|
|
203
|
+
text = this.mail.text;
|
|
204
|
+
} else {
|
|
205
|
+
text = {
|
|
206
|
+
content: this.mail.text
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
text.contentType = 'text/plain; charset=utf-8';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (this.mail.watchHtml) {
|
|
213
|
+
if (
|
|
214
|
+
typeof this.mail.watchHtml === 'object' &&
|
|
215
|
+
(this.mail.watchHtml.content || this.mail.watchHtml.path || this.mail.watchHtml.href || this.mail.watchHtml.raw)
|
|
216
|
+
) {
|
|
217
|
+
watchHtml = this.mail.watchHtml;
|
|
218
|
+
} else {
|
|
219
|
+
watchHtml = {
|
|
220
|
+
content: this.mail.watchHtml
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
watchHtml.contentType = 'text/watch-html; charset=utf-8';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (this.mail.amp) {
|
|
227
|
+
if (typeof this.mail.amp === 'object' && (this.mail.amp.content || this.mail.amp.path || this.mail.amp.href || this.mail.amp.raw)) {
|
|
228
|
+
amp = this.mail.amp;
|
|
229
|
+
} else {
|
|
230
|
+
amp = {
|
|
231
|
+
content: this.mail.amp
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
amp.contentType = 'text/x-amp-html; charset=utf-8';
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// NB! when including attachments with a calendar alternative you might end up in a blank screen on some clients
|
|
238
|
+
if (this.mail.icalEvent) {
|
|
239
|
+
if (
|
|
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
|
+
}
|
|
260
|
+
|
|
261
|
+
eventObject.filename = false;
|
|
262
|
+
eventObject.contentType = 'text/calendar; charset=utf-8; method=' + (eventObject.method || 'PUBLISH').toString().trim().toUpperCase();
|
|
263
|
+
if (!eventObject.headers) {
|
|
264
|
+
eventObject.headers = {};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (this.mail.html) {
|
|
269
|
+
if (typeof this.mail.html === 'object' && (this.mail.html.content || this.mail.html.path || this.mail.html.href || this.mail.html.raw)) {
|
|
270
|
+
html = this.mail.html;
|
|
271
|
+
} else {
|
|
272
|
+
html = {
|
|
273
|
+
content: this.mail.html
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
html.contentType = 'text/html; charset=utf-8';
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
[]
|
|
280
|
+
.concat(text || [])
|
|
281
|
+
.concat(watchHtml || [])
|
|
282
|
+
.concat(amp || [])
|
|
283
|
+
.concat(html || [])
|
|
284
|
+
.concat(eventObject || [])
|
|
285
|
+
.concat(this.mail.alternatives || [])
|
|
286
|
+
.forEach(alternative => {
|
|
287
|
+
let data;
|
|
288
|
+
|
|
289
|
+
if (/^data:/i.test(alternative.path || alternative.href)) {
|
|
290
|
+
alternative = this._processDataUrl(alternative);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
data = {
|
|
294
|
+
contentType: alternative.contentType || mimeFuncs.detectMimeType(alternative.filename || alternative.path || alternative.href || 'txt'),
|
|
295
|
+
contentTransferEncoding: alternative.contentTransferEncoding
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
if (alternative.filename) {
|
|
299
|
+
data.filename = alternative.filename;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (/^https?:\/\//i.test(alternative.path)) {
|
|
303
|
+
alternative.href = alternative.path;
|
|
304
|
+
alternative.path = undefined;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (alternative.raw) {
|
|
308
|
+
data.raw = alternative.raw;
|
|
309
|
+
} else if (alternative.path) {
|
|
310
|
+
data.content = {
|
|
311
|
+
path: alternative.path
|
|
312
|
+
};
|
|
313
|
+
} else if (alternative.href) {
|
|
314
|
+
data.content = {
|
|
315
|
+
href: alternative.href
|
|
316
|
+
};
|
|
317
|
+
} else {
|
|
318
|
+
data.content = alternative.content || '';
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (alternative.encoding) {
|
|
322
|
+
data.encoding = alternative.encoding;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
if (alternative.headers) {
|
|
326
|
+
data.headers = alternative.headers;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
alternatives.push(data);
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
return alternatives;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Builds multipart/mixed node. It should always contain different type of elements on the same level
|
|
337
|
+
* eg. text + attachments
|
|
338
|
+
*
|
|
339
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
|
340
|
+
* @returns {Object} MimeNode node element
|
|
341
|
+
*/
|
|
342
|
+
_createMixed(parentNode) {
|
|
343
|
+
let node;
|
|
344
|
+
|
|
345
|
+
if (!parentNode) {
|
|
346
|
+
node = new MimeNode('multipart/mixed', {
|
|
347
|
+
baseBoundary: this.mail.baseBoundary,
|
|
348
|
+
textEncoding: this.mail.textEncoding,
|
|
349
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
350
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
351
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
352
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
353
|
+
newline: this.mail.newline
|
|
354
|
+
});
|
|
355
|
+
} else {
|
|
356
|
+
node = parentNode.createChild('multipart/mixed', {
|
|
357
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
358
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
359
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
360
|
+
newline: this.mail.newline
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
if (this._useAlternative) {
|
|
365
|
+
this._createAlternative(node);
|
|
366
|
+
} else if (this._useRelated) {
|
|
367
|
+
this._createRelated(node);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
[]
|
|
371
|
+
.concat((!this._useAlternative && this._alternatives) || [])
|
|
372
|
+
.concat(this._attachments.attached || [])
|
|
373
|
+
.forEach(element => {
|
|
374
|
+
// if the element is a html node from related subpart then ignore it
|
|
375
|
+
if (!this._useRelated || element !== this._htmlNode) {
|
|
376
|
+
this._createContentNode(node, element);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
return node;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Builds multipart/alternative node. It should always contain same type of elements on the same level
|
|
385
|
+
* eg. text + html view of the same data
|
|
386
|
+
*
|
|
387
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
|
388
|
+
* @returns {Object} MimeNode node element
|
|
389
|
+
*/
|
|
390
|
+
_createAlternative(parentNode) {
|
|
391
|
+
let node;
|
|
392
|
+
|
|
393
|
+
if (!parentNode) {
|
|
394
|
+
node = new MimeNode('multipart/alternative', {
|
|
395
|
+
baseBoundary: this.mail.baseBoundary,
|
|
396
|
+
textEncoding: this.mail.textEncoding,
|
|
397
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
398
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
399
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
400
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
401
|
+
newline: this.mail.newline
|
|
402
|
+
});
|
|
403
|
+
} else {
|
|
404
|
+
node = parentNode.createChild('multipart/alternative', {
|
|
405
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
406
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
407
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
408
|
+
newline: this.mail.newline
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
this._alternatives.forEach(alternative => {
|
|
413
|
+
if (this._useRelated && this._htmlNode === alternative) {
|
|
414
|
+
this._createRelated(node);
|
|
415
|
+
} else {
|
|
416
|
+
this._createContentNode(node, alternative);
|
|
417
|
+
}
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
return node;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/**
|
|
424
|
+
* Builds multipart/related node. It should always contain html node with related attachments
|
|
425
|
+
*
|
|
426
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
|
427
|
+
* @returns {Object} MimeNode node element
|
|
428
|
+
*/
|
|
429
|
+
_createRelated(parentNode) {
|
|
430
|
+
let node;
|
|
431
|
+
|
|
432
|
+
if (!parentNode) {
|
|
433
|
+
node = new MimeNode('multipart/related; type="text/html"', {
|
|
434
|
+
baseBoundary: this.mail.baseBoundary,
|
|
435
|
+
textEncoding: this.mail.textEncoding,
|
|
436
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
437
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
438
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
439
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
440
|
+
newline: this.mail.newline
|
|
441
|
+
});
|
|
442
|
+
} else {
|
|
443
|
+
node = parentNode.createChild('multipart/related; type="text/html"', {
|
|
444
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
445
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
446
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
447
|
+
newline: this.mail.newline
|
|
448
|
+
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
this._createContentNode(node, this._htmlNode);
|
|
452
|
+
|
|
453
|
+
this._attachments.related.forEach(alternative => this._createContentNode(node, alternative));
|
|
454
|
+
|
|
455
|
+
return node;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/**
|
|
459
|
+
* Creates a regular node with contents
|
|
460
|
+
*
|
|
461
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
|
462
|
+
* @param {Object} element Node data
|
|
463
|
+
* @returns {Object} MimeNode node element
|
|
464
|
+
*/
|
|
465
|
+
_createContentNode(parentNode, element) {
|
|
466
|
+
element = element || {};
|
|
467
|
+
element.content = element.content || '';
|
|
468
|
+
|
|
469
|
+
let node;
|
|
470
|
+
let encoding = (element.encoding || 'utf8')
|
|
471
|
+
.toString()
|
|
472
|
+
.toLowerCase()
|
|
473
|
+
.replace(/[-_\s]/g, '');
|
|
474
|
+
|
|
475
|
+
if (!parentNode) {
|
|
476
|
+
node = new MimeNode(element.contentType, {
|
|
477
|
+
filename: element.filename,
|
|
478
|
+
baseBoundary: this.mail.baseBoundary,
|
|
479
|
+
textEncoding: this.mail.textEncoding,
|
|
480
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
|
481
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
482
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
483
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
484
|
+
newline: this.mail.newline
|
|
485
|
+
});
|
|
486
|
+
} else {
|
|
487
|
+
node = parentNode.createChild(element.contentType, {
|
|
488
|
+
filename: element.filename,
|
|
489
|
+
textEncoding: this.mail.textEncoding,
|
|
490
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
|
491
|
+
disableFileAccess: this.mail.disableFileAccess,
|
|
492
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
|
493
|
+
newline: this.mail.newline
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// add custom headers
|
|
498
|
+
if (element.headers) {
|
|
499
|
+
node.addHeader(element.headers);
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (element.cid) {
|
|
503
|
+
node.setHeader('Content-Id', '<' + element.cid.replace(/[<>]/g, '') + '>');
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
if (element.contentTransferEncoding) {
|
|
507
|
+
node.setHeader('Content-Transfer-Encoding', element.contentTransferEncoding);
|
|
508
|
+
} else if (this.mail.encoding && /^text\//i.test(element.contentType)) {
|
|
509
|
+
node.setHeader('Content-Transfer-Encoding', this.mail.encoding);
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
if (!/^text\//i.test(element.contentType) || element.contentDisposition) {
|
|
513
|
+
node.setHeader(
|
|
514
|
+
'Content-Disposition',
|
|
515
|
+
element.contentDisposition || (element.cid && /^image\//i.test(element.contentType) ? 'inline' : 'attachment')
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
if (typeof element.content === 'string' && !['utf8', 'usascii', 'ascii'].includes(encoding)) {
|
|
520
|
+
element.content = Buffer.from(element.content, encoding);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// prefer pregenerated raw content
|
|
524
|
+
if (element.raw) {
|
|
525
|
+
node.setRaw(element.raw);
|
|
526
|
+
} else {
|
|
527
|
+
node.setContent(element.content);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
return node;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
/**
|
|
534
|
+
* Parses data uri and converts it to a Buffer
|
|
535
|
+
*
|
|
536
|
+
* @param {Object} element Content element
|
|
537
|
+
* @return {Object} Parsed element
|
|
538
|
+
*/
|
|
539
|
+
_processDataUrl(element) {
|
|
540
|
+
let parts = (element.path || element.href).match(/^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/i);
|
|
541
|
+
if (!parts) {
|
|
542
|
+
return element;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
element.content = /\bbase64$/i.test(parts[1]) ? Buffer.from(parts[2], 'base64') : Buffer.from(decodeURIComponent(parts[2]));
|
|
546
|
+
|
|
547
|
+
if ('path' in element) {
|
|
548
|
+
element.path = false;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
if ('href' in element) {
|
|
552
|
+
element.href = false;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
parts[1].split(';').forEach(item => {
|
|
556
|
+
if (/^\w+\/[^/]+$/i.test(item)) {
|
|
557
|
+
element.contentType = element.contentType || item.toLowerCase();
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
return element;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
module.exports = MailComposer;
|