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