Comparing version 3.2.1 to 4.0.0
@@ -73,8 +73,9 @@ // Headers that cannot be canonicalized by naive camel casing: | ||
'x-csa-complaints': 'x-csa-complaints', | ||
'x-ua-compatible': 'X-UA-Compatible', | ||
'x-originating-ip': 'X-Originating-IP', | ||
'x-riferimento-message-id': 'X-Riferimento-Message-ID', | ||
'x-sg-eid': 'X-SG-EID', | ||
'x-tiporicevuta': 'X-TipoRicevuta', | ||
'x-ua-compatible': 'X-UA-Compatible', | ||
'x-verificasicurezza': 'X-VerificaSicurezza', | ||
'x-xss-protection': 'X-XSS-Protection' | ||
}; |
var foldHeaderLine = require('./foldHeaderLine'), | ||
formatHeaderName = require('./formatHeaderName'), | ||
isRegExp = require('./isRegExp'); | ||
isRegExp = require('./isRegExp'), | ||
rfc2231 = require('rfc2231'); | ||
@@ -176,5 +177,8 @@ function Headers(obj, doNotStringify) { | ||
Headers.prototype.set = function (headerName, valueOrValues) { | ||
Headers.prototype.set = function (headerName, valueOrValues, valueNumber) { | ||
var headerNameLowerCase = headerName.toLowerCase(); | ||
if (Array.isArray(valueOrValues)) { | ||
if (typeof valueNumber !== 'undefined') { | ||
throw new Error('Headers.set: valueNumber not supported when the values are provided as an array'); | ||
} | ||
if (valueOrValues.length === 0) { | ||
@@ -187,2 +191,4 @@ delete this.valuesByName[headerNameLowerCase]; | ||
} | ||
} else if (typeof valueNumber === 'number' && Array.isArray(this.valuesByName[headerNameLowerCase]) && valueNumber < this.valuesByName[headerNameLowerCase].length) { | ||
this.valuesByName[headerNameLowerCase][valueNumber] = this.parseHeaderValue(valueOrValues); | ||
} else { | ||
@@ -262,2 +268,52 @@ (this.valuesByName[headerNameLowerCase] = this.valuesByName[headerNameLowerCase] || []).push(this.parseHeaderValue(valueOrValues)); | ||
Headers.prototype.parameter = function (headerName, attributeName, attributeValue) { | ||
var headerValue = this.get(headerName, 0), | ||
rfc2231DisabledForThisHeader = this.isMessyHeadersWithRfc2047 && headerName.toLowerCase() === 'content-type'; | ||
if (headerValue) { | ||
// FIXME: Will break if a quoted parameter value contains a semicolon | ||
var tokens = headerValue.split(/\s*;\s*/), | ||
parameters = {}, | ||
usesRfc2231 = false; | ||
for (var i = 1 ; i < tokens.length ; i += 1) { | ||
var matchKeyValue = tokens[i].match(/^([^=]+)=(.*)$/); | ||
if (matchKeyValue && !(matchKeyValue[1] in parameters)) { | ||
var parameterName = matchKeyValue[1], | ||
value = matchKeyValue[2], | ||
matchQuotedValue = value.match(/^"(.*)"$/); | ||
if (matchQuotedValue) { | ||
value = matchQuotedValue[1].replace(/\\/, ''); | ||
} | ||
if (!usesRfc2231 && /\*$/.test(parameterName)) { | ||
usesRfc2231 = true; | ||
} | ||
parameters[parameterName] = value; | ||
} | ||
} | ||
if (usesRfc2231 && !rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.unfoldAndDecodeParameters(parameters); | ||
} | ||
if (attributeName) { | ||
if (attributeValue) { | ||
parameters[attributeName] = attributeValue; | ||
var tokensAfterUpdate = [tokens[0]]; | ||
if (!rfc2231DisabledForThisHeader) { | ||
parameters = rfc2231.encodeAndFoldParameters(parameters); | ||
} | ||
Object.keys(parameters).forEach(function (parameterName) { | ||
tokensAfterUpdate.push(parameterName + '=' + parameters[parameterName]); | ||
}); | ||
this.set(headerName, tokensAfterUpdate.join('; '), 0); | ||
} else { | ||
return parameters[attributeName]; | ||
} | ||
} else { | ||
return parameters; | ||
} | ||
} | ||
}; | ||
Headers.prototype.equals = function (other) { | ||
@@ -264,0 +320,0 @@ if (this === other) { |
@@ -11,2 +11,4 @@ var Headers = require('./Headers'), | ||
HeadersWithRfc2047.prototype.isMessyHeadersWithRfc2047 = true; | ||
HeadersWithRfc2047.prototype.serializeHeaderValue = function (parsedHeaderValue) { | ||
@@ -13,0 +15,0 @@ return rfc2047.encode(parsedHeaderValue); |
module.exports = { | ||
Headers: require('./Headers'), | ||
Message: require('./Message'), | ||
Mail: require('./Mail'), | ||
RequestLine: require('./RequestLine'), | ||
@@ -5,0 +6,0 @@ HttpRequest: require('./HttpRequest'), |
@@ -11,4 +11,6 @@ var Message = require('./Message'), | ||
Mail.prototype.isMessyMail = true; | ||
Mail.prototype.HeadersConstructor = HeadersWithRfc2047; | ||
module.exports = Mail; |
/*global unescape*/ | ||
var Headers = require('./Headers'), | ||
isRegExp = require('./isRegExp'), | ||
rfc2231 = require('rfc2231'); | ||
rfc2231 = require('rfc2231'), | ||
iconvLite = require('iconv-lite'), | ||
quotedPrintable = require('quoted-printable'), | ||
utf8 = require('utf8'); | ||
@@ -70,30 +73,18 @@ function quoteRegExp(str) { | ||
Message.prototype.populateMultipartBody = function () { | ||
if (this.isMultipart && typeof this.body !== 'undefined') { | ||
var boundary = this.boundary; | ||
if (boundary) { | ||
var bodyAsString; | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
bodyAsString = this.body.toString('ascii'); | ||
} else { | ||
bodyAsString = this.body; | ||
} | ||
var boundaryRegExp = new RegExp('(^|\r\n?|\n\r?)--' + quoteRegExp(boundary) + '(--)?(?:\r\n?|\n\r?|$)', 'g'), | ||
startIndex = -1, | ||
parts = [], | ||
match; | ||
// TODO: Basic validation of end marker etc. | ||
while ((match = boundaryRegExp.exec(bodyAsString))) { | ||
var index = match.index; | ||
if (startIndex !== -1) { | ||
parts.push(new Message(this.body.slice(startIndex, index))); | ||
} | ||
startIndex = index + match[0].length; | ||
} | ||
if (parts.length > 0) { | ||
this.body = parts; | ||
} | ||
Object.defineProperty(Message.prototype, 'hasTextualContentType', { | ||
get: function () { | ||
var contentType = this.headers.get('Content-Type'); | ||
if (typeof contentType === 'string') { | ||
contentType = contentType.toLowerCase().trim().replace(/\s*;.*$/, ''); | ||
return ( | ||
/^text\//.test(contentType) || | ||
/^application\/(json|javascript)$/.test(contentType) || | ||
/^application\/xml/.test(contentType) || | ||
/^application\/x-www-form-urlencoded\b/.test(contentType) || | ||
/\+xml$/.test(contentType) | ||
); | ||
} | ||
return false; | ||
} | ||
}; | ||
}); | ||
@@ -110,7 +101,14 @@ Object.defineProperty(Message.prototype, 'isMultipart', { | ||
get: function () { | ||
if (this.isMultipart) { | ||
var matchBoundary = this.headers.get('Content-Type').match(/\;\s*boundary=('|"|)(\S*?)\1(?:$|;)/); | ||
if (matchBoundary) { | ||
return matchBoundary[2]; | ||
} | ||
return this.isMultipart && this.headers.parameter('Content-Type', 'boundary'); | ||
} | ||
}); | ||
Object.defineProperty(Message.prototype, '_bodyMustBeBuffer', { | ||
get: function () { | ||
if (this._parts) { | ||
return this._parts.some(function (part) { | ||
return part._bodyMustBeBuffer; | ||
}); | ||
} else { | ||
return typeof Buffer === 'function' && Buffer.isBuffer(this.body); | ||
} | ||
@@ -120,48 +118,128 @@ } | ||
Object.defineProperty(Message.prototype, 'fileName', { | ||
Object.defineProperty(Message.prototype, 'body', { | ||
enumerable: true, | ||
get: function () { | ||
var contentDisposition = this.headers.get('Content-Disposition'); | ||
if (contentDisposition) { | ||
var tokens = contentDisposition.split(/\s*;\s*/), | ||
parameters = {}; | ||
for (var i = 1 ; i < tokens.length ; i += 1) { | ||
var matchKeyValue = tokens[i].match(/^([^=]+)=(.*)$/); | ||
if (matchKeyValue && !(matchKeyValue[1] in parameters)) { | ||
parameters[matchKeyValue[1]] = matchKeyValue[2]; | ||
if (this._parts) { | ||
if (this._parts.length === 0) { | ||
return; | ||
} else { | ||
if (this._bodyMustBeBuffer) { | ||
var chunks = []; | ||
this._parts.forEach(function (part, i) { | ||
if (i > 0) { | ||
chunks.push(new Buffer('\r\n')); | ||
} | ||
chunks.push(new Buffer('--' + this.boundary + '\r\n')); | ||
var serializedPart = part.serialize(); | ||
if (!Buffer.isBuffer(serializedPart)) { | ||
serializedPart = new Buffer(serializedPart); | ||
} | ||
chunks.push(serializedPart); | ||
}, this); | ||
chunks.push(new Buffer('\r\n--' + this.boundary + '--\r\n')) | ||
return Buffer.concat(chunks); | ||
} else { | ||
return; | ||
return '--' + this.boundary + '\r\n' + this._parts.join('\r\n--' + this.boundary + '\r\n') + '\r\n--' + this.boundary + '--\r\n'; | ||
} | ||
} | ||
var decodedParameters = rfc2231.unfoldAndDecodeParameters(parameters); | ||
if (decodedParameters.filename) { | ||
return decodedParameters.filename; | ||
} else { | ||
return this._body; | ||
} | ||
}, | ||
set: function (body) { | ||
this._body = body; | ||
this._parts = null; | ||
} | ||
}); | ||
Object.defineProperty(Message.prototype, 'decodedBody', { | ||
enumerable: true, | ||
get: function () { | ||
var decodedBody = this.body; | ||
if (decodedBody) { | ||
var contentTransferEncoding = this.headers.get('Content-Transfer-Encoding'), | ||
contentTransferEncodingIsHonored = !contentTransferEncoding; | ||
if (contentTransferEncoding) { | ||
contentTransferEncoding = contentTransferEncoding.trim().toLowerCase(); | ||
if (contentTransferEncoding === 'quoted-printable') { | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(decodedBody)) { | ||
decodedBody = decodedBody.toString('ascii'); | ||
} | ||
var qpDecodedBodyAsByteString = quotedPrintable.decode(decodedBody); | ||
decodedBody = new Buffer(qpDecodedBodyAsByteString.length); | ||
for (var i = 0 ; i < qpDecodedBodyAsByteString.length ; i += 1) { | ||
decodedBody[i] = qpDecodedBodyAsByteString.charCodeAt(i); | ||
} | ||
contentTransferEncodingIsHonored = true; | ||
} else if (contentTransferEncoding === 'base64') { | ||
decodedBody = new Buffer(decodedBody, 'base64'); | ||
contentTransferEncodingIsHonored = true; | ||
} else if (contentTransferEncoding === '8bit' || contentTransferEncoding === '7bit') { | ||
contentTransferEncodingIsHonored = true; | ||
} | ||
} | ||
if (this.hasTextualContentType && contentTransferEncodingIsHonored) { | ||
var charset = this.headers.parameter('Content-Type', 'charset') || 'iso-8859-1'; | ||
if (iconvLite.encodingExists(charset)) { | ||
decodedBody = iconvLite.decode(decodedBody, charset); | ||
} | ||
} | ||
} | ||
// TODO: Try falling back to the name attribute of the Content-Type header | ||
return decodedBody; | ||
} | ||
}); | ||
Object.defineProperty(Message.prototype, 'parts', { | ||
enumerable: true, | ||
set: function (parts) { | ||
this._parts = parts; | ||
}, | ||
set: function (fileName) { | ||
var contentDisposition = this.headers.get('Content-Disposition') || 'attachment'; | ||
if (contentDisposition) { | ||
var tokens = contentDisposition.split(/\s*;\s*/), | ||
tokensAfterUpdate = [tokens[0]]; | ||
for (var i = 1 ; i < tokens.length ; i += 1) { | ||
var matchKeyValue = tokens[i].match(/^([^=]+)=.*$/); | ||
if (matchKeyValue) { | ||
if (!/^filename(?:\*\d*)$/.test(matchKeyValue[1])) { | ||
tokensAfterUpdate.push(tokens[i]); | ||
} | ||
get: function () { | ||
if (!this._parts && this.isMultipart) { | ||
var boundary = this.boundary; | ||
if (boundary) { | ||
var bodyAsString; | ||
if (typeof Buffer === 'function' && Buffer.isBuffer(this.body)) { | ||
bodyAsString = this.body.toString('ascii'); | ||
} else { | ||
throw new Error('Cannot set fileName due to unparseable token ' + tokens[i]); | ||
bodyAsString = this.body; | ||
} | ||
var boundaryRegExp = new RegExp('(^|\r\n?|\n\r?)--' + quoteRegExp(boundary) + '(--)?(?:\r\n?|\n\r?|$)', 'g'), | ||
startIndex = -1, | ||
parts = [], | ||
match; | ||
// TODO: Basic validation of end marker etc. | ||
while ((match = boundaryRegExp.exec(bodyAsString))) { | ||
var index = match.index; | ||
if (startIndex !== -1) { | ||
parts.push(new Message(this.body.slice(startIndex, index))); | ||
} | ||
startIndex = index + match[0].length; | ||
} | ||
if (parts.length > 0) { | ||
this._parts = parts; | ||
} | ||
} | ||
var folded = rfc2231.encodeAndFoldParameters({filename: fileName}); | ||
Object.keys(folded).forEach(function (parameterName) { | ||
tokensAfterUpdate.push(parameterName + '=' + folded[parameterName]); | ||
}); | ||
this.headers.set('Content-Disposition', tokensAfterUpdate.join('; ')); | ||
} | ||
// TODO: Update the name attribute of the Content-Type header | ||
return this._parts; | ||
} | ||
}); | ||
Object.defineProperty(Message.prototype, 'fileName', { | ||
get: function () { | ||
return this.headers.parameter('Content-Disposition', 'filename') || (this.isMessyMail && this.headers.parameter('Content-Type', 'name')); | ||
}, | ||
set: function (fileName) { | ||
if (!this.headers.has('Content-Disposition')) { | ||
this.headers.set('Content-Disposition', 'attachment'); | ||
} | ||
this.headers.parameter('Content-Disposition', 'filename', fileName); | ||
if (this.isMessyMail && this.headers.has('Content-Type')) { | ||
this.headers.parameter('Content-Type', 'name', fileName); | ||
} | ||
} | ||
}); | ||
function buffersEqual(a, b) { | ||
@@ -192,3 +270,3 @@ if (a === b) { | ||
Message.prototype.toString = function (maxLineLength) { | ||
Message.prototype.serialize = function (maxLineLength, forceString) { | ||
if (typeof maxLineLength === 'undefined') { | ||
@@ -202,6 +280,8 @@ maxLineLength = 72; | ||
result += JSON.stringify(this.body); | ||
} else if (this.isMultipart && Array.isArray(this.body) && this.boundary) { | ||
result += '--' + this.boundary + '\r\n' + this.body.join('\r\n--' + this.boundary + '\r\n') + '\r\n--' + this.boundary + '--\r\n'; | ||
} else { | ||
result += this.body; | ||
if (!forceString && this._bodyMustBeBuffer) { | ||
result = Buffer.concat([new Buffer(result), this.body]); | ||
} else { | ||
result += this.body; | ||
} | ||
} | ||
@@ -212,2 +292,6 @@ } | ||
Message.prototype.toString = function (maxLineLength) { | ||
return this.serialize(maxLineLength, true); | ||
} | ||
Message.prototype.equals = function (other) { | ||
@@ -214,0 +298,0 @@ return this === other || ( |
{ | ||
"name": "messy", | ||
"version": "3.2.1", | ||
"version": "4.0.0", | ||
"description": "Object model for HTTP and RFC822 messages", | ||
@@ -35,6 +35,9 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"iconv-lite": "0.4.6", | ||
"quoted-printable": "1.0.0", | ||
"rfc2047": "1.0.1", | ||
"rfc2231": "1.1.1", | ||
"underscore": "^1.6.0" | ||
"rfc2231": "1.2.0", | ||
"underscore": "^1.6.0", | ||
"utf8": "2.1.0" | ||
} | ||
} |
@@ -150,2 +150,87 @@ /*global describe, it*/ | ||
}); | ||
describe('#parameter()', function () { | ||
describe('when called with just a header name', function () { | ||
it('should return a hash of attributes when the header has attributes', function () { | ||
expect(new Headers('Foo: bar; quux=baz').parameter('Foo'), 'to equal', {quux: 'baz'}); | ||
}); | ||
it('should unquote quoted parameters', function () { | ||
expect(new Headers('Foo: bar; quux="baz"').parameter('Foo'), 'to equal', {quux: 'baz'}); | ||
}); | ||
it('should return an empty hash when the header has no attributes', function () { | ||
expect(new Headers('Foo: bar').parameter('Foo'), 'to equal', {}); | ||
}); | ||
it('should return undefined when the header does not exist', function () { | ||
expect(new Headers('Foo: bar').parameter('Quux'), 'to equal', undefined); | ||
}); | ||
it('should decode rfc2231-encoded attributes', function () { | ||
expect(new Headers( | ||
'Content-Type: text/plain;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA').parameter('Content-Type'), 'to equal', { | ||
filename: 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺' | ||
}); | ||
}); | ||
}); | ||
describe('when called with a header name and an attribute name', function () { | ||
it('should return the attribute value', function () { | ||
expect(new Headers('Foo: hey').parameter('Foo'), 'to equal', {}); | ||
}); | ||
it('should return undefined when the header has no attributes', function () { | ||
expect(new Headers('Foo: hey').parameter('Foo', 'bar'), 'to equal', undefined); | ||
}); | ||
it('should return undefined when the header has attributes, but not the one being asked for', function () { | ||
expect(new Headers('Foo: hey; quux=blah').parameter('Foo', 'bar'), 'to equal', undefined); | ||
}); | ||
it('should return undefined when the header is not there', function () { | ||
expect(new Headers('Foo: bar').parameter('Bar', 'quux'), 'to equal', undefined); | ||
}); | ||
}); | ||
describe('when called with a header name, an attribute name, and an attribute value', function () { | ||
it('should define the attribute if it does not exist', function () { | ||
var headers = new Headers('Foo: hey'); | ||
headers.parameter('Foo', 'blah', 'baz') | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should update the attribute if it already exists', function () { | ||
var headers = new Headers('Foo: hey; blah=quux'); | ||
headers.parameter('Foo', 'blah', 'baz') | ||
expect(headers.toString(), 'to equal', 'Foo: hey; blah=baz\r\n'); | ||
}); | ||
it('should transparently encode non-ASCII attribute values using rfc2231', function () { | ||
var headers = new Headers('Content-Type: text/plain'); | ||
headers.parameter('Content-Type', 'filename', 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'); | ||
expect( | ||
headers.toString(), | ||
'to equal', | ||
'Content-Type: text/plain;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -16,2 +16,38 @@ /*global describe, it*/ | ||
}); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
var mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
it('should fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
var mail = new Mail( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(mail.fileName, 'to equal', 'æøå.png'); | ||
}); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should update the name property of the Content-Type header if available', function () { | ||
var mail = new Mail({headers: {'Content-Type': 'image/png'}}); | ||
mail.fileName = 'æøå.png'; | ||
expect( | ||
mail.toString(), | ||
'to equal', | ||
// TODO: Would be better to emit 'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="\r\n' + | ||
'Content-Type: image/png; =?iso-8859-1?Q?name==E6=F8=E5?=.png\r\n' + | ||
'Content-Disposition: attachment;\r\n' + | ||
" filename*=iso-8859-1''%E6%F8%E5%2E%70%6E%67\r\n" | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
@@ -223,5 +223,3 @@ /*global describe, it*/ | ||
message.populateMultipartBody(); | ||
expect(message.body, 'to equal', [ | ||
expect(message.parts, 'to equal', [ | ||
new Message(new Buffer('Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com', 'utf-8')), | ||
@@ -237,5 +235,3 @@ new Message(new Buffer('Content-Disposition: form-data; name="Name "\r\n\r\nThe name', 'utf-8')), | ||
message.populateMultipartBody(); | ||
expect(message.body, 'to equal', [ | ||
expect(message.parts, 'to equal', [ | ||
new Message('Content-Disposition: form-data; name="recipient"\r\n\r\nandreas@one.com'), | ||
@@ -247,37 +243,195 @@ new Message('Content-Disposition: form-data; name="Name "\r\n\r\nThe name'), | ||
}); | ||
it('#toString should serialize the (possibly mutated) decoded parts if available', function () { | ||
var message = new Message(src); | ||
message.parts.splice(1, 3); | ||
message.parts[0].headers.set('Foo', 'quux'); | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: multipart/form-data;\r\n' + | ||
' boundary=--------------------------231099812216460892104111\r\n' + | ||
'\r\n' + | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'Foo: quux\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
it('should reparse the parts if the body of the containing Message is updated', function () { | ||
var message = new Message(src); | ||
message.parts.splice(1, 3); | ||
message.body = src; | ||
expect(message.parts, 'to have length', 4); | ||
}); | ||
it('should support updating the parts property', function () { | ||
var message = new Message(src); | ||
message.parts = []; | ||
expect(message.parts, 'to equal', []); | ||
}); | ||
it('should recompute the body if the parts are updated', function () { | ||
var message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts.splice(1, 3); | ||
expect(message.body, 'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'andreas@one.com\r\n' + | ||
'----------------------------231099812216460892104111--\r\n'); | ||
}); | ||
it('should recompute the body if the parts are updated, binary mode', function () { | ||
var message = new Message(src); | ||
expect(message.parts, 'to have length', 4); | ||
message.parts[0].body = new Buffer([0]); | ||
message.parts.splice(1, 3); | ||
var body = message.body; | ||
expect(body, 'to be a', Buffer); | ||
expect(body.toString('utf-8'), 'to equal', | ||
'----------------------------231099812216460892104111\r\n' + | ||
'Content-Disposition: form-data; name="recipient"\r\n' + | ||
'\r\n' + | ||
'\x00\r\n' + | ||
'----------------------------231099812216460892104111--\r\n' | ||
); | ||
}); | ||
}); | ||
it('should support a fileName getter which automatically rfc2231 decodes the Content-Disposition filename', function () { | ||
var message = new Message( | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA'); | ||
expect(message.fileName, 'to equal', 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'); | ||
describe('#decodedBody', function () { | ||
it('should decode a base64 body to a string when the Content-Transfer-Encoding is base64 and the Content-Type is textual', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).decodedBody, 'to equal', 'foo'); | ||
}); | ||
it('should decode a base64 body to a Buffer when the Content-Transfer-Encoding is base64 and the Content-Type is not textual', function () { | ||
expect(new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).decodedBody, 'to equal', new Buffer('foo')); | ||
}); | ||
it('should decode quoted-printable when the Content-Transfer-Encoding header says so', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =C3=A6=C3=B8=C3=A5\r\n' | ||
).decodedBody, 'to equal', 'Abc æøå\r\n'); | ||
}); | ||
it('should not break if the Content-Transfer-Encoding is unsupported', function () { | ||
expect(new Message( | ||
'Content-Type: image/png; charset=UTF-8\r\n' + | ||
'Content-Transfer-Encoding: foo\r\n' + | ||
'\r\n' + | ||
'Zm9v\r\n' | ||
).decodedBody, 'to equal', 'Zm9v\r\n'); | ||
}); | ||
it('should support quoted-printable with iso-8859-1', function () { | ||
expect(new Message( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'Content-Transfer-Encoding: quoted-printable\r\n' + | ||
'\r\n' + | ||
'Abc =F8\r\n' | ||
).decodedBody, 'to equal', 'Abc ø\r\n'); | ||
}); | ||
it('should support quoted-printable with no Content-Transfer-Encoding', function () { | ||
expect(new Message( | ||
Buffer.concat([ | ||
new Buffer( | ||
'Content-Type: text/plain; charset=iso-8859-1\r\n' + | ||
'\r\n' + | ||
'Abc '), | ||
new Buffer([0xf8]), | ||
new Buffer('\r\n') | ||
]) | ||
).decodedBody, 'to equal', 'Abc ø\r\n'); | ||
}); | ||
}); | ||
it('should support a fileName setter which updates the Content-Disposition filename with the rfc2231 encoded representation', function () { | ||
var message = new Message({body: 'bar'}); | ||
message.fileName = 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' + | ||
'\r\n' + | ||
'bar' | ||
); | ||
describe('#fileName', function () { | ||
describe('when invoked as a getter', function () { | ||
it('should decode the Content-Disposition filename', function () { | ||
var message = new Message( | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA'); | ||
expect(message.fileName, 'to equal', 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when the Content-Disposition header has no filename parameter', function () { | ||
var message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Disposition: attachment\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
it('should not fall back to the name property of the Content-Type header when there is no Content-Disposition header', function () { | ||
var message = new Message( | ||
'Content-Transfer-Encoding: base64\r\n' + | ||
'Content-Type: image/png; name="=?iso-8859-1?Q?=E6=F8=E5.png?="' | ||
); | ||
expect(message.fileName, 'to equal', undefined); | ||
}); | ||
}); | ||
describe('when invoked as a setter', function () { | ||
it('should support a fileName setter which updates the Content-Disposition filename with the rfc2231 encoded representation', function () { | ||
var message = new Message({body: 'bar'}); | ||
message.fileName = 'ræally screwed up long attachment filename with smileys☺ and ☺and ¡Hola, señor! and foreign weirdnessםולש ןב ילטפנ in it.☺'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Disposition: attachment;\r\n' + | ||
' filename*0*=utf-8\'\'%72%C3%A6%61%6C%6C%79%20%73%63%72%65%77%65%64%20%75;\r\n' + | ||
' filename*1*=%70%20%6C%6F%6E%67%20%61%74%74%61%63%68%6D%65%6E%74%20%66%69;\r\n' + | ||
' filename*2*=%6C%65%6E%61%6D%65%20%77%69%74%68%20%73%6D%69%6C%65%79%73%E2;\r\n' + | ||
' filename*3*=%98%BA%20%61%6E%64%20%E2%98%BA%61%6E%64%20%C2%A1%48%6F%6C%61;\r\n' + | ||
' filename*4*=%2C%20%73%65%C3%B1%6F%72%21%20%61%6E%64%20%66%6F%72%65%69%67;\r\n' + | ||
' filename*5*=%6E%20%77%65%69%72%64%6E%65%73%73%D7%9D%D7%95%D7%9C%D7%A9%20;\r\n' + | ||
' filename*6*=%D7%9F%D7%91%20%D7%99%D7%9C%D7%98%D7%A4%D7%A0%20%69%6E%20%69;\r\n' + | ||
' filename*7*=%74%2E%E2%98%BA\r\n' + | ||
'\r\n' + | ||
'bar' | ||
); | ||
}); | ||
it('should not update the name property of the Content-Type header, even if available', function () { | ||
var message = new Message({headers: {'Content-Type': 'image/png'}}); | ||
message.fileName = 'æøå.png'; | ||
expect( | ||
message.toString(), | ||
'to equal', | ||
'Content-Type: image/png\r\n' + | ||
'Content-Disposition: attachment;\r\n' + | ||
" filename*=iso-8859-1''%E6%F8%E5%2E%70%6E%67\r\n" | ||
); | ||
}); | ||
}); | ||
}); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
108297
2264
6
+ Addediconv-lite@0.4.6
+ Addedquoted-printable@1.0.0
+ Addedutf8@2.1.0
+ Addediconv-lite@0.4.6(transitive)
+ Addedquoted-printable@1.0.0(transitive)
+ Addedrfc2231@1.2.0(transitive)
+ Addedutf8@2.1.0(transitive)
- Removedrfc2231@1.1.1(transitive)
Updatedrfc2231@1.2.0