196 lines
4.8 KiB
JavaScript
196 lines
4.8 KiB
JavaScript
/**
|
|
* @licstart The following is the entire license notice for the
|
|
* JavaScript code in this page
|
|
*
|
|
* Copyright 2022 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*
|
|
* @licend The above is the entire license notice for the
|
|
* JavaScript code in this page
|
|
*/
|
|
"use strict";
|
|
|
|
Object.defineProperty(exports, "__esModule", {
|
|
value: true
|
|
});
|
|
exports.getFilenameFromContentDispositionHeader = getFilenameFromContentDispositionHeader;
|
|
|
|
var _util = require("../shared/util.js");
|
|
|
|
function getFilenameFromContentDispositionHeader(contentDisposition) {
|
|
let needsEncodingFixup = true;
|
|
let tmp = toParamRegExp("filename\\*", "i").exec(contentDisposition);
|
|
|
|
if (tmp) {
|
|
tmp = tmp[1];
|
|
let filename = rfc2616unquote(tmp);
|
|
filename = unescape(filename);
|
|
filename = rfc5987decode(filename);
|
|
filename = rfc2047decode(filename);
|
|
return fixupEncoding(filename);
|
|
}
|
|
|
|
tmp = rfc2231getparam(contentDisposition);
|
|
|
|
if (tmp) {
|
|
const filename = rfc2047decode(tmp);
|
|
return fixupEncoding(filename);
|
|
}
|
|
|
|
tmp = toParamRegExp("filename", "i").exec(contentDisposition);
|
|
|
|
if (tmp) {
|
|
tmp = tmp[1];
|
|
let filename = rfc2616unquote(tmp);
|
|
filename = rfc2047decode(filename);
|
|
return fixupEncoding(filename);
|
|
}
|
|
|
|
function toParamRegExp(attributePattern, flags) {
|
|
return new RegExp("(?:^|;)\\s*" + attributePattern + "\\s*=\\s*" + "(" + '[^";\\s][^;\\s]*' + "|" + '"(?:[^"\\\\]|\\\\"?)+"?' + ")", flags);
|
|
}
|
|
|
|
function textdecode(encoding, value) {
|
|
if (encoding) {
|
|
if (!/^[\x00-\xFF]+$/.test(value)) {
|
|
return value;
|
|
}
|
|
|
|
try {
|
|
const decoder = new TextDecoder(encoding, {
|
|
fatal: true
|
|
});
|
|
const buffer = (0, _util.stringToBytes)(value);
|
|
value = decoder.decode(buffer);
|
|
needsEncodingFixup = false;
|
|
} catch (e) {}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function fixupEncoding(value) {
|
|
if (needsEncodingFixup && /[\x80-\xff]/.test(value)) {
|
|
value = textdecode("utf-8", value);
|
|
|
|
if (needsEncodingFixup) {
|
|
value = textdecode("iso-8859-1", value);
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function rfc2231getparam(contentDispositionStr) {
|
|
const matches = [];
|
|
let match;
|
|
const iter = toParamRegExp("filename\\*((?!0\\d)\\d+)(\\*?)", "ig");
|
|
|
|
while ((match = iter.exec(contentDispositionStr)) !== null) {
|
|
let [, n, quot, part] = match;
|
|
n = parseInt(n, 10);
|
|
|
|
if (n in matches) {
|
|
if (n === 0) {
|
|
break;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
matches[n] = [quot, part];
|
|
}
|
|
|
|
const parts = [];
|
|
|
|
for (let n = 0; n < matches.length; ++n) {
|
|
if (!(n in matches)) {
|
|
break;
|
|
}
|
|
|
|
let [quot, part] = matches[n];
|
|
part = rfc2616unquote(part);
|
|
|
|
if (quot) {
|
|
part = unescape(part);
|
|
|
|
if (n === 0) {
|
|
part = rfc5987decode(part);
|
|
}
|
|
}
|
|
|
|
parts.push(part);
|
|
}
|
|
|
|
return parts.join("");
|
|
}
|
|
|
|
function rfc2616unquote(value) {
|
|
if (value.startsWith('"')) {
|
|
const parts = value.slice(1).split('\\"');
|
|
|
|
for (let i = 0; i < parts.length; ++i) {
|
|
const quotindex = parts[i].indexOf('"');
|
|
|
|
if (quotindex !== -1) {
|
|
parts[i] = parts[i].slice(0, quotindex);
|
|
parts.length = i + 1;
|
|
}
|
|
|
|
parts[i] = parts[i].replace(/\\(.)/g, "$1");
|
|
}
|
|
|
|
value = parts.join('"');
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
function rfc5987decode(extvalue) {
|
|
const encodingend = extvalue.indexOf("'");
|
|
|
|
if (encodingend === -1) {
|
|
return extvalue;
|
|
}
|
|
|
|
const encoding = extvalue.slice(0, encodingend);
|
|
const langvalue = extvalue.slice(encodingend + 1);
|
|
const value = langvalue.replace(/^[^']*'/, "");
|
|
return textdecode(encoding, value);
|
|
}
|
|
|
|
function rfc2047decode(value) {
|
|
if (!value.startsWith("=?") || /[\x00-\x19\x80-\xff]/.test(value)) {
|
|
return value;
|
|
}
|
|
|
|
return value.replace(/=\?([\w-]*)\?([QqBb])\?((?:[^?]|\?(?!=))*)\?=/g, function (matches, charset, encoding, text) {
|
|
if (encoding === "q" || encoding === "Q") {
|
|
text = text.replace(/_/g, " ");
|
|
text = text.replace(/=([0-9a-fA-F]{2})/g, function (match, hex) {
|
|
return String.fromCharCode(parseInt(hex, 16));
|
|
});
|
|
return textdecode(charset, text);
|
|
}
|
|
|
|
try {
|
|
text = atob(text);
|
|
} catch (e) {}
|
|
|
|
return textdecode(charset, text);
|
|
});
|
|
}
|
|
|
|
return "";
|
|
} |