zhuha 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of zhuha might be problematic. Click here for more details.
- package/06-02.html +81 -0
- package/06-02.js +72 -0
- package/06-03.js +7 -0
- package/06-04.js +7 -0
- package/AnswersLW5.pdf +0 -0
- package/m0603/m0603.js +30 -0
- package/m0603/node_modules/.package-lock.json +16 -0
- package/m0603/node_modules/nodemailer/.gitattributes +6 -0
- package/m0603/node_modules/nodemailer/.prettierrc.js +8 -0
- package/m0603/node_modules/nodemailer/CHANGELOG.md +725 -0
- package/m0603/node_modules/nodemailer/CODE_OF_CONDUCT.md +76 -0
- package/m0603/node_modules/nodemailer/CONTRIBUTING.md +67 -0
- package/m0603/node_modules/nodemailer/LICENSE +16 -0
- package/m0603/node_modules/nodemailer/README.md +97 -0
- package/m0603/node_modules/nodemailer/SECURITY.txt +22 -0
- package/m0603/node_modules/nodemailer/lib/addressparser/index.js +313 -0
- package/m0603/node_modules/nodemailer/lib/base64/index.js +142 -0
- package/m0603/node_modules/nodemailer/lib/dkim/index.js +251 -0
- package/m0603/node_modules/nodemailer/lib/dkim/message-parser.js +155 -0
- package/m0603/node_modules/nodemailer/lib/dkim/relaxed-body.js +154 -0
- package/m0603/node_modules/nodemailer/lib/dkim/sign.js +117 -0
- package/m0603/node_modules/nodemailer/lib/fetch/cookies.js +281 -0
- package/m0603/node_modules/nodemailer/lib/fetch/index.js +274 -0
- package/m0603/node_modules/nodemailer/lib/json-transport/index.js +82 -0
- package/m0603/node_modules/nodemailer/lib/mail-composer/index.js +558 -0
- package/m0603/node_modules/nodemailer/lib/mailer/index.js +427 -0
- package/m0603/node_modules/nodemailer/lib/mailer/mail-message.js +315 -0
- package/m0603/node_modules/nodemailer/lib/mime-funcs/index.js +625 -0
- package/m0603/node_modules/nodemailer/lib/mime-funcs/mime-types.js +2102 -0
- package/m0603/node_modules/nodemailer/lib/mime-node/index.js +1290 -0
- package/m0603/node_modules/nodemailer/lib/mime-node/last-newline.js +33 -0
- package/m0603/node_modules/nodemailer/lib/mime-node/le-unix.js +43 -0
- package/m0603/node_modules/nodemailer/lib/mime-node/le-windows.js +52 -0
- package/m0603/node_modules/nodemailer/lib/nodemailer.js +143 -0
- package/m0603/node_modules/nodemailer/lib/qp/index.js +219 -0
- package/m0603/node_modules/nodemailer/lib/sendmail-transport/index.js +210 -0
- package/m0603/node_modules/nodemailer/lib/ses-transport/index.js +349 -0
- package/m0603/node_modules/nodemailer/lib/shared/index.js +638 -0
- package/m0603/node_modules/nodemailer/lib/smtp-connection/data-stream.js +108 -0
- package/m0603/node_modules/nodemailer/lib/smtp-connection/http-proxy-client.js +143 -0
- package/m0603/node_modules/nodemailer/lib/smtp-connection/index.js +1796 -0
- package/m0603/node_modules/nodemailer/lib/smtp-pool/index.js +648 -0
- package/m0603/node_modules/nodemailer/lib/smtp-pool/pool-resource.js +253 -0
- package/m0603/node_modules/nodemailer/lib/smtp-transport/index.js +416 -0
- package/m0603/node_modules/nodemailer/lib/stream-transport/index.js +135 -0
- package/m0603/node_modules/nodemailer/lib/well-known/index.js +47 -0
- package/m0603/node_modules/nodemailer/lib/well-known/services.json +286 -0
- package/m0603/node_modules/nodemailer/lib/xoauth2/index.js +376 -0
- package/m0603/node_modules/nodemailer/package.json +46 -0
- package/m0603/node_modules/nodemailer/postinstall.js +101 -0
- package/m0603/package-lock.json +31 -0
- package/m0603/package.json +15 -0
- package/package.json +16 -0
@@ -0,0 +1,82 @@
|
|
1
|
+
'use strict';
|
2
|
+
|
3
|
+
const packageData = require('../../package.json');
|
4
|
+
const shared = require('../shared');
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Generates a Transport object to generate JSON output
|
8
|
+
*
|
9
|
+
* @constructor
|
10
|
+
* @param {Object} optional config parameter
|
11
|
+
*/
|
12
|
+
class JSONTransport {
|
13
|
+
constructor(options) {
|
14
|
+
options = options || {};
|
15
|
+
|
16
|
+
this.options = options || {};
|
17
|
+
|
18
|
+
this.name = 'JSONTransport';
|
19
|
+
this.version = packageData.version;
|
20
|
+
|
21
|
+
this.logger = shared.getLogger(this.options, {
|
22
|
+
component: this.options.component || 'json-transport'
|
23
|
+
});
|
24
|
+
}
|
25
|
+
|
26
|
+
/**
|
27
|
+
* <p>Compiles a mailcomposer message and forwards it to handler that sends it.</p>
|
28
|
+
*
|
29
|
+
* @param {Object} emailMessage MailComposer object
|
30
|
+
* @param {Function} callback Callback function to run when the sending is completed
|
31
|
+
*/
|
32
|
+
send(mail, done) {
|
33
|
+
// Sendmail strips this header line by itself
|
34
|
+
mail.message.keepBcc = true;
|
35
|
+
|
36
|
+
let envelope = mail.data.envelope || mail.message.getEnvelope();
|
37
|
+
let messageId = mail.message.messageId();
|
38
|
+
|
39
|
+
let recipients = [].concat(envelope.to || []);
|
40
|
+
if (recipients.length > 3) {
|
41
|
+
recipients.push('...and ' + recipients.splice(2).length + ' more');
|
42
|
+
}
|
43
|
+
this.logger.info(
|
44
|
+
{
|
45
|
+
tnx: 'send',
|
46
|
+
messageId
|
47
|
+
},
|
48
|
+
'Composing JSON structure of %s to <%s>',
|
49
|
+
messageId,
|
50
|
+
recipients.join(', ')
|
51
|
+
);
|
52
|
+
|
53
|
+
setImmediate(() => {
|
54
|
+
mail.normalize((err, data) => {
|
55
|
+
if (err) {
|
56
|
+
this.logger.error(
|
57
|
+
{
|
58
|
+
err,
|
59
|
+
tnx: 'send',
|
60
|
+
messageId
|
61
|
+
},
|
62
|
+
'Failed building JSON structure for %s. %s',
|
63
|
+
messageId,
|
64
|
+
err.message
|
65
|
+
);
|
66
|
+
return done(err);
|
67
|
+
}
|
68
|
+
|
69
|
+
delete data.envelope;
|
70
|
+
delete data.normalizedHeaders;
|
71
|
+
|
72
|
+
return done(null, {
|
73
|
+
envelope,
|
74
|
+
messageId,
|
75
|
+
message: this.options.skipEncoding ? data : JSON.stringify(data)
|
76
|
+
});
|
77
|
+
});
|
78
|
+
});
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
module.exports = JSONTransport;
|
@@ -0,0 +1,558 @@
|
|
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
|
+
data = {
|
95
|
+
contentType: attachment.contentType || mimeFuncs.detectMimeType(attachment.filename || attachment.path || attachment.href || 'bin'),
|
96
|
+
contentDisposition: attachment.contentDisposition || (isMessageNode ? 'inline' : 'attachment'),
|
97
|
+
contentTransferEncoding: 'contentTransferEncoding' in attachment ? attachment.contentTransferEncoding : 'base64'
|
98
|
+
};
|
99
|
+
|
100
|
+
if (attachment.filename) {
|
101
|
+
data.filename = attachment.filename;
|
102
|
+
} else if (!isMessageNode && attachment.filename !== false) {
|
103
|
+
data.filename = (attachment.path || attachment.href || '').split('/').pop().split('?').shift() || 'attachment-' + (i + 1);
|
104
|
+
if (data.filename.indexOf('.') < 0) {
|
105
|
+
data.filename += '.' + mimeFuncs.detectExtension(data.contentType);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
if (/^https?:\/\//i.test(attachment.path)) {
|
110
|
+
attachment.href = attachment.path;
|
111
|
+
attachment.path = undefined;
|
112
|
+
}
|
113
|
+
|
114
|
+
if (attachment.cid) {
|
115
|
+
data.cid = attachment.cid;
|
116
|
+
}
|
117
|
+
|
118
|
+
if (attachment.raw) {
|
119
|
+
data.raw = attachment.raw;
|
120
|
+
} else if (attachment.path) {
|
121
|
+
data.content = {
|
122
|
+
path: attachment.path
|
123
|
+
};
|
124
|
+
} else if (attachment.href) {
|
125
|
+
data.content = {
|
126
|
+
href: attachment.href,
|
127
|
+
httpHeaders: attachment.httpHeaders
|
128
|
+
};
|
129
|
+
} else {
|
130
|
+
data.content = attachment.content || '';
|
131
|
+
}
|
132
|
+
|
133
|
+
if (attachment.encoding) {
|
134
|
+
data.encoding = attachment.encoding;
|
135
|
+
}
|
136
|
+
|
137
|
+
if (attachment.headers) {
|
138
|
+
data.headers = attachment.headers;
|
139
|
+
}
|
140
|
+
|
141
|
+
return data;
|
142
|
+
});
|
143
|
+
|
144
|
+
if (this.mail.icalEvent) {
|
145
|
+
if (
|
146
|
+
typeof this.mail.icalEvent === 'object' &&
|
147
|
+
(this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
|
148
|
+
) {
|
149
|
+
icalEvent = this.mail.icalEvent;
|
150
|
+
} else {
|
151
|
+
icalEvent = {
|
152
|
+
content: this.mail.icalEvent
|
153
|
+
};
|
154
|
+
}
|
155
|
+
|
156
|
+
eventObject = {};
|
157
|
+
Object.keys(icalEvent).forEach(key => {
|
158
|
+
eventObject[key] = icalEvent[key];
|
159
|
+
});
|
160
|
+
|
161
|
+
eventObject.contentType = 'application/ics';
|
162
|
+
if (!eventObject.headers) {
|
163
|
+
eventObject.headers = {};
|
164
|
+
}
|
165
|
+
eventObject.filename = eventObject.filename || 'invite.ics';
|
166
|
+
eventObject.headers['Content-Disposition'] = 'attachment';
|
167
|
+
eventObject.headers['Content-Transfer-Encoding'] = 'base64';
|
168
|
+
}
|
169
|
+
|
170
|
+
if (!findRelated) {
|
171
|
+
return {
|
172
|
+
attached: attachments.concat(eventObject || []),
|
173
|
+
related: []
|
174
|
+
};
|
175
|
+
} else {
|
176
|
+
return {
|
177
|
+
attached: attachments.filter(attachment => !attachment.cid).concat(eventObject || []),
|
178
|
+
related: attachments.filter(attachment => !!attachment.cid)
|
179
|
+
};
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
/**
|
184
|
+
* List alternatives. Resulting objects can be used as input for MimeNode nodes
|
185
|
+
*
|
186
|
+
* @returns {Array} An array of alternative elements. Includes the `text` and `html` values as well
|
187
|
+
*/
|
188
|
+
getAlternatives() {
|
189
|
+
let alternatives = [],
|
190
|
+
text,
|
191
|
+
html,
|
192
|
+
watchHtml,
|
193
|
+
amp,
|
194
|
+
icalEvent,
|
195
|
+
eventObject;
|
196
|
+
|
197
|
+
if (this.mail.text) {
|
198
|
+
if (typeof this.mail.text === 'object' && (this.mail.text.content || this.mail.text.path || this.mail.text.href || this.mail.text.raw)) {
|
199
|
+
text = this.mail.text;
|
200
|
+
} else {
|
201
|
+
text = {
|
202
|
+
content: this.mail.text
|
203
|
+
};
|
204
|
+
}
|
205
|
+
text.contentType = 'text/plain; charset=utf-8';
|
206
|
+
}
|
207
|
+
|
208
|
+
if (this.mail.watchHtml) {
|
209
|
+
if (
|
210
|
+
typeof this.mail.watchHtml === 'object' &&
|
211
|
+
(this.mail.watchHtml.content || this.mail.watchHtml.path || this.mail.watchHtml.href || this.mail.watchHtml.raw)
|
212
|
+
) {
|
213
|
+
watchHtml = this.mail.watchHtml;
|
214
|
+
} else {
|
215
|
+
watchHtml = {
|
216
|
+
content: this.mail.watchHtml
|
217
|
+
};
|
218
|
+
}
|
219
|
+
watchHtml.contentType = 'text/watch-html; charset=utf-8';
|
220
|
+
}
|
221
|
+
|
222
|
+
if (this.mail.amp) {
|
223
|
+
if (typeof this.mail.amp === 'object' && (this.mail.amp.content || this.mail.amp.path || this.mail.amp.href || this.mail.amp.raw)) {
|
224
|
+
amp = this.mail.amp;
|
225
|
+
} else {
|
226
|
+
amp = {
|
227
|
+
content: this.mail.amp
|
228
|
+
};
|
229
|
+
}
|
230
|
+
amp.contentType = 'text/x-amp-html; charset=utf-8';
|
231
|
+
}
|
232
|
+
|
233
|
+
// NB! when including attachments with a calendar alternative you might end up in a blank screen on some clients
|
234
|
+
if (this.mail.icalEvent) {
|
235
|
+
if (
|
236
|
+
typeof this.mail.icalEvent === 'object' &&
|
237
|
+
(this.mail.icalEvent.content || this.mail.icalEvent.path || this.mail.icalEvent.href || this.mail.icalEvent.raw)
|
238
|
+
) {
|
239
|
+
icalEvent = this.mail.icalEvent;
|
240
|
+
} else {
|
241
|
+
icalEvent = {
|
242
|
+
content: this.mail.icalEvent
|
243
|
+
};
|
244
|
+
}
|
245
|
+
|
246
|
+
eventObject = {};
|
247
|
+
Object.keys(icalEvent).forEach(key => {
|
248
|
+
eventObject[key] = icalEvent[key];
|
249
|
+
});
|
250
|
+
|
251
|
+
if (eventObject.content && typeof eventObject.content === 'object') {
|
252
|
+
// we are going to have the same attachment twice, so mark this to be
|
253
|
+
// resolved just once
|
254
|
+
eventObject.content._resolve = true;
|
255
|
+
}
|
256
|
+
|
257
|
+
eventObject.filename = false;
|
258
|
+
eventObject.contentType = 'text/calendar; charset=utf-8; method=' + (eventObject.method || 'PUBLISH').toString().trim().toUpperCase();
|
259
|
+
if (!eventObject.headers) {
|
260
|
+
eventObject.headers = {};
|
261
|
+
}
|
262
|
+
}
|
263
|
+
|
264
|
+
if (this.mail.html) {
|
265
|
+
if (typeof this.mail.html === 'object' && (this.mail.html.content || this.mail.html.path || this.mail.html.href || this.mail.html.raw)) {
|
266
|
+
html = this.mail.html;
|
267
|
+
} else {
|
268
|
+
html = {
|
269
|
+
content: this.mail.html
|
270
|
+
};
|
271
|
+
}
|
272
|
+
html.contentType = 'text/html; charset=utf-8';
|
273
|
+
}
|
274
|
+
|
275
|
+
[]
|
276
|
+
.concat(text || [])
|
277
|
+
.concat(watchHtml || [])
|
278
|
+
.concat(amp || [])
|
279
|
+
.concat(html || [])
|
280
|
+
.concat(eventObject || [])
|
281
|
+
.concat(this.mail.alternatives || [])
|
282
|
+
.forEach(alternative => {
|
283
|
+
let data;
|
284
|
+
|
285
|
+
if (/^data:/i.test(alternative.path || alternative.href)) {
|
286
|
+
alternative = this._processDataUrl(alternative);
|
287
|
+
}
|
288
|
+
|
289
|
+
data = {
|
290
|
+
contentType: alternative.contentType || mimeFuncs.detectMimeType(alternative.filename || alternative.path || alternative.href || 'txt'),
|
291
|
+
contentTransferEncoding: alternative.contentTransferEncoding
|
292
|
+
};
|
293
|
+
|
294
|
+
if (alternative.filename) {
|
295
|
+
data.filename = alternative.filename;
|
296
|
+
}
|
297
|
+
|
298
|
+
if (/^https?:\/\//i.test(alternative.path)) {
|
299
|
+
alternative.href = alternative.path;
|
300
|
+
alternative.path = undefined;
|
301
|
+
}
|
302
|
+
|
303
|
+
if (alternative.raw) {
|
304
|
+
data.raw = alternative.raw;
|
305
|
+
} else if (alternative.path) {
|
306
|
+
data.content = {
|
307
|
+
path: alternative.path
|
308
|
+
};
|
309
|
+
} else if (alternative.href) {
|
310
|
+
data.content = {
|
311
|
+
href: alternative.href
|
312
|
+
};
|
313
|
+
} else {
|
314
|
+
data.content = alternative.content || '';
|
315
|
+
}
|
316
|
+
|
317
|
+
if (alternative.encoding) {
|
318
|
+
data.encoding = alternative.encoding;
|
319
|
+
}
|
320
|
+
|
321
|
+
if (alternative.headers) {
|
322
|
+
data.headers = alternative.headers;
|
323
|
+
}
|
324
|
+
|
325
|
+
alternatives.push(data);
|
326
|
+
});
|
327
|
+
|
328
|
+
return alternatives;
|
329
|
+
}
|
330
|
+
|
331
|
+
/**
|
332
|
+
* Builds multipart/mixed node. It should always contain different type of elements on the same level
|
333
|
+
* eg. text + attachments
|
334
|
+
*
|
335
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
336
|
+
* @returns {Object} MimeNode node element
|
337
|
+
*/
|
338
|
+
_createMixed(parentNode) {
|
339
|
+
let node;
|
340
|
+
|
341
|
+
if (!parentNode) {
|
342
|
+
node = new MimeNode('multipart/mixed', {
|
343
|
+
baseBoundary: this.mail.baseBoundary,
|
344
|
+
textEncoding: this.mail.textEncoding,
|
345
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
346
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
347
|
+
disableFileAccess: this.mail.disableFileAccess,
|
348
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
349
|
+
newline: this.mail.newline
|
350
|
+
});
|
351
|
+
} else {
|
352
|
+
node = parentNode.createChild('multipart/mixed', {
|
353
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
354
|
+
disableFileAccess: this.mail.disableFileAccess,
|
355
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
356
|
+
newline: this.mail.newline
|
357
|
+
});
|
358
|
+
}
|
359
|
+
|
360
|
+
if (this._useAlternative) {
|
361
|
+
this._createAlternative(node);
|
362
|
+
} else if (this._useRelated) {
|
363
|
+
this._createRelated(node);
|
364
|
+
}
|
365
|
+
|
366
|
+
[]
|
367
|
+
.concat((!this._useAlternative && this._alternatives) || [])
|
368
|
+
.concat(this._attachments.attached || [])
|
369
|
+
.forEach(element => {
|
370
|
+
// if the element is a html node from related subpart then ignore it
|
371
|
+
if (!this._useRelated || element !== this._htmlNode) {
|
372
|
+
this._createContentNode(node, element);
|
373
|
+
}
|
374
|
+
});
|
375
|
+
|
376
|
+
return node;
|
377
|
+
}
|
378
|
+
|
379
|
+
/**
|
380
|
+
* Builds multipart/alternative node. It should always contain same type of elements on the same level
|
381
|
+
* eg. text + html view of the same data
|
382
|
+
*
|
383
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
384
|
+
* @returns {Object} MimeNode node element
|
385
|
+
*/
|
386
|
+
_createAlternative(parentNode) {
|
387
|
+
let node;
|
388
|
+
|
389
|
+
if (!parentNode) {
|
390
|
+
node = new MimeNode('multipart/alternative', {
|
391
|
+
baseBoundary: this.mail.baseBoundary,
|
392
|
+
textEncoding: this.mail.textEncoding,
|
393
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
394
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
395
|
+
disableFileAccess: this.mail.disableFileAccess,
|
396
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
397
|
+
newline: this.mail.newline
|
398
|
+
});
|
399
|
+
} else {
|
400
|
+
node = parentNode.createChild('multipart/alternative', {
|
401
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
402
|
+
disableFileAccess: this.mail.disableFileAccess,
|
403
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
404
|
+
newline: this.mail.newline
|
405
|
+
});
|
406
|
+
}
|
407
|
+
|
408
|
+
this._alternatives.forEach(alternative => {
|
409
|
+
if (this._useRelated && this._htmlNode === alternative) {
|
410
|
+
this._createRelated(node);
|
411
|
+
} else {
|
412
|
+
this._createContentNode(node, alternative);
|
413
|
+
}
|
414
|
+
});
|
415
|
+
|
416
|
+
return node;
|
417
|
+
}
|
418
|
+
|
419
|
+
/**
|
420
|
+
* Builds multipart/related node. It should always contain html node with related attachments
|
421
|
+
*
|
422
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
423
|
+
* @returns {Object} MimeNode node element
|
424
|
+
*/
|
425
|
+
_createRelated(parentNode) {
|
426
|
+
let node;
|
427
|
+
|
428
|
+
if (!parentNode) {
|
429
|
+
node = new MimeNode('multipart/related; type="text/html"', {
|
430
|
+
baseBoundary: this.mail.baseBoundary,
|
431
|
+
textEncoding: this.mail.textEncoding,
|
432
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
433
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
434
|
+
disableFileAccess: this.mail.disableFileAccess,
|
435
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
436
|
+
newline: this.mail.newline
|
437
|
+
});
|
438
|
+
} else {
|
439
|
+
node = parentNode.createChild('multipart/related; type="text/html"', {
|
440
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
441
|
+
disableFileAccess: this.mail.disableFileAccess,
|
442
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
443
|
+
newline: this.mail.newline
|
444
|
+
});
|
445
|
+
}
|
446
|
+
|
447
|
+
this._createContentNode(node, this._htmlNode);
|
448
|
+
|
449
|
+
this._attachments.related.forEach(alternative => this._createContentNode(node, alternative));
|
450
|
+
|
451
|
+
return node;
|
452
|
+
}
|
453
|
+
|
454
|
+
/**
|
455
|
+
* Creates a regular node with contents
|
456
|
+
*
|
457
|
+
* @param {Object} parentNode Parent for this note. If it does not exist, a root node is created
|
458
|
+
* @param {Object} element Node data
|
459
|
+
* @returns {Object} MimeNode node element
|
460
|
+
*/
|
461
|
+
_createContentNode(parentNode, element) {
|
462
|
+
element = element || {};
|
463
|
+
element.content = element.content || '';
|
464
|
+
|
465
|
+
let node;
|
466
|
+
let encoding = (element.encoding || 'utf8')
|
467
|
+
.toString()
|
468
|
+
.toLowerCase()
|
469
|
+
.replace(/[-_\s]/g, '');
|
470
|
+
|
471
|
+
if (!parentNode) {
|
472
|
+
node = new MimeNode(element.contentType, {
|
473
|
+
filename: element.filename,
|
474
|
+
baseBoundary: this.mail.baseBoundary,
|
475
|
+
textEncoding: this.mail.textEncoding,
|
476
|
+
boundaryPrefix: this.mail.boundaryPrefix,
|
477
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
478
|
+
disableFileAccess: this.mail.disableFileAccess,
|
479
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
480
|
+
newline: this.mail.newline
|
481
|
+
});
|
482
|
+
} else {
|
483
|
+
node = parentNode.createChild(element.contentType, {
|
484
|
+
filename: element.filename,
|
485
|
+
textEncoding: this.mail.textEncoding,
|
486
|
+
disableUrlAccess: this.mail.disableUrlAccess,
|
487
|
+
disableFileAccess: this.mail.disableFileAccess,
|
488
|
+
normalizeHeaderKey: this.mail.normalizeHeaderKey,
|
489
|
+
newline: this.mail.newline
|
490
|
+
});
|
491
|
+
}
|
492
|
+
|
493
|
+
// add custom headers
|
494
|
+
if (element.headers) {
|
495
|
+
node.addHeader(element.headers);
|
496
|
+
}
|
497
|
+
|
498
|
+
if (element.cid) {
|
499
|
+
node.setHeader('Content-Id', '<' + element.cid.replace(/[<>]/g, '') + '>');
|
500
|
+
}
|
501
|
+
|
502
|
+
if (element.contentTransferEncoding) {
|
503
|
+
node.setHeader('Content-Transfer-Encoding', element.contentTransferEncoding);
|
504
|
+
} else if (this.mail.encoding && /^text\//i.test(element.contentType)) {
|
505
|
+
node.setHeader('Content-Transfer-Encoding', this.mail.encoding);
|
506
|
+
}
|
507
|
+
|
508
|
+
if (!/^text\//i.test(element.contentType) || element.contentDisposition) {
|
509
|
+
node.setHeader('Content-Disposition', element.contentDisposition || (element.cid ? 'inline' : 'attachment'));
|
510
|
+
}
|
511
|
+
|
512
|
+
if (typeof element.content === 'string' && !['utf8', 'usascii', 'ascii'].includes(encoding)) {
|
513
|
+
element.content = Buffer.from(element.content, encoding);
|
514
|
+
}
|
515
|
+
|
516
|
+
// prefer pregenerated raw content
|
517
|
+
if (element.raw) {
|
518
|
+
node.setRaw(element.raw);
|
519
|
+
} else {
|
520
|
+
node.setContent(element.content);
|
521
|
+
}
|
522
|
+
|
523
|
+
return node;
|
524
|
+
}
|
525
|
+
|
526
|
+
/**
|
527
|
+
* Parses data uri and converts it to a Buffer
|
528
|
+
*
|
529
|
+
* @param {Object} element Content element
|
530
|
+
* @return {Object} Parsed element
|
531
|
+
*/
|
532
|
+
_processDataUrl(element) {
|
533
|
+
let parts = (element.path || element.href).match(/^data:((?:[^;]*;)*(?:[^,]*)),(.*)$/i);
|
534
|
+
if (!parts) {
|
535
|
+
return element;
|
536
|
+
}
|
537
|
+
|
538
|
+
element.content = /\bbase64$/i.test(parts[1]) ? Buffer.from(parts[2], 'base64') : Buffer.from(decodeURIComponent(parts[2]));
|
539
|
+
|
540
|
+
if ('path' in element) {
|
541
|
+
element.path = false;
|
542
|
+
}
|
543
|
+
|
544
|
+
if ('href' in element) {
|
545
|
+
element.href = false;
|
546
|
+
}
|
547
|
+
|
548
|
+
parts[1].split(';').forEach(item => {
|
549
|
+
if (/^\w+\/[^/]+$/i.test(item)) {
|
550
|
+
element.contentType = element.contentType || item.toLowerCase();
|
551
|
+
}
|
552
|
+
});
|
553
|
+
|
554
|
+
return element;
|
555
|
+
}
|
556
|
+
}
|
557
|
+
|
558
|
+
module.exports = MailComposer;
|