Spaces:
Sleeping
Sleeping
; | |
function parseContentType(str) { | |
if (str.length === 0) | |
return; | |
const params = Object.create(null); | |
let i = 0; | |
// Parse type | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
if (code !== 47/* '/' */ || i === 0) | |
return; | |
break; | |
} | |
} | |
// Check for type without subtype | |
if (i === str.length) | |
return; | |
const type = str.slice(0, i).toLowerCase(); | |
// Parse subtype | |
const subtypeStart = ++i; | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
// Make sure we have a subtype | |
if (i === subtypeStart) | |
return; | |
if (parseContentTypeParams(str, i, params) === undefined) | |
return; | |
break; | |
} | |
} | |
// Make sure we have a subtype | |
if (i === subtypeStart) | |
return; | |
const subtype = str.slice(subtypeStart, i).toLowerCase(); | |
return { type, subtype, params }; | |
} | |
function parseContentTypeParams(str, i, params) { | |
while (i < str.length) { | |
// Consume whitespace | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code !== 32/* ' ' */ && code !== 9/* '\t' */) | |
break; | |
} | |
// Ended on whitespace | |
if (i === str.length) | |
break; | |
// Check for malformed parameter | |
if (str.charCodeAt(i++) !== 59/* ';' */) | |
return; | |
// Consume whitespace | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code !== 32/* ' ' */ && code !== 9/* '\t' */) | |
break; | |
} | |
// Ended on whitespace (malformed) | |
if (i === str.length) | |
return; | |
let name; | |
const nameStart = i; | |
// Parse parameter name | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
if (code !== 61/* '=' */) | |
return; | |
break; | |
} | |
} | |
// No value (malformed) | |
if (i === str.length) | |
return; | |
name = str.slice(nameStart, i); | |
++i; // Skip over '=' | |
// No value (malformed) | |
if (i === str.length) | |
return; | |
let value = ''; | |
let valueStart; | |
if (str.charCodeAt(i) === 34/* '"' */) { | |
valueStart = ++i; | |
let escaping = false; | |
// Parse quoted value | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code === 92/* '\\' */) { | |
if (escaping) { | |
valueStart = i; | |
escaping = false; | |
} else { | |
value += str.slice(valueStart, i); | |
escaping = true; | |
} | |
continue; | |
} | |
if (code === 34/* '"' */) { | |
if (escaping) { | |
valueStart = i; | |
escaping = false; | |
continue; | |
} | |
value += str.slice(valueStart, i); | |
break; | |
} | |
if (escaping) { | |
valueStart = i - 1; | |
escaping = false; | |
} | |
// Invalid unescaped quoted character (malformed) | |
if (QDTEXT[code] !== 1) | |
return; | |
} | |
// No end quote (malformed) | |
if (i === str.length) | |
return; | |
++i; // Skip over double quote | |
} else { | |
valueStart = i; | |
// Parse unquoted value | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
// No value (malformed) | |
if (i === valueStart) | |
return; | |
break; | |
} | |
} | |
value = str.slice(valueStart, i); | |
} | |
name = name.toLowerCase(); | |
if (params[name] === undefined) | |
params[name] = value; | |
} | |
return params; | |
} | |
function parseDisposition(str, defDecoder) { | |
if (str.length === 0) | |
return; | |
const params = Object.create(null); | |
let i = 0; | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
if (parseDispositionParams(str, i, params, defDecoder) === undefined) | |
return; | |
break; | |
} | |
} | |
const type = str.slice(0, i).toLowerCase(); | |
return { type, params }; | |
} | |
function parseDispositionParams(str, i, params, defDecoder) { | |
while (i < str.length) { | |
// Consume whitespace | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code !== 32/* ' ' */ && code !== 9/* '\t' */) | |
break; | |
} | |
// Ended on whitespace | |
if (i === str.length) | |
break; | |
// Check for malformed parameter | |
if (str.charCodeAt(i++) !== 59/* ';' */) | |
return; | |
// Consume whitespace | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code !== 32/* ' ' */ && code !== 9/* '\t' */) | |
break; | |
} | |
// Ended on whitespace (malformed) | |
if (i === str.length) | |
return; | |
let name; | |
const nameStart = i; | |
// Parse parameter name | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
if (code === 61/* '=' */) | |
break; | |
return; | |
} | |
} | |
// No value (malformed) | |
if (i === str.length) | |
return; | |
let value = ''; | |
let valueStart; | |
let charset; | |
//~ let lang; | |
name = str.slice(nameStart, i); | |
if (name.charCodeAt(name.length - 1) === 42/* '*' */) { | |
// Extended value | |
const charsetStart = ++i; | |
// Parse charset name | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (CHARSET[code] !== 1) { | |
if (code !== 39/* '\'' */) | |
return; | |
break; | |
} | |
} | |
// Incomplete charset (malformed) | |
if (i === str.length) | |
return; | |
charset = str.slice(charsetStart, i); | |
++i; // Skip over the '\'' | |
//~ const langStart = ++i; | |
// Parse language name | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code === 39/* '\'' */) | |
break; | |
} | |
// Incomplete language (malformed) | |
if (i === str.length) | |
return; | |
//~ lang = str.slice(langStart, i); | |
++i; // Skip over the '\'' | |
// No value (malformed) | |
if (i === str.length) | |
return; | |
valueStart = i; | |
let encode = 0; | |
// Parse value | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (EXTENDED_VALUE[code] !== 1) { | |
if (code === 37/* '%' */) { | |
let hexUpper; | |
let hexLower; | |
if (i + 2 < str.length | |
&& (hexUpper = HEX_VALUES[str.charCodeAt(i + 1)]) !== -1 | |
&& (hexLower = HEX_VALUES[str.charCodeAt(i + 2)]) !== -1) { | |
const byteVal = (hexUpper << 4) + hexLower; | |
value += str.slice(valueStart, i); | |
value += String.fromCharCode(byteVal); | |
i += 2; | |
valueStart = i + 1; | |
if (byteVal >= 128) | |
encode = 2; | |
else if (encode === 0) | |
encode = 1; | |
continue; | |
} | |
// '%' disallowed in non-percent encoded contexts (malformed) | |
return; | |
} | |
break; | |
} | |
} | |
value += str.slice(valueStart, i); | |
value = convertToUTF8(value, charset, encode); | |
if (value === undefined) | |
return; | |
} else { | |
// Non-extended value | |
++i; // Skip over '=' | |
// No value (malformed) | |
if (i === str.length) | |
return; | |
if (str.charCodeAt(i) === 34/* '"' */) { | |
valueStart = ++i; | |
let escaping = false; | |
// Parse quoted value | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (code === 92/* '\\' */) { | |
if (escaping) { | |
valueStart = i; | |
escaping = false; | |
} else { | |
value += str.slice(valueStart, i); | |
escaping = true; | |
} | |
continue; | |
} | |
if (code === 34/* '"' */) { | |
if (escaping) { | |
valueStart = i; | |
escaping = false; | |
continue; | |
} | |
value += str.slice(valueStart, i); | |
break; | |
} | |
if (escaping) { | |
valueStart = i - 1; | |
escaping = false; | |
} | |
// Invalid unescaped quoted character (malformed) | |
if (QDTEXT[code] !== 1) | |
return; | |
} | |
// No end quote (malformed) | |
if (i === str.length) | |
return; | |
++i; // Skip over double quote | |
} else { | |
valueStart = i; | |
// Parse unquoted value | |
for (; i < str.length; ++i) { | |
const code = str.charCodeAt(i); | |
if (TOKEN[code] !== 1) { | |
// No value (malformed) | |
if (i === valueStart) | |
return; | |
break; | |
} | |
} | |
value = str.slice(valueStart, i); | |
} | |
value = defDecoder(value, 2); | |
if (value === undefined) | |
return; | |
} | |
name = name.toLowerCase(); | |
if (params[name] === undefined) | |
params[name] = value; | |
} | |
return params; | |
} | |
function getDecoder(charset) { | |
let lc; | |
while (true) { | |
switch (charset) { | |
case 'utf-8': | |
case 'utf8': | |
return decoders.utf8; | |
case 'latin1': | |
case 'ascii': // TODO: Make these a separate, strict decoder? | |
case 'us-ascii': | |
case 'iso-8859-1': | |
case 'iso8859-1': | |
case 'iso88591': | |
case 'iso_8859-1': | |
case 'windows-1252': | |
case 'iso_8859-1:1987': | |
case 'cp1252': | |
case 'x-cp1252': | |
return decoders.latin1; | |
case 'utf16le': | |
case 'utf-16le': | |
case 'ucs2': | |
case 'ucs-2': | |
return decoders.utf16le; | |
case 'base64': | |
return decoders.base64; | |
default: | |
if (lc === undefined) { | |
lc = true; | |
charset = charset.toLowerCase(); | |
continue; | |
} | |
return decoders.other.bind(charset); | |
} | |
} | |
} | |
const decoders = { | |
utf8: (data, hint) => { | |
if (data.length === 0) | |
return ''; | |
if (typeof data === 'string') { | |
// If `data` never had any percent-encoded bytes or never had any that | |
// were outside of the ASCII range, then we can safely just return the | |
// input since UTF-8 is ASCII compatible | |
if (hint < 2) | |
return data; | |
data = Buffer.from(data, 'latin1'); | |
} | |
return data.utf8Slice(0, data.length); | |
}, | |
latin1: (data, hint) => { | |
if (data.length === 0) | |
return ''; | |
if (typeof data === 'string') | |
return data; | |
return data.latin1Slice(0, data.length); | |
}, | |
utf16le: (data, hint) => { | |
if (data.length === 0) | |
return ''; | |
if (typeof data === 'string') | |
data = Buffer.from(data, 'latin1'); | |
return data.ucs2Slice(0, data.length); | |
}, | |
base64: (data, hint) => { | |
if (data.length === 0) | |
return ''; | |
if (typeof data === 'string') | |
data = Buffer.from(data, 'latin1'); | |
return data.base64Slice(0, data.length); | |
}, | |
other: (data, hint) => { | |
if (data.length === 0) | |
return ''; | |
if (typeof data === 'string') | |
data = Buffer.from(data, 'latin1'); | |
try { | |
const decoder = new TextDecoder(this); | |
return decoder.decode(data); | |
} catch {} | |
}, | |
}; | |
function convertToUTF8(data, charset, hint) { | |
const decode = getDecoder(charset); | |
if (decode) | |
return decode(data, hint); | |
} | |
function basename(path) { | |
if (typeof path !== 'string') | |
return ''; | |
for (let i = path.length - 1; i >= 0; --i) { | |
switch (path.charCodeAt(i)) { | |
case 0x2F: // '/' | |
case 0x5C: // '\' | |
path = path.slice(i + 1); | |
return (path === '..' || path === '.' ? '' : path); | |
} | |
} | |
return (path === '..' || path === '.' ? '' : path); | |
} | |
const TOKEN = [ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
]; | |
const QDTEXT = [ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
]; | |
const CHARSET = [ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
]; | |
const EXTENDED_VALUE = [ | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 1, 0, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, | |
0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, | |
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, | |
]; | |
/* eslint-disable no-multi-spaces */ | |
const HEX_VALUES = [ | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, | |
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, 10, 11, 12, 13, 14, 15, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, | |
]; | |
/* eslint-enable no-multi-spaces */ | |
module.exports = { | |
basename, | |
convertToUTF8, | |
getDecoder, | |
parseContentType, | |
parseDisposition, | |
}; | |