pax_global_header00006660000000000000000000000064124163245410014514gustar00rootroot0000000000000052 comment=f3c915f0c9d9f5ec79713dba24c8c6181b73305d content-disposition-0.5.0/000077500000000000000000000000001241632454100155325ustar00rootroot00000000000000content-disposition-0.5.0/.gitignore000066400000000000000000000000461241632454100175220ustar00rootroot00000000000000coverage/ node_modules/ npm-debug.log content-disposition-0.5.0/.travis.yml000066400000000000000000000006071241632454100176460ustar00rootroot00000000000000language: node_js node_js: - "0.6" - "0.8" - "0.10" - "0.11" matrix: allow_failures: - node_js: "0.11" fast_finish: true script: - "test $TRAVIS_NODE_VERSION != '0.6' || npm test" - "test $TRAVIS_NODE_VERSION = '0.6' || npm run-script test-travis" after_script: - "test $TRAVIS_NODE_VERSION = '0.10' && npm install coveralls@2 && cat ./coverage/lcov.info | coveralls" content-disposition-0.5.0/HISTORY.md000066400000000000000000000012731241632454100172200ustar00rootroot000000000000000.5.0 / 2014-10-11 ================== * Add `parse` function 0.4.0 / 2014-09-21 ================== * Expand non-Unicode `filename` to the full ISO-8859-1 charset 0.3.0 / 2014-09-20 ================== * Add `fallback` option * Add `type` option 0.2.0 / 2014-09-19 ================== * Reduce ambiguity of file names with hex escape in buggy browsers 0.1.2 / 2014-09-19 ================== * Fix periodic invalid Unicode filename header 0.1.1 / 2014-09-19 ================== * Fix invalid characters appearing in `filename*` parameter 0.1.0 / 2014-09-18 ================== * Make the `filename` argument optional 0.0.0 / 2014-09-18 ================== * Initial release content-disposition-0.5.0/LICENSE000066400000000000000000000021011241632454100165310ustar00rootroot00000000000000(The MIT License) Copyright (c) 2014 Douglas Christopher Wilson 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 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. content-disposition-0.5.0/README.md000066400000000000000000000120321241632454100170070ustar00rootroot00000000000000# content-disposition [![NPM Version][npm-image]][npm-url] [![NPM Downloads][downloads-image]][downloads-url] [![Node.js Version][node-version-image]][node-version-url] [![Build Status][travis-image]][travis-url] [![Test Coverage][coveralls-image]][coveralls-url] Create and parse HTTP `Content-Disposition` header ## Installation ```sh $ npm install content-disposition ``` ## API ```js var contentDisposition = require('content-disposition') ``` ### contentDisposition(filename, options) Create an attachment `Content-Disposition` header value using the given file name, if supplied. The `filename` is optional and if no file name is desired, but you want to specify `options`, set `filename` to `undefined`. ```js res.setHeader('Content-Disposition', contentDisposition('∫ maths.pdf')) ``` **note** HTTP headers are of the ISO-8859-1 character set. If you are writing this header through a means different from `setHeader` in Node.js, you'll want to specify the `'binary'` encoding in Node.js. #### Options `contentDisposition` accepts these properties in the options object. ##### fallback If the `filename` option is outside ISO-8859-1, then the file name is actually stored in a supplemental field for clients that support Unicode file names and a ISO-8859-1 version of the file name is automatically generated. This specifies the ISO-8859-1 file name to override the automatic generation or disables the generation all together, defaults to `true`. - A string will specify the ISO-8859-1 file name to use in place of automatic generation. - `false` will disable including a ISO-8859-1 file name and only include the Unicode version (unless the file name is already ISO-8859-1). - `true` will enable automatic generation if the file name is outside ISO-8859-1. If the `filename` option is ISO-8859-1 and this option is specified and has a different value, then the `filename` option is encoded in the extended field and this set as the fallback field, even though they are both ISO-8859-1. ##### type Specifies the disposition type, defaults to `"attachment"`. This can also be `"inline"`, or any other value (all values except inline are treated like `attachment`, but can convey additional information if both parties agree to it). The type is normalized to lower-case. ### contentDisposition.parse(string) ```js var disposition = contentDisposition.parse('attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt"'); ``` Parse a `Content-Disposition` header string. This automatically handles extended ("Unicode") parameters by decoding them and providing them under the standard parameter name. This will return an object with the following properties (examples are shown for the string `'attachment; filename="EURO rates.txt"; filename*=UTF-8\'\'%e2%82%ac%20rates.txt'`): - `type`: The disposition type (always lower case). Example: `'attachment'` - `parameters`: An object of the parameters in the disposition (name of parameter always lower case and extended versions replace non-extended versions). Example: `{filename: "€ rates.txt"}` ## Examples ### Send a file for download ```js var contentDisposition = require('content-disposition') var destroy = require('destroy') var http = require('http') var onFinished = require('on-finished') var filePath = '/path/to/public/plans.pdf' http.createServer(function onRequest(req, res) { // set headers res.setHeader('Content-Type', 'application/pdf') res.setHeader('Content-Disposition', contentDisposition(filePath)) // send file var stream = fs.createReadStream(filePath) stream.pipe(res) onFinished(res, function (err) { destroy(stream) }) }) ``` ## Testing ```sh $ npm test ``` ## References - [RFC 2616: Hypertext Transfer Protocol -- HTTP/1.1][rfc-2616] - [RFC 5987: Character Set and Language Encoding for Hypertext Transfer Protocol (HTTP) Header Field Parameters][rfc-5987] - [RFC 6266: Use of the Content-Disposition Header Field in the Hypertext Transfer Protocol (HTTP)][rfc-6266] - [Test Cases for HTTP Content-Disposition header field (RFC 6266) and the Encodings defined in RFCs 2047, 2231 and 5987][tc-2231] [rfc-2616]: https://tools.ietf.org/html/rfc2616 [rfc-5987]: https://tools.ietf.org/html/rfc5987 [rfc-6266]: https://tools.ietf.org/html/rfc6266 [tc-2231]: http://greenbytes.de/tech/tc2231/ ## License [MIT](LICENSE) [npm-image]: https://img.shields.io/npm/v/content-disposition.svg?style=flat [npm-url]: https://npmjs.org/package/content-disposition [node-version-image]: https://img.shields.io/node/v/content-disposition.svg?style=flat [node-version-url]: http://nodejs.org/download/ [travis-image]: https://img.shields.io/travis/jshttp/content-disposition.svg?style=flat [travis-url]: https://travis-ci.org/jshttp/content-disposition [coveralls-image]: https://img.shields.io/coveralls/jshttp/content-disposition.svg?style=flat [coveralls-url]: https://coveralls.io/r/jshttp/content-disposition?branch=master [downloads-image]: https://img.shields.io/npm/dm/content-disposition.svg?style=flat [downloads-url]: https://npmjs.org/package/content-disposition content-disposition-0.5.0/index.js000066400000000000000000000237221241632454100172050ustar00rootroot00000000000000/*! * content-disposition * Copyright(c) 2014 Douglas Christopher Wilson * MIT Licensed */ /** * Module exports. */ module.exports = contentDisposition module.exports.parse = parse /** * Module dependencies. */ var basename = require('path').basename /** * RegExp to match non attr-char, *after* encodeURIComponent (i.e. not including "%") */ var encodeUriAttrCharRegExp = /[\x00-\x20"'\(\)*,\/:;<=>?@\[\\\]\{\}\x7f]/g /** * RegExp to match percent encoding escape. */ var hexEscapeRegExp = /%[0-9A-Fa-f]{2}/ var hexEscapeReplaceRegExp = /%([0-9A-Fa-f]{2})/g /** * RegExp to match non-latin1 characters. */ var nonLatin1RegExp = /[^\x20-\x7e\xa0-\xff]/g /** * RegExp to match quoted-pair in RFC 2616 * * quoted-pair = "\" CHAR * CHAR = */ var qescRegExp = /\\([\u0000-\u007f])/g; /** * RegExp to match chars that must be quoted-pair in RFC 2616 */ var quoteRegExp = /([\\"])/g /** * RegExp for various RFC 2616 grammar * * parameter = token "=" ( token | quoted-string ) * token = 1* * separators = "(" | ")" | "<" | ">" | "@" * | "," | ";" | ":" | "\" | <"> * | "/" | "[" | "]" | "?" | "=" * | "{" | "}" | SP | HT * quoted-string = ( <"> *(qdtext | quoted-pair ) <"> ) * qdtext = > * quoted-pair = "\" CHAR * CHAR = * TEXT = * LWS = [CRLF] 1*( SP | HT ) * CRLF = CR LF * CR = * LF = * SP = * HT = * CTL = * OCTET = */ var paramRegExp = /; *([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *= *("(?:[ !\x23-\x5b\x5d-\x7e\x80-\xff]|\\[\x20-\x7e])*"|[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) */g var textRegExp = /^[\x20-\x7e\x80-\xff]+$/ var tokenRegExp = /^[!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+$/ /** * RegExp for various RFC 5987 grammar * * ext-value = charset "'" [ language ] "'" value-chars * charset = "UTF-8" / "ISO-8859-1" / mime-charset * mime-charset = 1*mime-charsetc * mime-charsetc = ALPHA / DIGIT * / "!" / "#" / "$" / "%" / "&" * / "+" / "-" / "^" / "_" / "`" * / "{" / "}" / "~" * language = ( 2*3ALPHA [ extlang ] ) * / 4ALPHA * / 5*8ALPHA * extlang = *3( "-" 3ALPHA ) * value-chars = *( pct-encoded / attr-char ) * pct-encoded = "%" HEXDIG HEXDIG * attr-char = ALPHA / DIGIT * / "!" / "#" / "$" / "&" / "+" / "-" / "." * / "^" / "_" / "`" / "|" / "~" */ var extValueRegExp = /^([A-Za-z0-9!#$%&+\-^_`{}~]+)'(?:[A-Za-z]{2,3}(?:-[A-Za-z]{3}){0,3}|[A-Za-z]{4,8}|)'((?:%[0-9A-Fa-f]{2}|[A-Za-z0-9!#$&+\-\.^_`|~])+)$/ /** * RegExp for various RFC 6266 grammar * * disposition-type = "inline" | "attachment" | disp-ext-type * disp-ext-type = token * disposition-parm = filename-parm | disp-ext-parm * filename-parm = "filename" "=" value * | "filename*" "=" ext-value * disp-ext-parm = token "=" value * | ext-token "=" ext-value * ext-token = */ var dispositionTypeRegExp = /^([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *(?:$|;)/ /** * Create an attachment Content-Disposition header. * * @param {string} [filename] * @param {object} [options] * @param {string} [options.type=attachment] * @param {string|boolean} [options.fallback=true] * @return {string} * @api public */ function contentDisposition(filename, options) { var opts = options || {} // get type var type = opts.type || 'attachment' // get parameters var params = createparams(filename, opts.fallback) // format into string return format(new ContentDisposition(type, params)) } /** * Create parameters object from filename and fallback. * * @param {string} [filename] * @param {string|boolean} [fallback=true] * @return {object} * @api private */ function createparams(filename, fallback) { if (filename === undefined) { return } var params = {} if (typeof filename !== 'string') { throw new TypeError('filename must be a string') } // fallback defaults to true if (fallback === undefined) { fallback = true } if (typeof fallback !== 'string' && typeof fallback !== 'boolean') { throw new TypeError('fallback must be a string or boolean') } if (typeof fallback === 'string' && nonLatin1RegExp.test(fallback)) { throw new TypeError('fallback must be ISO-8859-1 string') } // restrict to file base name var name = basename(filename) // determine if name is suitable for quoted string var isQuotedString = textRegExp.test(name) // generate fallback name var fallbackName = typeof fallback !== 'string' ? fallback && getlatin1(name) : basename(fallback) var hasFallback = typeof fallbackName === 'string' && fallbackName !== name // set extended filename parameter if (hasFallback || !isQuotedString || hexEscapeRegExp.test(name)) { params['filename*'] = name } // set filename parameter if (isQuotedString || hasFallback) { params.filename = hasFallback ? fallbackName : name } return params } /** * Format object to Content-Disposition header. * * @param {object} obj * @param {string} obj.type * @param {object} [obj.parameters] * @return {string} * @api private */ function format(obj) { var parameters = obj.parameters var type = obj.type if (!type || typeof type !== 'string' || !tokenRegExp.test(type)) { throw new TypeError('invalid type') } // start with normalized type var string = String(type).toLowerCase() // append parameters if (parameters && typeof parameters === 'object') { var param var params = Object.keys(parameters).sort() for (var i = 0; i < params.length; i++) { param = params[i] var val = param.substr(-1) === '*' ? ustring(parameters[param]) : qstring(parameters[param]) string += '; ' + param + '=' + val } } return string } /** * Decode a RFC 6987 field value (gracefully). * * @param {string} str * @return {string} * @api private */ function decodefield(str) { var match = extValueRegExp.exec(str) if (!match) { throw new TypeError('invalid extended field value') } var charset = match[1].toLowerCase() var encoded = match[2] var value // to binary string var binary = encoded.replace(hexEscapeReplaceRegExp, pdecode) switch (charset) { case 'iso-8859-1': value = getlatin1(binary) break case 'utf-8': value = new Buffer(binary, 'binary').toString('utf8') break default: throw new TypeError('unsupported charset in extended field') } return value } /** * Get ISO-8859-1 version of string. * * @param {string} val * @return {string} * @api private */ function getlatin1(val) { // simple Unicode -> ISO-8859-1 transformation return String(val).replace(nonLatin1RegExp, '?') } /** * Parse Content-Disposition header string. * * @param {string} string * @return {object} * @api private */ function parse(string) { if (!string || typeof string !== 'string') { throw new TypeError('argument string is required') } var match = dispositionTypeRegExp.exec(string) if (!match) { throw new TypeError('invalid type format') } // normalize type var index = match[0].length var type = match[1].toLowerCase() var key var names = [] var params = {} var value // calculate index to start at index = paramRegExp.lastIndex = match[0].substr(-1) === ';' ? index - 1 : index // match parameters while (match = paramRegExp.exec(string)) { if (match.index !== index) { throw new TypeError('invalid parameter format') } index += match[0].length key = match[1].toLowerCase() value = match[2] if (names.indexOf(key) !== -1) { throw new TypeError('invalid duplicate parameter') } names.push(key) if (key.indexOf('*') + 1 === key.length) { // decode extended value key = key.slice(0, -1) value = decodefield(value) // overwrite existing value params[key] = value continue } if (typeof params[key] === 'string') { continue } if (value[0] === '"') { // remove quotes and escapes value = value .substr(1, value.length - 2) .replace(qescRegExp, '$1') } params[key] = value } if (index !== -1 && index !== string.length) { throw new TypeError('invalid parameter format') } return new ContentDisposition(type, params) } /** * Percent decode a single character. * * @param {string} str * @param {string} hex * @return {string} * @api private */ function pdecode(str, hex) { return String.fromCharCode(parseInt(hex, 16)) } /** * Percent encode a single character. * * @param {string} char * @return {string} * @api private */ function pencode(char) { var hex = String(char) .charCodeAt(0) .toString(16) .toUpperCase() return hex.length === 1 ? '%0' + hex : '%' + hex } /** * Quote a string for HTTP. * * @param {string} val * @return {string} * @api private */ function qstring(val) { var str = String(val) return '"' + str.replace(quoteRegExp, '\\$1') + '"' } /** * Encode a Unicode string for HTTP (RFC 5987). * * @param {string} val * @return {string} * @api private */ function ustring(val) { var str = String(val) // percent encode as UTF-8 var encoded = encodeURIComponent(str) .replace(encodeUriAttrCharRegExp, pencode) return 'UTF-8\'\'' + encoded } /** * Class for parsed Content-Disposition header for v8 optimization */ function ContentDisposition(type, parameters) { this.type = type this.parameters = parameters } content-disposition-0.5.0/package.json000066400000000000000000000015411241632454100200210ustar00rootroot00000000000000{ "name": "content-disposition", "description": "Create and parse Content-Disposition header", "version": "0.5.0", "contributors": [ "Douglas Christopher Wilson " ], "license": "MIT", "keywords": [ "content-disposition", "http", "rfc6266", "res" ], "repository": "jshttp/content-disposition", "devDependencies": { "istanbul": "0.3.2", "mocha": "~1.21.4" }, "files": [ "LICENSE", "HISTORY.md", "README.md", "index.js" ], "engines": { "node": ">= 0.6" }, "scripts": { "test": "mocha --reporter spec --bail --check-leaks test/", "test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/", "test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/" } } content-disposition-0.5.0/test/000077500000000000000000000000001241632454100165115ustar00rootroot00000000000000content-disposition-0.5.0/test/test.js000066400000000000000000001120521241632454100200270ustar00rootroot00000000000000 var assert = require('assert') var contentDisposition = require('..') describe('contentDisposition()', function () { it('should create an attachment header', function () { assert.equal(contentDisposition(), 'attachment') }) }) describe('contentDisposition(filename)', function () { it('should require a string', function () { assert.throws(contentDisposition.bind(null, 42), /filename.*string/) }) it('should create a header with file name', function () { assert.equal(contentDisposition('plans.pdf'), 'attachment; filename="plans.pdf"') }) it('should use the basename of the string', function () { assert.equal(contentDisposition('/path/to/plans.pdf'), 'attachment; filename="plans.pdf"') }) describe('when "filename" is US-ASCII', function () { it('should only include filename parameter', function () { assert.equal(contentDisposition('plans.pdf'), 'attachment; filename="plans.pdf"') }) it('should escape quotes', function () { assert.equal(contentDisposition('the "plans".pdf'), 'attachment; filename="the \\"plans\\".pdf"') }) }) describe('when "filename" is ISO-8859-1', function () { it('should only include filename parameter', function () { assert.equal(contentDisposition('«plans».pdf'), 'attachment; filename="«plans».pdf"') }) it('should escape quotes', function () { assert.equal(contentDisposition('the "plans" (1µ).pdf'), 'attachment; filename="the \\"plans\\" (1µ).pdf"') }) }) describe('when "filename" is Unicode', function () { it('should include filename* parameter', function () { assert.equal(contentDisposition('планы.pdf'), 'attachment; filename="?????.pdf"; filename*=UTF-8\'\'%D0%BF%D0%BB%D0%B0%D0%BD%D1%8B.pdf') }) it('should include filename fallback', function () { assert.equal(contentDisposition('£ and € rates.pdf'), 'attachment; filename="£ and ? rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf') assert.equal(contentDisposition('€ rates.pdf'), 'attachment; filename="? rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf') }) it('should encode special characters', function () { assert.equal(contentDisposition('€\'*%().pdf'), 'attachment; filename="?\'*%().pdf"; filename*=UTF-8\'\'%E2%82%AC%27%2A%25%28%29.pdf') }) }) describe('when "filename" contains hex escape', function () { it('should include filename* parameter', function () { assert.equal(contentDisposition('the%20plans.pdf'), 'attachment; filename="the%20plans.pdf"; filename*=UTF-8\'\'the%2520plans.pdf') }) it('should handle Unicode', function () { assert.equal(contentDisposition('€%20£.pdf'), 'attachment; filename="?%20£.pdf"; filename*=UTF-8\'\'%E2%82%AC%2520%C2%A3.pdf') }) }) }) describe('contentDisposition(filename, options)', function () { describe('with "fallback" option', function () { it('should require a string or Boolean', function () { assert.throws(contentDisposition.bind(null, 'plans.pdf', { fallback: 42 }), /fallback.*string/) }) it('should default to true', function () { assert.equal(contentDisposition('€ rates.pdf'), 'attachment; filename="? rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf') }) describe('when "false"', function () { it('should not generate ISO-8859-1 fallback', function () { assert.equal(contentDisposition('£ and € rates.pdf', { fallback: false }), 'attachment; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf') }) it('should keep ISO-8859-1 filename', function () { assert.equal(contentDisposition('£ rates.pdf', { fallback: false }), 'attachment; filename="£ rates.pdf"') }) }) describe('when "true"', function () { it('should generate ISO-8859-1 fallback', function () { assert.equal(contentDisposition('£ and € rates.pdf', { fallback: true }), 'attachment; filename="£ and ? rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf') }) it('should pass through ISO-8859-1 filename', function () { assert.equal(contentDisposition('£ rates.pdf', { fallback: true }), 'attachment; filename="£ rates.pdf"') }) }) describe('when a string', function () { it('should require an ISO-8859-1 string', function () { assert.throws(contentDisposition.bind(null, '€ rates.pdf', { fallback: '€ rates.pdf' }), /fallback.*iso-8859-1/i) }) it('should use as ISO-8859-1 fallback', function () { assert.equal(contentDisposition('£ and € rates.pdf', { fallback: '£ and EURO rates.pdf' }), 'attachment; filename="£ and EURO rates.pdf"; filename*=UTF-8\'\'%C2%A3%20and%20%E2%82%AC%20rates.pdf') }) it('should use as fallback even when filename is ISO-8859-1', function () { assert.equal(contentDisposition('"£ rates".pdf', { fallback: '£ rates.pdf' }), 'attachment; filename="£ rates.pdf"; filename*=UTF-8\'\'%22%C2%A3%20rates%22.pdf') }) it('should do nothing if equal to filename', function () { assert.equal(contentDisposition('plans.pdf', { fallback: 'plans.pdf' }), 'attachment; filename="plans.pdf"') }) it('should use the basename of the string', function () { assert.equal(contentDisposition('€ rates.pdf', { fallback: '/path/to/EURO rates.pdf' }), 'attachment; filename="EURO rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf') }) it('should do nothing without filename option', function () { assert.equal(contentDisposition(undefined, { fallback: 'plans.pdf' }), 'attachment') }) }) }) describe('with "type" option', function () { it('should default to attachment', function () { assert.equal(contentDisposition(), 'attachment') }) it('should require a string', function () { assert.throws(contentDisposition.bind(null, undefined, { type: 42 }), /invalid type/) }) it('should require a valid type', function () { assert.throws(contentDisposition.bind(null, undefined, { type: 'invlaid;type' }), /invalid type/) }) it('should create a header with inline type', function () { assert.equal(contentDisposition(undefined, { type: 'inline' }), 'inline') }) it('should create a header with inline type & filename', function () { assert.equal(contentDisposition('plans.pdf', { type: 'inline' }), 'inline; filename="plans.pdf"') }) it('should normalize type', function () { assert.equal(contentDisposition(undefined, { type: 'INLINE' }), 'inline') }) }) }) describe('contentDisposition.parse(string)', function () { it('should require string', function () { assert.throws(contentDisposition.parse.bind(null), /argument string.*required/) }) it('should reject non-strings', function () { assert.throws(contentDisposition.parse.bind(null, 42), /argument string.*required/) }) describe('with only type', function () { it('should reject quoted value', function () { assert.throws(contentDisposition.parse.bind(null, '"attachment"'), /invalid type format/) }) it('should reject trailing semicolon', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment;'), /invalid.*format/) }) it('should parse "attachment"', function () { assert.deepEqual(contentDisposition.parse('attachment'), { type: 'attachment', parameters: {} }) }) it('should parse "inline"', function () { assert.deepEqual(contentDisposition.parse('inline'), { type: 'inline', parameters: {} }) }) it('should parse "form-data"', function () { assert.deepEqual(contentDisposition.parse('form-data'), { type: 'form-data', parameters: {} }) }) it('should parse with trailing LWS', function () { assert.deepEqual(contentDisposition.parse('attachment '), { type: 'attachment', parameters: {} }) }) it('should normalize to lower-case', function () { assert.deepEqual(contentDisposition.parse('ATTACHMENT'), { type: 'attachment', parameters: {} }) }) }) describe('with parameters', function () { it('should reject trailing semicolon', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="rates.pdf";'), /invalid parameter format/) }) it('should reject invalid parameter name', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename@="rates.pdf"'), /invalid parameter format/) }) it('should reject missing parameter value', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename='), /invalid parameter format/) }) it('should reject invalid parameter value', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=trolly,trains'), /invalid parameter format/) }) it('should reject invalid parameters', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=total/; foo=bar'), /invalid parameter format/) }) it('should reject duplicate parameters', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo; filename=bar'), /invalid duplicate parameter/) }) it('should reject missing type', function () { assert.throws(contentDisposition.parse.bind(null, 'filename="plans.pdf"'), /invalid type format/) assert.throws(contentDisposition.parse.bind(null, '; filename="plans.pdf"'), /invalid type format/) }) it('should lower-case parameter name', function () { assert.deepEqual(contentDisposition.parse('attachment; FILENAME="plans.pdf"'), { type: 'attachment', parameters: { filename: 'plans.pdf' } }) }) it('should parse quoted parameter value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="plans.pdf"'), { type: 'attachment', parameters: { filename: 'plans.pdf' } }) }) it('should parse & unescape quoted value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="the \\"plans\\".pdf"'), { type: 'attachment', parameters: { filename: 'the "plans".pdf' } }) }) it('should include all parameters', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="plans.pdf"; foo=bar'), { type: 'attachment', parameters: { filename: 'plans.pdf', foo: 'bar' } }) }) it('should parse token filename', function () { assert.deepEqual(contentDisposition.parse('attachment; filename=plans.pdf'), { type: 'attachment', parameters: { filename: 'plans.pdf' } }) }) it('should parse ISO-8859-1 filename', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="£ rates.pdf"'), { type: 'attachment', parameters: { filename: '£ rates.pdf' } }) }) }) describe('with extended parameters', function () { it('should reject quoted extended parameter value', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="UTF-8\'\'%E2%82%AC%20rates.pdf"'), /invalid extended.*value/) }) it('should parse UTF-8 extended parameter value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) }) it('should parse UTF-8 extended parameter value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E4%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '\ufffd rates.pdf' } }) }) it('should parse ISO-8859-1 extended parameter value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=ISO-8859-1\'\'%A3%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '£ rates.pdf' } }) assert.deepEqual(contentDisposition.parse('attachment; filename*=ISO-8859-1\'\'%82%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '? rates.pdf' } }) }) it('should not be case-sensitive for charser', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=utf-8\'\'%E2%82%AC%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) }) it('should reject unsupported charset', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=ISO-8859-2\'\'%A4%20rates.pdf'), /unsupported charset/) }) it('should parse with embedded language', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'en\'%E2%82%AC%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) }) it('should prefer extended parameter value', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="EURO rates.pdf"; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%E2%82%AC%20rates.pdf; filename="EURO rates.pdf"'), { type: 'attachment', parameters: { 'filename': '€ rates.pdf' } }) }) }) describe('from TC 2231', function () { describe('Disposition-Type Inline', function () { it('should parse "inline"', function () { assert.deepEqual(contentDisposition.parse('inline'), { type: 'inline', parameters: {} }) }) it('should reject ""inline""', function () { assert.throws(contentDisposition.parse.bind(null, '"inline"'), /invalid type format/) }) it('should parse "inline; filename="foo.html""', function () { assert.deepEqual(contentDisposition.parse('inline; filename="foo.html"'), { type: 'inline', parameters: { filename: 'foo.html' } }) }) it('should parse "inline; filename="Not an attachment!""', function () { assert.deepEqual(contentDisposition.parse('inline; filename="Not an attachment!"'), { type: 'inline', parameters: { filename: 'Not an attachment!' } }) }) it('should parse "inline; filename="foo.pdf""', function () { assert.deepEqual(contentDisposition.parse('inline; filename="foo.pdf"'), { type: 'inline', parameters: { filename: 'foo.pdf' } }) }) }) describe('Disposition-Type Attachment', function () { it('should parse "attachment"', function () { assert.deepEqual(contentDisposition.parse('attachment'), { type: 'attachment', parameters: {} }) }) it('should reject ""attachment""', function () { assert.throws(contentDisposition.parse.bind(null, '"attachment"'), /invalid type format/) }) it('should parse "ATTACHMENT"', function () { assert.deepEqual(contentDisposition.parse('ATTACHMENT'), { type: 'attachment', parameters: {} }) }) it('should parse "attachment; filename="foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html' } }) }) it('should parse "attachment; filename="0000000000111111111122222""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="0000000000111111111122222"'), { type: 'attachment', parameters: { filename: '0000000000111111111122222' } }) }) it('should parse "attachment; filename="00000000001111111111222222222233333""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="00000000001111111111222222222233333"'), { type: 'attachment', parameters: { filename: '00000000001111111111222222222233333' } }) }) it('should parse "attachment; filename="f\\oo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="f\\oo.html"'), { type: 'attachment', parameters: { filename: 'foo.html' } }) }) it('should parse "attachment; filename="\\"quoting\\" tested.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="\\"quoting\\" tested.html"'), { type: 'attachment', parameters: { filename: '"quoting" tested.html' } }) }) it('should parse "attachment; filename="Here\'s a semicolon;.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="Here\'s a semicolon;.html"'), { type: 'attachment', parameters: { filename: 'Here\'s a semicolon;.html' } }) }) it('should parse "attachment; foo="bar"; filename="foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; foo="bar"; filename="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html', foo: 'bar' } }) }) it('should parse "attachment; foo="\\"\\\\";filename="foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; foo="\\"\\\\";filename="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html', foo: '"\\' } }) }) it('should parse "attachment; FILENAME="foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; FILENAME="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html' } }) }) it('should parse "attachment; filename=foo.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename=foo.html'), { type: 'attachment', parameters: { filename: 'foo.html' } }) }) it('should reject "attachment; filename=foo,bar.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo,bar.html'), /invalid parameter format/) }) it('should reject "attachment; filename=foo.html ;"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo.html ;'), /invalid parameter format/) }) it('should reject "attachment; ;filename=foo"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; ;filename=foo'), /invalid parameter format/) }) it('should reject "attachment; filename=foo bar.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo bar.html'), /invalid parameter format/) }) it('should parse "attachment; filename=\'foo.bar\'', function () { assert.deepEqual(contentDisposition.parse('attachment; filename=\'foo.bar\''), { type: 'attachment', parameters: { filename: '\'foo.bar\'' } }) }) it('should parse "attachment; filename="foo-ä.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ä.html"'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename="foo-ä.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ä.html"'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename="foo-%41.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%41.html"'), { type: 'attachment', parameters: { filename: 'foo-%41.html' } }) }) it('should parse "attachment; filename="50%.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="50%.html"'), { type: 'attachment', parameters: { filename: '50%.html' } }) }) it('should parse "attachment; filename="foo-%\\41.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%\\41.html"'), { type: 'attachment', parameters: { filename: 'foo-%41.html' } }) }) it('should parse "attachment; name="foo-%41.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; name="foo-%41.html"'), { type: 'attachment', parameters: { name: 'foo-%41.html' } }) }) it('should parse "attachment; filename="ä-%41.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="ä-%41.html"'), { type: 'attachment', parameters: { filename: 'ä-%41.html' } }) }) it('should parse "attachment; filename="foo-%c3%a4-%e2%82%ac.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-%c3%a4-%e2%82%ac.html"'), { type: 'attachment', parameters: { filename: 'foo-%c3%a4-%e2%82%ac.html' } }) }) it('should parse "attachment; filename ="foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename ="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html' } }) }) it('should reject "attachment; filename="foo.html"; filename="bar.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="foo.html"; filename="bar.html"'), /invalid duplicate parameter/) }) it('should reject "attachment; filename=foo[1](2).html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo[1](2).html'), /invalid parameter format/) }) it('should reject "attachment; filename=foo-ä.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo-ä.html'), /invalid parameter format/) }) it('should reject "attachment; filename=foo-ä.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo-ä.html'), /invalid parameter format/) }) it('should reject "filename=foo.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html'), /invalid type format/) }) it('should reject "x=y; filename=foo.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'x=y; filename=foo.html'), /invalid type format/) }) it('should reject ""foo; filename=bar;baz"; filename=qux"', function () { assert.throws(contentDisposition.parse.bind(null, '"foo; filename=bar;baz"; filename=qux'), /invalid type format/) }) it('should reject "filename=foo.html, filename=bar.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html, filename=bar.html'), /invalid type format/) }) it('should reject "; filename=foo.html"', function () { assert.throws(contentDisposition.parse.bind(null, '; filename=foo.html'), /invalid type format/) }) it('should reject ": inline; attachment; filename=foo.html', function () { assert.throws(contentDisposition.parse.bind(null, ': inline; attachment; filename=foo.html'), /invalid type format/) }) it('should reject "inline; attachment; filename=foo.html', function () { assert.throws(contentDisposition.parse.bind(null, 'inline; attachment; filename=foo.html'), /invalid parameter format/) }) it('should reject "attachment; inline; filename=foo.html', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; inline; filename=foo.html'), /invalid parameter format/) }) it('should reject "attachment; filename="foo.html".txt', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="foo.html".txt'), /invalid parameter format/) }) it('should reject "attachment; filename="bar', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename="bar'), /invalid parameter format/) }) it('should reject "attachment; filename=foo"bar;baz"qux', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo"bar;baz"qux'), /invalid parameter format/) }) it('should reject "attachment; filename=foo.html, attachment; filename=bar.html', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=foo.html, attachment; filename=bar.html'), /invalid parameter format/) }) it('should reject "attachment; foo=foo filename=bar', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; foo=foo filename=bar'), /invalid parameter format/) }) it('should reject "attachment; filename=bar foo=foo', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename=bar foo=foo'), /invalid parameter format/) }) it('should reject "attachment filename=bar', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment filename=bar'), /invalid type format/) }) it('should reject "filename=foo.html; attachment', function () { assert.throws(contentDisposition.parse.bind(null, 'filename=foo.html; attachment'), /invalid type format/) }) it('should parse "attachment; xfilename=foo.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; xfilename=foo.html'), { type: 'attachment', parameters: { xfilename: 'foo.html' } }) }) it('should parse "attachment; filename="/foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="/foo.html"'), { type: 'attachment', parameters: { filename: '/foo.html' } }) }) it('should parse "attachment; filename="\\\\foo.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="\\\\foo.html"'), { type: 'attachment', parameters: { filename: '\\foo.html' } }) }) }) describe('Additional Parameters', function () { it('should parse "attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500""', function () { assert.deepEqual(contentDisposition.parse('attachment; creation-date="Wed, 12 Feb 1997 16:29:51 -0500"'), { type: 'attachment', parameters: { 'creation-date': 'Wed, 12 Feb 1997 16:29:51 -0500' } }) }) it('should parse "attachment; modification-date="Wed, 12 Feb 1997 16:29:51 -0500""', function () { assert.deepEqual(contentDisposition.parse('attachment; modification-date="Wed, 12 Feb 1997 16:29:51 -0500"'), { type: 'attachment', parameters: { 'modification-date': 'Wed, 12 Feb 1997 16:29:51 -0500' } }) }) }) describe('Disposition-Type Extension', function () { it('should parse "foobar"', function () { assert.deepEqual(contentDisposition.parse('foobar'), { type: 'foobar', parameters: {} }) }) it('should parse "attachment; example="filename=example.txt""', function () { assert.deepEqual(contentDisposition.parse('attachment; example="filename=example.txt"'), { type: 'attachment', parameters: { example: 'filename=example.txt' } }) }) }) describe('RFC 2231/5987 Encoding: Character Sets', function () { it('should parse "attachment; filename*=iso-8859-1\'\'foo-%E4.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=iso-8859-1\'\'foo-%E4.html'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename*=UTF-8\'\'foo-%c3%a4-%e2%82%ac.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-%c3%a4-%e2%82%ac.html'), { type: 'attachment', parameters: { filename: 'foo-ä-€.html' } }) }) it('should reject "attachment; filename*=\'\'foo-%c3%a4-%e2%82%ac.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=\'\'foo-%c3%a4-%e2%82%ac.html'), /invalid extended.*value/) }) it('should parse "attachment; filename*=UTF-8\'\'foo-a%cc%88.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-a%cc%88.html'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename*=iso-8859-1\'\'foo-%c3%a4-%e2%82%ac.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=iso-8859-1\'\'foo-%c3%a4-%e2%82%ac.html'), { type: 'attachment', parameters: { filename: 'foo-ä-â?¬.html' } }) }) it('should parse "attachment; filename*=utf-8\'\'foo-%E4.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=utf-8\'\'foo-%E4.html'), { type: 'attachment', parameters: { filename: 'foo-\ufffd.html' } }) }) it('should reject "attachment; filename *=UTF-8\'\'foo-%c3%a4.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename *=UTF-8\'\'foo-%c3%a4.html'), /invalid parameter format/) }) it('should parse "attachment; filename*= UTF-8\'\'foo-%c3%a4.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*= UTF-8\'\'foo-%c3%a4.html'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename* =UTF-8\'\'foo-%c3%a4.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename* =UTF-8\'\'foo-%c3%a4.html'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should reject "attachment; filename*="UTF-8\'\'foo-%c3%a4.html""', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="UTF-8\'\'foo-%c3%a4.html"'), /invalid extended field value/) }) it('should reject "attachment; filename*="foo%20bar.html""', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*="foo%20bar.html"'), /invalid extended field value/) }) it('should reject "attachment; filename*=UTF-8\'foo-%c3%a4.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'foo-%c3%a4.html'), /invalid extended field value/) }) it('should reject "attachment; filename*=UTF-8\'\'foo%"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'\'foo%'), /invalid extended field value/) }) it('should reject "attachment; filename*=UTF-8\'\'f%oo.html"', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename*=UTF-8\'\'f%oo.html'), /invalid extended field value/) }) it('should parse "attachment; filename*=UTF-8\'\'A-%2541.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'A-%2541.html'), { type: 'attachment', parameters: { filename: 'A-%41.html' } }) }) it('should parse "attachment; filename*=UTF-8\'\'%5cfoo.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'%5cfoo.html'), { type: 'attachment', parameters: { filename: '\\foo.html' } }) }) }) describe('RFC2231 Encoding: Continuations', function () { it('should parse "attachment; filename*0="foo."; filename*1="html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo."; filename*1="html"'), { type: 'attachment', parameters: { 'filename*0': 'foo.', 'filename*1': 'html' } }) }) it('should parse "attachment; filename*0="foo"; filename*1="\\b\\a\\r.html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*1="\\b\\a\\r.html"'), { type: 'attachment', parameters: { 'filename*0': 'foo', 'filename*1': 'bar.html' } }) }) it('should parse "attachment; filename*0*=UTF-8\'\'foo-%c3%a4; filename*1=".html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0*=UTF-8\'\'foo-%c3%a4; filename*1=".html"'), { type: 'attachment', parameters: { 'filename*0*': 'UTF-8\'\'foo-%c3%a4', 'filename*1': '.html' } }) }) it('should parse "attachment; filename*0="foo"; filename*01="bar""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*01="bar"'), { type: 'attachment', parameters: { 'filename*0': 'foo', 'filename*01': 'bar' } }) }) it('should parse "attachment; filename*0="foo"; filename*2="bar""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0="foo"; filename*2="bar"'), { type: 'attachment', parameters: { 'filename*0': 'foo', 'filename*2': 'bar' } }) }) it('should parse "attachment; filename*1="foo."; filename*2="html""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*1="foo."; filename*2="html"'), { type: 'attachment', parameters: { 'filename*1': 'foo.', 'filename*2': 'html' } }) }) it('should parse "attachment; filename*1="bar"; filename*0="foo""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*1="bar"; filename*0="foo"'), { type: 'attachment', parameters: { 'filename*1': 'bar', 'filename*0': 'foo' } }) }) }) describe('RFC2231 Encoding: Fallback Behaviour', function () { it('should parse "attachment; filename="foo-ae.html"; filename*=UTF-8\'\'foo-%c3%a4.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="foo-ae.html"; filename*=UTF-8\'\'foo-%c3%a4.html'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename*=UTF-8\'\'foo-%c3%a4.html; filename="foo-ae.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*=UTF-8\'\'foo-%c3%a4.html; filename="foo-ae.html"'), { type: 'attachment', parameters: { filename: 'foo-ä.html' } }) }) it('should parse "attachment; filename*0*=ISO-8859-15\'\'euro-sign%3d%a4; filename*=ISO-8859-1\'\'currency-sign%3d%a4', function () { assert.deepEqual(contentDisposition.parse('attachment; filename*0*=ISO-8859-15\'\'euro-sign%3d%a4; filename*=ISO-8859-1\'\'currency-sign%3d%a4'), { type: 'attachment', parameters: { filename: 'currency-sign=¤', 'filename*0*': 'ISO-8859-15\'\'euro-sign%3d%a4' } }) }) it('should parse "attachment; foobar=x; filename="foo.html"', function () { assert.deepEqual(contentDisposition.parse('attachment; foobar=x; filename="foo.html"'), { type: 'attachment', parameters: { filename: 'foo.html', foobar: 'x' } }) }) }) describe('RFC2047 Encoding', function () { it('should reject "attachment; filename==?ISO-8859-1?Q?foo-=E4.html?="', function () { assert.throws(contentDisposition.parse.bind(null, 'attachment; filename==?ISO-8859-1?Q?foo-=E4.html?='), /invalid parameter format/) }) it('should parse "attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?=""', function () { assert.deepEqual(contentDisposition.parse('attachment; filename="=?ISO-8859-1?Q?foo-=E4.html?="'), { type: 'attachment', parameters: { filename: '=?ISO-8859-1?Q?foo-=E4.html?=' } }) }) }) }) })