mailparser
Advanced tools
Comparing version 0.4.3 to 0.4.4
@@ -23,4 +23,4 @@ "use strict"; | ||
var STATES = { | ||
header: 0x1, | ||
body: 0x2, | ||
header: 0x1, | ||
body: 0x2, | ||
finished: 0x3 | ||
@@ -45,3 +45,3 @@ }; | ||
*/ | ||
function MailParser(options){ | ||
function MailParser(options) { | ||
@@ -54,19 +54,24 @@ // Make MailParser a Stream object | ||
* Options object | ||
* @public */ this.options = options || {}; | ||
* @public */ | ||
this.options = options || {}; | ||
/** | ||
* Indicates current state the parser is in | ||
* @private */ this._state = STATES.header; | ||
* @private */ | ||
this._state = STATES.header; | ||
/** | ||
* The remaining data from the previos chunk which is waiting to be processed | ||
* @private */ this._remainder = ""; | ||
* @private */ | ||
this._remainder = ""; | ||
/** | ||
* The complete tree structure of the e-mail | ||
* @public */ this.mimeTree = this._createMimeNode(); | ||
* @public */ | ||
this.mimeTree = this._createMimeNode(); | ||
/** | ||
* Current node of the multipart mime tree that is being processed | ||
* @private */ this._currentNode = this.mimeTree; | ||
* @private */ | ||
this._currentNode = this.mimeTree; | ||
@@ -78,7 +83,9 @@ // default values for the root node | ||
* An object of already used attachment filenames | ||
* @private */ this._fileNames = {}; | ||
* @private */ | ||
this._fileNames = {}; | ||
/** | ||
* An array of multipart nodes | ||
* @private */ this._multipartTree = []; | ||
* @private */ | ||
this._multipartTree = []; | ||
@@ -88,19 +95,24 @@ | ||
* This is the final mail structure object that is returned to the client | ||
* @public */ this.mailData = {}; | ||
* @public */ | ||
this.mailData = {}; | ||
/** | ||
* Line counter for debugging | ||
* @private */ this._lineCounter = 0; | ||
* @private */ | ||
this._lineCounter = 0; | ||
/** | ||
* Did the last chunk end with \r | ||
* @private */ this._lineFeed = false; | ||
* @private */ | ||
this._lineFeed = false; | ||
/** | ||
* Is the "headers" event already emitted | ||
* @private */ this._headersSent = false; | ||
* @private */ | ||
this._headersSent = false; | ||
/** | ||
* If the e-mail is in mbox format, unescape ">From " to "From " in body | ||
* @private */ this._isMbox = -1; | ||
* @private */ | ||
this._isMbox = -1; | ||
} | ||
@@ -117,7 +129,7 @@ // inherit methods and properties of Stream | ||
*/ | ||
MailParser.prototype.write = function(chunk, encoding){ | ||
if( this._write(chunk, encoding) ){ | ||
if(typeof setImmediate == "function"){ | ||
MailParser.prototype.write = function(chunk, encoding) { | ||
if (this._write(chunk, encoding)) { | ||
if (typeof setImmediate == "function") { | ||
setImmediate(this._process.bind(this)); | ||
}else{ | ||
} else { | ||
process.nextTick(this._process.bind(this)); | ||
@@ -137,12 +149,12 @@ } | ||
*/ | ||
MailParser.prototype.end = function(chunk, encoding){ | ||
MailParser.prototype.end = function(chunk, encoding) { | ||
this._write(chunk, encoding); | ||
if(this.options.debug && this._remainder){ | ||
console.log("REMAINDER: "+this._remainder); | ||
if (this.options.debug && this._remainder) { | ||
console.log("REMAINDER: " + this._remainder); | ||
} | ||
if(typeof setImmediate == "function"){ | ||
if (typeof setImmediate == "function") { | ||
setImmediate(this._process.bind(this, true)); | ||
}else{ | ||
} else { | ||
process.nextTick(this._process.bind(this, true)); | ||
@@ -159,4 +171,4 @@ } | ||
*/ | ||
MailParser.prototype._write = function(chunk, encoding){ | ||
if(typeof chunk == "string"){ | ||
MailParser.prototype._write = function(chunk, encoding) { | ||
if (typeof chunk == "string") { | ||
chunk = new Buffer(chunk, encoding); | ||
@@ -170,3 +182,3 @@ } | ||
// was already used, skip the \n | ||
if(this._lineFeed && chunk.charAt(0) === "\n"){ | ||
if (this._lineFeed && chunk.charAt(0) === "\n") { | ||
chunk = chunk.substr(1); | ||
@@ -176,3 +188,3 @@ } | ||
if(chunk && chunk.length){ | ||
if (chunk && chunk.length) { | ||
this._remainder += chunk; | ||
@@ -195,34 +207,33 @@ return true; | ||
*/ | ||
MailParser.prototype._process = function(finalPart){ | ||
MailParser.prototype._process = function(finalPart) { | ||
finalPart = !!finalPart; | ||
var lines = this._remainder.split(/\r?\n|\r/), | ||
line, i, len; | ||
if(!finalPart){ | ||
if (!finalPart) { | ||
this._remainder = lines.pop(); | ||
// force line to 1MB chunks if needed | ||
if(this._remainder.length>1048576){ | ||
this._remainder = this._remainder.replace(/(.{1048576}(?!\r?\n|\r))/g,"$&\n"); | ||
if (this._remainder.length > 1048576) { | ||
this._remainder = this._remainder.replace(/(.{1048576}(?!\r?\n|\r))/g, "$&\n"); | ||
} | ||
} | ||
for(i=0, len=lines.length; i < len; i++){ | ||
for (i = 0, len = lines.length; i < len; i++) { | ||
line = lines[i]; | ||
if(this.options.unescapeSMTP && line.substr(0,2)==".."){ | ||
if (this.options.unescapeSMTP && line.substr(0, 2) == "..") { | ||
line = line.substr(1); | ||
} | ||
if(this._isMbox === true && line.match(/^\>+From /)){ | ||
if (this._isMbox === true && line.match(/^\>+From /)) { | ||
line = line.substr(1); | ||
} | ||
if(this.options.debug){ | ||
console.log("LINE " + (++this._lineCounter) + " ("+this._state+"): "+line); | ||
if (this.options.debug) { | ||
console.log("LINE " + (++this._lineCounter) + " (" + this._state + "): " + line); | ||
} | ||
if(this._state == STATES.header){ | ||
if(this._processStateHeader(line) === true){ | ||
if (this._state == STATES.header) { | ||
if (this._processStateHeader(line) === true) { | ||
continue; | ||
@@ -232,15 +243,12 @@ } | ||
if(this._state == STATES.body){ | ||
if(this._processStateBody(line) === true){ | ||
if (this._state == STATES.body) { | ||
if (this._processStateBody(line) === true) { | ||
continue; | ||
} | ||
} | ||
} | ||
if(finalPart){ | ||
if(this._state == STATES.header && this._remainder){ | ||
if (finalPart) { | ||
if (this._state == STATES.header && this._remainder) { | ||
this._processStateHeader(this._remainder); | ||
if(!this._headersSent){ | ||
if (!this._headersSent) { | ||
this.emit("headers", this._currentNode.parsedHeaders); | ||
@@ -250,9 +258,9 @@ this._headersSent = true; | ||
} | ||
if(this._currentNode.content || this._currentNode.stream){ | ||
if (this._currentNode.content || this._currentNode.stream) { | ||
this._finalizeContents(); | ||
} | ||
this._state = STATES.finished; | ||
if(typeof setImmediate == "function"){ | ||
if (typeof setImmediate == "function") { | ||
setImmediate(this._processMimeTree.bind(this)); | ||
}else{ | ||
} else { | ||
process.nextTick(this._processMimeTree.bind(this)); | ||
@@ -274,12 +282,13 @@ } | ||
*/ | ||
MailParser.prototype._processStateHeader = function(line){ | ||
MailParser.prototype._processStateHeader = function(line) { | ||
var attachment, lastPos = this._currentNode.headers.length - 1, | ||
textContent = false, extension; | ||
textContent = false, | ||
extension; | ||
// Check if the header ends and body starts | ||
if(!line.length){ | ||
if(lastPos>=0){ | ||
if (!line.length) { | ||
if (lastPos >= 0) { | ||
this._processHeaderLine(lastPos); | ||
} | ||
if(!this._headersSent){ | ||
if (!this._headersSent) { | ||
this.emit("headers", this._currentNode.parsedHeaders); | ||
@@ -292,3 +301,3 @@ this._headersSent = true; | ||
// if there's unprocessed header data, do it now | ||
if(lastPos >= 0){ | ||
if (lastPos >= 0) { | ||
this._processHeaderLine(lastPos); | ||
@@ -298,3 +307,3 @@ } | ||
// this is a very simple e-mail, no content type set | ||
if(!this._currentNode.parentNode && !this._currentNode.meta.contentType){ | ||
if (!this._currentNode.parentNode && !this._currentNode.meta.contentType) { | ||
this._currentNode.meta.contentType = "text/plain"; | ||
@@ -306,6 +315,6 @@ } | ||
// detect if this is an attachment or a text node (some agents use inline dispositions for text) | ||
if(textContent && (!this._currentNode.meta.contentDisposition || this._currentNode.meta.contentDisposition == "inline")){ | ||
if (textContent && (!this._currentNode.meta.contentDisposition || this._currentNode.meta.contentDisposition == "inline")) { | ||
this._currentNode.attachment = false; | ||
}else if((!textContent || ["attachment", "inline"].indexOf(this._currentNode.meta.contentDisposition)>=0) && | ||
!this._currentNode.meta.mimeMultipart){ | ||
} else if ((!textContent || ["attachment", "inline"].indexOf(this._currentNode.meta.contentDisposition) >= 0) && | ||
!this._currentNode.meta.mimeMultipart) { | ||
this._currentNode.attachment = true; | ||
@@ -315,3 +324,3 @@ } | ||
// handle attachment start | ||
if(this._currentNode.attachment){ | ||
if (this._currentNode.attachment) { | ||
@@ -328,3 +337,3 @@ this._currentNode.checksum = crypto.createHash("md5"); | ||
// Update content-type if it's an application/octet-stream and file extension is available | ||
if(this._currentNode.meta.contentType == "application/octet-stream" && mime.lookup(extension)){ | ||
if (this._currentNode.meta.contentType == "application/octet-stream" && mime.lookup(extension)) { | ||
this._currentNode.meta.contentType = mime.lookup(extension); | ||
@@ -334,8 +343,10 @@ } | ||
attachment = this._currentNode.meta; | ||
if(this.options.streamAttachments){ | ||
if(this._currentNode.meta.transferEncoding == "base64"){ | ||
if (this.options.streamAttachments) { | ||
if (this._currentNode.meta.transferEncoding == "base64") { | ||
this._currentNode.stream = new Streams.Base64Stream(); | ||
}else if(this._currentNode.meta.transferEncoding == "quoted-printable"){ | ||
} else if (this._currentNode.meta.transferEncoding == "quoted-printable") { | ||
this._currentNode.stream = new Streams.QPStream("binary"); | ||
}else{ | ||
} else if (this._currentNode.meta.transferEncoding == "uuencode") { | ||
this._currentNode.stream = new Streams.UUEStream("binary"); | ||
} else { | ||
this._currentNode.stream = new Streams.BinaryStream(); | ||
@@ -346,3 +357,3 @@ } | ||
this.emit("attachment", attachment); | ||
}else{ | ||
} else { | ||
this._currentNode.content = undefined; | ||
@@ -356,7 +367,7 @@ } | ||
// unfold header lines if needed | ||
if(line.match(/^\s+/) && lastPos>=0){ | ||
if (line.match(/^\s+/) && lastPos >= 0) { | ||
this._currentNode.headers[lastPos] += " " + line.trim(); | ||
}else{ | ||
} else { | ||
this._currentNode.headers.push(line.trim()); | ||
if(lastPos>=0){ | ||
if (lastPos >= 0) { | ||
// if a complete header line is received, process it | ||
@@ -376,3 +387,3 @@ this._processHeaderLine(lastPos); | ||
*/ | ||
MailParser.prototype._processStateBody = function(line){ | ||
MailParser.prototype._processStateBody = function(line) { | ||
var i, len, node, | ||
@@ -382,9 +393,9 @@ nodeReady = false; | ||
// Handle multipart boundaries | ||
if(line.substr(0, 2) == "--"){ | ||
for(i=0, len = this._multipartTree.length; i<len; i++){ | ||
if (line.substr(0, 2) == "--") { | ||
for (i = 0, len = this._multipartTree.length; i < len; i++) { | ||
// check if a new element block starts | ||
if(line == "--" + this._multipartTree[i].boundary){ | ||
if (line == "--" + this._multipartTree[i].boundary) { | ||
if(this._currentNode.content || this._currentNode.stream){ | ||
if (this._currentNode.content || this._currentNode.stream) { | ||
this._finalizeContents(); | ||
@@ -399,13 +410,13 @@ } | ||
break; | ||
}else | ||
} else | ||
// check if a multipart block ends | ||
if(line == "--" + this._multipartTree[i].boundary + "--"){ | ||
if (line == "--" + this._multipartTree[i].boundary + "--") { | ||
if(this._currentNode.content || this._currentNode.stream){ | ||
if (this._currentNode.content || this._currentNode.stream) { | ||
this._finalizeContents(); | ||
} | ||
if(this._multipartTree[i].node.parentNode){ | ||
if (this._multipartTree[i].node.parentNode) { | ||
this._currentNode = this._multipartTree[i].node.parentNode; | ||
}else{ | ||
} else { | ||
this._currentNode = this._multipartTree[i].node; | ||
@@ -419,3 +430,3 @@ } | ||
} | ||
if(nodeReady){ | ||
if (nodeReady) { | ||
return true; | ||
@@ -425,6 +436,6 @@ } | ||
// handle text or attachment line | ||
if(["text/plain", "text/html", "text/calendar"].indexOf(this._currentNode.meta.contentType || "")>=0 && | ||
!this._currentNode.attachment){ | ||
if (["text/plain", "text/html", "text/calendar"].indexOf(this._currentNode.meta.contentType || "") >= 0 && | ||
!this._currentNode.attachment) { | ||
this._handleTextLine(line); | ||
}else if(this._currentNode.attachment){ | ||
} else if (this._currentNode.attachment) { | ||
this._handleAttachmentLine(line); | ||
@@ -448,3 +459,3 @@ } | ||
*/ | ||
MailParser.prototype._processHeaderLine = function(pos){ | ||
MailParser.prototype._processHeaderLine = function(pos) { | ||
var key, value, parts, line; | ||
@@ -454,8 +465,8 @@ | ||
if(!(line = this._currentNode.headers[pos]) || typeof line != "string"){ | ||
if (!(line = this._currentNode.headers[pos]) || typeof line != "string") { | ||
return; | ||
} | ||
if(!this._headersSent && this._isMbox < 0){ | ||
if((this._isMbox = !!line.match(/^From /))){ | ||
if (!this._headersSent && this._isMbox < 0) { | ||
if ((this._isMbox = !!line.match(/^From /))) { | ||
return; | ||
@@ -470,3 +481,3 @@ } | ||
switch(key){ | ||
switch (key) { | ||
case "content-type": | ||
@@ -480,3 +491,3 @@ this._parseContentType(value); | ||
this._currentNode.meta.date = new Date(value); | ||
if(Object.prototype.toString.call(this._currentNode.meta.date) != "[object Date]" || this._currentNode.meta.date.toString() == "Invalid Date"){ | ||
if (Object.prototype.toString.call(this._currentNode.meta.date) != "[object Date]" || this._currentNode.meta.date.toString() == "Invalid Date") { | ||
this._currentNode.meta.date = datetime.strtotime(value) && new Date(datetime.strtotime(value) * 1000); | ||
@@ -486,5 +497,5 @@ } | ||
case "to": | ||
if(this._currentNode.to && this._currentNode.to.length){ | ||
if (this._currentNode.to && this._currentNode.to.length) { | ||
this._currentNode.to = this._currentNode.to.concat(mimelib.parseAddresses(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.to = mimelib.parseAddresses(value); | ||
@@ -494,5 +505,5 @@ } | ||
case "from": | ||
if(this._currentNode.from && this._currentNode.from.length){ | ||
if (this._currentNode.from && this._currentNode.from.length) { | ||
this._currentNode.from = this._currentNode.from.concat(mimelib.parseAddresses(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.from = mimelib.parseAddresses(value); | ||
@@ -502,5 +513,5 @@ } | ||
case "reply-to": | ||
if(this._currentNode.replyTo && this._currentNode.replyTo.length){ | ||
if (this._currentNode.replyTo && this._currentNode.replyTo.length) { | ||
this._currentNode.replyTo = this._currentNode.replyTo.concat(mimelib.parseAddresses(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.replyTo = mimelib.parseAddresses(value); | ||
@@ -510,5 +521,5 @@ } | ||
case "cc": | ||
if(this._currentNode.cc && this._currentNode.cc.length){ | ||
if (this._currentNode.cc && this._currentNode.cc.length) { | ||
this._currentNode.cc = this._currentNode.cc.concat(mimelib.parseAddresses(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.cc = mimelib.parseAddresses(value); | ||
@@ -518,5 +529,5 @@ } | ||
case "bcc": | ||
if(this._currentNode.bcc && this._currentNode.bcc.length){ | ||
if (this._currentNode.bcc && this._currentNode.bcc.length) { | ||
this._currentNode.bcc = this._currentNode.bcc.concat(mimelib.parseAddresses(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.bcc = mimelib.parseAddresses(value); | ||
@@ -558,12 +569,15 @@ } | ||
if(this._currentNode.parsedHeaders[key]){ | ||
if(!Array.isArray(this._currentNode.parsedHeaders[key])){ | ||
if (this._currentNode.parsedHeaders[key]) { | ||
if (!Array.isArray(this._currentNode.parsedHeaders[key])) { | ||
this._currentNode.parsedHeaders[key] = [this._currentNode.parsedHeaders[key]]; | ||
} | ||
this._currentNode.parsedHeaders[key].push(this._replaceMimeWords(value)); | ||
}else{ | ||
} else { | ||
this._currentNode.parsedHeaders[key] = this._replaceMimeWords(value); | ||
} | ||
this._currentNode.headers[pos] = {key: key, value: value}; | ||
this._currentNode.headers[pos] = { | ||
key: key, | ||
value: value | ||
}; | ||
}; | ||
@@ -580,7 +594,7 @@ | ||
*/ | ||
MailParser.prototype._createMimeNode = function(parentNode){ | ||
MailParser.prototype._createMimeNode = function(parentNode) { | ||
var node = { | ||
parentNode: parentNode || this._currentNode || null, | ||
headers: [], | ||
parsedHeaders:{}, | ||
parsedHeaders: {}, | ||
meta: {}, | ||
@@ -611,3 +625,3 @@ childNodes: [] | ||
*/ | ||
MailParser.prototype._parseHeaderLineWithParams = function(value){ | ||
MailParser.prototype._parseHeaderLineWithParams = function(value) { | ||
var key, parts, returnValue = {}; | ||
@@ -618,3 +632,3 @@ | ||
for(var i=0, len = parts.length; i<len; i++){ | ||
for (var i = 0, len = parts.length; i < len; i++) { | ||
value = parts[i].split("="); | ||
@@ -641,26 +655,26 @@ key = value.shift().trim().toLowerCase(); | ||
*/ | ||
MailParser.prototype._parseContentType = function(value){ | ||
MailParser.prototype._parseContentType = function(value) { | ||
var fileName; | ||
value = this._parseHeaderLineWithParams(value); | ||
if(value){ | ||
if(value.defaultValue){ | ||
if (value) { | ||
if (value.defaultValue) { | ||
value.defaultValue = value.defaultValue.toLowerCase(); | ||
this._currentNode.meta.contentType = value.defaultValue; | ||
if(value.defaultValue.substr(0,"multipart/".length)=="multipart/"){ | ||
if (value.defaultValue.substr(0, "multipart/".length) == "multipart/") { | ||
this._currentNode.meta.mimeMultipart = value.defaultValue.substr("multipart/".length); | ||
} | ||
}else{ | ||
} else { | ||
this._currentNode.meta.contentType = "application/octet-stream"; | ||
} | ||
if(value.charset){ | ||
if (value.charset) { | ||
value.charset = value.charset.toLowerCase(); | ||
if(value.charset.substr(0,4)=="win-"){ | ||
value.charset = "windows-"+value.charset.substr(4); | ||
}else if(value.charset == "ks_c_5601-1987"){ | ||
if (value.charset.substr(0, 4) == "win-") { | ||
value.charset = "windows-" + value.charset.substr(4); | ||
} else if (value.charset == "ks_c_5601-1987") { | ||
value.charset = "cp949"; | ||
}else if(value.charset.match(/^utf\d/)){ | ||
value.charset = "utf-"+value.charset.substr(3); | ||
}else if(value.charset.match(/^latin[\-_]?\d/)){ | ||
value.charset = "iso-8859-"+value.charset.replace(/\D/g,""); | ||
}else if(value.charset.match(/^(us\-)?ascii$/)){ | ||
} else if (value.charset.match(/^utf\d/)) { | ||
value.charset = "utf-" + value.charset.substr(3); | ||
} else if (value.charset.match(/^latin[\-_]?\d/)) { | ||
value.charset = "iso-8859-" + value.charset.replace(/\D/g, ""); | ||
} else if (value.charset.match(/^(us\-)?ascii$/)) { | ||
value.charset = "utf-8"; | ||
@@ -670,13 +684,13 @@ } | ||
} | ||
if(value.format){ | ||
if (value.format) { | ||
this._currentNode.meta.textFormat = value.format.toLowerCase(); | ||
} | ||
if(value.delsp){ | ||
if (value.delsp) { | ||
this._currentNode.meta.textDelSp = value.delsp.toLowerCase(); | ||
} | ||
if(value.boundary){ | ||
if (value.boundary) { | ||
this._currentNode.meta.mimeBoundary = value.boundary; | ||
} | ||
if(value.method){ | ||
if (value.method) { | ||
this._currentNode.meta.method = value.method; | ||
@@ -686,7 +700,7 @@ } | ||
if(!this._currentNode.meta.fileName && (fileName = this._detectFilename(value))){ | ||
if (!this._currentNode.meta.fileName && (fileName = this._detectFilename(value))) { | ||
this._currentNode.meta.fileName = fileName; | ||
} | ||
if(value.boundary){ | ||
if (value.boundary) { | ||
this._currentNode.meta.mimeBoundary = value.boundary; | ||
@@ -711,10 +725,12 @@ this._multipartTree.push({ | ||
*/ | ||
MailParser.prototype._detectFilename = function(value){ | ||
var fileName="", i=0, parts, encoding, name; | ||
MailParser.prototype._detectFilename = function(value) { | ||
var fileName = "", | ||
i = 0, | ||
parts, encoding, name; | ||
if(value.name){ | ||
if (value.name) { | ||
return this._replaceMimeWords(value.name); | ||
} | ||
if(value.filename){ | ||
if (value.filename) { | ||
return this._replaceMimeWords(value.filename); | ||
@@ -724,22 +740,22 @@ } | ||
// RFC2231 | ||
if(value["name*"]){ | ||
if (value["name*"]) { | ||
fileName = value["name*"]; | ||
}else if(value["filename*"]){ | ||
} else if (value["filename*"]) { | ||
fileName = value["filename*"]; | ||
}else if(value["name*0*"]){ | ||
while(value["name*"+(i)+"*"]){ | ||
fileName += value["name*"+(i++)+"*"]; | ||
} else if (value["name*0*"]) { | ||
while (value["name*" + (i) + "*"]) { | ||
fileName += value["name*" + (i++) + "*"]; | ||
} | ||
}else if(value["filename*0*"]){ | ||
while(value["filename*"+(i)+"*"]){ | ||
fileName += value["filename*"+(i++)+"*"]; | ||
} else if (value["filename*0*"]) { | ||
while (value["filename*" + (i) + "*"]) { | ||
fileName += value["filename*" + (i++) + "*"]; | ||
} | ||
} | ||
if(fileName){ | ||
if (fileName) { | ||
parts = fileName.split("'"); | ||
encoding = parts.shift(); | ||
name = parts.pop(); | ||
if(name){ | ||
return this._replaceMimeWords(this._replaceMimeWords("=?"+(encoding || "us-ascii")+"?Q?" + name.replace(/%/g,"=")+"?=")); | ||
if (name) { | ||
return this._replaceMimeWords(this._replaceMimeWords("=?" + (encoding || "us-ascii") + "?Q?" + name.replace(/%/g, "=") + "?=")); | ||
} | ||
@@ -757,3 +773,3 @@ } | ||
*/ | ||
MailParser.prototype._parseContentDisposition = function(value){ | ||
MailParser.prototype._parseContentDisposition = function(value) { | ||
var fileName; | ||
@@ -763,7 +779,7 @@ | ||
if(value){ | ||
if(value.defaultValue){ | ||
if (value) { | ||
if (value.defaultValue) { | ||
this._currentNode.meta.contentDisposition = value.defaultValue.trim().toLowerCase(); | ||
} | ||
if((fileName = this._detectFilename(value))){ | ||
if ((fileName = this._detectFilename(value))) { | ||
this._currentNode.meta.fileName = fileName; | ||
@@ -779,9 +795,6 @@ } | ||
*/ | ||
MailParser.prototype._parseReferences = function(value){ | ||
MailParser.prototype._parseReferences = function(value) { | ||
this._currentNode.references = (this._currentNode.references || []).concat( | ||
(value || "").toString(). | ||
trim(). | ||
split(/\s+/). | ||
map(this._trimQuotes.bind(this)) | ||
); | ||
(value || "").toString().trim().split(/\s+/).map(this._trimQuotes.bind(this)) | ||
); | ||
}; | ||
@@ -794,9 +807,6 @@ | ||
*/ | ||
MailParser.prototype._parseInReplyTo = function(value){ | ||
MailParser.prototype._parseInReplyTo = function(value) { | ||
this._currentNode.inReplyTo = (this._currentNode.inReplyTo || []).concat( | ||
(value || "").toString(). | ||
trim(). | ||
split(/\s+/). | ||
map(this._trimQuotes.bind(this)) | ||
); | ||
(value || "").toString().trim().split(/\s+/).map(this._trimQuotes.bind(this)) | ||
); | ||
}; | ||
@@ -810,15 +820,15 @@ | ||
*/ | ||
MailParser.prototype._parsePriority = function(value){ | ||
MailParser.prototype._parsePriority = function(value) { | ||
value = value.toLowerCase().trim(); | ||
if(!isNaN(parseInt(value,10))){ // support "X-Priority: 1 (Highest)" | ||
if (!isNaN(parseInt(value, 10))) { // support "X-Priority: 1 (Highest)" | ||
value = parseInt(value, 10) || 0; | ||
if(value == 3){ | ||
if (value == 3) { | ||
return "normal"; | ||
}else if(value > 3){ | ||
} else if (value > 3) { | ||
return "low"; | ||
}else{ | ||
} else { | ||
return "high"; | ||
} | ||
}else{ | ||
switch(value){ | ||
} else { | ||
switch (value) { | ||
case "non-urgent": | ||
@@ -842,25 +852,25 @@ case "low": | ||
*/ | ||
MailParser.prototype._handleTextLine = function(line){ | ||
MailParser.prototype._handleTextLine = function(line) { | ||
if(["quoted-printable", "base64"].indexOf(this._currentNode.meta.transferEncoding)>=0 || this._currentNode.meta.textFormat != "flowed"){ | ||
if(typeof this._currentNode.content != "string"){ | ||
if (["quoted-printable", "base64"].indexOf(this._currentNode.meta.transferEncoding) >= 0 || this._currentNode.meta.textFormat != "flowed") { | ||
if (typeof this._currentNode.content != "string") { | ||
this._currentNode.content = line; | ||
}else{ | ||
this._currentNode.content += "\n"+line; | ||
} else { | ||
this._currentNode.content += "\n" + line; | ||
} | ||
}else{ | ||
if(typeof this._currentNode.content != "string"){ | ||
} else { | ||
if (typeof this._currentNode.content != "string") { | ||
this._currentNode.content = line; | ||
}else if(this._currentNode.content.match(/[ ]$/)){ | ||
if(this._currentNode.meta.textFormat == "flowed" && this._currentNode.content.match(/(^|\n)-- $/)){ | ||
} else if (this._currentNode.content.match(/[ ]$/)) { | ||
if (this._currentNode.meta.textFormat == "flowed" && this._currentNode.content.match(/(^|\n)-- $/)) { | ||
// handle special case for usenet signatures | ||
this._currentNode.content += "\n"+line; | ||
}else{ | ||
if(this._currentNode.meta.textDelSp == "yes"){ | ||
this._currentNode.content = this._currentNode.content.replace(/[ ]+$/,""); | ||
this._currentNode.content += "\n" + line; | ||
} else { | ||
if (this._currentNode.meta.textDelSp == "yes") { | ||
this._currentNode.content = this._currentNode.content.replace(/[ ]+$/, ""); | ||
} | ||
this._currentNode.content += line; | ||
} | ||
}else{ | ||
this._currentNode.content += "\n"+line; | ||
} else { | ||
this._currentNode.content += "\n" + line; | ||
} | ||
@@ -878,17 +888,17 @@ } | ||
*/ | ||
MailParser.prototype._handleAttachmentLine = function(line){ | ||
if(!this._currentNode.attachment){ | ||
MailParser.prototype._handleAttachmentLine = function(line) { | ||
if (!this._currentNode.attachment) { | ||
return; | ||
} | ||
if(this._currentNode.stream){ | ||
if(!this._currentNode.streamStarted){ | ||
if (this._currentNode.stream) { | ||
if (!this._currentNode.streamStarted) { | ||
this._currentNode.streamStarted = true; | ||
this._currentNode.stream.write(new Buffer(line, "binary")); | ||
}else{ | ||
this._currentNode.stream.write(new Buffer("\r\n"+line, "binary")); | ||
} else { | ||
this._currentNode.stream.write(new Buffer("\r\n" + line, "binary")); | ||
} | ||
}else if("content" in this._currentNode){ | ||
if(typeof this._currentNode.content!="string"){ | ||
} else if ("content" in this._currentNode) { | ||
if (typeof this._currentNode.content != "string") { | ||
this._currentNode.content = line; | ||
}else{ | ||
} else { | ||
this._currentNode.content += "\r\n" + line; | ||
@@ -906,31 +916,31 @@ } | ||
*/ | ||
MailParser.prototype._finalizeContents = function(){ | ||
MailParser.prototype._finalizeContents = function() { | ||
var streamInfo; | ||
if(this._currentNode.content){ | ||
if (this._currentNode.content) { | ||
if(!this._currentNode.attachment){ | ||
if (!this._currentNode.attachment) { | ||
if(this._currentNode.meta.contentType == "text/html"){ | ||
this._currentNode.meta.charset = this._detectHTMLCharset(this._currentNode.content) || this._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1"; | ||
if (this._currentNode.meta.contentType == "text/html") { | ||
this._currentNode.meta.charset = this._detectHTMLCharset(this._currentNode.content) || this._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1"; | ||
} | ||
if(this._currentNode.meta.transferEncoding == "quoted-printable"){ | ||
if (this._currentNode.meta.transferEncoding == "quoted-printable") { | ||
this._currentNode.content = mimelib.decodeQuotedPrintable(this._currentNode.content, false, this._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1"); | ||
if(this._currentNode.meta.textFormat == "flowed"){ | ||
if(this._currentNode.meta.textDelSp == "yes"){ | ||
if (this._currentNode.meta.textFormat == "flowed") { | ||
if (this._currentNode.meta.textDelSp == "yes") { | ||
this._currentNode.content = this._currentNode.content.replace(/(^|\n)-- \n/g, '$1-- \u0000').replace(/ \n/g, '').replace(/(^|\n)-- \u0000/g, '$1-- \n'); | ||
}else{ | ||
} else { | ||
this._currentNode.content = this._currentNode.content.replace(/(^|\n)-- \n/g, '$1-- \u0000').replace(/ \n/g, ' ').replace(/(^|\n)-- \u0000/g, '$1-- \n'); | ||
} | ||
} | ||
}else if(this._currentNode.meta.transferEncoding == "base64"){ | ||
} else if (this._currentNode.meta.transferEncoding == "base64") { | ||
this._currentNode.content = mimelib.decodeBase64(this._currentNode.content, this._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1"); | ||
}else{ | ||
} else { | ||
this._currentNode.content = this._convertStringToUTF8(this._currentNode.content); | ||
} | ||
}else{ | ||
if(this._currentNode.meta.transferEncoding == "quoted-printable"){ | ||
} else { | ||
if (this._currentNode.meta.transferEncoding == "quoted-printable") { | ||
this._currentNode.content = mimelib.decodeQuotedPrintable(this._currentNode.content, false, "binary"); | ||
}else if(this._currentNode.meta.transferEncoding == "base64"){ | ||
} else if (this._currentNode.meta.transferEncoding == "base64") { | ||
@@ -940,3 +950,6 @@ // WTF? if newlines are not removed, the resulting hash is *always* different | ||
}else{ | ||
} else if (this._currentNode.meta.transferEncoding == "uuencode") { | ||
var uuestream = new Streams.UUEStream("binary"); | ||
this._currentNode.content = uuestream.decode(new Buffer(this._currentNode.content, "binary")); | ||
} else { | ||
this._currentNode.content = new Buffer(this._currentNode.content, "binary"); | ||
@@ -951,8 +964,8 @@ } | ||
if(this._currentNode.stream){ | ||
if (this._currentNode.stream) { | ||
streamInfo = this._currentNode.stream.end() || {}; | ||
if(streamInfo.checksum){ | ||
if (streamInfo.checksum) { | ||
this._currentNode.meta.checksum = streamInfo.checksum; | ||
} | ||
if(streamInfo.length){ | ||
if (streamInfo.length) { | ||
this._currentNode.meta.length = streamInfo.length; | ||
@@ -971,18 +984,24 @@ } | ||
*/ | ||
MailParser.prototype._processMimeTree = function(){ | ||
var returnValue = {}, i, len; | ||
MailParser.prototype._processMimeTree = function() { | ||
var returnValue = {}, | ||
i, len; | ||
this.mailData = {html:[], text:[], calendar:[], attachments:[]}; | ||
this.mailData = { | ||
html: [], | ||
text: [], | ||
calendar: [], | ||
attachments: [] | ||
}; | ||
if(!this.mimeTree.meta.mimeMultipart){ | ||
if (!this.mimeTree.meta.mimeMultipart) { | ||
this._processMimeNode(this.mimeTree, 0); | ||
}else{ | ||
} else { | ||
this._walkMimeTree(this.mimeTree); | ||
} | ||
if(this.mailData.html.length){ | ||
for(i=0, len=this.mailData.html.length; i<len; i++){ | ||
if(!returnValue.html && this.mailData.html[i].content){ | ||
if (this.mailData.html.length) { | ||
for (i = 0, len = this.mailData.html.length; i < len; i++) { | ||
if (!returnValue.html && this.mailData.html[i].content) { | ||
returnValue.html = this.mailData.html[i].content; | ||
}else if(this.mailData.html[i].content){ | ||
} else if (this.mailData.html[i].content) { | ||
returnValue.html = this._concatHTML(returnValue.html, this.mailData.html[i].content); | ||
@@ -993,7 +1012,7 @@ } | ||
if(this.mailData.text.length){ | ||
for(i=0, len=this.mailData.text.length; i<len; i++){ | ||
if(!returnValue.text && this.mailData.text[i].content){ | ||
if (this.mailData.text.length) { | ||
for (i = 0, len = this.mailData.text.length; i < len; i++) { | ||
if (!returnValue.text && this.mailData.text[i].content) { | ||
returnValue.text = this.mailData.text[i].content; | ||
}else if(this.mailData.text[i].content){ | ||
} else if (this.mailData.text[i].content) { | ||
returnValue.text += this.mailData.text[i].content; | ||
@@ -1005,5 +1024,5 @@ } | ||
if(this.mailData.calendar.length){ | ||
if (this.mailData.calendar.length) { | ||
returnValue.alternatives = []; | ||
for(i=0, len=this.mailData.calendar.length; i<len; i++){ | ||
for (i = 0, len = this.mailData.calendar.length; i < len; i++) { | ||
returnValue.alternatives.push(this.mailData.calendar[i].content); | ||
@@ -1015,49 +1034,49 @@ } | ||
if(this.mimeTree.subject){ | ||
if (this.mimeTree.subject) { | ||
returnValue.subject = this.mimeTree.subject; | ||
} | ||
if(this.mimeTree.references){ | ||
if (this.mimeTree.references) { | ||
returnValue.references = this.mimeTree.references; | ||
} | ||
if(this.mimeTree.messageId){ | ||
if (this.mimeTree.messageId) { | ||
returnValue.messageId = this.mimeTree.messageId; | ||
} | ||
if(this.mimeTree.inReplyTo){ | ||
if (this.mimeTree.inReplyTo) { | ||
returnValue.inReplyTo = this.mimeTree.inReplyTo; | ||
} | ||
if(this.mimeTree.priority){ | ||
if (this.mimeTree.priority) { | ||
returnValue.priority = this.mimeTree.priority; | ||
} | ||
if(this.mimeTree.from){ | ||
if (this.mimeTree.from) { | ||
returnValue.from = this.mimeTree.from; | ||
} | ||
if(this.mimeTree.replyTo){ | ||
if (this.mimeTree.replyTo) { | ||
returnValue.replyTo = this.mimeTree.replyTo; | ||
} | ||
if(this.mimeTree.to){ | ||
if (this.mimeTree.to) { | ||
returnValue.to = this.mimeTree.to; | ||
} | ||
if(this.mimeTree.cc){ | ||
if (this.mimeTree.cc) { | ||
returnValue.cc = this.mimeTree.cc; | ||
} | ||
if(this.mimeTree.bcc){ | ||
if (this.mimeTree.bcc) { | ||
returnValue.bcc = this.mimeTree.bcc; | ||
} | ||
if(this.mimeTree.meta.date){ | ||
if (this.mimeTree.meta.date) { | ||
returnValue.date = this.mimeTree.meta.date; | ||
} | ||
if(this.mailData.attachments.length){ | ||
if (this.mailData.attachments.length) { | ||
returnValue.attachments = []; | ||
for(i=0, len=this.mailData.attachments.length; i<len; i++){ | ||
for (i = 0, len = this.mailData.attachments.length; i < len; i++) { | ||
returnValue.attachments.push(this.mailData.attachments[i].content); | ||
@@ -1067,5 +1086,5 @@ } | ||
if(typeof setImmediate == "function"){ | ||
if (typeof setImmediate == "function") { | ||
setImmediate(this.emit.bind(this, "end", returnValue)); | ||
}else{ | ||
} else { | ||
process.nextTick(this.emit.bind(this, "end", returnValue)); | ||
@@ -1081,7 +1100,7 @@ } | ||
*/ | ||
MailParser.prototype._walkMimeTree = function(node, level){ | ||
MailParser.prototype._walkMimeTree = function(node, level) { | ||
level = level || 1; | ||
for(var i=0, len = node.childNodes.length; i<len; i++){ | ||
for (var i = 0, len = node.childNodes.length; i < len; i++) { | ||
this._processMimeNode(node.childNodes[i], level, node.meta.mimeMultipart); | ||
this._walkMimeTree(node.childNodes[i], level+1); | ||
this._walkMimeTree(node.childNodes[i], level + 1); | ||
} | ||
@@ -1099,3 +1118,3 @@ }; | ||
*/ | ||
MailParser.prototype._processMimeNode = function(node, level, mimeMultipart){ | ||
MailParser.prototype._processMimeNode = function(node, level, mimeMultipart) { | ||
var i, len; | ||
@@ -1105,8 +1124,8 @@ | ||
if(!node.attachment){ | ||
switch(node.meta.contentType){ | ||
if (!node.attachment) { | ||
switch (node.meta.contentType) { | ||
case "text/html": | ||
if(mimeMultipart == "mixed" && this.mailData.html.length){ | ||
for(i=0, len = this.mailData.html.length; i<len; i++){ | ||
if(this.mailData.html[i].level == level){ | ||
if (mimeMultipart == "mixed" && this.mailData.html.length) { | ||
for (i = 0, len = this.mailData.html.length; i < len; i++) { | ||
if (this.mailData.html[i].level == level) { | ||
this._joinHTMLNodes(this.mailData.html[i], node.content); | ||
@@ -1117,24 +1136,36 @@ return; | ||
} | ||
this.mailData.html.push({content: this._updateHTMLCharset(node.content || ""), level: level}); | ||
this.mailData.html.push({ | ||
content: this._updateHTMLCharset(node.content || ""), | ||
level: level | ||
}); | ||
return; | ||
case "text/plain": | ||
this.mailData.text.push({content: node.content || "", level: level}); | ||
this.mailData.text.push({ | ||
content: node.content || "", | ||
level: level | ||
}); | ||
return; | ||
case "text/calendar": | ||
if(node.content){ | ||
if (node.content) { | ||
node.meta.content = node.content; | ||
} | ||
this.mailData.calendar.push({content: node.meta || {}, level: level}); | ||
this.mailData.calendar.push({ | ||
content: node.meta || {}, | ||
level: level | ||
}); | ||
return; | ||
} | ||
}else{ | ||
} else { | ||
node.meta = node.meta || {}; | ||
if(node.content){ | ||
if (node.content) { | ||
node.meta.content = node.content; | ||
} | ||
this.mailData.attachments.push({content: node.meta || {}, level: level}); | ||
this.mailData.attachments.push({ | ||
content: node.meta || {}, | ||
level: level | ||
}); | ||
if(this.options.showAttachmentLinks && mimeMultipart == "mixed" && this.mailData.html.length){ | ||
for(i=0, len = this.mailData.html.length; i<len; i++){ | ||
if(this.mailData.html[i].level == level){ | ||
if (this.options.showAttachmentLinks && mimeMultipart == "mixed" && this.mailData.html.length) { | ||
for (i = 0, len = this.mailData.html.length; i < len; i++) { | ||
if (this.mailData.html[i].level == level) { | ||
this._joinHTMLAttachment(this.mailData.html[i], node.meta); | ||
@@ -1154,3 +1185,3 @@ return; | ||
*/ | ||
MailParser.prototype._joinHTMLNodes = function(htmlNode, newHTML){ | ||
MailParser.prototype._joinHTMLNodes = function(htmlNode, newHTML) { | ||
var inserted = false; | ||
@@ -1166,7 +1197,7 @@ | ||
newHTML = newHTML.replace(/<head( [^>]*)?>(.*)<\/head( [^>]*)?>/gi, ""). | ||
replace(/<\/?html( [^>]*)?>/gi, ""). | ||
trim(); | ||
replace(/<\/?html( [^>]*)?>/gi, ""). | ||
trim(); | ||
// keep only text between <body> tags (if <body exists) | ||
newHTML.replace(/<body(?: [^>]*)?>(.*)<\/body( [^>]*)?>/gi, function(match, body){ | ||
newHTML.replace(/<body(?: [^>]*)?>(.*)<\/body( [^>]*)?>/gi, function(match, body) { | ||
newHTML = body.trim(); | ||
@@ -1177,3 +1208,3 @@ }); | ||
htmlNode.content = htmlNode.content.replace(/<\/body( [^>]*)?>/i, function(match){ | ||
htmlNode.content = htmlNode.content.replace(/<\/body( [^>]*)?>/i, function(match) { | ||
inserted = true; | ||
@@ -1183,3 +1214,3 @@ return "<br/>\n" + newHTML + match; | ||
if(!inserted){ | ||
if (!inserted) { | ||
htmlNode.content += "<br/>\n" + newHTML; | ||
@@ -1195,3 +1226,3 @@ } | ||
*/ | ||
MailParser.prototype._joinHTMLAttachment = function(htmlNode, attachment){ | ||
MailParser.prototype._joinHTMLAttachment = function(htmlNode, attachment) { | ||
var inserted = false, | ||
@@ -1205,3 +1236,3 @@ fname = attachment.generatedFileName.replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """), | ||
htmlNode.content = htmlNode.content.replace(/<\/body\b[^>]*>/i, function(match){ | ||
htmlNode.content = htmlNode.content.replace(/<\/body\b[^>]*>/i, function(match) { | ||
inserted = true; | ||
@@ -1211,3 +1242,3 @@ return "<br/>\n" + newHTML + match; | ||
if(!inserted){ | ||
if (!inserted) { | ||
htmlNode.content += "<br/>\n" + newHTML; | ||
@@ -1224,3 +1255,3 @@ } | ||
*/ | ||
MailParser.prototype._concatHTML = function(firstNode, secondNode){ | ||
MailParser.prototype._concatHTML = function(firstNode, secondNode) { | ||
var headerNode = "", | ||
@@ -1232,17 +1263,17 @@ htmlHeader = ""; | ||
if(!secondNode){ | ||
if (!secondNode) { | ||
return firstNode; | ||
} | ||
if(!firstNode){ | ||
if (!firstNode) { | ||
return secondNode; | ||
} | ||
if(firstNode.substr(0, 1024).replace(/\r?\n/g,"\u0000").match(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*?>/i)){ | ||
if (firstNode.substr(0, 1024).replace(/\r?\n/g, "\u0000").match(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*?>/i)) { | ||
headerNode = firstNode; | ||
}else if(secondNode.substr(0, 1024).replace(/\r?\n/g,"\u0000").match(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*?>/i)){ | ||
} else if (secondNode.substr(0, 1024).replace(/\r?\n/g, "\u0000").match(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*?>/i)) { | ||
headerNode = secondNode; | ||
} | ||
if(headerNode){ | ||
headerNode.replace(/\r?\n/g, "\u0000").replace(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*>.*?<\/(head)\b[^>]*>(.*?<body\b[^>]*>)?/i, function(h){ | ||
if (headerNode) { | ||
headerNode.replace(/\r?\n/g, "\u0000").replace(/^[\s\u0000]*(<\!doctype\b[^>]*?>)?[\s\u0000]*<(html|head)\b[^>]*>.*?<\/(head)\b[^>]*>(.*?<body\b[^>]*>)?/i, function(h) { | ||
var doctype = h.match(/^[\s\u0000]*(<\!doctype\b[^>]*?>)/i), | ||
@@ -1264,12 +1295,12 @@ html = h.match(/<html\b[^>]*?>/i), | ||
firstNode = firstNode.replace(/\r?\n/g, "\u0000"). | ||
replace(/[\s\u0000]*<head\b[^>]*>.*?<\/(head|body)\b[^>]*>/gi, ""). | ||
replace(/[\s\u0000]*<[\!\/]?(doctype|html|body)\b[^>]*>[\s\u0000]*/gi, ""). | ||
replace(/\u0000/g, "\n"); | ||
replace(/[\s\u0000]*<head\b[^>]*>.*?<\/(head|body)\b[^>]*>/gi, ""). | ||
replace(/[\s\u0000]*<[\!\/]?(doctype|html|body)\b[^>]*>[\s\u0000]*/gi, ""). | ||
replace(/\u0000/g, "\n"); | ||
secondNode = secondNode.replace(/\r?\n/g, "\u0000"). | ||
replace(/[\s\u0000]*<head\b[^>]*>.*?<\/(head|body)\b[^>]*>/gi, ""). | ||
replace(/[\s\u0000]*<[\!\/]?(doctype|html|body)\b[^>]*>[\s\u0000]*/gi, ""). | ||
replace(/\u0000/g, "\n"); | ||
replace(/[\s\u0000]*<head\b[^>]*>.*?<\/(head|body)\b[^>]*>/gi, ""). | ||
replace(/[\s\u0000]*<[\!\/]?(doctype|html|body)\b[^>]*>[\s\u0000]*/gi, ""). | ||
replace(/\u0000/g, "\n"); | ||
return htmlHeader + firstNode + secondNode + (htmlHeader? (firstNode || secondNode ? "\n" : "") + "</body>\n</html>" : ""); | ||
return htmlHeader + firstNode + secondNode + (htmlHeader ? (firstNode || secondNode ? "\n" : "") + "</body>\n</html>" : ""); | ||
}; | ||
@@ -1285,9 +1316,9 @@ | ||
*/ | ||
MailParser.prototype._convertString = function(value, fromCharset, toCharset){ | ||
MailParser.prototype._convertString = function(value, fromCharset, toCharset) { | ||
toCharset = (toCharset || "utf-8").toUpperCase(); | ||
fromCharset = (fromCharset || "utf-8").toUpperCase(); | ||
value = typeof value=="string"?new Buffer(value, "binary"):value; | ||
value = typeof value == "string" ? new Buffer(value, "binary") : value; | ||
if(toCharset == fromCharset){ | ||
if (toCharset == fromCharset) { | ||
return value; | ||
@@ -1307,3 +1338,3 @@ } | ||
*/ | ||
MailParser.prototype._convertStringToUTF8 = function(value){ | ||
MailParser.prototype._convertStringToUTF8 = function(value) { | ||
value = this._convertString(value, this._currentNode.meta.charset || this.options.defaultCharset || "iso-8859-1").toString("utf-8"); | ||
@@ -1319,3 +1350,3 @@ return value; | ||
*/ | ||
MailParser.prototype._encodeString = function(value){ | ||
MailParser.prototype._encodeString = function(value) { | ||
value = this._replaceMimeWords(this._convertStringToUTF8(value)); | ||
@@ -1331,8 +1362,8 @@ return value; | ||
*/ | ||
MailParser.prototype._replaceMimeWords = function(value){ | ||
MailParser.prototype._replaceMimeWords = function(value) { | ||
return value. | ||
replace(/(=\?[^?]+\?[QqBb]\?[^?]+\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]+\?=)/g, "$1"). // join mimeWords | ||
replace(/\=\?[^?]+\?[QqBb]\?[^?]+\?=/g, (function(a){ | ||
return mimelib.decodeMimeWord(a.replace(/\s/g,'')); | ||
}).bind(this)); | ||
replace(/(=\?[^?]+\?[QqBb]\?[^?]+\?=)\s+(?==\?[^?]+\?[QqBb]\?[^?]+\?=)/g, "$1"). // join mimeWords | ||
replace(/\=\?[^?]+\?[QqBb]\?[^?]+\?=/g, (function(a) { | ||
return mimelib.decodeMimeWord(a.replace(/\s/g, '')); | ||
}).bind(this)); | ||
}; | ||
@@ -1346,8 +1377,8 @@ | ||
*/ | ||
MailParser.prototype._trimQuotes = function(value){ | ||
MailParser.prototype._trimQuotes = function(value) { | ||
value = (value || "").trim(); | ||
if((value.charAt(0)=='"' && value.charAt(value.length-1)=='"') || | ||
(value.charAt(0)=="'" && value.charAt(value.length-1)=="'") || | ||
(value.charAt(0)=="<" && value.charAt(value.length-1)==">")){ | ||
value = value.substr(1,value.length-2); | ||
if ((value.charAt(0) == '"' && value.charAt(value.length - 1) == '"') || | ||
(value.charAt(0) == "'" && value.charAt(value.length - 1) == "'") || | ||
(value.charAt(0) == "<" && value.charAt(value.length - 1) == ">")) { | ||
value = value.substr(1, value.length - 2); | ||
} | ||
@@ -1372,25 +1403,26 @@ return value; | ||
*/ | ||
MailParser.prototype._generateFileName = function(fileName, contentType){ | ||
var ext, defaultExt = "", fileRootName; | ||
MailParser.prototype._generateFileName = function(fileName, contentType) { | ||
var ext, defaultExt = "", | ||
fileRootName; | ||
if(contentType){ | ||
if (contentType) { | ||
defaultExt = mime.extension(contentType); | ||
defaultExt = defaultExt?"."+defaultExt:""; | ||
defaultExt = defaultExt ? "." + defaultExt : ""; | ||
} | ||
fileName = fileName || "attachment"+defaultExt; | ||
fileName = fileName || "attachment" + defaultExt; | ||
// remove path if it is included in the filename | ||
fileName = fileName.toString().split(/[\/\\]+/).pop().replace(/^\.+/,"") || "attachment"; | ||
fileName = fileName.toString().split(/[\/\\]+/).pop().replace(/^\.+/, "") || "attachment"; | ||
fileRootName = fileName.replace(/(?:\-\d+)+(\.[^.]*)$/, "$1") || "attachment"; | ||
if(fileRootName in this._fileNames){ | ||
if (fileRootName in this._fileNames) { | ||
this._fileNames[fileRootName]++; | ||
ext = fileName.substr((fileName.lastIndexOf(".") || 0)+1); | ||
if(ext == fileName){ | ||
fileName += "-" + this._fileNames[fileRootName]; | ||
}else{ | ||
ext = fileName.substr((fileName.lastIndexOf(".") || 0) + 1); | ||
if (ext == fileName) { | ||
fileName += "-" + this._fileNames[fileRootName]; | ||
} else { | ||
fileName = fileName.substr(0, fileName.length - ext.length - 1) + "-" + this._fileNames[fileRootName] + "." + ext; | ||
} | ||
}else{ | ||
} else { | ||
this._fileNames[fileRootName] = 0; | ||
@@ -1409,15 +1441,15 @@ } | ||
*/ | ||
MailParser.prototype._updateHTMLCharset = function(html){ | ||
MailParser.prototype._updateHTMLCharset = function(html) { | ||
html = html.replace(/\n/g,"\u0000"). | ||
replace(/<meta[^>]*>/gi, function(meta){ | ||
if(meta.match(/http\-equiv\s*=\s*"?content\-type/i)){ | ||
return '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'; | ||
} | ||
if(meta.match(/\scharset\s*=\s*['"]?[\w\-]+["'\s>\/]/i)){ | ||
return '<meta charset="utf-8"/>'; | ||
} | ||
return meta; | ||
}). | ||
replace(/\u0000/g,"\n"); | ||
html = html.replace(/\n/g, "\u0000"). | ||
replace(/<meta[^>]*>/gi, function(meta) { | ||
if (meta.match(/http\-equiv\s*=\s*"?content\-type/i)) { | ||
return '<meta http-equiv="content-type" content="text/html; charset=utf-8" />'; | ||
} | ||
if (meta.match(/\scharset\s*=\s*['"]?[\w\-]+["'\s>\/]/i)) { | ||
return '<meta charset="utf-8"/>'; | ||
} | ||
return meta; | ||
}). | ||
replace(/\u0000/g, "\n"); | ||
@@ -1433,16 +1465,16 @@ return html; | ||
*/ | ||
MailParser.prototype._detectHTMLCharset = function(html){ | ||
MailParser.prototype._detectHTMLCharset = function(html) { | ||
var charset, input, meta; | ||
if(typeof html !=" string"){ | ||
if (typeof html != " string") { | ||
html = html.toString("ascii"); | ||
} | ||
if((meta = html.match(/<meta\s+http-equiv=["']content-type["'][^>]*?>/i))){ | ||
if ((meta = html.match(/<meta\s+http-equiv=["']content-type["'][^>]*?>/i))) { | ||
input = meta[0]; | ||
} | ||
if(input){ | ||
if (input) { | ||
charset = input.match(/charset\s?=\s?([a-zA-Z\-_:0-9]*);?/); | ||
if(charset){ | ||
if (charset) { | ||
charset = (charset[1] || "").trim().toLowerCase(); | ||
@@ -1452,3 +1484,3 @@ } | ||
if(!charset && (meta = html.match(/<meta\s+charset=["']([^'"<\/]*?)["']/i))){ | ||
if (!charset && (meta = html.match(/<meta\s+charset=["']([^'"<\/]*?)["']/i))) { | ||
charset = (meta[1] || "").trim().toLowerCase(); | ||
@@ -1458,2 +1490,2 @@ } | ||
return charset; | ||
}; | ||
}; |
@@ -7,3 +7,4 @@ "use strict"; | ||
encodinglib = require("encoding"), | ||
crypto = require("crypto"); | ||
crypto = require("crypto"), | ||
uue = require('uue'); | ||
@@ -13,2 +14,3 @@ module.exports.Base64Stream = Base64Stream; | ||
module.exports.BinaryStream = BinaryStream; | ||
module.exports.UUEStream = UUEStream; | ||
@@ -149,2 +151,65 @@ function Base64Stream(){ | ||
}; | ||
}; | ||
// this is not a stream, it buffers data and decodes after end | ||
function UUEStream(charset){ | ||
Stream.call(this); | ||
this.writable = true; | ||
this.checksum = crypto.createHash("md5"); | ||
this.length = 0; | ||
this.buf = []; | ||
this.buflen = 0; | ||
this.charset = charset || "UTF-8"; | ||
this.current = undefined; | ||
} | ||
utillib.inherits(UUEStream, Stream); | ||
UUEStream.prototype.write = function(data){ | ||
this.buf.push(data); | ||
this.buflen += data.length; | ||
return true; | ||
}; | ||
UUEStream.prototype.end = function(data){ | ||
if(data){ | ||
this.write(data); | ||
} | ||
this.flush(); | ||
this.emit("end"); | ||
return { | ||
length: this.length, | ||
checksum: this.checksum.digest("hex") | ||
}; | ||
}; | ||
UUEStream.prototype.flush = function(){ | ||
var buffer = this.decode(Buffer.concat(this.buf, this.buflen)); | ||
this.length += buffer.length; | ||
this.checksum.update(buffer); | ||
this.emit("data", buffer); | ||
}; | ||
UUEStream.prototype.decode = function(buffer){ | ||
var filename; | ||
filename = buffer.slice(0, Math.min(buffer.length, 1024)).toString().split(/\s/)[2] || ''; | ||
if(!filename){ | ||
return new Buffer(0); | ||
} | ||
buffer = uue.decodeFile(buffer.toString('ascii').replace(/\r\n/g, '\n'), filename); | ||
if(this.charset.toLowerCase() == "binary"){ | ||
// do nothing | ||
}else if(this.charset.toLowerCase() != "utf-8"){ | ||
buffer = encodinglib.convert(buffer, "utf-8", this.charset); | ||
} | ||
return buffer; | ||
}; |
{ | ||
"name": "mailparser", | ||
"description": "Asynchronous and non-blocking parser for mime encoded e-mail messages", | ||
"version": "0.4.3", | ||
"author" : "Andris Reinman", | ||
"maintainers":[ | ||
{ | ||
"name":"andris", | ||
"email":"andris@node.ee" | ||
} | ||
], | ||
"repository" : { | ||
"type" : "git", | ||
"url" : "http://github.com/andris9/mailparser.git" | ||
}, | ||
"scripts":{ | ||
"test": "nodeunit test/" | ||
}, | ||
"main" : "./lib/mailparser", | ||
"licenses" : [ | ||
{ | ||
"type": "MIT", | ||
"url": "http://github.com/andris9/mailparser/blob/master/LICENSE" | ||
} | ||
], | ||
"dependencies": { | ||
"mimelib": ">=0.2.6", | ||
"encoding": ">=0.1.4", | ||
"mime": "*" | ||
}, | ||
"devDependencies": { | ||
"nodeunit": "*" | ||
}, | ||
"engine": { | ||
"node": ">=0.4" | ||
}, | ||
"keywords": ["e-mail", "mime", "parser"] | ||
"name": "mailparser", | ||
"description": "Asynchronous and non-blocking parser for mime encoded e-mail messages", | ||
"version": "0.4.4", | ||
"author": "Andris Reinman", | ||
"maintainers": [ | ||
{ | ||
"name": "andris", | ||
"email": "andris@node.ee" | ||
} | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "http://github.com/andris9/mailparser.git" | ||
}, | ||
"scripts": { | ||
"test": "nodeunit test/" | ||
}, | ||
"main": "./lib/mailparser", | ||
"licenses": [ | ||
{ | ||
"type": "MIT", | ||
"url": "http://github.com/andris9/mailparser/blob/master/LICENSE" | ||
} | ||
], | ||
"dependencies": { | ||
"mimelib": ">=0.2.6", | ||
"encoding": ">=0.1.4", | ||
"mime": "*", | ||
"uue": "~1.0.0" | ||
}, | ||
"devDependencies": { | ||
"nodeunit": "*" | ||
}, | ||
"engine": { | ||
"node": ">=0.4" | ||
}, | ||
"keywords": [ | ||
"e-mail", | ||
"mime", | ||
"parser" | ||
] | ||
} |
@@ -424,2 +424,19 @@ var MailParser = require("../lib/mailparser").MailParser, | ||
}); | ||
}, | ||
"UUENCODE": function(test){ | ||
var encodedText = "Content-Type: application/octet-stream\r\n"+ | ||
"Content-Transfer-Encoding: uuencode\r\n"+ | ||
"\r\n"+ | ||
"begin 644 buffer.bin\r\n"+ | ||
"#0V%T\r\n"+ | ||
"`\r\n"+ | ||
"end", | ||
mail = new Buffer(encodedText, "utf-8"); | ||
var mailparser = new MailParser(); | ||
mailparser.end(mail); | ||
mailparser.on("end", function(mail){ | ||
test.equal(mail.attachments[0].content.toString(), "Cat"); | ||
test.done(); | ||
}); | ||
} | ||
@@ -1203,2 +1220,37 @@ | ||
}, | ||
"Stream integrity - uuencode": function(test){ | ||
var encodedText = "Content-type: multipart/mixed; boundary=ABC\r\n"+ | ||
"\r\n"+ | ||
"--ABC\r\n"+ | ||
"Content-Type: application/octet-stream\r\n"+ | ||
"Content-Transfer-Encoding: uuencode\r\n"+ | ||
"\r\n"+ | ||
"begin 644 buffer.bin\r\n"+ | ||
"#0V%T\r\n"+ | ||
"`\r\n"+ | ||
"end\r\n"+ | ||
"--ABC--", | ||
expectedHash = "fa3ebd6742c360b2d9652b7f78d9bd7d", | ||
mail = new Buffer(encodedText, "utf-8"); | ||
var mailparser = new MailParser({streamAttachments: true}); | ||
for(var i=0, len = mail.length; i<len; i++){ | ||
mailparser.write(new Buffer([mail[i]])); | ||
} | ||
test.expect(3); | ||
mailparser.on("attachment", function(attachment){ | ||
test.ok(attachment.stream, "Stream detected"); | ||
}); | ||
mailparser.end(); | ||
mailparser.on("end", function(mail){ | ||
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].checksum, expectedHash); | ||
test.equal(mail.attachments && mail.attachments[0] && mail.attachments[0].length, 3); | ||
test.done(); | ||
}); | ||
}, | ||
"Stream multiple attachments": function(test){ | ||
@@ -1205,0 +1257,0 @@ var encodedText = "Content-type: multipart/mixed; boundary=ABC\r\n"+ |
135699
2956
4
+ Addeduue@~1.0.0
+ Addeduue@1.0.0(transitive)