aboutsummaryrefslogtreecommitdiffstats
path: root/lib/PublicSuffixList.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/PublicSuffixList.jsm')
-rw-r--r--lib/PublicSuffixList.jsm308
1 files changed, 308 insertions, 0 deletions
diff --git a/lib/PublicSuffixList.jsm b/lib/PublicSuffixList.jsm
new file mode 100644
index 0000000..72b0669
--- /dev/null
+++ b/lib/PublicSuffixList.jsm
@@ -0,0 +1,308 @@
+/*******************************************************************************
+
+ ηMatrix - a browser extension to black/white list requests.
+ Copyright (C) 2014-2019 The uMatrix/uBlock Origin authors
+ Copyright (C) 2019 Alessio Vanni
+
+ 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://gitlab.com/vannilla/ematrix
+ uMatrix Home: https://github.com/gorhill/uMatrix
+*/
+
+'use strict';
+
+var EXPORTED_SYMBOLS = ['publicSuffixList'];
+
+var exceptions = {};
+var rules = {};
+var magic = 'iscjsfsaolnm';
+
+// This value dictate how the search will be performed:
+// < cutoffLength → indexOf()
+// >= cutoffLength → binary search
+var cutoffLength = 256;
+
+var reMustPunycode = /[^\w.*-]/;
+
+var onChangedListeners = [];
+
+function search(store, hostname) {
+ let pos = hostname.lastIndexOf('.');
+ let tld;
+ let remainder;
+
+ if (pos < 0) {
+ tld = hostname;
+ remainder = hostname;
+ } else {
+ tld = hostname.slice(pos+1);
+ remainder = hostname.slice(0, pos);
+ }
+
+ let sub = store[tld];
+ if (!sub) {
+ return false;
+ }
+
+ if (typeof sub === 'string') {
+ return (sub.indexOf(' '+remainder+' ') >= 0);
+ }
+
+ let l = remainder.length;
+ let val = sub[l];
+ if (!val) {
+ return false;
+ }
+
+ let left = 0;
+ let right = Math.floor(val.length/l+0.5);
+
+ while (left < right) {
+ let i = left+right >> 1;
+ let key = val.substr(l*i, l);
+ if (remainder < key) {
+ right = i;
+ } else if (remainder > key) {
+ left = i+1;
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+function getPublicSuffix(hostname) {
+ if (!hostname) {
+ return '';
+ }
+
+ while (true) {
+ let pos = hostname.indexOf('.');
+ if (pos < 0) {
+ return hostname;
+ }
+
+ if (search(exceptions, hostname)) {
+ return hostname.slice(pos+1);
+ }
+
+ if (search(rules, hostname)) {
+ return hostname;
+ }
+
+ if (search(rules, '*'+hostname.slice(pos))) {
+ return hostname;
+ }
+
+ hostname = hostname.slice(pos+1);
+ }
+}
+
+function getDomain(hostname) {
+ if (!hostname || hostname.charAt(0) == '.') {
+ return '';
+ }
+
+ hostname = hostname.toLowerCase();
+
+ let suffix = getPublicSuffix(hostname);
+ if (suffix === hostname) {
+ return '';
+ }
+
+ let len = hostname.length-suffix.length;
+ let pos = hostname.lastIndexOf('.', hostname.lastIndexOf('.', len) - 1);
+ if (pos <= 0) {
+ return hostname;
+ }
+
+ return hostname.slice(pos+1);
+}
+
+function crystallize(store) {
+ for (let tld in store) {
+ if (!store.hasOwnProperty(tld)) {
+ continue;
+ }
+
+ let suff = store[tld].join(' ');
+ if (!suff) {
+ store[tld] = '';
+ continue;
+ }
+
+ if (suff.length < cutoffLength) {
+ store[tld] = ' ' + suff + ' ';
+ continue;
+ }
+
+ suff = [];
+ for (let i=store[tld].length-1; i>=0; --i) {
+ let s = store[tld][i];
+ let l = s.length;
+ if (!suff[l]) {
+ suff[l] = [];
+ }
+ suff[l].push(s);
+ }
+
+ for (let i=suff.length-1; i>=0; --i) {
+ if (suff[i]) {
+ suff[i] = suff[i].sort().join('');
+ }
+ }
+
+ store[tld] = suff;
+ }
+
+ return store;
+}
+
+function parse(text, toAscii) {
+ exceptions = {};
+ rules = {};
+
+ let beg = 0;
+ let end = 0;
+ let tend = text.length;
+
+ while (beg < tend) {
+ end = text.indexOf('\n', beg);
+ if (end < 0) {
+ end = text.indexOf('\r', beg);
+ if (end < 0) {
+ end = tend;
+ }
+ }
+
+ let line = text.slice(beg, end).trim();
+ beg = end+1;
+
+ if (line.length === 0) {
+ continue;
+ }
+
+ let pos = line.indexOf('//');
+ if (pos >= 0) {
+ line = line.slice(0, pos);
+ }
+
+ line = line.trim();
+ if (!line) {
+ continue;
+ }
+
+ let store;
+ if (line.charAt(0) == '!') {
+ store = exceptions;
+ line = line.slice(1);
+ } else {
+ store = rules;
+ }
+
+ if (reMustPunycode.test(line)) {
+ line = toAscii(line);
+ }
+
+ line = line.toLowerCase();
+
+ let tld;
+ pos = line.lastIndexOf('.');
+ if (pos < 0) {
+ tld = line;
+ } else {
+ tld = line.slice(pos+1);
+ line = line.slice(0, pos);
+ }
+
+ if (!store.hasOwnProperty(tld)) {
+ store[tld] = [];
+ }
+
+ if (line) {
+ store[tld].push(line);
+ }
+ }
+
+ crystallize(exceptions);
+ crystallize(rules);
+
+ callListeners(onChangedListeners);
+}
+
+function toSelfie() {
+ return {
+ magic: magic,
+ rules: rules,
+ exceptions: exception,
+ };
+}
+
+function fromSelfie(selfie) {
+ if (typeof selfie !== 'object' || typeof selfie.magic !== 'string'
+ || selfie.magic !== magic) {
+ return false;
+ }
+
+ rules = selfie.rules;
+ exceptions = selfie.exceptions;
+ callListeners(onChangedListeners);
+
+ return true;
+}
+
+var addListener = function (listeners, callback) {
+ if (typeof callback !== 'function') {
+ return;
+ }
+
+ if (listeners.indexOf(callback) === -1) {
+ listeners.push(callback);
+ }
+};
+
+var removeListener = function (listeners, callback) {
+ let pos = listeners.indexOf(callback);
+ if (pos !== -1) {
+ listeners.splice(pos, 1);
+ }
+};
+
+var callListeners = function (listeners) {
+ for (let i=0; i<listeners.length; ++i) {
+ listeners[i]();
+ }
+};
+
+var onChanged = {
+ addListener: function (callback) {
+ addListener(onChangedListeners, callback);
+ },
+ removeListener: function (callback) {
+ removeListener(onChangedListeners, callback);
+ },
+};
+
+var publicSuffixList = {
+ version: '1.0',
+
+ parse: parse,
+ getDomain: getDomain,
+ getPublicSuffix: getPublicSuffix,
+ toSelfie: toSelfie,
+ fromSelfie: fromSelfie,
+ onChanged: onChanged,
+}