You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.3 KiB
199 lines
5.3 KiB
const { TextDecoder } = require('util')
|
|
const { TextDecoder: PolyfillTextDecoder, getEncoding } = require('text-decoding')
|
|
|
|
const RE_ENCODED = /%([a-fA-F0-9]{2})/g
|
|
const RE_PLUS = /\+/g
|
|
|
|
const HEX = [
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
|
0, 0, 0, 0, 0, 0, 0, 0, 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, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
|
|
0, 1, 1, 1, 1, 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, 1, 1, 1, 1, 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
|
|
]
|
|
|
|
// Node has always utf-8
|
|
const textDecoders = new Map()
|
|
textDecoders.set('utf-8', new TextDecoder('utf-8'))
|
|
textDecoders.set('utf8', textDecoders.get('utf-8'))
|
|
|
|
function encodedReplacer (match, byte) {
|
|
return String.fromCharCode(parseInt(byte, 16))
|
|
}
|
|
|
|
function parseParams (str) {
|
|
const res = []
|
|
let state = 'key'
|
|
let charset = ''
|
|
let inquote = false
|
|
let escaping = false
|
|
let p = 0
|
|
let tmp = ''
|
|
|
|
for (var i = 0, len = str.length; i < len; ++i) { // eslint-disable-line no-var
|
|
const char = str[i]
|
|
if (char === '\\' && inquote) {
|
|
if (escaping) { escaping = false } else {
|
|
escaping = true
|
|
continue
|
|
}
|
|
} else if (char === '"') {
|
|
if (!escaping) {
|
|
if (inquote) {
|
|
inquote = false
|
|
state = 'key'
|
|
} else { inquote = true }
|
|
continue
|
|
} else { escaping = false }
|
|
} else {
|
|
if (escaping && inquote) { tmp += '\\' }
|
|
escaping = false
|
|
if ((state === 'charset' || state === 'lang') && char === "'") {
|
|
if (state === 'charset') {
|
|
state = 'lang'
|
|
charset = tmp.substring(1)
|
|
} else { state = 'value' }
|
|
tmp = ''
|
|
continue
|
|
} else if (state === 'key' &&
|
|
(char === '*' || char === '=') &&
|
|
res.length) {
|
|
if (char === '*') { state = 'charset' } else { state = 'value' }
|
|
res[p] = [tmp, undefined]
|
|
tmp = ''
|
|
continue
|
|
} else if (!inquote && char === ';') {
|
|
state = 'key'
|
|
if (charset) {
|
|
if (tmp.length) {
|
|
tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer),
|
|
'binary',
|
|
charset)
|
|
}
|
|
charset = ''
|
|
} else if (tmp.length) {
|
|
tmp = decodeText(tmp, 'binary', 'utf8')
|
|
}
|
|
if (res[p] === undefined) { res[p] = tmp } else { res[p][1] = tmp }
|
|
tmp = ''
|
|
++p
|
|
continue
|
|
} else if (!inquote && (char === ' ' || char === '\t')) { continue }
|
|
}
|
|
tmp += char
|
|
}
|
|
if (charset && tmp.length) {
|
|
tmp = decodeText(tmp.replace(RE_ENCODED, encodedReplacer),
|
|
'binary',
|
|
charset)
|
|
} else if (tmp) {
|
|
tmp = decodeText(tmp, 'binary', 'utf8')
|
|
}
|
|
|
|
if (res[p] === undefined) {
|
|
if (tmp) { res[p] = tmp }
|
|
} else { res[p][1] = tmp }
|
|
|
|
return res
|
|
}
|
|
|
|
function decodeText (text, textEncoding, destEncoding) {
|
|
if (text) {
|
|
if (textDecoders.has(destEncoding)) {
|
|
try {
|
|
return textDecoders.get(destEncoding).decode(Buffer.from(text, textEncoding))
|
|
} catch (e) { }
|
|
} else {
|
|
try {
|
|
textDecoders.set(destEncoding, new TextDecoder(destEncoding))
|
|
return textDecoders.get(destEncoding).decode(Buffer.from(text, textEncoding))
|
|
} catch (e) {
|
|
if (getEncoding(destEncoding)) {
|
|
try {
|
|
textDecoders.set(destEncoding, new PolyfillTextDecoder(destEncoding))
|
|
return textDecoders.get(destEncoding).decode(Buffer.from(text, textEncoding))
|
|
} catch (e) { }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return text
|
|
}
|
|
|
|
function Decoder () {
|
|
this.buffer = undefined
|
|
}
|
|
Decoder.prototype.write = function (str) {
|
|
// Replace '+' with ' ' before decoding
|
|
str = str.replace(RE_PLUS, ' ')
|
|
let res = ''
|
|
let i = 0; let p = 0; const len = str.length
|
|
for (; i < len; ++i) {
|
|
if (this.buffer !== undefined) {
|
|
if (!HEX[str.charCodeAt(i)]) {
|
|
res += '%' + this.buffer
|
|
this.buffer = undefined
|
|
--i // retry character
|
|
} else {
|
|
this.buffer += str[i]
|
|
++p
|
|
if (this.buffer.length === 2) {
|
|
res += String.fromCharCode(parseInt(this.buffer, 16))
|
|
this.buffer = undefined
|
|
}
|
|
}
|
|
} else if (str[i] === '%') {
|
|
if (i > p) {
|
|
res += str.substring(p, i)
|
|
p = i
|
|
}
|
|
this.buffer = ''
|
|
++p
|
|
}
|
|
}
|
|
if (p < len && this.buffer === undefined) { res += str.substring(p) }
|
|
return res
|
|
}
|
|
Decoder.prototype.reset = function () {
|
|
this.buffer = undefined
|
|
}
|
|
|
|
function basename (path) {
|
|
if (typeof path !== 'string') { return '' }
|
|
for (var i = path.length - 1; i >= 0; --i) { // eslint-disable-line no-var
|
|
switch (path.charCodeAt(i)) {
|
|
case 0x2F: // '/'
|
|
case 0x5C: // '\'
|
|
path = path.slice(i + 1)
|
|
return (path === '..' || path === '.' ? '' : path)
|
|
}
|
|
}
|
|
return (path === '..' || path === '.' ? '' : path)
|
|
}
|
|
|
|
function getLimit (limits, name, defaultLimit) {
|
|
if (
|
|
!limits ||
|
|
limits[name] === undefined ||
|
|
limits[name] === null
|
|
) { return defaultLimit }
|
|
|
|
if (
|
|
typeof limits[name] !== 'number' ||
|
|
isNaN(limits[name])
|
|
) { throw new TypeError('Limit ' + name + ' is not a valid number') }
|
|
|
|
return limits[name]
|
|
}
|
|
|
|
module.exports = {
|
|
Decoder,
|
|
basename,
|
|
getLimit,
|
|
parseParams,
|
|
decodeText
|
|
}
|