mailparser
This commit is contained in:
3
.npmignore
Normal file
3
.npmignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules
|
||||
.DS_Store
|
||||
npm-debug.log
|
12
.travis.yml
Normal file
12
.travis.yml
Normal 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
16
LICENSE
Normal 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
217
README.md
Normal file
@@ -0,0 +1,217 @@
|
||||
MailParser
|
||||
==========
|
||||
|
||||
[](http://travis-ci.org/andris9/mailparser)
|
||||
[](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
|
||||
|
||||
[](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
304
lib/datetime.js
Normal 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
1329
lib/mailparser.js
Normal file
File diff suppressed because it is too large
Load Diff
145
lib/streams.js
Normal file
145
lib/streams.js
Normal 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
77
package.json
Normal 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
1337
test/mailparser.js
Normal file
File diff suppressed because it is too large
Load Diff
63
test/nested.eml
Normal file
63
test/nested.eml
Normal 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
22
test/windowsfail.js
Normal 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();
|
||||
});
|
||||
};
|
Reference in New Issue
Block a user