Socket
Socket
Sign inDemoInstall

content-disposition

Package Overview
Dependencies
0
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 0.4.0 to 0.5.0

5

HISTORY.md

@@ -0,1 +1,6 @@

0.5.0 / 2014-10-11
==================
* Add `parse` function
0.4.0 / 2014-09-21

@@ -2,0 +7,0 @@ ==================

333

index.js

@@ -12,2 +12,3 @@ /*!

module.exports = contentDisposition
module.exports.parse = parse

@@ -30,11 +31,21 @@ /**

var hexEscapeRegExp = /%[0-9A-F]{2}/i
var hexEscapeRegExp = /%[0-9A-Fa-f]{2}/
var hexEscapeReplaceRegExp = /%([0-9A-Fa-f]{2})/g
/**
* RegExp to match non-RFC 2616 text characters.
* RegExp to match non-latin1 characters.
*/
var nonTextRegExp = /[^\x20-\x7e\x80-\xff]/g
var nonLatin1RegExp = /[^\x20-\x7e\xa0-\xff]/g
/**
* RegExp to match quoted-pair in RFC 2616
*
* quoted-pair = "\" CHAR
* CHAR = <any US-ASCII character (octets 0 - 127)>
*/
var qescRegExp = /\\([\u0000-\u007f])/g;
/**
* RegExp to match chars that must be quoted-pair in RFC 2616

@@ -48,18 +59,66 @@ */

*
* token = 1*<any CHAR except CTLs or separators>
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
* CHAR = <any US-ASCII character (octets 0 - 127)>
* TEXT = <any OCTET except CTLs, but including LWS>
* SP = <US-ASCII SP, space (32)>
* HT = <US-ASCII HT, horizontal-tab (9)>
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
* parameter = token "=" ( token | quoted-string )
* token = 1*<any CHAR except CTLs or separators>
* separators = "(" | ")" | "<" | ">" | "@"
* | "," | ";" | ":" | "\" | <">
* | "/" | "[" | "]" | "?" | "="
* | "{" | "}" | SP | HT
* quoted-string = ( <"> *(qdtext | quoted-pair ) <"> )
* qdtext = <any TEXT except <">>
* quoted-pair = "\" CHAR
* CHAR = <any US-ASCII character (octets 0 - 127)>
* TEXT = <any OCTET except CTLs, but including LWS>
* LWS = [CRLF] 1*( SP | HT )
* CRLF = CR LF
* CR = <US-ASCII CR, carriage return (13)>
* LF = <US-ASCII LF, linefeed (10)>
* SP = <US-ASCII SP, space (32)>
* HT = <US-ASCII HT, horizontal-tab (9)>
* CTL = <any US-ASCII control character (octets 0 - 31) and DEL (127)>
* OCTET = <any 8-bit sequence of data>
*/
var textRegExp = /^[\u0020-\u007e\u0080-\u00ff]+$/
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 = <the characters in token, followed by "*">
*/
var dispositionTypeRegExp = /^([!#$%&'\*\+\-\.0-9A-Z\^_`a-z\|~]+) *(?:$|;)/
/**
* Create an attachment Content-Disposition header.

@@ -81,32 +140,40 @@ *

if (typeof type !== 'string') {
throw new TypeError('option type must be a string')
}
// get parameters
var params = createparams(filename, opts.fallback)
if (!tokenRegExp.test(type)) {
throw new TypeError('option type must be a valid token')
}
// format into string
return format(new ContentDisposition(type, params))
}
// normalize type
type = type.toLowerCase()
/**
* 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 type
return
}
var params = {}
if (typeof filename !== 'string') {
throw new TypeError('argument filename must be a string')
throw new TypeError('filename must be a string')
}
// get fallback
var fallback = opts.fallback !== undefined
? opts.fallback
: true
// fallback defaults to true
if (fallback === undefined) {
fallback = true
}
if (typeof fallback !== 'string' && typeof fallback !== 'boolean') {
throw new TypeError('option fallback must be a string or boolean')
throw new TypeError('fallback must be a string or boolean')
}
if (typeof fallback === 'string' && nonTextRegExp.test(fallback)) {
throw new TypeError('option fallback must be ISO-8859-1 string')
if (typeof fallback === 'string' && nonLatin1RegExp.test(fallback)) {
throw new TypeError('fallback must be ISO-8859-1 string')
}

@@ -117,2 +184,5 @@

// determine if name is suitable for quoted string
var isQuotedString = textRegExp.test(name)
// generate fallback name

@@ -122,19 +192,96 @@ var fallbackName = typeof fallback !== 'string'

: basename(fallback)
var hasFallback = typeof fallbackName === 'string' && fallbackName !== name
var isSimpleHeader = (typeof fallbackName !== 'string' || fallbackName === name)
&& !hexEscapeRegExp.test(name)
&& textRegExp.test(name)
// set extended filename parameter
if (hasFallback || !isQuotedString || hexEscapeRegExp.test(name)) {
params['filename*'] = name
}
if (isSimpleHeader) {
// simple header
// file name is always quoted and not a token for RFC 2616 compatibility
return type + '; filename=' + qstring(name)
// set filename parameter
if (isQuotedString || hasFallback) {
params.filename = hasFallback
? fallbackName
: name
}
return type
+ (fallbackName !== false ? '; filename=' + qstring(fallbackName) : '')
+ '; filename*=' + ustring(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.

@@ -149,6 +296,99 @@ *

// simple Unicode -> ISO-8859-1 transformation
return String(val).replace(nonTextRegExp, '?')
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.

@@ -202,1 +442,10 @@ *

}
/**
* Class for parsed Content-Disposition header for v8 optimization
*/
function ContentDisposition(type, parameters) {
this.type = type
this.parameters = parameters
}

4

package.json
{
"name": "content-disposition",
"description": "Create an attachment Content-Disposition header",
"version": "0.4.0",
"description": "Create and parse Content-Disposition header",
"version": "0.5.0",
"contributors": [

@@ -6,0 +6,0 @@ "Douglas Christopher Wilson <doug@somethingdoug.com>"

@@ -9,3 +9,3 @@ # content-disposition

Create an attachment Content-Disposition header
Create and parse HTTP `Content-Disposition` header

@@ -68,2 +68,19 @@ ## Installation

### 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

@@ -70,0 +87,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc