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 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
- mailparser.on("end", function(mail){
69
- mail; // object structure for parsed e-mail
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
- mailparser = new MailParser();
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
- // setup an event listener when the parsing finishes
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
- // send the email source to the parser
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
- mailparser = new MailParser(),
117
- fs = require("fs");
118
- mailparser.on("end", function(mail_object){
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
- attachments = [{
143
- contentType: 'image/png',
144
- fileName: 'image.png',
145
- contentDisposition: 'attachment',
146
- contentId: '5.1321281380971@localhost',
147
- transferEncoding: 'base64',
148
- length: 126,
149
- generatedFileName: 'image.png',
150
- checksum: 'e4cef4c6e26037bcf8166905207ea09b',
151
- content: <Buffer ...>
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, this._currentNode.parentNode || this._currentNode);
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) ||  [value];
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.shift();
800
+ encoding = (parts.length > 1) ? parts[0] : "us-ascii";
794
801
  name = parts.pop();
795
802
  if (name) {
796
- return this._replaceMimeWords(this._replaceMimeWords("=?" + (encoding || "us-ascii") + "?Q?" + name.replace(/%/g, "=") + "?="));
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 "hight":
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._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1";
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
- for (i = 0, len = this.mailData.text.length; i < len; i++) {
1040
- if (!returnValue.text && this.mailData.text[i].content) {
1041
- returnValue.text = this.mailData.text[i].content;
1042
- } else if (this.mailData.text[i].content) {
1043
- returnValue.text += this.mailData.text[i].content;
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)[1] || '';
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.5.2",
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.2.19",
21
- "encoding": "^0.1.11",
20
+ "mimelib": "^0.3.0",
21
+ "encoding": "^0.1.12",
22
22
  "mime": "^1.3.4",
23
- "uue": "^2.1.1"
23
+ "uue": "^3.1.0"
24
24
  },
25
25
  "devDependencies": {
26
- "nodeunit": "^0.9.1"
26
+ "nodeunit": "^0.10.2"
27
27
  },
28
28
  "keywords": [
29
29
  "e-mail",
@@ -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, "ef694232fea1c01c16fb8a03a0ca710c@mailparser");
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 + "/nested.eml");
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--