From de6769c3303b4b727e1d1556dabf0de25b544763 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com>
Date: Wed, 10 Apr 2024 21:30:31 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=20pac.js=20=E7=9A=84?=
=?UTF-8?q?=E4=BB=A3=E7=A0=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../src/lib/proxy/middleware/source/pac.js | 1233 ++++++++---------
1 file changed, 609 insertions(+), 624 deletions(-)
diff --git a/packages/mitmproxy/src/lib/proxy/middleware/source/pac.js b/packages/mitmproxy/src/lib/proxy/middleware/source/pac.js
index 0e9c4a94..1a1ce3c3 100644
--- a/packages/mitmproxy/src/lib/proxy/middleware/source/pac.js
+++ b/packages/mitmproxy/src/lib/proxy/middleware/source/pac.js
@@ -3,17 +3,16 @@ const path = require('path')
const log = require('../../../../utils/util.log')
function createPacClient (pacFilePath) {
- var __PROXY__ = 'PROXY 127.0.0.1:1080;'
- var __USERRULES__ = []
+ const __PROXY__ = 'PROXY 127.0.0.1:1080;'
function readFile (location) {
try {
- log.info('pac root dir', path.resolve('./'))
+ log.info('pac root dir:', path.resolve('./'))
const filePath = path.resolve(location)
log.info('read pac path:', filePath)
return fs.readFileSync(location).toString()
} catch (e) {
- log.error('读取pac失败')
+ log.error('读取pac失败:', e)
return ''
}
}
@@ -34,7 +33,7 @@ function createPacClient (pacFilePath) {
}
return rules
}
- var __RULES__ = getRules(pacFilePath)
+ const __RULES__ = getRules(pacFilePath)
/* eslint-disable */
// Was generated by gfwlist2pac in precise mode
@@ -43,650 +42,636 @@ function createPacClient (pacFilePath) {
// 2019-10-06: More 'javascript' way to interaction with main program
// 2019-02-08: Updated to support shadowsocks-windows user rules.
- var proxy = __PROXY__;
- var userrules = [];
- var rules = [];
+ const proxy = __PROXY__
+ const rules = []
// convert to abp grammar
- for (var i = 0; i < __RULES__.length; i++) {
- var s = __RULES__[i];
- if (s.substring(0, 2) == "||") s += "^";
- rules.push(s);
+ for (let i = 0; i < __RULES__.length; i++) {
+ let s = __RULES__[i]
+ if (s.substring(0, 2) === "||") s += "^"
+ rules.push(s)
+ }
+
+ /*
+ * This file is part of Adblock Plus ,
+ * Copyright (C) 2006-2014 Eyeo GmbH
+ *
+ * Adblock Plus is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * Adblock Plus 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 Adblock Plus. If not, see .
+ */
+
+ function createDict () {
+ const result = {}
+ result.__proto__ = null
+ return result
+ }
+
+ function getOwnPropertyDescriptor (obj, key) {
+ if (obj.hasOwnProperty(key)) {
+ return obj[key]
}
+ return null
+ }
- for (var i = 0; i < __USERRULES__.length; i++) {
- var s = __USERRULES__[i];
- if (s.substring(0, 2) == "||") s += "^";
- userrules.push(s);
- }
-
- /*
- * This file is part of Adblock Plus ,
- * Copyright (C) 2006-2014 Eyeo GmbH
- *
- * Adblock Plus is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 3 as
- * published by the Free Software Foundation.
- *
- * Adblock Plus 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 Adblock Plus. If not, see .
- */
-
- function createDict() {
- var result = {};
- result.__proto__ = null;
- return result;
- }
-
- function getOwnPropertyDescriptor(obj, key) {
- if (obj.hasOwnProperty(key)) {
- return obj[key];
+ function extend (subClass, superClass, definition) {
+ if (Object.__proto__) {
+ definition.__proto__ = superClass.prototype
+ subClass.prototype = definition
+ } else {
+ const tmpClass = function () {}
+ tmpClass.prototype = superClass.prototype
+ subClass.prototype = new tmpClass()
+ subClass.prototype.constructor = superClass
+ for (const key in definition) {
+ if (definition.hasOwnProperty(key)) {
+ subClass.prototype[key] = definition[key]
}
- return null;
+ }
}
+ }
- function extend(subclass, superclass, definition) {
- if (Object.__proto__) {
- definition.__proto__ = superclass.prototype;
- subclass.prototype = definition;
+ function Filter (text) {
+ this.text = text
+ this.subscriptions = []
+ }
+
+ Filter.prototype = {
+ text: null,
+ subscriptions: null,
+ toString: function () {
+ return this.text
+ }
+ }
+ Filter.knownFilters = createDict()
+ Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/
+ Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/
+ Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/
+ Filter.fromText = function (text) {
+ if (text in Filter.knownFilters) {
+ return Filter.knownFilters[text]
+ }
+ let ret
+ if (text.charAt(0) === "!") {
+ ret = new CommentFilter(text)
+ } else {
+ ret = RegExpFilter.fromText(text)
+ }
+ Filter.knownFilters[ret.text] = ret
+ return ret
+ }
+
+ function InvalidFilter (text, reason) {
+ Filter.call(this, text)
+ this.reason = reason
+ }
+
+ extend(InvalidFilter, Filter, {
+ reason: null
+ })
+
+ function CommentFilter (text) {
+ Filter.call(this, text)
+ }
+
+ extend(CommentFilter, Filter, {})
+
+ function ActiveFilter (text, domains) {
+ Filter.call(this, text)
+ this.domainSource = domains
+ }
+
+ extend(ActiveFilter, Filter, {
+ domainSource: null,
+ domainSeparator: null,
+ ignoreTrailingDot: true,
+ domainSourceIsUpperCase: false,
+ getDomains: function () {
+ const prop = getOwnPropertyDescriptor(this, "domains")
+ if (prop) {
+ return prop
+ }
+ let domains = null
+ if (this.domainSource) {
+ let source = this.domainSource
+ if (!this.domainSourceIsUpperCase) {
+ source = source.toUpperCase()
+ }
+ const list = source.split(this.domainSeparator)
+ if (list.length === 1 && (list[0]).charAt(0) !== "~") {
+ domains = createDict()
+ domains[""] = false
+ if (this.ignoreTrailingDot) {
+ list[0] = list[0].replace(/\.+$/, "")
+ }
+ domains[list[0]] = true
} else {
- var tmpclass = function () {
- }, ret;
- tmpclass.prototype = superclass.prototype;
- subclass.prototype = new tmpclass();
- subclass.prototype.constructor = superclass;
- for (var i in definition) {
- if (definition.hasOwnProperty(i)) {
- subclass.prototype[i] = definition[i];
- }
- }
- }
- }
-
- function Filter(text) {
- this.text = text;
- this.subscriptions = [];
- }
-
- Filter.prototype = {
- text: null,
- subscriptions: null,
- toString: function () {
- return this.text;
- }
- };
- Filter.knownFilters = createDict();
- Filter.elemhideRegExp = /^([^\/\*\|\@"!]*?)#(\@)?(?:([\w\-]+|\*)((?:\([\w\-]+(?:[$^*]?=[^\(\)"]*)?\))*)|#([^{}]+))$/;
- Filter.regexpRegExp = /^(@@)?\/.*\/(?:\$~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)?$/;
- Filter.optionsRegExp = /\$(~?[\w\-]+(?:=[^,\s]+)?(?:,~?[\w\-]+(?:=[^,\s]+)?)*)$/;
- Filter.fromText = function (text) {
- if (text in Filter.knownFilters) {
- return Filter.knownFilters[text];
- }
- var ret;
- if (text.charAt(0) == "!") {
- ret = new CommentFilter(text);
- } else {
- ret = RegExpFilter.fromText(text);
- }
- Filter.knownFilters[ret.text] = ret;
- return ret;
- };
-
- function InvalidFilter(text, reason) {
- Filter.call(this, text);
- this.reason = reason;
- }
-
- extend(InvalidFilter, Filter, {
- reason: null
- });
-
- function CommentFilter(text) {
- Filter.call(this, text);
- }
-
- extend(CommentFilter, Filter, {});
-
- function ActiveFilter(text, domains) {
- Filter.call(this, text);
- this.domainSource = domains;
- }
-
- extend(ActiveFilter, Filter, {
- domainSource: null,
- domainSeparator: null,
- ignoreTrailingDot: true,
- domainSourceIsUpperCase: false,
- getDomains: function () {
- var prop = getOwnPropertyDescriptor(this, "domains");
- if (prop) {
- return prop;
- }
- var domains = null;
- if (this.domainSource) {
- var source = this.domainSource;
- if (!this.domainSourceIsUpperCase) {
- source = source.toUpperCase();
- }
- var list = source.split(this.domainSeparator);
- if (list.length == 1 && (list[0]).charAt(0) != "~") {
- domains = createDict();
- domains[""] = false;
- if (this.ignoreTrailingDot) {
- list[0] = list[0].replace(/\.+$/, "");
- }
- domains[list[0]] = true;
- } else {
- var hasIncludes = false;
- for (var i = 0; i < list.length; i++) {
- var domain = list[i];
- if (this.ignoreTrailingDot) {
- domain = domain.replace(/\.+$/, "");
- }
- if (domain == "") {
- continue;
- }
- var include;
- if (domain.charAt(0) == "~") {
- include = false;
- domain = domain.substr(1);
- } else {
- include = true;
- hasIncludes = true;
- }
- if (!domains) {
- domains = createDict();
- }
- domains[domain] = include;
- }
- domains[""] = !hasIncludes;
- }
- this.domainSource = null;
- }
- return this.domains;
- },
- sitekeys: null,
- isActiveOnDomain: function (docDomain, sitekey) {
- if (this.getSitekeys() && (!sitekey || this.getSitekeys().indexOf(sitekey.toUpperCase()) < 0)) {
- return false;
- }
- if (!this.getDomains()) {
- return true;
- }
- if (!docDomain) {
- return this.getDomains()[""];
- }
+ let hasIncludes = false
+ for (let i = 0; i < list.length; i++) {
+ let domain = list[i]
if (this.ignoreTrailingDot) {
- docDomain = docDomain.replace(/\.+$/, "");
+ domain = domain.replace(/\.+$/, "")
}
- docDomain = docDomain.toUpperCase();
- while (true) {
- if (docDomain in this.getDomains()) {
- return this.domains[docDomain];
- }
- var nextDot = docDomain.indexOf(".");
- if (nextDot < 0) {
- break;
- }
- docDomain = docDomain.substr(nextDot + 1);
+ if (domain === "") {
+ continue
}
- return this.domains[""];
- },
- isActiveOnlyOnDomain: function (docDomain) {
- if (!docDomain || !this.getDomains() || this.getDomains()[""]) {
- return false;
+ let include
+ if (domain.charAt(0) === "~") {
+ include = false
+ domain = domain.substr(1)
+ } else {
+ include = true
+ hasIncludes = true
}
- if (this.ignoreTrailingDot) {
- docDomain = docDomain.replace(/\.+$/, "");
+ if (!domains) {
+ domains = createDict()
}
- docDomain = docDomain.toUpperCase();
- for (var domain in this.getDomains()) {
- if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) {
- return false;
- }
- }
- return true;
+ domains[domain] = include
+ }
+ domains[""] = !hasIncludes
}
- });
+ this.domainSource = null
+ }
+ return this.domains
+ },
+ siteKeys: null,
+ isActiveOnDomain: function (docDomain, siteKey) {
+ if (this.getSiteKeys() && (!siteKey || this.getSiteKeys().indexOf(siteKey.toUpperCase()) < 0)) {
+ return false
+ }
+ if (!this.getDomains()) {
+ return true
+ }
+ if (!docDomain) {
+ return this.getDomains()[""]
+ }
+ if (this.ignoreTrailingDot) {
+ docDomain = docDomain.replace(/\.+$/, "")
+ }
+ docDomain = docDomain.toUpperCase()
+ while (true) {
+ if (docDomain in this.getDomains()) {
+ return this.domains[docDomain]
+ }
+ const nextDot = docDomain.indexOf(".")
+ if (nextDot < 0) {
+ break
+ }
+ docDomain = docDomain.substr(nextDot + 1)
+ }
+ return this.domains[""]
+ }/*,
+ isActiveOnlyOnDomain: function (docDomain) {
+ if (!docDomain || !this.getDomains() || this.getDomains()[""]) {
+ return false
+ }
+ if (this.ignoreTrailingDot) {
+ docDomain = docDomain.replace(/\.+$/, "")
+ }
+ docDomain = docDomain.toUpperCase()
+ for (const domain in this.getDomains()) {
+ if (this.domains[domain] && domain != docDomain && (domain.length <= docDomain.length || domain.indexOf("." + docDomain) != domain.length - docDomain.length - 1)) {
+ return false
+ }
+ }
+ return true
+ }*/
+ })
- function RegExpFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) {
- ActiveFilter.call(this, text, domains, sitekeys);
- if (contentType != null) {
- this.contentType = contentType;
+ function RegExpFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys) {
+ ActiveFilter.call(this, text, domains, siteKeys)
+ if (contentType != null) {
+ this.contentType = contentType
+ }
+ if (matchCase) {
+ this.matchCase = matchCase
+ }
+ if (thirdParty != null) {
+ this.thirdParty = thirdParty
+ }
+ if (siteKeys != null) {
+ this.siteKeySource = siteKeys
+ }
+ if (regexpSource.length >= 2 && regexpSource.charAt(0) === "/" && regexpSource.charAt(regexpSource.length - 1) === "/") {
+ this.regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i")
+ } else {
+ this.regexpSource = regexpSource
+ }
+ }
+
+ extend(RegExpFilter, ActiveFilter, {
+ domainSourceIsUpperCase: true,
+ length: 1,
+ domainSeparator: "|",
+ regexpSource: null,
+ getRegexp: function () {
+ const prop = getOwnPropertyDescriptor(this, "regexp")
+ if (prop) {
+ return prop
+ }
+ const source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, "")
+ const regexp = new RegExp(source, this.matchCase ? "" : "i")
+ this.regexp = regexp
+ return regexp
+ },
+ contentType: 2147483647,
+ matchCase: false,
+ thirdParty: null,
+ siteKeySource: null,
+ getSiteKeys: function () {
+ const prop = getOwnPropertyDescriptor(this, "siteKeys")
+ if (prop) {
+ return prop
+ }
+ let siteKeys = null
+ if (this.siteKeySource) {
+ siteKeys = this.siteKeySource.split("|")
+ this.siteKeySource = null
+ }
+ this.siteKeys = siteKeys
+ return this.siteKeys
+ },
+ matches: function (location, contentType, docDomain, thirdParty, siteKey) {
+ return !!(this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, siteKey))
+ }
+ })
+ RegExpFilter.prototype["0"] = "#this"
+ RegExpFilter.fromText = function (text) {
+ let blocking = true
+ const origText = text
+ if (text.indexOf("@@") === 0) {
+ blocking = false
+ text = text.substr(2)
+ }
+ let contentType = null
+ let matchCase = null
+ let domains = null
+ let siteKeys = null
+ let thirdParty = null
+ let collapse = null
+ let options
+ const match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null
+ if (match) {
+ options = match[1].toUpperCase().split(",")
+ text = match.input.substr(0, match.index)
+ for (let _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) {
+ let option = options[_loopIndex6]
+ let value = null
+ const separatorIndex = option.indexOf("=")
+ if (separatorIndex >= 0) {
+ value = option.substr(separatorIndex + 1)
+ option = option.substr(0, separatorIndex)
}
- if (matchCase) {
- this.matchCase = matchCase;
- }
- if (thirdParty != null) {
- this.thirdParty = thirdParty;
- }
- if (sitekeys != null) {
- this.sitekeySource = sitekeys;
- }
- if (regexpSource.length >= 2 && regexpSource.charAt(0) == "/" && regexpSource.charAt(regexpSource.length - 1) == "/") {
- var regexp = new RegExp(regexpSource.substr(1, regexpSource.length - 2), this.matchCase ? "" : "i");
- this.regexp = regexp;
+ option = option.replace(/-/, "_")
+ if (option in RegExpFilter.typeMap) {
+ if (contentType == null) {
+ contentType = 0
+ }
+ contentType |= RegExpFilter.typeMap[option]
+ } else if (option.charAt(0) === "~" && option.substr(1) in RegExpFilter.typeMap) {
+ if (contentType == null) {
+ contentType = RegExpFilter.prototype.contentType
+ }
+ contentType &= ~RegExpFilter.typeMap[option.substr(1)]
+ } else if (option === "MATCH_CASE") {
+ matchCase = true
+ } else if (option === "~MATCH_CASE") {
+ matchCase = false
+ } else if (option === "DOMAIN" && typeof value != "undefined") {
+ domains = value
+ } else if (option === "THIRD_PARTY") {
+ thirdParty = true
+ } else if (option === "~THIRD_PARTY") {
+ thirdParty = false
+ } else if (option === "COLLAPSE") {
+ collapse = true
+ } else if (option === "~COLLAPSE") {
+ collapse = false
+ } else if (option === "SITEKEY" && typeof value != "undefined") {
+ siteKeys = value
} else {
- this.regexpSource = regexpSource;
+ return new InvalidFilter(origText, "Unknown option " + option.toLowerCase())
}
+ }
+ }
+ if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) {
+ if (contentType == null) {
+ contentType = RegExpFilter.prototype.contentType
+ }
+ contentType &= ~RegExpFilter.typeMap.DOCUMENT
+ }
+ try {
+ if (blocking) {
+ return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, siteKeys, collapse)
+ } else {
+ return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, siteKeys)
+ }
+ } catch (e) {
+ return new InvalidFilter(origText, e)
+ }
+ }
+ RegExpFilter.typeMap = {
+ OTHER: 1,
+ SCRIPT: 2,
+ IMAGE: 4,
+ STYLESHEET: 8,
+ OBJECT: 16,
+ SUBDOCUMENT: 32,
+ DOCUMENT: 64,
+ XBL: 1,
+ PING: 1,
+ XMLHTTPREQUEST: 2048,
+ OBJECT_SUBREQUEST: 4096,
+ DTD: 1,
+ MEDIA: 16384,
+ FONT: 32768,
+ BACKGROUND: 4,
+ POPUP: 268435456,
+ ELEMHIDE: 1073741824
+ }
+ RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP)
+
+ function BlockingFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys, collapse) {
+ RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys)
+ this.collapse = collapse
+ }
+
+ extend(BlockingFilter, RegExpFilter, {
+ collapse: null
+ })
+
+ function WhitelistFilter (text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys) {
+ RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, siteKeys)
+ }
+
+ extend(WhitelistFilter, RegExpFilter, {})
+
+ function Matcher () {
+ this.clear()
+ }
+
+ Matcher.prototype = {
+ filterByKeyword: null,
+ keywordByFilter: null,
+ clear: function () {
+ this.filterByKeyword = createDict()
+ this.keywordByFilter = createDict()
+ },
+ add: function (filter) {
+ if (filter.text in this.keywordByFilter) {
+ return
+ }
+ const keyword = this.findKeyword(filter)
+ const oldEntry = this.filterByKeyword[keyword]
+ if (typeof oldEntry == "undefined") {
+ this.filterByKeyword[keyword] = filter
+ } else if (oldEntry.length === 1) {
+ this.filterByKeyword[keyword] = [oldEntry, filter]
+ } else {
+ oldEntry.push(filter)
+ }
+ this.keywordByFilter[filter.text] = keyword
+ },
+ remove: function (filter) {
+ if (!(filter.text in this.keywordByFilter)) {
+ return
+ }
+ const keyword = this.keywordByFilter[filter.text]
+ const list = this.filterByKeyword[keyword]
+ if (list.length <= 1) {
+ delete this.filterByKeyword[keyword]
+ } else {
+ const index = list.indexOf(filter)
+ if (index >= 0) {
+ list.splice(index, 1)
+ if (list.length === 1) {
+ this.filterByKeyword[keyword] = list[0]
+ }
+ }
+ }
+ delete this.keywordByFilter[filter.text]
+ },
+ findKeyword: function (filter) {
+ let result = ""
+ let text = filter.text
+ if (Filter.regexpRegExp.test(text)) {
+ return result
+ }
+ const match = Filter.optionsRegExp.exec(text)
+ if (match) {
+ text = match.input.substr(0, match.index)
+ }
+ if (text.substr(0, 2) === "@@") {
+ text = text.substr(2)
+ }
+ const candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g)
+ if (!candidates) {
+ return result
+ }
+ const hash = this.filterByKeyword
+ let resultCount = 16777215
+ let resultLength = 0
+ for (let i = 0, l = candidates.length; i < l; i++) {
+ const candidate = candidates[i].substr(1)
+ const count = candidate in hash ? hash[candidate].length : 0
+ if (count < resultCount || count === resultCount && candidate.length > resultLength) {
+ result = candidate
+ resultCount = count
+ resultLength = candidate.length
+ }
+ }
+ return result
+ },
+ hasFilter: function (filter) {
+ return filter.text in this.keywordByFilter
+ },
+ getKeywordForFilter: function (filter) {
+ if (filter.text in this.keywordByFilter) {
+ return this.keywordByFilter[filter.text]
+ } else {
+ return null
+ }
+ },
+ _checkEntryMatch: function (keyword, location, contentType, docDomain, thirdParty, siteKey) {
+ const list = this.filterByKeyword[keyword]
+ for (let i = 0; i < list.length; i++) {
+ let filter = list[i]
+ if (filter === "#this") {
+ filter = list
+ }
+ if (filter.matches(location, contentType, docDomain, thirdParty, siteKey)) {
+ return filter
+ }
+ }
+ return null
+ }/*,
+ matchesAny: function (location, contentType, docDomain, thirdParty, siteKey) {
+ let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g)
+ if (candidates === null) {
+ candidates = []
+ }
+ candidates.push("")
+ for (let i = 0, l = candidates.length; i < l; i++) {
+ const substr = candidates[i]
+ if (substr in this.filterByKeyword) {
+ const result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
+ if (result) {
+ return result
+ }
+ }
+ }
+ return null
+ }*/
+ }
+
+ function CombinedMatcher () {
+ this.blacklist = new Matcher()
+ this.whitelist = new Matcher()
+ this.resultCache = createDict()
+ }
+
+ CombinedMatcher.maxCacheEntries = 1000
+ CombinedMatcher.prototype = {
+ blacklist: null,
+ whitelist: null,
+ resultCache: null,
+ cacheEntries: 0,
+ clear: function () {
+ this.blacklist.clear()
+ this.whitelist.clear()
+ this.resultCache = createDict()
+ this.cacheEntries = 0
+ },
+ add: function (filter) {
+ if (filter instanceof WhitelistFilter) {
+ this.whitelist.add(filter)
+ } else {
+ this.blacklist.add(filter)
+ }
+ if (this.cacheEntries > 0) {
+ this.resultCache = createDict()
+ this.cacheEntries = 0
+ }
+ },
+ remove: function (filter) {
+ if (filter instanceof WhitelistFilter) {
+ this.whitelist.remove(filter)
+ } else {
+ this.blacklist.remove(filter)
+ }
+ if (this.cacheEntries > 0) {
+ this.resultCache = createDict()
+ this.cacheEntries = 0
+ }
+ },
+ findKeyword: function (filter) {
+ if (filter instanceof WhitelistFilter) {
+ return this.whitelist.findKeyword(filter)
+ } else {
+ return this.blacklist.findKeyword(filter)
+ }
+ },
+ hasFilter: function (filter) {
+ if (filter instanceof WhitelistFilter) {
+ return this.whitelist.hasFilter(filter)
+ } else {
+ return this.blacklist.hasFilter(filter)
+ }
+ },
+ getKeywordForFilter: function (filter) {
+ if (filter instanceof WhitelistFilter) {
+ return this.whitelist.getKeywordForFilter(filter)
+ } else {
+ return this.blacklist.getKeywordForFilter(filter)
+ }
+ },
+ /*isSlowFilter: function (filter) {
+ const matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist
+ if (matcher.hasFilter(filter)) {
+ return !matcher.getKeywordForFilter(filter)
+ } else {
+ return !matcher.findKeyword(filter)
+ }
+ },*/
+ matchesAnyInternal: function (location, contentType, docDomain, thirdParty, siteKey) {
+ let candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g)
+ if (candidates === null) {
+ candidates = []
+ }
+ candidates.push("")
+ let blacklistHit = null
+ for (let i = 0, l = candidates.length; i < l; i++) {
+ const substr = candidates[i]
+ if (substr in this.whitelist.filterByKeyword) {
+ const result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
+ if (result) {
+ return result
+ }
+ }
+ if (substr in this.blacklist.filterByKeyword && blacklistHit === null) {
+ blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, siteKey)
+ }
+ }
+ return blacklistHit
+ },
+ matchesAny: function (location, docDomain) {
+ const key = location + " " + docDomain + " "
+ if (key in this.resultCache) {
+ return this.resultCache[key]
+ }
+ const result = this.matchesAnyInternal(location, 0, docDomain, null, null)
+ if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) {
+ this.resultCache = createDict()
+ this.cacheEntries = 0
+ }
+ this.resultCache[key] = result
+ this.cacheEntries++
+ return result
+ }
+ }
+
+ const userrulesMatcher = new CombinedMatcher()
+ const defaultMatcher = new CombinedMatcher()
+
+ const direct = 'DIRECT;'
+
+ for (let i = 0; i < rules.length; i++) {
+ defaultMatcher.add(Filter.fromText(rules[i]))
+ }
+
+ function FindProxyForURL (url, host) {
+ let matchedResult = userrulesMatcher.matchesAny(url, host)
+ if (matchedResult instanceof BlockingFilter) {
+ return proxy
+ } else if (matchedResult instanceof WhitelistFilter) {
+ return direct
}
- extend(RegExpFilter, ActiveFilter, {
- domainSourceIsUpperCase: true,
- length: 1,
- domainSeparator: "|",
- regexpSource: null,
- getRegexp: function () {
- var prop = getOwnPropertyDescriptor(this, "regexp");
- if (prop) {
- return prop;
- }
- var source = this.regexpSource.replace(/\*+/g, "*").replace(/\^\|$/, "^").replace(/\W/g, "\\$&").replace(/\\\*/g, ".*").replace(/\\\^/g, "(?:[\\x00-\\x24\\x26-\\x2C\\x2F\\x3A-\\x40\\x5B-\\x5E\\x60\\x7B-\\x7F]|$)").replace(/^\\\|\\\|/, "^[\\w\\-]+:\\/+(?!\\/)(?:[^\\/]+\\.)?").replace(/^\\\|/, "^").replace(/\\\|$/, "$").replace(/^(\.\*)/, "").replace(/(\.\*)$/, "");
- var regexp = new RegExp(source, this.matchCase ? "" : "i");
- this.regexp = regexp;
- return regexp;
- },
- contentType: 2147483647,
- matchCase: false,
- thirdParty: null,
- sitekeySource: null,
- getSitekeys: function () {
- var prop = getOwnPropertyDescriptor(this, "sitekeys");
- if (prop) {
- return prop;
- }
- var sitekeys = null;
- if (this.sitekeySource) {
- sitekeys = this.sitekeySource.split("|");
- this.sitekeySource = null;
- }
- this.sitekeys = sitekeys;
- return this.sitekeys;
- },
- matches: function (location, contentType, docDomain, thirdParty, sitekey) {
- if (this.getRegexp().test(location) && this.isActiveOnDomain(docDomain, sitekey)) {
- return true;
- }
- return false;
- }
- });
- RegExpFilter.prototype["0"] = "#this";
- RegExpFilter.fromText = function (text) {
- var blocking = true;
- var origText = text;
- if (text.indexOf("@@") == 0) {
- blocking = false;
- text = text.substr(2);
- }
- var contentType = null;
- var matchCase = null;
- var domains = null;
- var sitekeys = null;
- var thirdParty = null;
- var collapse = null;
- var options;
- var match = text.indexOf("$") >= 0 ? Filter.optionsRegExp.exec(text) : null;
- if (match) {
- options = match[1].toUpperCase().split(",");
- text = match.input.substr(0, match.index);
- for (var _loopIndex6 = 0; _loopIndex6 < options.length; ++_loopIndex6) {
- var option = options[_loopIndex6];
- var value = null;
- var separatorIndex = option.indexOf("=");
- if (separatorIndex >= 0) {
- value = option.substr(separatorIndex + 1);
- option = option.substr(0, separatorIndex);
- }
- option = option.replace(/-/, "_");
- if (option in RegExpFilter.typeMap) {
- if (contentType == null) {
- contentType = 0;
- }
- contentType |= RegExpFilter.typeMap[option];
- } else if (option.charAt(0) == "~" && option.substr(1) in RegExpFilter.typeMap) {
- if (contentType == null) {
- contentType = RegExpFilter.prototype.contentType;
- }
- contentType &= ~RegExpFilter.typeMap[option.substr(1)];
- } else if (option == "MATCH_CASE") {
- matchCase = true;
- } else if (option == "~MATCH_CASE") {
- matchCase = false;
- } else if (option == "DOMAIN" && typeof value != "undefined") {
- domains = value;
- } else if (option == "THIRD_PARTY") {
- thirdParty = true;
- } else if (option == "~THIRD_PARTY") {
- thirdParty = false;
- } else if (option == "COLLAPSE") {
- collapse = true;
- } else if (option == "~COLLAPSE") {
- collapse = false;
- } else if (option == "SITEKEY" && typeof value != "undefined") {
- sitekeys = value;
- } else {
- return new InvalidFilter(origText, "Unknown option " + option.toLowerCase());
- }
- }
- }
- if (!blocking && (contentType == null || contentType & RegExpFilter.typeMap.DOCUMENT) && (!options || options.indexOf("DOCUMENT") < 0) && !/^\|?[\w\-]+:/.test(text)) {
- if (contentType == null) {
- contentType = RegExpFilter.prototype.contentType;
- }
- contentType &= ~RegExpFilter.typeMap.DOCUMENT;
- }
- try {
- if (blocking) {
- return new BlockingFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys, collapse);
- } else {
- return new WhitelistFilter(origText, text, contentType, matchCase, domains, thirdParty, sitekeys);
- }
- } catch (e) {
- return new InvalidFilter(origText, e);
- }
- };
- RegExpFilter.typeMap = {
- OTHER: 1,
- SCRIPT: 2,
- IMAGE: 4,
- STYLESHEET: 8,
- OBJECT: 16,
- SUBDOCUMENT: 32,
- DOCUMENT: 64,
- XBL: 1,
- PING: 1,
- XMLHTTPREQUEST: 2048,
- OBJECT_SUBREQUEST: 4096,
- DTD: 1,
- MEDIA: 16384,
- FONT: 32768,
- BACKGROUND: 4,
- POPUP: 268435456,
- ELEMHIDE: 1073741824
- };
- RegExpFilter.prototype.contentType &= ~(RegExpFilter.typeMap.ELEMHIDE | RegExpFilter.typeMap.POPUP);
-
- function BlockingFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys, collapse) {
- RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
- this.collapse = collapse;
+ // Hack for Geosite, it provides a whitelist...
+ matchedResult = defaultMatcher.matchesAny(url, host)
+ if (matchedResult instanceof BlockingFilter) {
+ return proxy
+ } else if (matchedResult instanceof WhitelistFilter) {
+ return direct
}
- extend(BlockingFilter, RegExpFilter, {
- collapse: null
- });
-
- function WhitelistFilter(text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys) {
- RegExpFilter.call(this, text, regexpSource, contentType, matchCase, domains, thirdParty, sitekeys);
- }
-
- extend(WhitelistFilter, RegExpFilter, {});
-
- function Matcher() {
- this.clear();
- }
-
- Matcher.prototype = {
- filterByKeyword: null,
- keywordByFilter: null,
- clear: function () {
- this.filterByKeyword = createDict();
- this.keywordByFilter = createDict();
- },
- add: function (filter) {
- if (filter.text in this.keywordByFilter) {
- return;
- }
- var keyword = this.findKeyword(filter);
- var oldEntry = this.filterByKeyword[keyword];
- if (typeof oldEntry == "undefined") {
- this.filterByKeyword[keyword] = filter;
- } else if (oldEntry.length == 1) {
- this.filterByKeyword[keyword] = [oldEntry, filter];
- } else {
- oldEntry.push(filter);
- }
- this.keywordByFilter[filter.text] = keyword;
- },
- remove: function (filter) {
- if (!(filter.text in this.keywordByFilter)) {
- return;
- }
- var keyword = this.keywordByFilter[filter.text];
- var list = this.filterByKeyword[keyword];
- if (list.length <= 1) {
- delete this.filterByKeyword[keyword];
- } else {
- var index = list.indexOf(filter);
- if (index >= 0) {
- list.splice(index, 1);
- if (list.length == 1) {
- this.filterByKeyword[keyword] = list[0];
- }
- }
- }
- delete this.keywordByFilter[filter.text];
- },
- findKeyword: function (filter) {
- var result = "";
- var text = filter.text;
- if (Filter.regexpRegExp.test(text)) {
- return result;
- }
- var match = Filter.optionsRegExp.exec(text);
- if (match) {
- text = match.input.substr(0, match.index);
- }
- if (text.substr(0, 2) == "@@") {
- text = text.substr(2);
- }
- var candidates = text.toLowerCase().match(/[^a-z0-9%*][a-z0-9%]{3,}(?=[^a-z0-9%*])/g);
- if (!candidates) {
- return result;
- }
- var hash = this.filterByKeyword;
- var resultCount = 16777215;
- var resultLength = 0;
- for (var i = 0, l = candidates.length; i < l; i++) {
- var candidate = candidates[i].substr(1);
- var count = candidate in hash ? hash[candidate].length : 0;
- if (count < resultCount || count == resultCount && candidate.length > resultLength) {
- result = candidate;
- resultCount = count;
- resultLength = candidate.length;
- }
- }
- return result;
- },
- hasFilter: function (filter) {
- return filter.text in this.keywordByFilter;
- },
- getKeywordForFilter: function (filter) {
- if (filter.text in this.keywordByFilter) {
- return this.keywordByFilter[filter.text];
- } else {
- return null;
- }
- },
- _checkEntryMatch: function (keyword, location, contentType, docDomain, thirdParty, sitekey) {
- var list = this.filterByKeyword[keyword];
- for (var i = 0; i < list.length; i++) {
- var filter = list[i];
- if (filter == "#this") {
- filter = list;
- }
- if (filter.matches(location, contentType, docDomain, thirdParty, sitekey)) {
- return filter;
- }
- }
- return null;
- },
- matchesAny: function (location, contentType, docDomain, thirdParty, sitekey) {
- var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
- if (candidates === null) {
- candidates = [];
- }
- candidates.push("");
- for (var i = 0, l = candidates.length; i < l; i++) {
- var substr = candidates[i];
- if (substr in this.filterByKeyword) {
- var result = this._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
- if (result) {
- return result;
- }
- }
- }
- return null;
- }
- };
-
- function CombinedMatcher() {
- this.blacklist = new Matcher();
- this.whitelist = new Matcher();
- this.resultCache = createDict();
- }
-
- CombinedMatcher.maxCacheEntries = 1000;
- CombinedMatcher.prototype = {
- blacklist: null,
- whitelist: null,
- resultCache: null,
- cacheEntries: 0,
- clear: function () {
- this.blacklist.clear();
- this.whitelist.clear();
- this.resultCache = createDict();
- this.cacheEntries = 0;
- },
- add: function (filter) {
- if (filter instanceof WhitelistFilter) {
- this.whitelist.add(filter);
- } else {
- this.blacklist.add(filter);
- }
- if (this.cacheEntries > 0) {
- this.resultCache = createDict();
- this.cacheEntries = 0;
- }
- },
- remove: function (filter) {
- if (filter instanceof WhitelistFilter) {
- this.whitelist.remove(filter);
- } else {
- this.blacklist.remove(filter);
- }
- if (this.cacheEntries > 0) {
- this.resultCache = createDict();
- this.cacheEntries = 0;
- }
- },
- findKeyword: function (filter) {
- if (filter instanceof WhitelistFilter) {
- return this.whitelist.findKeyword(filter);
- } else {
- return this.blacklist.findKeyword(filter);
- }
- },
- hasFilter: function (filter) {
- if (filter instanceof WhitelistFilter) {
- return this.whitelist.hasFilter(filter);
- } else {
- return this.blacklist.hasFilter(filter);
- }
- },
- getKeywordForFilter: function (filter) {
- if (filter instanceof WhitelistFilter) {
- return this.whitelist.getKeywordForFilter(filter);
- } else {
- return this.blacklist.getKeywordForFilter(filter);
- }
- },
- isSlowFilter: function (filter) {
- var matcher = filter instanceof WhitelistFilter ? this.whitelist : this.blacklist;
- if (matcher.hasFilter(filter)) {
- return !matcher.getKeywordForFilter(filter);
- } else {
- return !matcher.findKeyword(filter);
- }
- },
- matchesAnyInternal: function (location, contentType, docDomain, thirdParty, sitekey) {
- var candidates = location.toLowerCase().match(/[a-z0-9%]{3,}/g);
- if (candidates === null) {
- candidates = [];
- }
- candidates.push("");
- var blacklistHit = null;
- for (var i = 0, l = candidates.length; i < l; i++) {
- var substr = candidates[i];
- if (substr in this.whitelist.filterByKeyword) {
- var result = this.whitelist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
- if (result) {
- return result;
- }
- }
- if (substr in this.blacklist.filterByKeyword && blacklistHit === null) {
- blacklistHit = this.blacklist._checkEntryMatch(substr, location, contentType, docDomain, thirdParty, sitekey);
- }
- }
- return blacklistHit;
- },
- matchesAny: function (location, docDomain) {
- var key = location + " " + docDomain + " ";
- if (key in this.resultCache) {
- return this.resultCache[key];
- }
- var result = this.matchesAnyInternal(location, 0, docDomain, null, null);
- if (this.cacheEntries >= CombinedMatcher.maxCacheEntries) {
- this.resultCache = createDict();
- this.cacheEntries = 0;
- }
- this.resultCache[key] = result;
- this.cacheEntries++;
- return result;
- }
- };
-
- var userrulesMatcher = new CombinedMatcher();
- var defaultMatcher = new CombinedMatcher();
-
- var direct = 'DIRECT;';
-
- for (var i = 0; i < userrules.length; i++) {
- userrulesMatcher.add(Filter.fromText(userrules[i]));
- }
-
- for (var i = 0; i < rules.length; i++) {
- defaultMatcher.add(Filter.fromText(rules[i]));
- }
-
- function FindProxyForURL(url, host) {
- if (userrulesMatcher.matchesAny(url, host) instanceof BlockingFilter) {
- return proxy;
- }
- if (userrulesMatcher.matchesAny(url, host) instanceof WhitelistFilter) {
- return direct;
- }
- // Hack for Geosite, it provides a whitelist...
- if (defaultMatcher.matchesAny(url, host) instanceof WhitelistFilter) {
- return direct;
- }
- if (defaultMatcher.matchesAny(url, host) instanceof BlockingFilter) {
- return proxy;
- }
- return direct;
- }
+ return direct
+ }
- return {
- FindProxyForURL,
- }
+ return {
+ FindProxyForURL
+ }
}
module.exports = {
- createPacClient
+ createPacClient
}