mailparser 0.5.2 → 0.6.2
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/README.md +32 -22
- package/lib/mailparser.js +36 -16
- package/lib/streams.js +2 -2
- package/package.json +5 -5
- package/test/mailparser.js +56 -17
- package/test/mixed.eml +37 -0
package/README.md
CHANGED
|
@@ -9,6 +9,12 @@ MailParser
|
|
|
9
9
|
Handles even large attachments with ease - attachments can be parsed
|
|
10
10
|
in chunks and streamed if needed.
|
|
11
11
|
|
|
12
|
+
### Community version
|
|
13
|
+
|
|
14
|
+
This module is unmaintained. For an upgrade see [Mailparser2](https://gitlab.com/nodemailer/mailparser2). The upgraded version is more effective, has vastly better stream handling through Streams3 interface and is able to build up more accurate versions of HTML and Text contents.
|
|
15
|
+
|
|
16
|
+
### Usage
|
|
17
|
+
|
|
12
18
|
**MailParser** parses raw source of e-mail messages into a structured object.
|
|
13
19
|
|
|
14
20
|
No need to worry about charsets or decoding *quoted-printable* or
|
|
@@ -65,9 +71,9 @@ When the parsing ends an `'end'` event is emitted which has an
|
|
|
65
71
|
object with parsed e-mail structure as a parameter.
|
|
66
72
|
|
|
67
73
|
```javascript
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
74
|
+
mailparser.on("end", function(mail){
|
|
75
|
+
mail; // object structure for parsed e-mail
|
|
76
|
+
});
|
|
71
77
|
```
|
|
72
78
|
### Parsed mail object
|
|
73
79
|
|
|
@@ -90,21 +96,23 @@ object with parsed e-mail structure as a parameter.
|
|
|
90
96
|
This example decodes an e-mail from a string
|
|
91
97
|
|
|
92
98
|
```javascript
|
|
93
|
-
var MailParser = require("mailparser").MailParser
|
|
94
|
-
|
|
99
|
+
var MailParser = require("mailparser").MailParser;
|
|
100
|
+
var mailparser = new MailParser();
|
|
95
101
|
|
|
96
102
|
var email = "From: 'Sender Name' <sender@example.com>\r\n"+
|
|
97
103
|
"To: 'Receiver Name' <receiver@example.com>\r\n"+
|
|
98
104
|
"Subject: Hello world!\r\n"+
|
|
99
105
|
"\r\n"+
|
|
100
106
|
"How are you today?";
|
|
101
|
-
|
|
107
|
+
|
|
108
|
+
// setup an event listener when the parsing finishes
|
|
102
109
|
mailparser.on("end", function(mail_object){
|
|
103
110
|
console.log("From:", mail_object.from); //[{address:'sender@example.com',name:'Sender Name'}]
|
|
104
111
|
console.log("Subject:", mail_object.subject); // Hello world!
|
|
105
112
|
console.log("Text body:", mail_object.text); // How are you today?
|
|
106
113
|
});
|
|
107
|
-
|
|
114
|
+
|
|
115
|
+
// send the email source to the parser
|
|
108
116
|
mailparser.write(email);
|
|
109
117
|
mailparser.end();
|
|
110
118
|
```
|
|
@@ -112,10 +120,11 @@ mailparser.end();
|
|
|
112
120
|
|
|
113
121
|
This example pipes a `readableStream` file to **MailParser**
|
|
114
122
|
```javascript
|
|
115
|
-
var MailParser = require("mailparser").MailParser
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
123
|
+
var MailParser = require("mailparser").MailParser;
|
|
124
|
+
var mailparser = new MailParser();
|
|
125
|
+
var fs = require("fs");
|
|
126
|
+
|
|
127
|
+
mailparser.on("end", function(mail_object){
|
|
119
128
|
console.log("Subject:", mail_object.subject);
|
|
120
129
|
});
|
|
121
130
|
|
|
@@ -139,17 +148,17 @@ mailparser.on("end", function(mail_object){
|
|
|
139
148
|
|
|
140
149
|
By default attachments will be included in the attachment objects as Buffers.
|
|
141
150
|
```javascript
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
151
|
+
attachments = [{
|
|
152
|
+
contentType: 'image/png',
|
|
153
|
+
fileName: 'image.png',
|
|
154
|
+
contentDisposition: 'attachment',
|
|
155
|
+
contentId: '5.1321281380971@localhost',
|
|
156
|
+
transferEncoding: 'base64',
|
|
157
|
+
length: 126,
|
|
158
|
+
generatedFileName: 'image.png',
|
|
159
|
+
checksum: 'e4cef4c6e26037bcf8166905207ea09b',
|
|
160
|
+
content: <Buffer ...>
|
|
161
|
+
}];
|
|
153
162
|
```
|
|
154
163
|
The property `generatedFileName` is usually the same as `fileName` but if several
|
|
155
164
|
different attachments with the same name exist or there is no `fileName` set, an
|
|
@@ -176,6 +185,7 @@ object. The parameter provided includes file information (`contentType`,
|
|
|
176
185
|
var mp = new MailParser({
|
|
177
186
|
streamAttachments: true
|
|
178
187
|
}
|
|
188
|
+
|
|
179
189
|
mp.on("attachment", function(attachment, mail){
|
|
180
190
|
var output = fs.createWriteStream(attachment.generatedFileName);
|
|
181
191
|
attachment.stream.pipe(output);
|
package/lib/mailparser.js
CHANGED
|
@@ -269,6 +269,7 @@ MailParser.prototype._process = function(finalPart) {
|
|
|
269
269
|
MailParser.prototype._processStateHeader = function(line) {
|
|
270
270
|
var attachment, lastPos = this._currentNode.headers.length - 1,
|
|
271
271
|
textContent = false,
|
|
272
|
+
rootNode,
|
|
272
273
|
extension;
|
|
273
274
|
|
|
274
275
|
// Check if the header ends and body starts
|
|
@@ -309,7 +310,7 @@ MailParser.prototype._processStateHeader = function(line) {
|
|
|
309
310
|
this._currentNode.meta.generatedFileName = this._generateFileName(this._currentNode.meta.fileName, this._currentNode.meta.contentType);
|
|
310
311
|
|
|
311
312
|
this._currentNode.meta.contentId = this._currentNode.meta.contentId ||
|
|
312
|
-
crypto.createHash("md5").update(this._currentNode.meta.generatedFileName).digest("hex") + "@mailparser";
|
|
313
|
+
crypto.createHash("md5").update(new Buffer(this._currentNode.meta.generatedFileName, 'utf-8')).digest("hex") + "@mailparser";
|
|
313
314
|
|
|
314
315
|
extension = this._currentNode.meta.generatedFileName.split(".").pop().toLowerCase();
|
|
315
316
|
|
|
@@ -330,8 +331,14 @@ MailParser.prototype._processStateHeader = function(line) {
|
|
|
330
331
|
this._currentNode.stream = new Streams.BinaryStream();
|
|
331
332
|
}
|
|
332
333
|
attachment.stream = this._currentNode.stream;
|
|
334
|
+
|
|
335
|
+
rootNode = this._currentNode;
|
|
336
|
+
|
|
337
|
+
while (rootNode.parentNode) {
|
|
338
|
+
rootNode = rootNode.parentNode;
|
|
339
|
+
}
|
|
333
340
|
|
|
334
|
-
this.emit("attachment", attachment,
|
|
341
|
+
this.emit("attachment", attachment, rootNode);
|
|
335
342
|
} else {
|
|
336
343
|
this._currentNode.content = undefined;
|
|
337
344
|
}
|
|
@@ -589,7 +596,7 @@ MailParser.prototype._createMimeNode = function(parentNode) {
|
|
|
589
596
|
MailParser.prototype._parseHeaderLineWithParams = function(value) {
|
|
590
597
|
var key, parts, returnValue = {};
|
|
591
598
|
|
|
592
|
-
parts = value.match(/(?:[^;"]+|"[^"]*")+/g) ||
|
|
599
|
+
parts = value.match(/(?:[^;"]+|"[^"]*")+/g) || [value];
|
|
593
600
|
returnValue.defaultValue = parts.shift().toLowerCase();
|
|
594
601
|
|
|
595
602
|
for (var i = 0, len = parts.length; i < len; i++) {
|
|
@@ -790,10 +797,10 @@ MailParser.prototype._detectFilename = function(value) {
|
|
|
790
797
|
|
|
791
798
|
if (fileName) {
|
|
792
799
|
parts = fileName.split("'");
|
|
793
|
-
encoding = parts.
|
|
800
|
+
encoding = (parts.length > 1) ? parts[0] : "us-ascii";
|
|
794
801
|
name = parts.pop();
|
|
795
802
|
if (name) {
|
|
796
|
-
return this._replaceMimeWords(this._replaceMimeWords("=?" +
|
|
803
|
+
return this._replaceMimeWords(this._replaceMimeWords("=?" + encoding + "?Q?" + name.replace(/%/g, "=") + "?="));
|
|
797
804
|
}
|
|
798
805
|
}
|
|
799
806
|
return "";
|
|
@@ -866,7 +873,7 @@ MailParser.prototype._parsePriority = function(value) {
|
|
|
866
873
|
case "low":
|
|
867
874
|
return "low";
|
|
868
875
|
case "urgent":
|
|
869
|
-
case "
|
|
876
|
+
case "high":
|
|
870
877
|
return "high";
|
|
871
878
|
}
|
|
872
879
|
}
|
|
@@ -949,8 +956,8 @@ MailParser.prototype._finalizeContents = function() {
|
|
|
949
956
|
|
|
950
957
|
if (!this._currentNode.attachment) {
|
|
951
958
|
|
|
952
|
-
if (this._currentNode.meta.contentType == "text/html") {
|
|
953
|
-
this._currentNode.meta.charset = this._detectHTMLCharset(this._currentNode.content) || this.
|
|
959
|
+
if (this._currentNode.meta.contentType == "text/html" && !this._currentNode.meta.charset) {
|
|
960
|
+
this._currentNode.meta.charset = this._detectHTMLCharset(this._currentNode.content) || this.options.defaultCharset || "iso-8859-1";
|
|
954
961
|
}
|
|
955
962
|
|
|
956
963
|
if (this._currentNode.meta.transferEncoding == "quoted-printable") {
|
|
@@ -1036,16 +1043,29 @@ MailParser.prototype._processMimeTree = function() {
|
|
|
1036
1043
|
}
|
|
1037
1044
|
|
|
1038
1045
|
if (this.mailData.text.length) {
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1046
|
+
var len = this.mailData.text.length;
|
|
1047
|
+
// if we have both html and text, process text till the length of html assuming its alternative for html
|
|
1048
|
+
if (this.mailData.html.length) {
|
|
1049
|
+
len = Math.min(len, this.mailData.html.length);
|
|
1050
|
+
}
|
|
1051
|
+
for (i = 0, len; i < len; i++) {
|
|
1052
|
+
if (!returnValue.text && this.mailData.text[i].content) {
|
|
1053
|
+
returnValue.text = this.mailData.text[i].content;
|
|
1054
|
+
} else if (this.mailData.text[i].content) {
|
|
1055
|
+
returnValue.text += this.mailData.text[i].content;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
// all remaining text contents if present assumed as additional content and concatenated with html as well as text
|
|
1059
|
+
for (len = this.mailData.text.length; i < len; i++) {
|
|
1060
|
+
if (this.mailData.text[i].content) {
|
|
1061
|
+
// concatenate to both text and html so that text and html are always same content
|
|
1062
|
+
// user should be able to chose any one of them
|
|
1063
|
+
returnValue.text += this.mailData.text[i].content;
|
|
1064
|
+
returnValue.html += this.mailData.text[i].content;
|
|
1045
1065
|
}
|
|
1066
|
+
}
|
|
1046
1067
|
}
|
|
1047
1068
|
|
|
1048
|
-
|
|
1049
1069
|
if (this.mailData.calendar.length) {
|
|
1050
1070
|
returnValue.alternatives = [];
|
|
1051
1071
|
for (i = 0, len = this.mailData.calendar.length; i < len; i++) {
|
|
@@ -1490,4 +1510,4 @@ MailParser.prototype._detectHTMLCharset = function(html) {
|
|
|
1490
1510
|
}
|
|
1491
1511
|
|
|
1492
1512
|
return charset;
|
|
1493
|
-
};
|
|
1513
|
+
};
|
package/lib/streams.js
CHANGED
|
@@ -196,12 +196,12 @@ UUEStream.prototype.decode = function(buffer) {
|
|
|
196
196
|
var filename;
|
|
197
197
|
|
|
198
198
|
var re = /^begin [0-7]{3} (.*)/;
|
|
199
|
-
filename = buffer.slice(0, Math.min(buffer.length, 1024)).toString().match(re)
|
|
199
|
+
filename = buffer.slice(0, Math.min(buffer.length, 1024)).toString().match(re) || '';
|
|
200
200
|
if (!filename) {
|
|
201
201
|
return new Buffer(0);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
buffer = uue.decodeFile(buffer.toString('ascii').replace(/\r\n/g, '\n'), filename);
|
|
204
|
+
buffer = uue.decodeFile(buffer.toString('ascii').replace(/\r\n/g, '\n'), filename[1]);
|
|
205
205
|
|
|
206
206
|
if (this.charset.toLowerCase() == "binary") {
|
|
207
207
|
// do nothing
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mailparser",
|
|
3
3
|
"description": "Asynchronous and non-blocking parser for mime encoded e-mail messages",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.6.2",
|
|
5
5
|
"author": "Andris Reinman",
|
|
6
6
|
"maintainers": [{
|
|
7
7
|
"name": "andris",
|
|
@@ -17,13 +17,13 @@
|
|
|
17
17
|
"main": "./lib/mailparser",
|
|
18
18
|
"license": "MIT",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"mimelib": "^0.
|
|
21
|
-
"encoding": "^0.1.
|
|
20
|
+
"mimelib": "^0.3.0",
|
|
21
|
+
"encoding": "^0.1.12",
|
|
22
22
|
"mime": "^1.3.4",
|
|
23
|
-
"uue": "^
|
|
23
|
+
"uue": "^3.1.0"
|
|
24
24
|
},
|
|
25
25
|
"devDependencies": {
|
|
26
|
-
"nodeunit": "^0.
|
|
26
|
+
"nodeunit": "^0.10.2"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"e-mail",
|
package/test/mailparser.js
CHANGED
|
@@ -340,21 +340,6 @@ exports["Text encodings"] = {
|
|
|
340
340
|
});
|
|
341
341
|
},
|
|
342
342
|
|
|
343
|
-
"HTML encoding: Conflicting headers": function(test) {
|
|
344
|
-
var encodedText = "Content-Type: text/html; charset=iso-8859-1\r\n" +
|
|
345
|
-
"\r\n" +
|
|
346
|
-
"<html><head><meta charset=\"utf-8\"/></head><body>ÕÄÖÜ",
|
|
347
|
-
mail = new Buffer(encodedText, "utf-8");
|
|
348
|
-
|
|
349
|
-
test.expect(1);
|
|
350
|
-
|
|
351
|
-
var mailparser = new MailParser();
|
|
352
|
-
mailparser.end(mail);
|
|
353
|
-
mailparser.on("end", function(mail) {
|
|
354
|
-
test.equal((mail.html || "").substr(-4), "ÕÄÖÜ");
|
|
355
|
-
test.done();
|
|
356
|
-
});
|
|
357
|
-
},
|
|
358
343
|
"HTML encoding: Header defined": function(test) {
|
|
359
344
|
var encodedText = "Content-Type: text/html; charset=iso-UTF-8\r\n" +
|
|
360
345
|
"\r\n" +
|
|
@@ -461,7 +446,7 @@ exports["Attachment Content-Id"] = {
|
|
|
461
446
|
var mailparser = new MailParser();
|
|
462
447
|
mailparser.end(mail);
|
|
463
448
|
mailparser.on("end", function(mail) {
|
|
464
|
-
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].contentId, "
|
|
449
|
+
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].contentId, "7c7cf35ce5becf62faea56ed8d0ad6e4@mailparser");
|
|
465
450
|
test.done();
|
|
466
451
|
});
|
|
467
452
|
},
|
|
@@ -516,6 +501,24 @@ exports["Attachment filename"] = {
|
|
|
516
501
|
test.done();
|
|
517
502
|
});
|
|
518
503
|
},
|
|
504
|
+
"Content-Disposition filename*X": function(test) {
|
|
505
|
+
var encodedText = "Content-Type: application/octet-stream\r\n" +
|
|
506
|
+
"Content-Transfer-Encoding: QUOTED-PRINTABLE\r\n" +
|
|
507
|
+
"Content-Disposition: attachment;\r\n" +
|
|
508
|
+
" filename*0=OA;\r\n" +
|
|
509
|
+
" filename*1=U;\r\n" +
|
|
510
|
+
" filename*2=.txt\r\n" +
|
|
511
|
+
"\r\n" +
|
|
512
|
+
"=00=01=02=03=FD=FE=FF",
|
|
513
|
+
mail = new Buffer(encodedText, "utf-8");
|
|
514
|
+
|
|
515
|
+
var mailparser = new MailParser();
|
|
516
|
+
mailparser.end(mail);
|
|
517
|
+
mailparser.on("end", function(mail) {
|
|
518
|
+
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].content && mail.attachments[0].fileName, "OAU.txt");
|
|
519
|
+
test.done();
|
|
520
|
+
});
|
|
521
|
+
},
|
|
519
522
|
"Content-Disposition filename*X*": function(test) {
|
|
520
523
|
var encodedText = "Content-Type: application/octet-stream\r\n" +
|
|
521
524
|
"Content-Transfer-Encoding: QUOTED-PRINTABLE\r\n" +
|
|
@@ -566,6 +569,23 @@ exports["Attachment filename"] = {
|
|
|
566
569
|
test.done();
|
|
567
570
|
});
|
|
568
571
|
},
|
|
572
|
+
"Content-Type ; name": function(test) {
|
|
573
|
+
var encodedText = "Content-Type: ; name=\"test\"\r\n" +
|
|
574
|
+
"Content-Transfer-Encoding: QUOTED-PRINTABLE\r\n" +
|
|
575
|
+
"\r\n" +
|
|
576
|
+
"=00=01=02=03=FD=FE=FF",
|
|
577
|
+
mail = new Buffer(encodedText, "utf-8");
|
|
578
|
+
|
|
579
|
+
var mailparser = new MailParser();
|
|
580
|
+
|
|
581
|
+
mailparser.write(mail);
|
|
582
|
+
mailparser.end();
|
|
583
|
+
|
|
584
|
+
mailparser.on("end", function(mail) {
|
|
585
|
+
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].fileName, "test");
|
|
586
|
+
test.done();
|
|
587
|
+
});
|
|
588
|
+
},
|
|
569
589
|
"Content-Type name*": function(test) {
|
|
570
590
|
var encodedText = "Content-Type: application/octet-stream;\r\n" +
|
|
571
591
|
" name*=UTF-8''%C3%95%C3%84%C3%96%C3%9C\r\n" +
|
|
@@ -1508,7 +1528,7 @@ exports["Attachment info"] = {
|
|
|
1508
1528
|
};
|
|
1509
1529
|
|
|
1510
1530
|
exports["Advanced nested HTML"] = function(test) {
|
|
1511
|
-
var mail = fs.readFileSync(__dirname + "/
|
|
1531
|
+
var mail = fs.readFileSync(__dirname + "/ali.eml");
|
|
1512
1532
|
|
|
1513
1533
|
test.expect(2);
|
|
1514
1534
|
var mailparser = new MailParser();
|
|
@@ -1519,12 +1539,31 @@ exports["Advanced nested HTML"] = function(test) {
|
|
|
1519
1539
|
|
|
1520
1540
|
mailparser.end();
|
|
1521
1541
|
mailparser.on("end", function(mail) {
|
|
1542
|
+
console.log(require('util').inspect(mail, false, 22));
|
|
1522
1543
|
test.equal(mail.text, "\nDear Sir,\n\nGood evening.\n\n\n \n\n\n\nThe footer\n");
|
|
1523
1544
|
test.equal(mail.html, "<p>Dear Sir</p>\n<p>Good evening.</p>\n<p></p><p>The footer</p>\n");
|
|
1524
1545
|
test.done();
|
|
1525
1546
|
});
|
|
1526
1547
|
};
|
|
1527
1548
|
|
|
1549
|
+
exports["Additional text"] = function(test) {
|
|
1550
|
+
var mail = fs.readFileSync(__dirname + "/mixed.eml");
|
|
1551
|
+
|
|
1552
|
+
test.expect(2);
|
|
1553
|
+
var mailparser = new MailParser();
|
|
1554
|
+
|
|
1555
|
+
for (var i = 0, len = mail.length; i < len; i++) {
|
|
1556
|
+
mailparser.write(new Buffer([mail[i]]));
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
mailparser.end();
|
|
1560
|
+
mailparser.on("end", function(mail) {
|
|
1561
|
+
test.equal(mail.text, "\nThis e-mail message has been scanned for Viruses and Content and cleared\nGood Morning;\n\n");
|
|
1562
|
+
test.equal(mail.html, "<HTML><HEAD>\n</HEAD><BODY> \n\n<HR>\nThis e-mail message has been scanned for Viruses and Content and cleared\n<HR>\n</BODY></HTML>\nGood Morning;\n\n");
|
|
1563
|
+
test.done();
|
|
1564
|
+
});
|
|
1565
|
+
};
|
|
1566
|
+
|
|
1528
1567
|
exports["MBOX format"] = {
|
|
1529
1568
|
"Not a mbox": function(test) {
|
|
1530
1569
|
var encodedText = "Content-Type: text/plain; charset=utf-8\r\n" +
|
package/test/mixed.eml
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
Content-Type: multipart/mixed;
|
|
2
|
+
boundary="--=b73b47c7-75f9-4775-93eb-475a43310901"
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
----=b73b47c7-75f9-4775-93eb-475a43310901
|
|
6
|
+
Content-Type: multipart/alternative;
|
|
7
|
+
boundary="--=4d3d5f03-fd7a-40f2-ab55-6bd2de649863"
|
|
8
|
+
|
|
9
|
+
----=4d3d5f03-fd7a-40f2-ab55-6bd2de649863
|
|
10
|
+
Content-Type: text/plain
|
|
11
|
+
Content-Transfer-Encoding: 7bit
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
This e-mail message has been scanned for Viruses and Content and cleared
|
|
15
|
+
|
|
16
|
+
----=4d3d5f03-fd7a-40f2-ab55-6bd2de649863
|
|
17
|
+
Content-Type: text/html
|
|
18
|
+
Content-Transfer-Encoding: quoted-printable
|
|
19
|
+
|
|
20
|
+
<HTML><HEAD>
|
|
21
|
+
</HEAD><BODY>=20
|
|
22
|
+
|
|
23
|
+
<HR>
|
|
24
|
+
This e-mail message has been scanned for Viruses and Content and cleared
|
|
25
|
+
<HR>
|
|
26
|
+
</BODY></HTML>
|
|
27
|
+
|
|
28
|
+
----=4d3d5f03-fd7a-40f2-ab55-6bd2de649863--
|
|
29
|
+
|
|
30
|
+
----=b73b47c7-75f9-4775-93eb-475a43310901
|
|
31
|
+
Content-Type: text/plain; charset="UTF-8"
|
|
32
|
+
Content-Transfer-Encoding: quoted-printable
|
|
33
|
+
|
|
34
|
+
Good Morning;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
----=b73b47c7-75f9-4775-93eb-475a43310901--
|