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
			| 
											3 years ago
										 | 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 | ||
|  | } |