1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-11-17 07:52:42 +01:00
uBlock/src/js/uri-utils.js
2021-12-10 07:47:21 -05:00

176 lines
5.3 KiB
JavaScript

/*******************************************************************************
uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-present Raymond Hill
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see {http://www.gnu.org/licenses/}.
Home: https://github.com/gorhill/uBlock
*/
'use strict';
/******************************************************************************/
import publicSuffixList from '../lib/publicsuffixlist/publicsuffixlist.js';
import punycode from '../lib/punycode.js';
/******************************************************************************/
// Originally:
// https://github.com/gorhill/uBlock/blob/8b5733a58d3acf9fb62815e14699c986bd1c2fdc/src/js/uritools.js
const reHostnameFromCommonURL =
/^https:\/\/[0-9a-z._-]+[0-9a-z]\//;
const reAuthorityFromURI =
/^(?:[^:\/?#]+:)?(\/\/[^\/?#]+)/;
const reHostFromNakedAuthority =
/^[0-9a-z._-]+[0-9a-z]$/i;
const reHostFromAuthority =
/^(?:[^@]*@)?([^:]+)(?::\d*)?$/;
const reIPv6FromAuthority =
/^(?:[^@]*@)?(\[[0-9a-f:]+\])(?::\d*)?$/i;
const reMustNormalizeHostname =
/[^0-9a-z._-]/;
const reOriginFromURI =
/^[^:\/?#]+:\/\/[^\/?#]+/;
const reHostnameFromNetworkURL =
/^(?:http|ws|ftp)s?:\/\/([0-9a-z_][0-9a-z._-]*[0-9a-z])(?::\d+)?\//;
const reIPAddressNaive =
/^\d+\.\d+\.\d+\.\d+$|^\[[\da-zA-Z:]+\]$/;
const reNetworkURI =
/^(?:ftps?|https?|wss?):\/\//;
// For performance purpose, as simple tests as possible
const reIPv4VeryCoarse = /\.\d+$/;
const reHostnameVeryCoarse = /[g-z_\-]/;
/******************************************************************************/
function domainFromHostname(hostname) {
return reIPAddressNaive.test(hostname)
? hostname
: publicSuffixList.getDomain(hostname);
}
function domainFromURI(uri) {
if ( !uri ) { return ''; }
return domainFromHostname(hostnameFromURI(uri));
}
function entityFromDomain(domain) {
const pos = domain.indexOf('.');
return pos !== -1 ? domain.slice(0, pos) + '.*' : '';
}
function hostnameFromURI(uri) {
let match = reHostnameFromCommonURL.exec(uri);
if ( match !== null ) { return match[0].slice(8, -1); }
match = reAuthorityFromURI.exec(uri);
if ( match === null ) { return ''; }
const authority = match[1].slice(2);
if ( reHostFromNakedAuthority.test(authority) ) {
return authority.toLowerCase();
}
match = reHostFromAuthority.exec(authority);
if ( match === null ) {
match = reIPv6FromAuthority.exec(authority);
if ( match === null ) { return ''; }
}
let hostname = match[1];
while ( hostname.endsWith('.') ) {
hostname = hostname.slice(0, -1);
}
if ( reMustNormalizeHostname.test(hostname) ) {
hostname = punycode.toASCII(hostname.toLowerCase());
}
return hostname;
}
function hostnameFromNetworkURL(url) {
const matches = reHostnameFromNetworkURL.exec(url);
return matches !== null ? matches[1] : '';
}
function originFromURI(uri) {
let match = reHostnameFromCommonURL.exec(uri);
if ( match !== null ) { return match[0].slice(0, -1); }
match = reOriginFromURI.exec(uri);
return match !== null ? match[0].toLowerCase() : '';
}
function isNetworkURI(uri) {
return reNetworkURI.test(uri);
}
/******************************************************************************/
function toBroaderHostname(hostname) {
const pos = hostname.indexOf('.');
if ( pos !== -1 ) {
return hostname.slice(pos + 1);
}
return hostname !== '*' && hostname !== '' ? '*' : '';
}
function toBroaderIPv4Address(ipaddress) {
if ( ipaddress === '*' || ipaddress === '' ) { return ''; }
const pos = ipaddress.lastIndexOf('.');
if ( pos === -1 ) { return '*'; }
return ipaddress.slice(0, pos);
}
function toBroaderIPv6Address(ipaddress) {
return ipaddress !== '*' && ipaddress !== '' ? '*' : '';
}
function decomposeHostname(hostname, out) {
if ( out.length !== 0 && out[0] === hostname ) {
return out;
}
let broadenFn;
if ( reHostnameVeryCoarse.test(hostname) === false ) {
if ( reIPv4VeryCoarse.test(hostname) ) {
broadenFn = toBroaderIPv4Address;
} else if ( hostname.startsWith('[') ) {
broadenFn = toBroaderIPv6Address;
}
}
if ( broadenFn === undefined ) {
broadenFn = toBroaderHostname;
}
out[0] = hostname;
let i = 1;
for (;;) {
hostname = broadenFn(hostname);
if ( hostname === '' ) { break; }
out[i++] = hostname;
}
out.length = i;
return out;
}
/******************************************************************************/
export {
decomposeHostname,
domainFromHostname,
domainFromURI,
entityFromDomain,
hostnameFromNetworkURL,
hostnameFromURI,
isNetworkURI,
originFromURI,
};