eml-parser-qaap 1.1.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.
@@ -0,0 +1,1404 @@
1
+ import { Base64 } from 'js-base64';
2
+ export { Base64 } from 'js-base64';
3
+ import { TextEncoder, TextDecoder } from '@sinonjs/text-encoding';
4
+
5
+ /**
6
+ * Encodes an unicode string into an Uint8Array object as UTF-8
7
+ *
8
+ * @param {String} str String to be encoded
9
+ * @return {Uint8Array} UTF-8 encoded typed array
10
+ */
11
+ var encode = function (str, fromCharset) {
12
+ if (fromCharset === void 0) { fromCharset = 'utf-8'; }
13
+ return new TextEncoder(fromCharset).encode(str);
14
+ };
15
+ var arr2str = function (arr) {
16
+ var CHUNK_SZ = 0x8000;
17
+ var strs = [];
18
+ for (var i = 0; i < arr.length; i += CHUNK_SZ) {
19
+ strs.push(String.fromCharCode.apply(null, arr.subarray(i, i + CHUNK_SZ)));
20
+ }
21
+ return strs.join('');
22
+ };
23
+ /**
24
+ * Decodes a string from Uint8Array to an unicode string using specified encoding
25
+ *
26
+ * @param {Uint8Array} buf Binary data to be decoded
27
+ * @param {String} Binary data is decoded into string using this charset
28
+ * @return {String} Decoded string
29
+ */
30
+ function decode(buf, fromCharset) {
31
+ if (fromCharset === void 0) { fromCharset = 'utf-8'; }
32
+ var charsets = [
33
+ { charset: normalizeCharset(fromCharset), fatal: false },
34
+ { charset: 'utf-8', fatal: true },
35
+ { charset: 'iso-8859-15', fatal: false },
36
+ ];
37
+ for (var _i = 0, charsets_1 = charsets; _i < charsets_1.length; _i++) {
38
+ var _a = charsets_1[_i], charset = _a.charset, fatal = _a.fatal;
39
+ try {
40
+ return new TextDecoder(charset, { fatal: fatal }).decode(buf);
41
+ // eslint-disable-next-line no-empty
42
+ }
43
+ catch (e) { }
44
+ }
45
+ return arr2str(buf); // all else fails, treat it as binary
46
+ }
47
+ /**
48
+ * Convert a string from specific encoding to UTF-8 Uint8Array
49
+ *
50
+ * @param {String|Uint8Array} data Data to be encoded
51
+ * @param {String} Source encoding for the string (optional for data of type String)
52
+ * @return {Uint8Array} UTF-8 encoded typed array
53
+ */
54
+ var convert = function (data, fromCharset) {
55
+ return typeof data === 'string' ? encode(data) : encode(decode(data, fromCharset));
56
+ };
57
+ function normalizeCharset(charset) {
58
+ if (charset === void 0) { charset = 'utf-8'; }
59
+ var match;
60
+ if ((match = charset.match(/^utf[-_]?(\d+)$/i))) {
61
+ return 'UTF-' + match[1];
62
+ }
63
+ if ((match = charset.match(/^win[-_]?(\d+)$/i))) {
64
+ return 'WINDOWS-' + match[1];
65
+ }
66
+ if ((match = charset.match(/^latin[-_]?(\d+)$/i))) {
67
+ return 'ISO-8859-' + match[1];
68
+ }
69
+ return charset;
70
+ }
71
+
72
+ /**
73
+ * Gets the boundary name
74
+ * @param contentType - string
75
+ */
76
+ function getBoundary(contentType) {
77
+ var match = /(?:B|b)oundary=(?:'|")?(.+?)(?:'|")?(\s*;[\s\S]*)?$/g.exec(contentType);
78
+ return match ? match[1] : undefined;
79
+ }
80
+ //Gets the character encoding name for iconv, e.g. 'iso-8859-2' -> 'iso88592'
81
+ function getCharsetName(charset) {
82
+ return charset.toLowerCase().replace(/[^0-9a-z]/g, '');
83
+ }
84
+ //Generates a random id
85
+ function guid() {
86
+ return 'xxxxxxxxxxxx-4xxx-yxxx-xxxxxxxxxxxx'
87
+ .replace(/[xy]/g, function (c) {
88
+ var r = (Math.random() * 16) | 0, v = c == 'x' ? r : (r & 0x3) | 0x8;
89
+ return v.toString(16);
90
+ })
91
+ .replace('-', '');
92
+ }
93
+ //Word-wrap the string 's' to 'i' chars per row
94
+ function wrap(s, i) {
95
+ var a = [];
96
+ do {
97
+ a.push(s.substring(0, i));
98
+ } while ((s = s.substring(i, s.length)) != '');
99
+ return a.join('\r\n');
100
+ }
101
+ /**
102
+ * Decodes mime encoded string to an unicode string
103
+ *
104
+ * @param {String} str Mime encoded string
105
+ * @param {String} [fromCharset='UTF-8'] Source encoding
106
+ * @return {String} Decoded unicode string
107
+ */
108
+ function mimeDecode(str, fromCharset) {
109
+ if (str === void 0) { str = ''; }
110
+ if (fromCharset === void 0) { fromCharset = 'UTF-8'; }
111
+ var encodedBytesCount = (str.match(/=[\da-fA-F]{2}/g) || []).length;
112
+ var buffer = new Uint8Array(str.length - encodedBytesCount * 2);
113
+ for (var i = 0, len = str.length, bufferPos = 0; i < len; i++) {
114
+ var hex = str.substr(i + 1, 2);
115
+ var chr = str.charAt(i);
116
+ if (chr === '=' && hex && /[\da-fA-F]{2}/.test(hex)) {
117
+ buffer[bufferPos++] = parseInt(hex, 16);
118
+ i += 2;
119
+ }
120
+ else {
121
+ buffer[bufferPos++] = chr.charCodeAt(0);
122
+ }
123
+ }
124
+ return decode(buffer, fromCharset);
125
+ }
126
+ /**
127
+ * converting strings from gbk to utf-8
128
+ */
129
+ var GB2312UTF8 = {
130
+ Dig2Dec: function (s) {
131
+ var retV = 0;
132
+ if (s.length == 4) {
133
+ for (var i = 0; i < 4; i++) {
134
+ retV += eval(s.charAt(i)) * Math.pow(2, 3 - i);
135
+ }
136
+ return retV;
137
+ }
138
+ return -1;
139
+ },
140
+ Hex2Utf8: function (s) {
141
+ var retS = '';
142
+ var tempS = '';
143
+ var ss = '';
144
+ if (s.length == 16) {
145
+ tempS = '1110' + s.substring(0, 4);
146
+ tempS += '10' + s.substring(4, 10);
147
+ tempS += '10' + s.substring(10, 16);
148
+ var sss = '0123456789ABCDEF';
149
+ for (var i = 0; i < 3; i++) {
150
+ retS += '%';
151
+ ss = tempS.substring(i * 8, (eval(i.toString()) + 1) * 8);
152
+ retS += sss.charAt(this.Dig2Dec(ss.substring(0, 4)));
153
+ retS += sss.charAt(this.Dig2Dec(ss.substring(4, 8)));
154
+ }
155
+ return retS;
156
+ }
157
+ return '';
158
+ },
159
+ Dec2Dig: function (n1) {
160
+ var s = '';
161
+ var n2 = 0;
162
+ for (var i = 0; i < 4; i++) {
163
+ n2 = Math.pow(2, 3 - i);
164
+ if (n1 >= n2) {
165
+ s += '1';
166
+ n1 = n1 - n2;
167
+ }
168
+ else {
169
+ s += '0';
170
+ }
171
+ }
172
+ return s;
173
+ },
174
+ Str2Hex: function (s) {
175
+ var c = '';
176
+ var n;
177
+ var ss = '0123456789ABCDEF';
178
+ var digS = '';
179
+ for (var i = 0; i < s.length; i++) {
180
+ c = s.charAt(i);
181
+ n = ss.indexOf(c);
182
+ digS += this.Dec2Dig(eval(n.toString()));
183
+ }
184
+ return digS;
185
+ },
186
+ GB2312ToUTF8: function (s1) {
187
+ var s = escape(s1);
188
+ var sa = s.split('%');
189
+ var retV = '';
190
+ if (sa[0] != '') {
191
+ retV = sa[0];
192
+ }
193
+ for (var i = 1; i < sa.length; i++) {
194
+ if (sa[i].substring(0, 1) == 'u') {
195
+ retV += this.Hex2Utf8(this.Str2Hex(sa[i].substring(1, 5)));
196
+ if (sa[i].length) {
197
+ retV += sa[i].substring(5);
198
+ }
199
+ }
200
+ else {
201
+ retV += unescape('%' + sa[i]);
202
+ if (sa[i].length) {
203
+ retV += sa[i].substring(5);
204
+ }
205
+ }
206
+ }
207
+ return retV;
208
+ },
209
+ UTF8ToGB2312: function (str1) {
210
+ var substr = '';
211
+ var a = '';
212
+ var b = '';
213
+ var c = '';
214
+ var i = -1;
215
+ i = str1.indexOf('%');
216
+ if (i == -1) {
217
+ return str1;
218
+ }
219
+ while (i != -1) {
220
+ if (i < 3) {
221
+ substr = substr + str1.substr(0, i - 1);
222
+ str1 = str1.substr(i + 1, str1.length - i);
223
+ a = str1.substr(0, 2);
224
+ str1 = str1.substr(2, str1.length - 2);
225
+ if ((parseInt('0x' + a) & 0x80) === 0) {
226
+ substr = substr + String.fromCharCode(parseInt('0x' + a));
227
+ }
228
+ else if ((parseInt('0x' + a) & 0xe0) === 0xc0) {
229
+ //two byte
230
+ b = str1.substr(1, 2);
231
+ str1 = str1.substr(3, str1.length - 3);
232
+ var widechar = (parseInt('0x' + a) & 0x1f) << 6;
233
+ widechar = widechar | (parseInt('0x' + b) & 0x3f);
234
+ substr = substr + String.fromCharCode(widechar);
235
+ }
236
+ else {
237
+ b = str1.substr(1, 2);
238
+ str1 = str1.substr(3, str1.length - 3);
239
+ c = str1.substr(1, 2);
240
+ str1 = str1.substr(3, str1.length - 3);
241
+ var widechar = (parseInt('0x' + a) & 0x0f) << 12;
242
+ widechar = widechar | ((parseInt('0x' + b) & 0x3f) << 6);
243
+ widechar = widechar | (parseInt('0x' + c) & 0x3f);
244
+ substr = substr + String.fromCharCode(widechar);
245
+ }
246
+ }
247
+ else {
248
+ substr = substr + str1.substring(0, i);
249
+ str1 = str1.substring(i);
250
+ }
251
+ i = str1.indexOf('%');
252
+ }
253
+ return substr + str1;
254
+ },
255
+ };
256
+
257
+ /**
258
+ * Converts tokens for a single address into an address object
259
+ *
260
+ * @param {Array} tokens Tokens object
261
+ * @return {Object} Address object
262
+ */
263
+ function _handleAddress(tokens) {
264
+ var token;
265
+ var isGroup = false;
266
+ var state = 'text';
267
+ var address;
268
+ var addresses = [];
269
+ var data = {
270
+ address: [],
271
+ comment: [],
272
+ group: [],
273
+ text: [],
274
+ };
275
+ var i;
276
+ var len;
277
+ // Filter out <addresses>, (comments) and regular text
278
+ for (i = 0, len = tokens.length; i < len; i++) {
279
+ token = tokens[i];
280
+ if (token.type === 'operator') {
281
+ switch (token.value) {
282
+ case '<':
283
+ state = 'address';
284
+ break;
285
+ case '(':
286
+ state = 'comment';
287
+ break;
288
+ case ':':
289
+ state = 'group';
290
+ isGroup = true;
291
+ break;
292
+ default:
293
+ state = 'text';
294
+ }
295
+ }
296
+ else if (token.value) {
297
+ if (state === 'address') {
298
+ // handle use case where unquoted name includes a "<"
299
+ // Apple Mail truncates everything between an unexpected < and an address
300
+ // and so will we
301
+ token.value = token.value.replace(/^[^<]*<\s*/, '');
302
+ }
303
+ data[state].push(token.value);
304
+ }
305
+ }
306
+ // If there is no text but a comment, replace the two
307
+ if (!data.text.length && data.comment.length) {
308
+ data.text = data.comment;
309
+ data.comment = [];
310
+ }
311
+ if (isGroup) {
312
+ // http://tools.ietf.org/html/rfc2822#appendix-A.1.3
313
+ data.text = data.text.join(' ');
314
+ addresses.push({
315
+ name: data.text || (address && address.name),
316
+ group: data.group.length ? addressparser(data.group.join(',')) : [],
317
+ });
318
+ }
319
+ else {
320
+ // If no address was found, try to detect one from regular text
321
+ if (!data.address.length && data.text.length) {
322
+ for (i = data.text.length - 1; i >= 0; i--) {
323
+ if (data.text[i].match(/^[^@\s]+@[^@\s]+$/)) {
324
+ data.address = data.text.splice(i, 1);
325
+ break;
326
+ }
327
+ }
328
+ var _regexHandler = function (address) {
329
+ if (!data.address.length) {
330
+ data.address = [address.trim()];
331
+ return ' ';
332
+ }
333
+ else {
334
+ return address;
335
+ }
336
+ };
337
+ // still no address
338
+ if (!data.address.length) {
339
+ for (i = data.text.length - 1; i >= 0; i--) {
340
+ // fixed the regex to parse email address correctly when email address has more than one @
341
+ data.text[i] = data.text[i].replace(/\s*\b[^@\s]+@[^\s]+\b\s*/, _regexHandler).trim();
342
+ if (data.address.length) {
343
+ break;
344
+ }
345
+ }
346
+ }
347
+ }
348
+ // If there's still is no text but a comment exixts, replace the two
349
+ if (!data.text.length && data.comment.length) {
350
+ data.text = data.comment;
351
+ data.comment = [];
352
+ }
353
+ // Keep only the first address occurence, push others to regular text
354
+ if (data.address.length > 1) {
355
+ data.text = data.text.concat(data.address.splice(1));
356
+ }
357
+ // Join values with spaces
358
+ data.text = data.text.join(' ');
359
+ data.address = data.address.join(' ');
360
+ if (!data.address && isGroup) {
361
+ return [];
362
+ }
363
+ else {
364
+ address = {
365
+ address: data.address || data.text || '',
366
+ name: data.text || data.address || '',
367
+ };
368
+ if (address.address === address.name) {
369
+ if ((address.address || '').match(/@/)) {
370
+ address.name = '';
371
+ }
372
+ else {
373
+ address.address = '';
374
+ }
375
+ }
376
+ addresses.push(address);
377
+ }
378
+ }
379
+ return addresses;
380
+ }
381
+ /**
382
+ * Creates a Tokenizer object for tokenizing address field strings
383
+ *
384
+ * @constructor
385
+ * @param {String} str Address field string
386
+ */
387
+ var Tokenizer = /** @class */ (function () {
388
+ function Tokenizer(str) {
389
+ this.str = (str || '').toString();
390
+ this.operatorCurrent = '';
391
+ this.operatorExpecting = '';
392
+ this.node = null;
393
+ this.escaped = false;
394
+ this.list = [];
395
+ /**
396
+ * Operator tokens and which tokens are expected to end the sequence
397
+ */
398
+ this.operators = {
399
+ '"': '"',
400
+ '(': ')',
401
+ '<': '>',
402
+ ',': '',
403
+ ':': ';',
404
+ // Semicolons are not a legal delimiter per the RFC2822 grammar other
405
+ // than for terminating a group, but they are also not valid for any
406
+ // other use in this context. Given that some mail clients have
407
+ // historically allowed the semicolon as a delimiter equivalent to the
408
+ // comma in their UI, it makes sense to treat them the same as a comma
409
+ // when used outside of a group.
410
+ ';': '',
411
+ };
412
+ }
413
+ /**
414
+ * Tokenizes the original input string
415
+ *
416
+ * @return {Array} An array of operator|text tokens
417
+ */
418
+ Tokenizer.prototype.tokenize = function () {
419
+ var chr, list = [];
420
+ for (var i = 0, len = this.str.length; i < len; i++) {
421
+ chr = this.str.charAt(i);
422
+ this.checkChar(chr);
423
+ }
424
+ this.list.forEach(function (node) {
425
+ node.value = (node.value || '').toString().trim();
426
+ if (node.value) {
427
+ list.push(node);
428
+ }
429
+ });
430
+ return list;
431
+ };
432
+ /**
433
+ * Checks if a character is an operator or text and acts accordingly
434
+ *
435
+ * @param {String} chr Character from the address field
436
+ */
437
+ Tokenizer.prototype.checkChar = function (chr) {
438
+ if (this.escaped) ;
439
+ else if (chr === this.operatorExpecting) {
440
+ this.node = {
441
+ type: 'operator',
442
+ value: chr,
443
+ };
444
+ this.list.push(this.node);
445
+ this.node = null;
446
+ this.operatorExpecting = '';
447
+ this.escaped = false;
448
+ return;
449
+ }
450
+ else if (!this.operatorExpecting && chr in this.operators) {
451
+ this.node = {
452
+ type: 'operator',
453
+ value: chr,
454
+ };
455
+ this.list.push(this.node);
456
+ this.node = null;
457
+ this.operatorExpecting = this.operators[chr];
458
+ this.escaped = false;
459
+ return;
460
+ }
461
+ else if (['"', "'"].includes(this.operatorExpecting) && chr === '\\') {
462
+ this.escaped = true;
463
+ return;
464
+ }
465
+ if (!this.node) {
466
+ this.node = {
467
+ type: 'text',
468
+ value: '',
469
+ };
470
+ this.list.push(this.node);
471
+ }
472
+ if (chr === '\n') {
473
+ // Convert newlines to spaces. Carriage return is ignored as \r and \n usually
474
+ // go together anyway and there already is a WS for \n. Lone \r means something is fishy.
475
+ chr = ' ';
476
+ }
477
+ if (chr.charCodeAt(0) >= 0x21 || [' ', '\t'].includes(chr)) {
478
+ // skip command bytes
479
+ this.node.value += chr;
480
+ }
481
+ this.escaped = false;
482
+ };
483
+ return Tokenizer;
484
+ }());
485
+ /**
486
+ * Parses structured e-mail addresses from an address field
487
+ *
488
+ * Example:
489
+ *
490
+ * 'Name <address@domain>'
491
+ *
492
+ * will be converted to
493
+ *
494
+ * [{name: 'Name', address: 'address@domain'}]
495
+ *
496
+ * @param {String} str Address field
497
+ * @return {Array} An array of address objects
498
+ */
499
+ function addressparser(str, options) {
500
+ options = options || {};
501
+ var tokenizer = new Tokenizer(str);
502
+ var tokens = tokenizer.tokenize();
503
+ var addresses = [];
504
+ var address = [];
505
+ var parsedAddresses = [];
506
+ tokens.forEach(function (token) {
507
+ if (token.type === 'operator' && (token.value === ',' || token.value === ';')) {
508
+ if (address.length) {
509
+ addresses.push(address);
510
+ }
511
+ address = [];
512
+ }
513
+ else {
514
+ address.push(token);
515
+ }
516
+ });
517
+ if (address.length) {
518
+ addresses.push(address);
519
+ }
520
+ addresses.forEach(function (address) {
521
+ address = _handleAddress(address);
522
+ if (address.length) {
523
+ parsedAddresses = parsedAddresses.concat(address);
524
+ }
525
+ });
526
+ if (options.flatten) {
527
+ var addresses_1 = [];
528
+ var walkAddressList_1 = function (list) {
529
+ list.forEach(function (address) {
530
+ if (address.group) {
531
+ return walkAddressList_1(address.group);
532
+ }
533
+ else {
534
+ addresses_1.push(address);
535
+ }
536
+ });
537
+ };
538
+ walkAddressList_1(parsedAddresses);
539
+ return addresses_1;
540
+ }
541
+ return parsedAddresses;
542
+ }
543
+
544
+ /**
545
+ * @author superchow
546
+ * @emil superchow@live.cn
547
+ */
548
+ /**
549
+ * log for test
550
+ */
551
+ var verbose = false;
552
+ var defaultCharset = 'utf-8';
553
+ /**
554
+ * create a boundary
555
+ */
556
+ function createBoundary() {
557
+ return '----=' + guid();
558
+ }
559
+ /**
560
+ * Builds e-mail address string, e.g. { name: 'PayPal', email: 'noreply@paypal.com' } => 'PayPal' <noreply@paypal.com>
561
+ * @param {String|EmailAddress|EmailAddress[]|null} data
562
+ */
563
+ function toEmailAddress(data) {
564
+ var email = '';
565
+ if (typeof data === 'undefined') ;
566
+ else if (typeof data === 'string') {
567
+ email = data;
568
+ }
569
+ else if (typeof data === 'object') {
570
+ if (Array.isArray(data)) {
571
+ email += data
572
+ .map(function (item) {
573
+ var str = '';
574
+ if (item.name) {
575
+ str += '"' + item.name.replace(/^"|"\s*$/g, '') + '" ';
576
+ }
577
+ if (item.email) {
578
+ str += '<' + item.email + '>';
579
+ }
580
+ return str;
581
+ })
582
+ .filter(function (a) { return a; })
583
+ .join(', ');
584
+ }
585
+ else {
586
+ if (data) {
587
+ if (data.name) {
588
+ email += '"' + data.name.replace(/^"|"\s*$/g, '') + '" ';
589
+ }
590
+ if (data.email) {
591
+ email += '<' + data.email + '>';
592
+ }
593
+ }
594
+ }
595
+ }
596
+ return email;
597
+ }
598
+ /**
599
+ * Gets character set name, e.g. contentType='.....charset='iso-8859-2'....'
600
+ * @param {String} contentType
601
+ * @returns {String|undefined}
602
+ */
603
+ function getCharset(contentType) {
604
+ var match = /charset\s*=\W*([\w\-]+)/g.exec(contentType);
605
+ return match ? match[1] : undefined;
606
+ }
607
+ /**
608
+ * Gets name and e-mail address from a string, e.g. 'PayPal' <noreply@paypal.com> => { name: 'PayPal', email: 'noreply@paypal.com' }
609
+ * @param {String} raw
610
+ * @returns { EmailAddress | EmailAddress[] | null}
611
+ */
612
+ function getEmailAddress(rawStr) {
613
+ var raw = unquoteString(rawStr);
614
+ var parseList = addressparser(raw);
615
+ var list = parseList.map(function (v) { return ({ name: v.name, email: v.address }); });
616
+ //Return result
617
+ if (list.length === 0) {
618
+ return null; //No e-mail address
619
+ }
620
+ if (list.length === 1) {
621
+ return list[0]; //Only one record, return as object, required to preserve backward compatibility
622
+ }
623
+ return list; //Multiple e-mail addresses as array
624
+ }
625
+ /**
626
+ * decode one joint
627
+ * @param {String} str
628
+ * @returns {String}
629
+ */
630
+ function decodeJoint(str) {
631
+ var match = /=\?([^?]+)\?(B|Q)\?(.+?)(\?=)/gi.exec(str);
632
+ if (match) {
633
+ var charset = getCharsetName(match[1] || defaultCharset); //eq. match[1] = 'iso-8859-2'; charset = 'iso88592'
634
+ var type = match[2].toUpperCase();
635
+ var value = match[3];
636
+ if (type === 'B') {
637
+ //Base64
638
+ if (charset === 'utf8') {
639
+ return decode(encode(Base64.fromBase64(value.replace(/\r?\n/g, ''))), 'utf8');
640
+ }
641
+ else {
642
+ return decode(Base64.toUint8Array(value.replace(/\r?\n/g, '')), charset);
643
+ }
644
+ }
645
+ else if (type === 'Q') {
646
+ //Quoted printable
647
+ return unquotePrintable(value, charset, true);
648
+ }
649
+ }
650
+ return str;
651
+ }
652
+ /**
653
+ * decode section
654
+ * @param {String} str
655
+ * @returns {String}
656
+ */
657
+ function unquoteString(str) {
658
+ var regex = /=\?([^?]+)\?(B|Q)\?(.+?)(\?=)/gi;
659
+ var decodedString = str || '';
660
+ var spinOffMatch = decodedString.match(regex);
661
+ if (spinOffMatch) {
662
+ spinOffMatch.forEach(function (spin) {
663
+ decodedString = decodedString.replace(spin, decodeJoint(spin));
664
+ });
665
+ }
666
+ return decodedString.replace(/\r?\n/g, '');
667
+ }
668
+ /**
669
+ * Decodes 'quoted-printable'
670
+ * @param {String} value
671
+ * @param {String} charset
672
+ * @param {String} qEncoding whether the encoding is RFC-2047’s Q-encoding, meaning special handling of underscores.
673
+ * @returns {String}
674
+ */
675
+ function unquotePrintable(value, charset, qEncoding) {
676
+ //Convert =0D to '\r', =20 to ' ', etc.
677
+ // if (!charset || charset == "utf8" || charset == "utf-8") {
678
+ // return value
679
+ // .replace(/=([\w\d]{2})=([\w\d]{2})=([\w\d]{2})/gi, function (matcher, p1, p2, p3, offset, string) {
680
+ if (qEncoding === void 0) { qEncoding = false; }
681
+ // })
682
+ // .replace(/=([\w\d]{2})=([\w\d]{2})/gi, function (matcher, p1, p2, offset, string) {
683
+ // })
684
+ // .replace(/=([\w\d]{2})/gi, function (matcher, p1, offset, string) { return String.fromCharCode(parseInt(p1, 16)); })
685
+ // .replace(/=\r?\n/gi, ""); //Join line
686
+ // } else {
687
+ // return value
688
+ // .replace(/=([\w\d]{2})=([\w\d]{2})/gi, function (matcher, p1, p2, offset, string) {
689
+ // })
690
+ // .replace(/=([\w\d]{2})/gi, function (matcher, p1, offset, string) {
691
+ // })
692
+ // .replace(/=\r?\n/gi, ''); //Join line
693
+ // }
694
+ var rawString = value
695
+ .replace(/[\t ]+$/gm, '') // remove invalid whitespace from the end of lines
696
+ .replace(/=(?:\r?\n|$)/g, ''); // remove soft line breaks
697
+ if (qEncoding) {
698
+ rawString = rawString.replace(/_/g, decode(new Uint8Array([0x20]), charset));
699
+ }
700
+ return mimeDecode(rawString, charset);
701
+ }
702
+ /**
703
+ * Parses EML file content and returns object-oriented representation of the content.
704
+ * @param {String} eml
705
+ * @param {OptionOrNull | CallbackFn<ParsedEmlJson>} options
706
+ * @param {CallbackFn<ParsedEmlJson>} callback
707
+ * @returns {string | Error | ParsedEmlJson}
708
+ */
709
+ function parse(eml, options, callback) {
710
+ //Shift arguments
711
+ if (typeof options === 'function' && typeof callback === 'undefined') {
712
+ callback = options;
713
+ options = null;
714
+ }
715
+ if (typeof options !== 'object') {
716
+ options = { headersOnly: false };
717
+ }
718
+ var error;
719
+ var result = {};
720
+ try {
721
+ if (typeof eml !== 'string') {
722
+ throw new Error('Argument "eml" expected to be string!');
723
+ }
724
+ var lines = eml.split(/\r?\n/);
725
+ result = parseRecursive(lines, 0, result, options);
726
+ }
727
+ catch (e) {
728
+ error = e;
729
+ }
730
+ callback && callback(error, result);
731
+ return error || result || new Error('read EML failed!');
732
+ }
733
+ /**
734
+ * Parses EML file content.
735
+ * @param {String[]} lines
736
+ * @param {Number} start
737
+ * @param {Options} options
738
+ * @returns {ParsedEmlJson}
739
+ */
740
+ function parseRecursive(lines, start, parent, options) {
741
+ var boundary = null;
742
+ var lastHeaderName = '';
743
+ var findBoundary = '';
744
+ var insideBody = false;
745
+ var insideBoundary = false;
746
+ var isMultiHeader = false;
747
+ var isMultipart = false;
748
+ var checkedForCt = false;
749
+ var ctInBody = false;
750
+ parent.headers = {};
751
+ //parent.body = null;
752
+ function complete(boundary) {
753
+ //boundary.part = boundary.lines.join("\r\n");
754
+ boundary.part = {};
755
+ parseRecursive(boundary.lines, 0, boundary.part, options);
756
+ delete boundary.lines;
757
+ }
758
+ //Read line by line
759
+ for (var i = start; i < lines.length; i++) {
760
+ var line = lines[i];
761
+ //Header
762
+ if (!insideBody) {
763
+ //Search for empty line
764
+ if (line == '') {
765
+ insideBody = true;
766
+ if (options && options.headersOnly) {
767
+ break;
768
+ }
769
+ //Expected boundary
770
+ var ct = parent.headers['Content-Type'] || parent.headers['Content-type'];
771
+ if (!ct) {
772
+ if (checkedForCt) {
773
+ insideBody = !ctInBody;
774
+ }
775
+ else {
776
+ checkedForCt = true;
777
+ var lineClone = Array.from(lines);
778
+ var string = lineClone.splice(i).join('\r\n');
779
+ var trimmedStrin = string.trim();
780
+ if (trimmedStrin.indexOf('Content-Type') === 0 || trimmedStrin.indexOf('Content-type') === 0) {
781
+ insideBody = false;
782
+ ctInBody = true;
783
+ }
784
+ else {
785
+ console.warn('Warning: undefined Content-Type');
786
+ }
787
+ }
788
+ }
789
+ else if (/^multipart\//g.test(ct)) {
790
+ var b = getBoundary(ct);
791
+ if (b && b.length) {
792
+ findBoundary = b;
793
+ isMultipart = true;
794
+ parent.body = [];
795
+ }
796
+ }
797
+ continue;
798
+ }
799
+ //Header value with new line
800
+ var match = /^\s+([^\r\n]+)/g.exec(line);
801
+ if (match) {
802
+ if (isMultiHeader) {
803
+ parent.headers[lastHeaderName][parent.headers[lastHeaderName].length - 1] += '\r\n' + match[1];
804
+ }
805
+ else {
806
+ parent.headers[lastHeaderName] += '\r\n' + match[1];
807
+ }
808
+ continue;
809
+ }
810
+ //Header name and value
811
+ match = /^([\w\d\-]+):\s*([^\r\n]*)/gi.exec(line);
812
+ if (match) {
813
+ lastHeaderName = match[1];
814
+ if (parent.headers[lastHeaderName]) {
815
+ //Multiple headers with the same name
816
+ isMultiHeader = true;
817
+ if (typeof parent.headers[lastHeaderName] == 'string') {
818
+ parent.headers[lastHeaderName] = [parent.headers[lastHeaderName]];
819
+ }
820
+ parent.headers[lastHeaderName].push(match[2]);
821
+ }
822
+ else {
823
+ //Header first appeared here
824
+ isMultiHeader = false;
825
+ parent.headers[lastHeaderName] = match[2];
826
+ }
827
+ continue;
828
+ }
829
+ }
830
+ //Body
831
+ else {
832
+ //Multipart body
833
+ if (isMultipart) {
834
+ //Search for boundary start
835
+ //Updated on 2019-10-12: A line before the boundary marker is not required to be an empty line
836
+ //if (lines[i - 1] == "" && line.indexOf("--" + findBoundary) == 0 && !/\-\-(\r?\n)?$/g.test(line)) {
837
+ if (line.indexOf('--' + findBoundary) == 0 && !/\-\-(\r?\n)?$/g.test(line)) {
838
+ insideBoundary = true;
839
+ //Complete the previous boundary
840
+ if (boundary && boundary.lines) {
841
+ complete(boundary);
842
+ }
843
+ //Start a new boundary
844
+ var match = /^\-\-([^\r\n]+)(\r?\n)?$/g.exec(line);
845
+ boundary = { boundary: match[1], lines: [] };
846
+ parent.body.push(boundary);
847
+ continue;
848
+ }
849
+ if (insideBoundary) {
850
+ //Search for boundary end
851
+ if ((boundary === null || boundary === void 0 ? void 0 : boundary.boundary) && lines[i - 1] == '' && line.indexOf('--' + findBoundary + '--') == 0) {
852
+ insideBoundary = false;
853
+ complete(boundary);
854
+ continue;
855
+ }
856
+ if ((boundary === null || boundary === void 0 ? void 0 : boundary.boundary) && line.indexOf('--' + findBoundary + '--') == 0) {
857
+ continue;
858
+ }
859
+ boundary === null || boundary === void 0 ? void 0 : boundary.lines.push(line);
860
+ }
861
+ }
862
+ else {
863
+ //Solid string body
864
+ parent.body = lines.splice(i).join('\r\n');
865
+ break;
866
+ }
867
+ }
868
+ }
869
+ //Complete the last boundary
870
+ if (parent.body && parent.body.length && parent.body[parent.body.length - 1].lines) {
871
+ complete(parent.body[parent.body.length - 1]);
872
+ }
873
+ return parent;
874
+ }
875
+ /**
876
+ * Convert BoundaryRawData to BoundaryConvertedData
877
+ * @param {BoundaryRawData} boundary
878
+ * @returns {BoundaryConvertedData} Obj
879
+ */
880
+ function completeBoundary(boundary) {
881
+ if (!boundary || !boundary.boundary) {
882
+ return null;
883
+ }
884
+ var lines = boundary.lines || [];
885
+ var result = {
886
+ boundary: boundary.boundary,
887
+ part: {
888
+ headers: {},
889
+ },
890
+ };
891
+ var lastHeaderName = '';
892
+ var insideBody = false;
893
+ var childBoundary;
894
+ for (var index = 0; index < lines.length; index++) {
895
+ var line = lines[index];
896
+ if (!insideBody) {
897
+ if (line === '') {
898
+ insideBody = true;
899
+ continue;
900
+ }
901
+ var match = /^([\w\d\-]+):\s*([^\r\n]*)/gi.exec(line);
902
+ if (match) {
903
+ lastHeaderName = match[1];
904
+ result.part.headers[lastHeaderName] = match[2];
905
+ continue;
906
+ }
907
+ //Header value with new line
908
+ var lineMatch = /^\s+([^\r\n]+)/g.exec(line);
909
+ if (lineMatch) {
910
+ result.part.headers[lastHeaderName] += '\r\n' + lineMatch[1];
911
+ continue;
912
+ }
913
+ }
914
+ else {
915
+ // part.body
916
+ var match = /^\-\-([^\r\n]+)(\r?\n)?$/g.exec(line);
917
+ var childBoundaryStr = getBoundary(result.part.headers['Content-Type'] || result.part.headers['Content-type']);
918
+ if (match && line.indexOf('--' + childBoundaryStr) === 0 && !childBoundary) {
919
+ childBoundary = { boundary: match ? match[1] : '', lines: [] };
920
+ continue;
921
+ }
922
+ else if (!!childBoundary && childBoundary.boundary) {
923
+ if (lines[index - 1] === '' && line.indexOf('--' + childBoundary.boundary) === 0) {
924
+ var child = completeBoundary(childBoundary);
925
+ if (child) {
926
+ if (Array.isArray(result.part.body)) {
927
+ result.part.body.push(child);
928
+ }
929
+ else {
930
+ result.part.body = [child];
931
+ }
932
+ }
933
+ else {
934
+ result.part.body = childBoundary.lines.join('\r\n');
935
+ }
936
+ // next line child
937
+ if (!!lines[index + 1]) {
938
+ childBoundary.lines = [];
939
+ continue;
940
+ }
941
+ // end line child And this boundary's end
942
+ if (line.indexOf('--' + childBoundary.boundary + '--') === 0 && lines[index + 1] === '') {
943
+ childBoundary = undefined;
944
+ break;
945
+ }
946
+ }
947
+ childBoundary.lines.push(line);
948
+ }
949
+ else {
950
+ result.part.body = lines.splice(index).join('\r\n');
951
+ break;
952
+ }
953
+ }
954
+ }
955
+ return result;
956
+ }
957
+ /**
958
+ * buid EML file by ReadedEmlJson or EML file content
959
+ * @param {ReadedEmlJson} data
960
+ * @param {BuildOptions | CallbackFn<string> | null} options
961
+ * @param {CallbackFn<string>} callback
962
+ */
963
+ function build(data, options, callback) {
964
+ //Shift arguments
965
+ if (typeof options === 'function' && typeof callback === 'undefined') {
966
+ callback = options;
967
+ options = null;
968
+ }
969
+ var error;
970
+ var eml = '';
971
+ var EOL = '\r\n'; //End-of-line
972
+ try {
973
+ if (!data) {
974
+ throw new Error('Argument "data" expected to be an object! or string');
975
+ }
976
+ if (typeof data === 'string') {
977
+ var readResult = read(data);
978
+ if (typeof readResult === 'string') {
979
+ throw new Error(readResult);
980
+ }
981
+ else if (readResult instanceof Error) {
982
+ throw readResult;
983
+ }
984
+ else {
985
+ data = readResult;
986
+ }
987
+ }
988
+ if (!data.headers) {
989
+ throw new Error('Argument "data" expected to be has headers');
990
+ }
991
+ if (typeof data.subject === 'string') {
992
+ data.headers['Subject'] = data.subject;
993
+ }
994
+ if (typeof data.from !== 'undefined') {
995
+ data.headers['From'] = toEmailAddress(data.from);
996
+ }
997
+ if (typeof data.to !== 'undefined') {
998
+ data.headers['To'] = toEmailAddress(data.to);
999
+ }
1000
+ if (typeof data.cc !== 'undefined') {
1001
+ data.headers['Cc'] = toEmailAddress(data.cc);
1002
+ }
1003
+ // if (!data.headers['To']) {
1004
+ // throw new Error('Missing "To" e-mail address!');
1005
+ // }
1006
+ var emlBoundary = getBoundary(data.headers['Content-Type'] || data.headers['Content-type'] || '');
1007
+ var hasBoundary = false;
1008
+ var boundary = createBoundary();
1009
+ var multipartBoundary = '';
1010
+ if (data.multipartAlternative) {
1011
+ multipartBoundary = '' + (getBoundary(data.multipartAlternative['Content-Type']) || '');
1012
+ hasBoundary = true;
1013
+ }
1014
+ if (emlBoundary) {
1015
+ boundary = emlBoundary;
1016
+ hasBoundary = true;
1017
+ }
1018
+ else {
1019
+ data.headers['Content-Type'] = data.headers['Content-type'] || 'multipart/mixed;' + EOL + 'boundary="' + boundary + '"';
1020
+ // Restrained
1021
+ // hasBoundary = true;
1022
+ }
1023
+ //Build headers
1024
+ var keys = Object.keys(data.headers);
1025
+ for (var i = 0; i < keys.length; i++) {
1026
+ var key = keys[i];
1027
+ var value = data.headers[key];
1028
+ if (typeof value === 'undefined') {
1029
+ continue; //Skip missing headers
1030
+ }
1031
+ else if (typeof value === 'string') {
1032
+ eml += key + ': ' + value.replace(/\r?\n/g, EOL + ' ') + EOL;
1033
+ }
1034
+ else {
1035
+ //Array
1036
+ for (var j = 0; j < value.length; j++) {
1037
+ eml += key + ': ' + value[j].replace(/\r?\n/g, EOL + ' ') + EOL;
1038
+ }
1039
+ }
1040
+ }
1041
+ if (data.multipartAlternative) {
1042
+ eml += EOL;
1043
+ eml += '--' + emlBoundary + EOL;
1044
+ eml += 'Content-Type: ' + data.multipartAlternative['Content-Type'].replace(/\r?\n/g, EOL + ' ') + EOL;
1045
+ }
1046
+ //Start the body
1047
+ eml += EOL;
1048
+ //Plain text content
1049
+ if (data.text) {
1050
+ // Encode opened and self headers keeped
1051
+ if (typeof options === 'object' && !!options && options.encode && data.textheaders) {
1052
+ eml += '--' + boundary + EOL;
1053
+ for (var key in data.textheaders) {
1054
+ if (data.textheaders.hasOwnProperty(key)) {
1055
+ eml += key + ": " + data.textheaders[key].replace(/\r?\n/g, EOL + ' ');
1056
+ }
1057
+ }
1058
+ }
1059
+ else if (hasBoundary) {
1060
+ // else Assembly
1061
+ eml += '--' + (multipartBoundary ? multipartBoundary : boundary) + EOL;
1062
+ eml += 'Content-Type: text/plain; charset="utf-8"' + EOL;
1063
+ }
1064
+ eml += EOL + data.text;
1065
+ eml += EOL;
1066
+ }
1067
+ //HTML content
1068
+ if (data.html) {
1069
+ // Encode opened and self headers keeped
1070
+ if (typeof options === 'object' && !!options && options.encode && data.textheaders) {
1071
+ eml += '--' + boundary + EOL;
1072
+ for (var key in data.textheaders) {
1073
+ if (data.textheaders.hasOwnProperty(key)) {
1074
+ eml += key + ": " + data.textheaders[key].replace(/\r?\n/g, EOL + ' ');
1075
+ }
1076
+ }
1077
+ }
1078
+ else if (hasBoundary) {
1079
+ eml += '--' + (multipartBoundary ? multipartBoundary : boundary) + EOL;
1080
+ eml += 'Content-Type: text/html; charset="utf-8"' + EOL;
1081
+ }
1082
+ if (verbose) {
1083
+ console.info("line 765 " + hasBoundary + ", emlBoundary: " + emlBoundary + ", multipartBoundary: " + multipartBoundary + ", boundary: " + boundary);
1084
+ }
1085
+ eml += EOL + data.html;
1086
+ eml += EOL;
1087
+ }
1088
+ //Append attachments
1089
+ if (data.attachments) {
1090
+ for (var i = 0; i < data.attachments.length; i++) {
1091
+ var attachment = data.attachments[i];
1092
+ eml += '--' + boundary + EOL;
1093
+ eml += 'Content-Type: ' + (attachment.contentType.replace(/\r?\n/g, EOL + ' ') || 'application/octet-stream') + EOL;
1094
+ eml += 'Content-Transfer-Encoding: base64' + EOL;
1095
+ eml +=
1096
+ 'Content-Disposition: ' +
1097
+ (attachment.inline ? 'inline' : 'attachment') +
1098
+ '; filename="' +
1099
+ (attachment.filename || attachment.name || 'attachment_' + (i + 1)) +
1100
+ '"' +
1101
+ EOL;
1102
+ if (attachment.cid) {
1103
+ eml += 'Content-ID: <' + attachment.cid + '>' + EOL;
1104
+ }
1105
+ eml += EOL;
1106
+ if (typeof attachment.data === 'string') {
1107
+ var content = Base64.toBase64(attachment.data);
1108
+ eml += wrap(content, 72) + EOL;
1109
+ }
1110
+ else {
1111
+ //Buffer
1112
+ // Uint8Array to string by new TextEncoder
1113
+ var content = decode(attachment.data);
1114
+ eml += wrap(content, 72) + EOL;
1115
+ }
1116
+ eml += EOL;
1117
+ }
1118
+ }
1119
+ //Finish the boundary
1120
+ if (hasBoundary) {
1121
+ eml += '--' + boundary + '--' + EOL;
1122
+ }
1123
+ }
1124
+ catch (e) {
1125
+ error = e;
1126
+ }
1127
+ callback && callback(error, eml);
1128
+ return error || eml;
1129
+ }
1130
+ /**
1131
+ * Parses EML file content and return user-friendly object.
1132
+ * @param {String | ParsedEmlJson} eml EML file content or object from 'parse'
1133
+ * @param { OptionOrNull | CallbackFn<ReadedEmlJson>} options EML parse options
1134
+ * @param {CallbackFn<ReadedEmlJson>} callback Callback function(error, data)
1135
+ */
1136
+ function read(eml, options, callback) {
1137
+ //Shift arguments
1138
+ if (typeof options === 'function' && typeof callback === 'undefined') {
1139
+ callback = options;
1140
+ options = null;
1141
+ }
1142
+ var error;
1143
+ var result;
1144
+ //Appends the boundary to the result
1145
+ function _append(headers, content, result) {
1146
+ var contentType = headers['Content-Type'] || headers['Content-type'];
1147
+ var contentDisposition = headers['Content-Disposition'];
1148
+ var charset = getCharsetName(getCharset(contentType) || defaultCharset);
1149
+ var encoding = headers['Content-Transfer-Encoding'] || headers['Content-transfer-encoding'];
1150
+ if (typeof encoding === 'string') {
1151
+ encoding = encoding.toLowerCase();
1152
+ }
1153
+ if (encoding === 'base64') {
1154
+ if (contentType && contentType.indexOf('gbk') >= 0) {
1155
+ // is work? I'm not sure
1156
+ content = encode(GB2312UTF8.GB2312ToUTF8(content.replace(/\r?\n/g, '')));
1157
+ }
1158
+ else {
1159
+ // string to Uint8Array by TextEncoder
1160
+ content = encode(content.replace(/\r?\n/g, ''));
1161
+ }
1162
+ }
1163
+ else if (encoding === 'quoted-printable') {
1164
+ content = unquotePrintable(content, charset);
1165
+ }
1166
+ else if (encoding && charset !== 'utf8' && encoding.search(/binary|8bit/) === 0) {
1167
+ //'8bit', 'binary', '8bitmime', 'binarymime'
1168
+ content = decode(content, charset);
1169
+ }
1170
+ if (!contentDisposition && contentType && contentType.indexOf('text/html') >= 0) {
1171
+ if (typeof content !== 'string') {
1172
+ content = decode(content, charset);
1173
+ }
1174
+ var htmlContent = content.replace(/\r\n|(&quot;)/g, '').replace(/\"/g, "\"");
1175
+ try {
1176
+ if (encoding === 'base64') {
1177
+ htmlContent = Base64.decode(htmlContent);
1178
+ }
1179
+ else if (Base64.btoa(Base64.atob(htmlContent)) == htmlContent) {
1180
+ htmlContent = Base64.atob(htmlContent);
1181
+ }
1182
+ }
1183
+ catch (error) {
1184
+ console.error(error);
1185
+ }
1186
+ if (result.html) {
1187
+ result.html += htmlContent;
1188
+ }
1189
+ else {
1190
+ result.html = htmlContent;
1191
+ }
1192
+ result.htmlheaders = {
1193
+ 'Content-Type': contentType,
1194
+ 'Content-Transfer-Encoding': encoding || '',
1195
+ };
1196
+ // self boundary Not used at conversion
1197
+ }
1198
+ else if (!contentDisposition && contentType && contentType.indexOf('text/plain') >= 0) {
1199
+ if (typeof content !== 'string') {
1200
+ content = decode(content, charset);
1201
+ }
1202
+ if (encoding === 'base64') {
1203
+ content = Base64.decode(content);
1204
+ }
1205
+ //Plain text message
1206
+ if (result.text) {
1207
+ result.text += content;
1208
+ }
1209
+ else {
1210
+ result.text = content;
1211
+ }
1212
+ result.textheaders = {
1213
+ 'Content-Type': contentType,
1214
+ 'Content-Transfer-Encoding': encoding || '',
1215
+ };
1216
+ // self boundary Not used at conversion
1217
+ }
1218
+ else {
1219
+ //Get the attachment
1220
+ if (!result.attachments) {
1221
+ result.attachments = [];
1222
+ }
1223
+ var attachment = {};
1224
+ var id = headers['Content-ID'] || headers['Content-Id'];
1225
+ if (id) {
1226
+ attachment.id = id;
1227
+ }
1228
+ var qaaapId = headers['X-Qaap-Object-Id'];
1229
+ if (qaaapId) {
1230
+ attachment.qaapId = qaaapId;
1231
+ }
1232
+ var NameContainer = ['Content-Disposition', 'Content-Type', 'Content-type'];
1233
+ var result_name = void 0;
1234
+ for (var _i = 0, NameContainer_1 = NameContainer; _i < NameContainer_1.length; _i++) {
1235
+ var key = NameContainer_1[_i];
1236
+ var name = headers[key];
1237
+ if (name) {
1238
+ result_name = name
1239
+ .replace(/(\s|'|utf-8|\*[0-9]\*)/g, '')
1240
+ .split(';')
1241
+ .map(function (v) { return /name[\*]?="?(.+?)"?$/gi.exec(v); })
1242
+ .reduce(function (a, b) {
1243
+ if (b && b[1]) {
1244
+ a += b[1];
1245
+ }
1246
+ return a;
1247
+ }, '');
1248
+ if (result_name) {
1249
+ break;
1250
+ }
1251
+ }
1252
+ }
1253
+ if (result_name) {
1254
+ attachment.name = decodeURI(result_name);
1255
+ }
1256
+ var ct = headers['Content-Type'] || headers['Content-type'];
1257
+ if (ct) {
1258
+ attachment.contentType = ct;
1259
+ }
1260
+ var cd = headers['Content-Disposition'];
1261
+ if (cd) {
1262
+ attachment.inline = /^\s*inline/g.test(cd);
1263
+ }
1264
+ attachment.data = content;
1265
+ attachment.data64 = decode(content, charset);
1266
+ result.attachments.push(attachment);
1267
+ }
1268
+ }
1269
+ function _read(data) {
1270
+ if (!data) {
1271
+ return 'no data';
1272
+ }
1273
+ try {
1274
+ var result_1 = {};
1275
+ if (!data.headers) {
1276
+ throw new Error("data does't has headers");
1277
+ }
1278
+ if (data.headers['Date']) {
1279
+ result_1.date = new Date(data.headers['Date']);
1280
+ }
1281
+ if (data.headers['Subject']) {
1282
+ result_1.subject = unquoteString(data.headers['Subject']);
1283
+ }
1284
+ if (data.headers['From']) {
1285
+ result_1.from = getEmailAddress(data.headers['From']);
1286
+ }
1287
+ if (data.headers['To']) {
1288
+ result_1.to = getEmailAddress(data.headers['To']);
1289
+ }
1290
+ if (data.headers['CC']) {
1291
+ result_1.cc = getEmailAddress(data.headers['CC']);
1292
+ }
1293
+ if (data.headers['Cc']) {
1294
+ result_1.cc = getEmailAddress(data.headers['Cc']);
1295
+ }
1296
+ result_1.headers = data.headers;
1297
+ //Content mime type
1298
+ var boundary = null;
1299
+ var ct = data.headers['Content-Type'] || data.headers['Content-type'];
1300
+ if (ct && /^multipart\//g.test(ct)) {
1301
+ var b = getBoundary(ct);
1302
+ if (b && b.length) {
1303
+ boundary = b;
1304
+ }
1305
+ }
1306
+ if (boundary && Array.isArray(data.body)) {
1307
+ for (var i = 0; i < data.body.length; i++) {
1308
+ var boundaryBlock = data.body[i];
1309
+ if (!boundaryBlock) {
1310
+ continue;
1311
+ }
1312
+ //Get the message content
1313
+ if (typeof boundaryBlock.part === 'undefined') {
1314
+ verbose && console.warn('Warning: undefined b.part');
1315
+ }
1316
+ else if (typeof boundaryBlock.part === 'string') {
1317
+ result_1.data = boundaryBlock.part;
1318
+ }
1319
+ else {
1320
+ if (typeof boundaryBlock.part.body === 'undefined') {
1321
+ verbose && console.warn('Warning: undefined b.part.body');
1322
+ }
1323
+ else if (typeof boundaryBlock.part.body === 'string') {
1324
+ _append(boundaryBlock.part.headers, boundaryBlock.part.body, result_1);
1325
+ }
1326
+ else {
1327
+ // keep multipart/alternative
1328
+ var currentHeaders = boundaryBlock.part.headers;
1329
+ var currentHeadersContentType = currentHeaders['Content-Type'] || currentHeaders['Content-type'];
1330
+ if (verbose) {
1331
+ console.log("line 969 currentHeadersContentType: " + currentHeadersContentType);
1332
+ }
1333
+ // Hasmore ?
1334
+ if (currentHeadersContentType && currentHeadersContentType.indexOf('multipart') >= 0 && !result_1.multipartAlternative) {
1335
+ result_1.multipartAlternative = {
1336
+ 'Content-Type': currentHeadersContentType,
1337
+ };
1338
+ }
1339
+ for (var j = 0; j < boundaryBlock.part.body.length; j++) {
1340
+ var selfBoundary = boundaryBlock.part.body[j];
1341
+ if (typeof selfBoundary === 'string') {
1342
+ result_1.data = selfBoundary;
1343
+ continue;
1344
+ }
1345
+ var headers = selfBoundary.part.headers;
1346
+ var content = selfBoundary.part.body;
1347
+ if (Array.isArray(content)) {
1348
+ content.forEach(function (bound) {
1349
+ _append(bound.part.headers, bound.part.body, result_1);
1350
+ });
1351
+ }
1352
+ else {
1353
+ _append(headers, content, result_1);
1354
+ }
1355
+ }
1356
+ }
1357
+ }
1358
+ }
1359
+ }
1360
+ else if (typeof data.body === 'string') {
1361
+ _append(data.headers, data.body, result_1);
1362
+ }
1363
+ return result_1;
1364
+ }
1365
+ catch (e) {
1366
+ return e;
1367
+ }
1368
+ }
1369
+ if (typeof eml === 'string') {
1370
+ var parseResult = parse(eml, options);
1371
+ if (typeof parseResult === 'string' || parseResult instanceof Error) {
1372
+ error = parseResult;
1373
+ }
1374
+ else {
1375
+ var readResult = _read(parseResult);
1376
+ if (typeof readResult === 'string' || readResult instanceof Error) {
1377
+ error = readResult;
1378
+ }
1379
+ else {
1380
+ result = readResult;
1381
+ }
1382
+ }
1383
+ }
1384
+ else if (typeof eml === 'object') {
1385
+ var readResult = _read(eml);
1386
+ if (typeof readResult === 'string' || readResult instanceof Error) {
1387
+ error = readResult;
1388
+ }
1389
+ else {
1390
+ result = readResult;
1391
+ }
1392
+ }
1393
+ else {
1394
+ error = new Error('Missing EML file content!');
1395
+ }
1396
+ callback && callback(error, result);
1397
+ return error || result || new Error('read EML failed!');
1398
+ }
1399
+ // const GBKUTF8 = GB2312UTF8;
1400
+ // const parseEml = parse;
1401
+ // const readEml = read;
1402
+ // const buildEml = build;
1403
+
1404
+ export { GB2312UTF8 as GBKUTF8, build as buildEml, completeBoundary, convert, createBoundary, decode, encode, getBoundary, getCharset, getEmailAddress, mimeDecode, parse as parseEml, read as readEml, toEmailAddress, unquotePrintable, unquoteString };