mailparser

This commit is contained in:
s2
2017-11-13 15:06:12 +01:00
commit e410a7c7df
11 changed files with 3525 additions and 0 deletions

3
.npmignore Normal file
View File

@@ -0,0 +1,3 @@
node_modules
.DS_Store
npm-debug.log

12
.travis.yml Normal file
View File

@@ -0,0 +1,12 @@
language: node_js
node_js:
- 0.6
- 0.8
- 0.9
notifications:
email:
recipients:
- andris@node.ee
on_success: change
on_failure: change

16
LICENSE Normal file
View File

@@ -0,0 +1,16 @@
Copyright (c) 2012 Andris Reinman
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

217
README.md Normal file
View File

@@ -0,0 +1,217 @@
MailParser
==========
[![Build Status](https://secure.travis-ci.org/andris9/mailparser.png)](http://travis-ci.org/andris9/mailparser)
[![NPM version](https://badge.fury.io/js/mailparser.png)](http://badge.fury.io/js/mailparser)
**MailParser** is an asynchronous and non-blocking parser for
[node.js](http://nodejs.org) to parse mime encoded e-mail messages.
Handles even large attachments with ease - attachments can be parsed
in chunks and streamed if needed.
**MailParser** parses raw source of e-mail messages into a structured object.
No need to worry about charsets or decoding *quoted-printable* or
*base64* data, **MailParser** does all of it for you. All the textual output
from **MailParser** (subject line, addressee names, message body) is always UTF-8.
For a 25MB e-mail it takes less than a second to parse if attachments are not streamed but buffered and about 3-4 seconds if they are streamed. Expect high RAM usage though if you do not stream the attachments.
If you want to send e-mail instead of parsing it, check out my other module [Nodemailer](/andris9/Nodemailer).
## Support mailparser development
[![Donate to author](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DB26KWR2BQX5W)
Installation
------------
npm install mailparser
Usage
-----
Require MailParser module
var MailParser = require("mailparser").MailParser;
Create a new MailParser object
var mailparser = new MailParser([options]);
Options parameter is an object with the following properties:
* **debug** - if set to true print all incoming lines to console
* **streamAttachments** - if set to true, stream attachments instead of including them
* **unescapeSMTP** - if set to true replace double dots in the beginning of the file
* **defaultCharset** - the default charset for *text/plain* and *text/html* content, if not set reverts to *Latin-1*
* **showAttachmentLinks** - if set to true, show inlined attachment links `<a href="cid:...">filename</a>`
MailParser object is a writable Stream - you can pipe directly
files to it or you can send chunks with `mailparser.write`
When the headers have received, "headers" is emitted. The headers have not been pre-processed (except that mime words have been converted to UTF-8 text).
mailparser.on("headers", function(headers){
console.log(headers.received);
});
When the parsing ends an `'end'` event is emitted which has an
object with parsed e-mail structure as a parameter.
mailparser.on("end", function(mail){
mail; // object structure for parsed e-mail
});
### Parsed mail object
* **headers** - unprocessed headers in the form of - `{key: value}` - if there were multiple fields with the same key then the value is an array
* **from** - an array of parsed `From` addresses - `[{address:'sender@example.com',name:'Sender Name'}]` (should be only one though)
* **to** - an array of parsed `To` addresses
* **cc** - an array of parsed `Cc` addresses
* **bcc** - an array of parsed 'Bcc' addresses
* **subject** - the subject line
* **references** - an array of reference message id values (not set if no reference values present)
* **inReplyTo** - an array of In-Reply-To message id values (not set if no in-reply-to values present)
* **priority** - priority of the e-mail, always one of the following: *normal* (default), *high*, *low*
* **text** - text body
* **html** - html body
* **attachments** - an array of attachments
### Decode a simple e-mail
This example decodes an e-mail from a string
var MailParser = require("mailparser").MailParser,
mailparser = new MailParser();
var email = "From: 'Sender Name' <sender@example.com>\r\n"+
"To: 'Receiver Name' <receiver@example.com>\r\n"+
"Subject: Hello world!\r\n"+
"\r\n"+
"How are you today?";
// setup an event listener when the parsing finishes
mailparser.on("end", function(mail_object){
console.log("From:", mail_object.from); //[{address:'sender@example.com',name:'Sender Name'}]
console.log("Subject:", mail_object.subject); // Hello world!
console.log("Text body:", mail_object.text); // How are you today?
});
// send the email source to the parser
mailparser.write(email);
mailparser.end();
### Pipe file to MailParser
This example pipes a `readableStream` file to **MailParser**
var MailParser = require("mailparser").MailParser,
mailparser = new MailParser(),
fs = require("fs");
mailparser.on("end", function(mail_object){
console.log("Subject:", mail_object.subject);
});
fs.createReadStream("email.eml").pipe(mailparser);
### Attachments
By default any attachment found from the e-mail will be included fully in the
final mail structure object as Buffer objects. With large files this might not
be desirable so optionally it is possible to redirect the attachments to a Stream
and keep only the metadata about the file in the mail structure.
mailparser.on("end", function(mail_object){
for(var i=0; i<mail_object.attachments.length; i++){
console.log(mail_object.attachments[i].fileName);
}
});
#### Default behavior
By default attachments will be included in the attachment objects as Buffers.
attachments = [{
contentType: 'image/png',
fileName: 'image.png',
contentDisposition: 'attachment',
contentId: '5.1321281380971@localhost',
transferEncoding: 'base64',
length: 126,
generatedFileName: 'image.png',
checksum: 'e4cef4c6e26037bcf8166905207ea09b',
content: <Buffer ...>
}];
The property `generatedFileName` is usually the same as `fileName` but if several
different attachments with the same name exist or there is no `fileName` set, an
unique name is generated.
Property `content` is always a Buffer object (or SlowBuffer on some occasions)
#### Attachment streaming
Attachment streaming can be used when providing an optional options parameter
to the `MailParser` constructor.
var mp = new MailParser({
streamAttachments: true
}
This way there will be no `content` property on final attachment objects
(but the other fields will remain).
To catch the streams you should listen for `attachment` events on the MailParser
object. The parameter provided includes file information (`contentType`,
`fileName`, `contentId`) and a readable Stream object `stream`.
var mp = new MailParser({
streamAttachments: true
}
mp.on("attachment", function(attachment){
var output = fs.createWriteStream(attachment.generatedFileName);
attachment.stream.pipe(output);
});
`generatedFileName` is unique for the parsed mail - if several attachments with
the same name exist, `generatedFileName` is updated accordingly. Also there
might not be `fileName` parameter at all, so it is better to rely on
`generatedFileName`.
#### Testing attachment integrity
Attachment objects include `length` property which is the length of the attachment
in bytes and `checksum` property which is a `md5` hash of the file.
### Running tests
Install **MailParser** with dev dependencies
npm install --dev mailparser
And then run
npm test mailparser
There aren't many tests yet but basics should be covered.
## Issues
**S/MIME**
Currently it is not possible to verify signed content as the incoming text is
split to lines when parsing and line ending characters are not preserved. One
can assume it is always \r\n but this might not be always the case.
**Seeking**
Due to the line based parsing it is also not possible to explicitly state
the beginning and ending bytes of the attachments for later source seeking.
Node.js doesn't support the concept of seeking very well anyway.
## License
**MIT**

304
lib/datetime.js Normal file
View File

@@ -0,0 +1,304 @@
/*
* More info at: http://phpjs.org
*
* This is version: 3.18
* php.js is copyright 2010 Kevin van Zonneveld.
*
* Portions copyright Brett Zamir (http://brett-zamir.me), Kevin van Zonneveld
* (http://kevin.vanzonneveld.net), Onno Marsman, Theriault, Michael White
* (http://getsprink.com), Waldo Malqui Silva, Paulo Freitas, Jonas Raoni
* Soares Silva (http://www.jsfromhell.com), Jack, Philip Peterson, Ates Goral
* (http://magnetiq.com), Legaev Andrey, Ratheous, Alex, Martijn Wieringa,
* Nate, lmeyrick (https://sourceforge.net/projects/bcmath-js/), Philippe
* Baumann, Enrique Gonzalez, Webtoolkit.info (http://www.webtoolkit.info/),
* Ash Searle (http://hexmen.com/blog/), travc, Jani Hartikainen, Carlos R. L.
* Rodrigues (http://www.jsfromhell.com), Ole Vrijenhoek, WebDevHobo
* (http://webdevhobo.blogspot.com/), T.Wild,
* http://stackoverflow.com/questions/57803/how-to-convert-decimal-to-hex-in-javascript,
* pilus, GeekFG (http://geekfg.blogspot.com), Rafał Kukawski
* (http://blog.kukawski.pl), Johnny Mast (http://www.phpvrouwen.nl), Michael
* Grier, Erkekjetter, d3x, marrtins, Andrea Giammarchi
* (http://webreflection.blogspot.com), stag019, mdsjack
* (http://www.mdsjack.bo.it), Chris, Steven Levithan
* (http://blog.stevenlevithan.com), Arpad Ray (mailto:arpad@php.net), David,
* Joris, Tim de Koning (http://www.kingsquare.nl), Marc Palau, Michael White,
* Public Domain (http://www.json.org/json2.js), gettimeofday, felix, Aman
* Gupta, Pellentesque Malesuada, Thunder.m, Tyler Akins (http://rumkin.com),
* Karol Kowalski, Felix Geisendoerfer (http://www.debuggable.com/felix),
* Alfonso Jimenez (http://www.alfonsojimenez.com), Diplom@t
* (http://difane.com/), majak, Mirek Slugen, Mailfaker
* (http://www.weedem.fr/), Breaking Par Consulting Inc
* (http://www.breakingpar.com/bkp/home.nsf/0/87256B280015193F87256CFB006C45F7),
* Josh Fraser
* (http://onlineaspect.com/2007/06/08/auto-detect-a-time-zone-with-javascript/),
* Martin (http://www.erlenwiese.de/), Paul Smith, KELAN, Robin, saulius, AJ,
* Oleg Eremeev, Steve Hilder, gorthaur, Kankrelune
* (http://www.webfaktory.info/), Caio Ariede (http://caioariede.com), Lars
* Fischer, Sakimori, Imgen Tata (http://www.myipdf.com/), uestla, Artur
* Tchernychev, Wagner B. Soares, Christoph, nord_ua, class_exists, Der Simon
* (http://innerdom.sourceforge.net/), echo is bad, XoraX
* (http://www.xorax.info), Ozh, Alan C, Taras Bogach, Brad Touesnard, MeEtc
* (http://yass.meetcweb.com), Peter-Paul Koch
* (http://www.quirksmode.org/js/beat.html), T0bsn, Tim Wiel, Bryan Elliott,
* jpfle, JT, Thomas Beaucourt (http://www.webapp.fr), David Randall, Frank
* Forte, Eugene Bulkin (http://doubleaw.com/), noname, kenneth, Hyam Singer
* (http://www.impact-computing.com/), Marco, Raphael (Ao RUDLER), Ole
* Vrijenhoek (http://www.nervous.nl/), David James, Steve Clay, Jason Wong
* (http://carrot.org/), T. Wild, Paul, J A R, LH, strcasecmp, strcmp, JB,
* Daniel Esteban, strftime, madipta, Valentina De Rosa, Marc Jansen,
* Francesco, Stoyan Kyosev (http://www.svest.org/), metjay, Soren Hansen,
* 0m3r, Sanjoy Roy, Shingo, sankai, sowberry, hitwork, Rob, Norman "zEh"
* Fuchs, Subhasis Deb, josh, Yves Sucaet, Ulrich, Scott Baker, ejsanders,
* Nick Callen, Steven Levithan (stevenlevithan.com), Aidan Lister
* (http://aidanlister.com/), Philippe Jausions
* (http://pear.php.net/user/jausions), Zahlii, Denny Wardhana, Oskar Larsson
* Högfeldt (http://oskar-lh.name/), Brian Tafoya
* (http://www.premasolutions.com/), johnrembo, Gilbert, duncan, Thiago Mata
* (http://thiagomata.blog.com), Alexander Ermolaev
* (http://snippets.dzone.com/user/AlexanderErmolaev), Linuxworld, lmeyrick
* (https://sourceforge.net/projects/bcmath-js/this.), Jon Hohle, Pyerre,
* merabi, Saulo Vallory, HKM, ChaosNo1, djmix, Lincoln Ramsay, Adam Wallner
* (http://web2.bitbaro.hu/), paulo kuong, jmweb, Orlando, kilops, dptr1988,
* DxGx, Pedro Tainha (http://www.pedrotainha.com), Bayron Guevara, Le Torbi,
* James, Douglas Crockford (http://javascript.crockford.com), Devan
* Penner-Woelk, Jay Klehr, Kheang Hok Chin (http://www.distantia.ca/), Luke
* Smith (http://lucassmith.name), Rival, Amir Habibi
* (http://www.residence-mixte.com/), Blues (http://tech.bluesmoon.info/), Ben
* Bryan, booeyOH, Dreamer, Cagri Ekin, Diogo Resende, Howard Yeend, Pul,
* 3D-GRAF, jakes, Yannoo, Luke Godfrey, daniel airton wermann
* (http://wermann.com.br), Allan Jensen (http://www.winternet.no), Benjamin
* Lupton, davook, Atli Þór, Maximusya, Leslie Hoare, Bug?, setcookie, YUI
* Library: http://developer.yahoo.com/yui/docs/YAHOO.util.DateLocale.html,
* Blues at http://hacks.bluesmoon.info/strftime/strftime.js, Andreas,
* Michael, Christian Doebler, Gabriel Paderni, Marco van Oort, Philipp
* Lenssen, Arnout Kazemier (http://www.3rd-Eden.com), penutbutterjelly, Anton
* Ongson, DtTvB (http://dt.in.th/2008-09-16.string-length-in-bytes.html),
* meo, Greenseed, Yen-Wei Liu, mk.keck, William, rem, Jamie Beck
* (http://www.terabit.ca/), Russell Walker (http://www.nbill.co.uk/),
* Garagoth, Dino, Andrej Pavlovic, gabriel paderni, FGFEmperor, Scott Cariss,
* Slawomir Kaniecki, ReverseSyntax, Mateusz "loonquawl" Zalega, Francois,
* Kirk Strobeck, Billy, vlado houba, Jalal Berrami, date, Itsacon
* (http://www.itsacon.net/), Martin Pool, Pierre-Luc Paour, ger, john
* (http://www.jd-tech.net), mktime, Simon Willison
* (http://simonwillison.net), Nick Kolosov (http://sammy.ru), marc andreu,
* Arno, Nathan, Kristof Coomans (SCK-CEN Belgian Nucleair Research Centre),
* Fox, nobbler, stensi, Matteo, Riddler (http://www.frontierwebdev.com/),
* Tomasz Wesolowski, T.J. Leahy, rezna, Eric Nagel, Alexander M Beedie, baris
* ozdil, Greg Frazier, Bobby Drake, Ryan W Tenney (http://ryan.10e.us), Tod
* Gentille, Rafał Kukawski, FremyCompany, Manish, Cord, fearphage
* (http://http/my.opera.com/fearphage/), Victor, Brant Messenger
* (http://www.brantmessenger.com/), Matt Bradley, Luis Salazar
* (http://www.freaky-media.com/), Tim de Koning, taith, Rick Waldron, Mick@el
*
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included
* in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL KEVIN VAN ZONNEVELD BE LIABLE FOR ANY CLAIM, DAMAGES
* OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
this.strtotime = function(str, now) {
// http://kevin.vanzonneveld.net
// + original by: Caio Ariede (http://caioariede.com)
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: David
// + improved by: Caio Ariede (http://caioariede.com)
// + improved by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Wagner B. Soares
// + bugfixed by: Artur Tchernychev
// % note 1: Examples all have a fixed timestamp to prevent tests to fail because of variable time(zones)
// * example 1: strtotime('+1 day', 1129633200);
// * returns 1: 1129719600
// * example 2: strtotime('+1 week 2 days 4 hours 2 seconds', 1129633200);
// * returns 2: 1130425202
// * example 3: strtotime('last month', 1129633200);
// * returns 3: 1127041200
// * example 4: strtotime('2009-05-04 08:30:00');
// * returns 4: 1241418600
var i, match, s, strTmp = '', parse = '';
strTmp = str;
strTmp = strTmp.replace(/\s{2,}|^\s|\s$/g, ' '); // unecessary spaces
strTmp = strTmp.replace(/[ \r\n]/g, ''); // unecessary chars
if (strTmp == 'now') {
return (new Date()).getTime()/1000; // Return seconds, not milli-seconds
} else if (!isNaN(parse = Date.parse(strTmp))) {
return (parse/1000);
} else if (now) {
now = new Date(now*1000); // Accept PHP-style seconds
} else {
now = new Date();
}
strTmp = strTmp.toLowerCase();
var __is =
{
day:
{
'sun': 0,
'mon': 1,
'tue': 2,
'wed': 3,
'thu': 4,
'fri': 5,
'sat': 6
},
mon:
{
'jan': 0,
'feb': 1,
'mar': 2,
'apr': 3,
'may': 4,
'jun': 5,
'jul': 6,
'aug': 7,
'sep': 8,
'oct': 9,
'nov': 10,
'dec': 11
}
};
var process = function (m) {
var ago = (m[2] && m[2] == 'ago');
var num = (num = m[0] == 'last' ? -1 : 1) * (ago ? -1 : 1);
switch (m[0]) {
case 'last':
case 'next':
switch (m[1].substring(0, 3)) {
case 'yea':
now.setFullYear(now.getFullYear() + num);
break;
case 'mon':
now.setMonth(now.getMonth() + num);
break;
case 'wee':
now.setDate(now.getDate() + (num * 7));
break;
case 'day':
now.setDate(now.getDate() + num);
break;
case 'hou':
now.setHours(now.getHours() + num);
break;
case 'min':
now.setMinutes(now.getMinutes() + num);
break;
case 'sec':
now.setSeconds(now.getSeconds() + num);
break;
default:
var day;
if (typeof (day = __is.day[m[1].substring(0, 3)]) != 'undefined') {
var diff = day - now.getDay();
if (diff == 0) {
diff = 7 * num;
} else if (diff > 0) {
if (m[0] == 'last') {diff -= 7;}
} else {
if (m[0] == 'next') {diff += 7;}
}
now.setDate(now.getDate() + diff);
}
}
break;
default:
if (/\d+/.test(m[0])) {
num *= parseInt(m[0], 10);
switch (m[1].substring(0, 3)) {
case 'yea':
now.setFullYear(now.getFullYear() + num);
break;
case 'mon':
now.setMonth(now.getMonth() + num);
break;
case 'wee':
now.setDate(now.getDate() + (num * 7));
break;
case 'day':
now.setDate(now.getDate() + num);
break;
case 'hou':
now.setHours(now.getHours() + num);
break;
case 'min':
now.setMinutes(now.getMinutes() + num);
break;
case 'sec':
now.setSeconds(now.getSeconds() + num);
break;
}
} else {
return false;
}
break;
}
return true;
};
match = strTmp.match(/^(\d{2,4}-\d{2}-\d{2})(?:\s(\d{1,2}:\d{2}(:\d{2})?)?(?:\.(\d+))?)?$/);
if (match != null) {
if (!match[2]) {
match[2] = '00:00:00';
} else if (!match[3]) {
match[2] += ':00';
}
s = match[1].split(/-/g);
for (i in __is.mon) {
if (__is.mon[i] == s[1] - 1) {
s[1] = i;
}
}
s[0] = parseInt(s[0], 10);
s[0] = (s[0] >= 0 && s[0] <= 69) ? '20'+(s[0] < 10 ? '0'+s[0] : s[0]+'') : (s[0] >= 70 && s[0] <= 99) ? '19'+s[0] : s[0]+'';
return parseInt(this.strtotime(s[2] + ' ' + s[1] + ' ' + s[0] + ' ' + match[2])+(match[4] ? match[4]/1000 : ''), 10);
}
var regex = '([+-]?\\d+\\s'+
'(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+
'|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday'+
'|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday)'+
'|(last|next)\\s'+
'(years?|months?|weeks?|days?|hours?|min|minutes?|sec|seconds?'+
'|sun\\.?|sunday|mon\\.?|monday|tue\\.?|tuesday|wed\\.?|wednesday'+
'|thu\\.?|thursday|fri\\.?|friday|sat\\.?|saturday))'+
'(\\sago)?';
match = strTmp.match(new RegExp(regex, 'gi')); // Brett: seems should be case insensitive per docs, so added 'i'
if (match == null) {
return false;
}
for (i = 0; i < match.length; i++) {
if (!process(match[i].split(' '))) {
return false;
}
}
return (now.getTime()/1000);
}

1329
lib/mailparser.js Normal file

File diff suppressed because it is too large Load Diff

145
lib/streams.js Normal file
View File

@@ -0,0 +1,145 @@
var Stream = require('stream').Stream,
utillib = require('util'),
mimelib = require("mimelib"),
encodinglib = require("encoding"),
crypto = require("crypto");
module.exports.Base64Stream = Base64Stream;
module.exports.QPStream = QPStream;
module.exports.BinaryStream = BinaryStream;
function Base64Stream(){
Stream.call(this);
this.writable = true;
this.checksum = crypto.createHash("md5");
this.length = 0;
this.current = "";
}
utillib.inherits(Base64Stream, Stream);
Base64Stream.prototype.write = function(data){
this.handleInput(data);
return true;
};
Base64Stream.prototype.end = function(data){
this.handleInput(data);
this.emit("end");
return {
length: this.length,
checksum: this.checksum.digest("hex")
};
};
Base64Stream.prototype.handleInput = function(data){
if(!data || !data.length){
return;
}
data = (data || "").toString("utf-8");
var remainder = 0;
this.current += data.replace(/[^\w\+\/=]/g,'');
var buffer = new Buffer(this.current.substr(0, this.current.length - this.current.length % 4),"base64");
if(buffer.length){
this.length += buffer.length;
this.checksum.update(buffer);
this.emit("data", buffer);
}
this.current = (remainder=this.current.length % 4)?this.current.substr(- remainder):"";
};
function QPStream(charset){
Stream.call(this);
this.writable = true;
this.checksum = crypto.createHash("md5");
this.length = 0;
this.charset = charset || "UTF-8";
this.current = undefined;
}
utillib.inherits(QPStream, Stream);
QPStream.prototype.write = function(data){
this.handleInput(data);
return true;
};
QPStream.prototype.end = function(data){
this.handleInput(data);
this.flush();
this.emit("end");
return {
length: this.length,
checksum: this.checksum.digest("hex")
};
};
QPStream.prototype.handleInput = function(data){
if(!data || !data.length){
return;
}
data = (data || "").toString("utf-8");
if(data.match(/^\r\n/)){
data = data.substr(2);
}
if(typeof this.current !="string"){
this.current = data;
}else{
this.current += "\r\n" + data;
}
};
QPStream.prototype.flush = function(){
var buffer = mimelib.decodeQuotedPrintable(this.current, false, this.charset);
if(this.charset.toLowerCase() == "binary"){
// do nothing
}else if(this.charset.toLowerCase() != "utf-8"){
buffer = encodinglib.convert(buffer, "utf-8", this.charset);
}else{
buffer = new Buffer(buffer, "utf-8");
}
this.length += buffer.length;
this.checksum.update(buffer);
this.emit("data", buffer);
};
function BinaryStream(charset){
Stream.call(this);
this.writable = true;
this.checksum = crypto.createHash("md5");
this.length = 0;
this.charset = charset || "UTF-8";
this.current = "";
}
utillib.inherits(BinaryStream, Stream);
BinaryStream.prototype.write = function(data){
if(data && data.length){
this.length += data.length;
this.checksum.update(data);
this.emit("data", data);
}
return true;
};
BinaryStream.prototype.end = function(data){
if(data && data.length){
this.emit("data", data);
}
this.emit("end");
return {
length: this.length,
checksum: this.checksum.digest("hex")
};
};

77
package.json Normal file
View File

@@ -0,0 +1,77 @@
{
"_from": "mailparser@0.3.x",
"_id": "mailparser@0.3.6",
"_inBundle": false,
"_integrity": "sha1-Y8XfNt4kccqDZ0ZpcZLCtvY3Dmc=",
"_location": "/mailparser",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "mailparser@0.3.x",
"name": "mailparser",
"escapedName": "mailparser",
"rawSpec": "0.3.x",
"saveSpec": null,
"fetchSpec": "0.3.x"
},
"_requiredBy": [
"/"
],
"_resolved": "https://registry.npmjs.org/mailparser/-/mailparser-0.3.6.tgz",
"_shasum": "63c5df36de2471ca836746697192c2b6f6370e67",
"_spec": "mailparser@0.3.x",
"_where": "/home/s2/Documents/Code/eclipse workspace/n3wz/backend",
"author": {
"name": "Andris Reinman"
},
"bugs": {
"url": "https://github.com/andris9/mailparser/issues"
},
"bundleDependencies": false,
"dependencies": {
"encoding": ">=0.1.4",
"iconv": "*",
"mime": "*",
"mimelib": ">=0.2.6"
},
"deprecated": false,
"description": "Asynchronous and non-blocking parser for mime encoded e-mail messages",
"devDependencies": {
"nodeunit": "*"
},
"engine": {
"node": ">=0.4"
},
"homepage": "https://github.com/andris9/mailparser#readme",
"keywords": [
"e-mail",
"mime",
"parser"
],
"licenses": [
{
"type": "MIT",
"url": "http://github.com/andris9/mailparser/blob/master/LICENSE"
}
],
"main": "./lib/mailparser",
"maintainers": [
{
"name": "andris",
"email": "andris@node.ee"
}
],
"name": "mailparser",
"optionalDependencies": {
"iconv": "*"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/andris9/mailparser.git"
},
"scripts": {
"test": "nodeunit test/"
},
"version": "0.3.6"
}

1337
test/mailparser.js Normal file

File diff suppressed because it is too large Load Diff

63
test/nested.eml Normal file
View File

@@ -0,0 +1,63 @@
From: u@example.com
Content-Type: multipart/mixed;
boundary="----=_NextPart_000_0D48_01CE140D.19527DD0"
------=_NextPart_000_0D48_01CE140D.19527DD0
Content-Type: multipart/related;
boundary="----=_NextPart_001_0D49_01CE140D.19527DD0"
------=_NextPart_001_0D49_01CE140D.19527DD0
Content-Type: multipart/alternative;
boundary="----=_NextPart_002_0D4A_01CE140D.19527DD0"
------=_NextPart_002_0D4A_01CE140D.19527DD0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
charset="utf-8"
Dear Sir,
Good evening.
------=_NextPart_002_0D4A_01CE140D.19527DD0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/html;
charset="utf-8"
<p>Dear Sir</p>
<p>Good evening.</p>
<p></p>
------=_NextPart_002_0D4A_01CE140D.19527DD0--
------=_NextPart_000_0D48_01CE140D.19527DD0
Content-Type: multipart/alternative; boundary="===============1276485360=="
MIME-Version: 1.0
Content-Disposition: inline
--===============1276485360==
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
The footer
--===============1276485360==
Content-Type: text/html; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: quoted-printable
<p>The footer</p>
--===============1276485360==--
------=_NextPart_000_0D48_01CE140D.19527DD0--

22
test/windowsfail.js Normal file
View File

@@ -0,0 +1,22 @@
var MailParser = require("../lib/mailparser").MailParser,
testCase = require('nodeunit').testCase,
utillib = require("util"),
encodinglib = require("encoding");
// This test fails in windows as iconv-lite does not support CP949
exports["ks_c_5601-1987"] = function(test){
var encodedText = "Subject: =?ks_c_5601-1987?B?vcU=?=\r\n"+
"Content-Type: text/plain; charset=ks_c_5601-1987\r\n"+
"Content-Transfer-Encoding: base64\r\n"+
"\r\n"+
"vcU=",
mail = new Buffer(encodedText, "utf-8");
var mailparser = new MailParser();
mailparser.end(mail);
mailparser.on("end", function(mail){
test.equal(mail.subject, "신");
test.equal(mail.text.trim(), "신");
test.done();
});
};