mirror of
https://github.com/anticensority/runet-censorship-bypass.git
synced 2024-11-24 02:13:43 +03:00
Remodulize, refactor, start adding exceptions
This commit is contained in:
parent
170b101a48
commit
ff5c51af11
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
const IF_DEBUG = false;
|
const IF_DEBUG = true;
|
||||||
|
|
||||||
if (!IF_DEBUG) {
|
if (!IF_DEBUG) {
|
||||||
// I believe logging objects precludes them from being GCed.
|
// I believe logging objects precludes them from being GCed.
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
'use strict';
|
||||||
|
/*
|
||||||
|
* Error Library
|
||||||
|
* PURPOSE 1:
|
||||||
|
* Allow wrapping errors with clarifications when they bubble up.
|
||||||
|
* Why:
|
||||||
|
* Sometimes low level errors may bubble up through a chain of callbacks.
|
||||||
|
* And when they reach top level of a user they loose context and convey
|
||||||
|
* nonsense like "Error 404: Can't find file".
|
||||||
|
* -- What file? WTH, I just hit update button?!
|
||||||
|
* PURPOSE 2:
|
||||||
|
* Supply separate class for warnings.
|
||||||
|
* Why:
|
||||||
|
* Some callbacks expect warnings which are like non-fatal errors.
|
||||||
|
* I want Warnings and FooError to be distinctable by code readers,
|
||||||
|
* so I create separate class for warnings.
|
||||||
|
**/
|
||||||
|
{
|
||||||
|
|
||||||
|
const mandatory = window.utils.mandatory;
|
||||||
|
|
||||||
|
const self = window.apis.errorsLib = {
|
||||||
|
|
||||||
|
// I don't use Error class, because we don't need stack here.
|
||||||
|
Warning: class {
|
||||||
|
|
||||||
|
constructor(message = mandatory()) {
|
||||||
|
|
||||||
|
this.message = message;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
clarify: function(err, message = mandatory(), {data} = {}) {
|
||||||
|
|
||||||
|
if (!err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
const warn = new self.Warning(message);
|
||||||
|
warn.wrapped = err;
|
||||||
|
if (data) {
|
||||||
|
warn.data = data;
|
||||||
|
}
|
||||||
|
return warn;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
clarifyThen: function(message, cb = mandatory()) {
|
||||||
|
|
||||||
|
return (err, ...args) => cb( clarify(err, message), ...args );
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,69 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const mandatory = window.utils.mandatory;
|
||||||
|
const errorsLib = window.apis.errorsLib;
|
||||||
|
|
||||||
|
const checkCon = 'Что-то не так с сетью, проверьте соединение.';
|
||||||
|
|
||||||
|
window.apis.httpLib = {
|
||||||
|
|
||||||
|
ifModifiedSince(
|
||||||
|
url,
|
||||||
|
lastModified,
|
||||||
|
cb = mandatory()
|
||||||
|
) {
|
||||||
|
|
||||||
|
const wasModified = new Date(0).toUTCString();
|
||||||
|
const notModifiedCode = 304;
|
||||||
|
fetch(url, {
|
||||||
|
method: 'HEAD',
|
||||||
|
headers: new Headers({
|
||||||
|
'If-Modified-Since': lastModified,
|
||||||
|
}),
|
||||||
|
}).then(
|
||||||
|
(res) => {
|
||||||
|
cb(
|
||||||
|
null,
|
||||||
|
res.status === notModifiedCode ?
|
||||||
|
false :
|
||||||
|
(res.headers.get('Last-Modified') || wasModified)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
errorsLib.clarifyThen(checkCon, (err) => cb(err, wasModifed))
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
get(url, cb = mandatory()) {
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
fetch(url, {cache: 'no-store'}).then(
|
||||||
|
(res) => {
|
||||||
|
|
||||||
|
const textCb =
|
||||||
|
(err) => res.text().then( (text) => cb(err, text), cb );
|
||||||
|
|
||||||
|
const status = res.status;
|
||||||
|
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
|
||||||
|
return textCb(
|
||||||
|
errorsLib.clarify(
|
||||||
|
res,
|
||||||
|
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('GETed with success:', url, Date.now() - start);
|
||||||
|
textCb();
|
||||||
|
|
||||||
|
},
|
||||||
|
errorsLib.clarifyThen(checkCon, cb)
|
||||||
|
);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,300 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
{
|
||||||
|
|
||||||
|
const mandatory = window.utils.mandatory;
|
||||||
|
const httpLib = window.apis.httpLib;
|
||||||
|
const clarify = window.apis.errorsLib.clarify;
|
||||||
|
|
||||||
|
// IP REGEX starts.
|
||||||
|
|
||||||
|
const portOpt = '(:\\d+)?'; // The only capturing group, sic!
|
||||||
|
const ipv4portOpt = '(?:[0-9]{1,3}\\.){3}[0-9]{1,3}' + portOpt;
|
||||||
|
const ipv6nake = '(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}';
|
||||||
|
const ipv6portOpt = '(?:' + ipv6nake + '|' + '\\[' + ipv6nake + '\\]' + portOpt + ')';
|
||||||
|
|
||||||
|
const ipv4Re = new RegExp('^' + ipv4portOpt + '$');
|
||||||
|
const ipv6Re = new RegExp('^' + ipv6portOpt + '$');
|
||||||
|
|
||||||
|
const _match = function _match(ipRe, str) {
|
||||||
|
|
||||||
|
let m = (str.match(ipRe) || []).filter( (c) => c );
|
||||||
|
const port = m.length > 1 ? m.pop() : false;
|
||||||
|
return { ifMatched: m.length, port: port };
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const _test = {
|
||||||
|
|
||||||
|
ipv4: _match.bind(null, ipv4Re),
|
||||||
|
ipv6: _match.bind(null, ipv6Re),
|
||||||
|
ipv4v6: function(str) {
|
||||||
|
|
||||||
|
let mr = this.ipv4(str);
|
||||||
|
if (mr.ifMatched) {
|
||||||
|
mr.ifv4 = true;
|
||||||
|
mr.canonical = str.replace(mr.port, '');
|
||||||
|
return mr;
|
||||||
|
}
|
||||||
|
mr = this.ipv6(str);
|
||||||
|
if (mr.ifMatched) {
|
||||||
|
mr.ifv6 = true;
|
||||||
|
mr.canonical = str.replace(mr.port, '').replace(/[\[\]]/g, '');
|
||||||
|
return mr;
|
||||||
|
}
|
||||||
|
return mr;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
// IP REGEX ends.
|
||||||
|
|
||||||
|
const _state = window.utils.createStorage('ip-to-host');
|
||||||
|
const ip2host = '';
|
||||||
|
|
||||||
|
const privates = {};
|
||||||
|
|
||||||
|
const _getHostObj = function _getHostObj(hostStr) {
|
||||||
|
|
||||||
|
let hostObj = privates._strToHostObj[hostStr];
|
||||||
|
if (!hostObj) {
|
||||||
|
hostObj = privates._strToHostObj[hostStr] = { host: hostStr };
|
||||||
|
}
|
||||||
|
return hostObj;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const init = function init() {
|
||||||
|
|
||||||
|
// Defaults.
|
||||||
|
const _antizapret = {
|
||||||
|
/* Don't use directly, please.
|
||||||
|
Encoded to counter abuse. */
|
||||||
|
host: '\x70\x72\x6f\x78\x79\x2e\x61\x6e\x74\x69\x7a\x61\x70\x72\x65\x74\x2e\x70\x72\x6f\x73\x74\x6f\x76\x70\x6e\x2e\x6f\x72\x67',
|
||||||
|
};
|
||||||
|
privates._strToHostObj = {
|
||||||
|
[_antizapret.host]: _antizapret,
|
||||||
|
};
|
||||||
|
|
||||||
|
privates._ipToHostObj = {};
|
||||||
|
for( const ip of [
|
||||||
|
// IPs of Antizapret.
|
||||||
|
'195.123.209.38',
|
||||||
|
'137.74.171.91',
|
||||||
|
'51.15.39.201',
|
||||||
|
'2001:bc8:4700:2300::1:d07',
|
||||||
|
'2a02:27ac::10',
|
||||||
|
] ) {
|
||||||
|
privates._ipToHostObj[ip] = _antizapret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Persisted.
|
||||||
|
const ipToHost = _state(ip2host);
|
||||||
|
if (ipToHost) {
|
||||||
|
for( const ip of Object.keys(ipToHost) ) {
|
||||||
|
const host = ipToHost[ip];
|
||||||
|
privates._ipToHostObj[ip] = _getHostObj(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
init();
|
||||||
|
|
||||||
|
const getIpsFor = function getIpsFor(host, cb = mandatory()) {
|
||||||
|
|
||||||
|
if (host.trim() === 'localhost') {
|
||||||
|
return cb(null, ['127.0.0.1', '::1']);
|
||||||
|
}
|
||||||
|
const types = [1, 28];
|
||||||
|
const promises = types.map(
|
||||||
|
(type) => new Promise((resolve) =>
|
||||||
|
httpLib.get(
|
||||||
|
'https://dns.google.com/resolve?type=' + type + '&name=' + host,
|
||||||
|
(err, res) => {
|
||||||
|
|
||||||
|
if (res) {
|
||||||
|
try {
|
||||||
|
res = JSON.parse(res);
|
||||||
|
console.log('Json parsed.');
|
||||||
|
if (err || res.Status) {
|
||||||
|
const msg = ['Answer', 'Comment', 'Status']
|
||||||
|
.filter( (prop) => res[prop] )
|
||||||
|
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
|
||||||
|
.join(', \n');
|
||||||
|
err = clarify(err || {}, 'Сервер (json): ' + msg, {data: res});
|
||||||
|
} else {
|
||||||
|
res = res.Answer || [];
|
||||||
|
res = res.filter(
|
||||||
|
(record) => types.includes(record.type)
|
||||||
|
).map( (ans) => ans.data );
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
err = clarify(
|
||||||
|
e,
|
||||||
|
'Сервер (текст): ' + res, err ? {data: err} : undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resolve([err, res]);
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
Promise.all(promises).then(
|
||||||
|
([[v4err, v4res], [v6err, v6res]]) => {
|
||||||
|
|
||||||
|
if(v4err) {
|
||||||
|
return cb(v4err, v4res);
|
||||||
|
}
|
||||||
|
const ips = v4res;
|
||||||
|
let warns = [];
|
||||||
|
if (!v6err) {
|
||||||
|
ips.push(...v6res);
|
||||||
|
} else {
|
||||||
|
warns = [v6err];
|
||||||
|
}
|
||||||
|
cb(null, ips, ...warns);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
const _canonize = function canonize(addrArr) {
|
||||||
|
|
||||||
|
const ipSet = new Set();
|
||||||
|
const hostSet = new Set();
|
||||||
|
|
||||||
|
for( const addr of addrArr ) {
|
||||||
|
|
||||||
|
const ipm = _test.ipv4v6(addr);
|
||||||
|
if (ipm.ifMatched) {
|
||||||
|
ipSet.add( ipm.canonical );
|
||||||
|
} else {
|
||||||
|
hostSet.add( addr.replace(/:\d+$/, '') );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('Canonized hosts/ips:', hostSet.size + '/' + ipSet.size);
|
||||||
|
return [ipSet, hostSet];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const self = window.apis.ipToHost = {
|
||||||
|
|
||||||
|
persistVoid() {
|
||||||
|
|
||||||
|
console.log('Persisting ipToHost...', privates);
|
||||||
|
const ipToHost = {};
|
||||||
|
for( const ip of Object.keys(privates._ipToHostObj) ) {
|
||||||
|
ipToHost[ ip ] = privates._ipToHostObj[ ip ].host;
|
||||||
|
}
|
||||||
|
_state(ip2host, ipToHost);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
resetToDefaultsVoid() {
|
||||||
|
|
||||||
|
_state(ip2host, null);
|
||||||
|
init();
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_addAsync(hostStr, cb = mandatory()) {
|
||||||
|
|
||||||
|
getIpsFor(hostStr, (err, ips, ...warns) => {
|
||||||
|
|
||||||
|
console.log('IPS', ips);
|
||||||
|
if (!err) {
|
||||||
|
// Object may be shared, string can't.
|
||||||
|
const hostObj = _getHostObj(hostStr);
|
||||||
|
for(const ip of ips) {
|
||||||
|
privates._ipToHostObj[ip] = hostObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cb(err, null, ...warns);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_replaceAllAsync(hostArr = mandatory(), cb) {
|
||||||
|
|
||||||
|
if (typeof(hostArr) === 'function') {
|
||||||
|
cb = hostArr;
|
||||||
|
hostArr = Object.keys(privates._strToHostObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.resetToDefaultsVoid();
|
||||||
|
|
||||||
|
const promises = hostArr.map(
|
||||||
|
(hostStr) => new Promise( (resolve) => this._addAsync(hostStr, (...args) => resolve(args) ) )
|
||||||
|
);
|
||||||
|
Promise.all( promises ).then( (cbsRes) => {
|
||||||
|
|
||||||
|
const errors = cbsRes.map( ([err]) => err ).filter( (err) => err );
|
||||||
|
let newError;
|
||||||
|
const ifAllErrors = cbsRes.length === errors.length;
|
||||||
|
if (errors.length) {
|
||||||
|
if (ifAllErrors) {
|
||||||
|
newError = errors.shift();
|
||||||
|
} else {
|
||||||
|
newError = errors;
|
||||||
|
}
|
||||||
|
newError = clarify(
|
||||||
|
newError,
|
||||||
|
'Не удалось получить один или несколько IP адресов для' +
|
||||||
|
' прокси-серверов. Иконка для уведомления об обходе' +
|
||||||
|
' блокировок может не отображаться.'
|
||||||
|
);
|
||||||
|
if (ifAllErrors) {
|
||||||
|
return cb(newError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cb(null, null, newError);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
replaceAllAsync(addrArr, cb = mandatory()) {
|
||||||
|
|
||||||
|
console.log('Replacing...');
|
||||||
|
const [ipSet, hostSet] = _canonize(addrArr);
|
||||||
|
for( const ip of ipSet ) {
|
||||||
|
const host = _getHostObj(ip);
|
||||||
|
privates._ipToHostObj[ip] = host;
|
||||||
|
}
|
||||||
|
|
||||||
|
const hostArr = Array.from(hostSet);
|
||||||
|
this._replaceAllAsync(hostArr, (allErr, ...args) => {
|
||||||
|
|
||||||
|
if (!allErr) {
|
||||||
|
this.persistVoid();
|
||||||
|
}
|
||||||
|
cb(allErr, ...args);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAllAsync(cb = mandatory()) {
|
||||||
|
|
||||||
|
this._replaceAllAsync(cb);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
get(ip) {
|
||||||
|
|
||||||
|
const tmp = privates._ipToHostObj[ip];
|
||||||
|
return tmp && tmp.host;
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -99,7 +99,9 @@
|
||||||
if (this.customProxyStringRaw) {
|
if (this.customProxyStringRaw) {
|
||||||
customProxyArray = this.customProxyStringRaw
|
customProxyArray = this.customProxyStringRaw
|
||||||
.replace(/#.*$/mg, '') // Strip comments.
|
.replace(/#.*$/mg, '') // Strip comments.
|
||||||
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g ).filter( (p) => p.trim() );
|
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g )
|
||||||
|
.map( (p) => p.trim() )
|
||||||
|
.filter( (p) => p && /\s+/g.test(p) );
|
||||||
if (this.ifUseSecureProxiesOnly) {
|
if (this.ifUseSecureProxiesOnly) {
|
||||||
customProxyArray = customProxyArray.filter( (p) => !p.startsWith('HTTP ') );
|
customProxyArray = customProxyArray.filter( (p) => !p.startsWith('HTTP ') );
|
||||||
}
|
}
|
||||||
|
@ -218,7 +220,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
kitchenState(ifIncontinence, true);
|
kitchenState(ifIncontinence, true);
|
||||||
cb(new TypeError(
|
cb(null, null, new TypeError(
|
||||||
'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.'
|
'Не найдено активного PAC-скрипта! Изменения будут применены при возвращении контроля настроек прокси или установке нового PAC-скрипта.'
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -226,7 +228,7 @@
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
checkIncontinence(details) {
|
checkIncontinenceVoid(details) {
|
||||||
|
|
||||||
if ( kitchenState(ifIncontinence) ) {
|
if ( kitchenState(ifIncontinence) ) {
|
||||||
this._tryNowAsync(details, () => {/* Swallow. */});
|
this._tryNowAsync(details, () => {/* Swallow. */});
|
||||||
|
@ -237,12 +239,10 @@
|
||||||
|
|
||||||
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) {
|
keepCookedNowAsync(pacMods = mandatory(), cb = throwIfError) {
|
||||||
|
|
||||||
|
console.log('Keep cooked now...');
|
||||||
if (typeof(pacMods) === 'function') {
|
if (typeof(pacMods) === 'function') {
|
||||||
cb = pacMods;
|
cb = pacMods;
|
||||||
const pacMods = getCurrentConfigs();
|
pacMods = this.getCurrentConfigs();
|
||||||
if (!pacMods) {
|
|
||||||
return cb(TypeError('PAC mods were never initialized and you haven\'t supplied any.'));
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
try {
|
try {
|
||||||
pacMods = new PacModifiers(pacMods);
|
pacMods = new PacModifiers(pacMods);
|
||||||
|
@ -251,7 +251,27 @@
|
||||||
}
|
}
|
||||||
kitchenState('mods', pacMods);
|
kitchenState('mods', pacMods);
|
||||||
}
|
}
|
||||||
this._tryNowAsync( (err) => cb(null, null, err && [err]) );
|
this._tryNowAsync(
|
||||||
|
(err, res, ...warns) => {
|
||||||
|
|
||||||
|
console.log('Try now cb...', err);
|
||||||
|
if (err) {
|
||||||
|
return cb(err, res, ...warns);
|
||||||
|
}
|
||||||
|
|
||||||
|
const par = pacMods.customProxyArray;
|
||||||
|
if (!(par && par.length)) {
|
||||||
|
return cb(null, res, ...warns);
|
||||||
|
}
|
||||||
|
|
||||||
|
const hosts = par.map( (ps) => ps.split(/\s+/)[1] )
|
||||||
|
window.apis.ipToHost.replaceAllAsync(
|
||||||
|
hosts,
|
||||||
|
(...args) => cb(...args, ...warns)
|
||||||
|
);
|
||||||
|
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -288,7 +308,7 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pacKitchen.checkIncontinence();
|
pacKitchen.checkIncontinenceVoid();
|
||||||
chrome.proxy.settings.onChange.addListener( pacKitchen.checkIncontinence.bind(pacKitchen) );
|
chrome.proxy.settings.onChange.addListener( pacKitchen.checkIncontinenceVoid.bind(pacKitchen) );
|
||||||
|
|
||||||
} // Private namespace ends.
|
} // Private namespace ends.
|
|
@ -26,6 +26,12 @@
|
||||||
const chromified = window.utils.chromified;
|
const chromified = window.utils.chromified;
|
||||||
const checkChromeError = window.utils.checkChromeError;
|
const checkChromeError = window.utils.checkChromeError;
|
||||||
|
|
||||||
|
const clarify = window.apis.errorsLib.clarify;
|
||||||
|
const clarifyThen = window.apis.errorsLib.clarifyThen;
|
||||||
|
const Warning = window.apis.errorsLib.Warning;
|
||||||
|
|
||||||
|
const httpLib = window.apis.httpLib;
|
||||||
|
|
||||||
const asyncLogGroup = function asyncLogGroup(...args) {
|
const asyncLogGroup = function asyncLogGroup(...args) {
|
||||||
|
|
||||||
const cb = args.pop();
|
const cb = args.pop();
|
||||||
|
@ -43,43 +49,6 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class Clarification {
|
|
||||||
|
|
||||||
constructor(message = mandatory(), prevClarification) {
|
|
||||||
|
|
||||||
this.message = message;
|
|
||||||
if (prevClarification) {
|
|
||||||
this.prev = prevClarification;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const clarify = function clarify(
|
|
||||||
err = mandatory(),
|
|
||||||
message = mandatory(),
|
|
||||||
{data} = {}) {
|
|
||||||
|
|
||||||
err.clarification = new Clarification(message, err.clarification);
|
|
||||||
if (data) {
|
|
||||||
err.clarification.data = data;
|
|
||||||
}
|
|
||||||
return err;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
class Warning {
|
|
||||||
|
|
||||||
constructor(message) {
|
|
||||||
|
|
||||||
clarify(this, message);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const setPacAsync = function setPacAsync(
|
const setPacAsync = function setPacAsync(
|
||||||
pacData = mandatory(), cb = throwIfError
|
pacData = mandatory(), cb = throwIfError
|
||||||
) {
|
) {
|
||||||
|
@ -104,7 +73,7 @@
|
||||||
|
|
||||||
console.warn('Failed, other extension is in control.');
|
console.warn('Failed, other extension is in control.');
|
||||||
return cb(
|
return cb(
|
||||||
new Warning( window.utils.messages.whichExtensionHtml() )
|
new Error( window.utils.messages.whichExtensionHtml() )
|
||||||
);
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -116,181 +85,20 @@
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const clarifyErrorThen = function clarifyFetchErrorThen(message, cb) {
|
|
||||||
|
|
||||||
return (err, ...args) => cb( clarify(err || {}, message), ...args );
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const clarifyFetchErrorThen = (cb) =>
|
|
||||||
clarifyErrorThen('Что-то не так с сетью, проверьте соединение.', cb);
|
|
||||||
|
|
||||||
const ifModifiedSince = function ifModifiedSince(
|
|
||||||
url = mandatory(),
|
|
||||||
lastModified = mandatory(),
|
|
||||||
cb = mandatory()
|
|
||||||
) {
|
|
||||||
|
|
||||||
const wasModified = new Date(0).toUTCString();
|
|
||||||
const notModifiedCode = 304;
|
|
||||||
fetch(url, {
|
|
||||||
method: 'HEAD',
|
|
||||||
headers: new Headers({
|
|
||||||
'If-Modified-Since': lastModified,
|
|
||||||
}),
|
|
||||||
}).then(
|
|
||||||
(res) => {
|
|
||||||
cb(
|
|
||||||
null,
|
|
||||||
res.status === notModifiedCode ?
|
|
||||||
false :
|
|
||||||
(res.headers.get('Last-Modified') || wasModified)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
clarifyFetchErrorThen((err) => cb(err, wasModified))
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const httpGet = function httpGet(url, cb = mandatory()) {
|
|
||||||
|
|
||||||
const start = Date.now();
|
|
||||||
fetch(url, {cache: 'no-store'}).then(
|
|
||||||
(res) => {
|
|
||||||
|
|
||||||
const textCb =
|
|
||||||
(err) => res.text().then( (text) => cb(err, text), cb );
|
|
||||||
|
|
||||||
const status = res.status;
|
|
||||||
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
|
|
||||||
return textCb(
|
|
||||||
clarify(
|
|
||||||
res,
|
|
||||||
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('GETed with success:', url, Date.now() - start);
|
|
||||||
textCb();
|
|
||||||
|
|
||||||
},
|
|
||||||
clarifyFetchErrorThen(cb)
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const getIpsFor = function getIpsFor(host = mandatory(), cb = mandatory()) {
|
|
||||||
|
|
||||||
const types = [1, 28];
|
|
||||||
const promises = types.map(
|
|
||||||
(type) => new Promise((resolve) =>
|
|
||||||
httpGet(
|
|
||||||
'https://dns.google.com/resolve?type=' + type + '&name=' + host,
|
|
||||||
(err, res) => {
|
|
||||||
|
|
||||||
if (res) {
|
|
||||||
try {
|
|
||||||
res = JSON.parse(res);
|
|
||||||
console.log('Json parsed.');
|
|
||||||
if (err || res.Status) {
|
|
||||||
const msg = ['Answer', 'Comment', 'Status']
|
|
||||||
.filter( (prop) => res[prop] )
|
|
||||||
.map( (prop) => prop + ': ' + JSON.stringify( res[prop] ) )
|
|
||||||
.join(', \n');
|
|
||||||
clarify(err, 'Сервер (json): ' + msg, {data: res});
|
|
||||||
} else {
|
|
||||||
res = res.Answer || [];
|
|
||||||
res = res.filter(
|
|
||||||
(record) => types.includes(record.type)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
err = clarify(
|
|
||||||
e,
|
|
||||||
'Сервер (текст): ' + res, err ? {data: err} : null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resolve([err, res]);
|
|
||||||
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
Promise.all(promises).then(
|
|
||||||
([[v4err, v4res], [v6err, v6res]]) => {
|
|
||||||
|
|
||||||
if(v4err) {
|
|
||||||
return cb(v4err, v4res);
|
|
||||||
}
|
|
||||||
const ips = v4res;
|
|
||||||
let warns = [];
|
|
||||||
if (!v6err) {
|
|
||||||
ips.push(...v6res);
|
|
||||||
} else {
|
|
||||||
warns = [v6err];
|
|
||||||
}
|
|
||||||
cb(null, ips, ...warns);
|
|
||||||
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
const updatePacProxyIps = function updatePacProxyIps(
|
const updatePacProxyIps = function updatePacProxyIps(
|
||||||
provider,
|
|
||||||
cb = throwIfError
|
cb = throwIfError
|
||||||
) {
|
) {
|
||||||
|
|
||||||
cb = asyncLogGroup(
|
cb = asyncLogGroup(
|
||||||
'Getting IP for '+ provider.proxyHosts.join(', ') + '...',
|
'Getting IPs for PAC hosts...',
|
||||||
cb
|
cb
|
||||||
);
|
);
|
||||||
let failure = {
|
window.apis.ipToHost.updateAllAsync(cb);
|
||||||
errors: {},
|
|
||||||
};
|
|
||||||
let hostsProcessed = 0;
|
|
||||||
provider.proxyHosts.forEach(
|
|
||||||
(proxyHost) => getIpsFor(
|
|
||||||
proxyHost,
|
|
||||||
(err, ips, ...warns) => {
|
|
||||||
|
|
||||||
if (!err) {
|
|
||||||
provider.proxyIps = provider.proxyIps || {};
|
|
||||||
ips.forEach(
|
|
||||||
(ip) => provider.proxyIps[ip] = proxyHost
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (err || warns.length) {
|
|
||||||
failure.errors[proxyHost] = err || warns;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ++hostsProcessed < provider.proxyHosts.length ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// All hosts were processed.
|
|
||||||
const errorsCount = Object.keys(failure.errors).length;
|
|
||||||
if (!errorsCount) {
|
|
||||||
return cb();
|
|
||||||
}
|
|
||||||
clarify(
|
|
||||||
failure,
|
|
||||||
'Не удалось получить один или несколько IP адресов для' +
|
|
||||||
' прокси-серверов. Иконка для уведомления об обходе' +
|
|
||||||
' блокировок может не отображаться.'
|
|
||||||
);
|
|
||||||
errorsCount === hostsProcessed
|
|
||||||
? cb(failure)
|
|
||||||
: cb(null, null, failure);
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
|
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
|
||||||
provider = mandatory(), lastModified = mandatory(), cb = throwIfError
|
provider, lastModified = mandatory(), cb = throwIfError
|
||||||
) {
|
) {
|
||||||
|
|
||||||
const pacUrl = provider.pacUrls[0];
|
const pacUrl = provider.pacUrls[0];
|
||||||
|
@ -299,7 +107,7 @@
|
||||||
cb
|
cb
|
||||||
);
|
);
|
||||||
|
|
||||||
ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
httpLib.ifModifiedSince(pacUrl, lastModified, (err, newLastModified) => {
|
||||||
|
|
||||||
if (!newLastModified) {
|
if (!newLastModified) {
|
||||||
return cb(
|
return cb(
|
||||||
|
@ -318,7 +126,7 @@
|
||||||
|
|
||||||
pacDataPromise = pacDataPromise.catch(
|
pacDataPromise = pacDataPromise.catch(
|
||||||
(err) => new Promise(
|
(err) => new Promise(
|
||||||
(resolve, reject) => httpGet(
|
(resolve, reject) => httpLib.get(
|
||||||
url,
|
url,
|
||||||
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
|
(newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
|
||||||
)
|
)
|
||||||
|
@ -339,7 +147,7 @@
|
||||||
);
|
);
|
||||||
|
|
||||||
},
|
},
|
||||||
clarifyErrorThen(
|
clarifyThen(
|
||||||
'Не удалось скачать PAC-скрипт с адресов: [ '
|
'Не удалось скачать PAC-скрипт с адресов: [ '
|
||||||
+ provider.pacUrls.join(' , ') + ' ].',
|
+ provider.pacUrls.join(' , ') + ' ].',
|
||||||
cb
|
cb
|
||||||
|
@ -363,14 +171,6 @@
|
||||||
' <br/> <a href="https://antizapret.prostovpn.org">Страница проекта</a>.',
|
' <br/> <a href="https://antizapret.prostovpn.org">Страница проекта</a>.',
|
||||||
|
|
||||||
pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
|
pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
|
||||||
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
|
||||||
proxyIps: {
|
|
||||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Антицензорити: {
|
Антицензорити: {
|
||||||
label: 'Антицензорити',
|
label: 'Антицензорити',
|
||||||
|
@ -393,14 +193,6 @@
|
||||||
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x72\x61\x77\x2e\x67\x69\x74\x68\x75\x62\x75\x73\x65\x72\x63\x6f\x6e\x74\x65\x6e\x74\x2e\x63\x6f\x6d\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x73\x68\x69\x70\x2d\x72\x75\x73\x73\x69\x61\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x6d\x61\x73\x74\x65\x72\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63', // eslint-disable-line max-len
|
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x72\x61\x77\x2e\x67\x69\x74\x68\x75\x62\x75\x73\x65\x72\x63\x6f\x6e\x74\x65\x6e\x74\x2e\x63\x6f\x6d\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x73\x68\x69\x70\x2d\x72\x75\x73\x73\x69\x61\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x6d\x61\x73\x74\x65\x72\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63', // eslint-disable-line max-len
|
||||||
// Google Drive (0.17):
|
// Google Drive (0.17):
|
||||||
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x72\x69\x76\x65\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x75\x63\x3f\x65\x78\x70\x6f\x72\x74\x3d\x64\x6f\x77\x6e\x6c\x6f\x61\x64\x26\x69\x64\x3d\x30\x42\x2d\x5a\x43\x56\x53\x76\x75\x4e\x57\x66\x30\x54\x44\x46\x52\x4f\x47\x35\x46\x62\x55\x39\x4f\x64\x44\x67'], // eslint-disable-line max-len
|
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x72\x69\x76\x65\x2e\x67\x6f\x6f\x67\x6c\x65\x2e\x63\x6f\x6d\x2f\x75\x63\x3f\x65\x78\x70\x6f\x72\x74\x3d\x64\x6f\x77\x6e\x6c\x6f\x61\x64\x26\x69\x64\x3d\x30\x42\x2d\x5a\x43\x56\x53\x76\x75\x4e\x57\x66\x30\x54\x44\x46\x52\x4f\x47\x35\x46\x62\x55\x39\x4f\x64\x44\x67'], // eslint-disable-line max-len
|
||||||
proxyHosts: ['proxy.antizapret.prostovpn.org'],
|
|
||||||
proxyIps: {
|
|
||||||
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'137.74.171.91': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'51.15.39.201': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2001:bc8:4700:2300::1:d07': 'proxy.antizapret.prostovpn.org',
|
|
||||||
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -422,22 +214,13 @@
|
||||||
return new Date(0).toUTCString();
|
return new Date(0).toUTCString();
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setLastModified(newValue = mandatory()) {
|
setLastModified(newValue = mandatory()) {
|
||||||
|
|
||||||
this._currentPacProviderLastModified = newValue;
|
this._currentPacProviderLastModified = newValue;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
isProxied(ip) {
|
|
||||||
|
|
||||||
// Executed on each request with ip. Make it as fast as possible.
|
|
||||||
// Property lookups are cheap (I tested).
|
|
||||||
return this._currentPacProviderKey
|
|
||||||
&& this.pacProviders[this._currentPacProviderKey]
|
|
||||||
.proxyIps.hasOwnProperty(ip);
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
mustBeKey(key = mandatory()) {
|
mustBeKey(key = mandatory()) {
|
||||||
|
|
||||||
if ( !(key === null || this.pacProviders[key]) ) {
|
if ( !(key === null || this.pacProviders[key]) ) {
|
||||||
|
@ -451,6 +234,7 @@
|
||||||
return this._currentPacProviderKey;
|
return this._currentPacProviderKey;
|
||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
setCurrentPacProviderKey(
|
setCurrentPacProviderKey(
|
||||||
newKey = mandatory(),
|
newKey = mandatory(),
|
||||||
lastModified = new Date().toUTCString()
|
lastModified = new Date().toUTCString()
|
||||||
|
@ -510,7 +294,7 @@
|
||||||
|
|
||||||
if (key === null) {
|
if (key === null) {
|
||||||
// No pac provider set.
|
// No pac provider set.
|
||||||
return clarifyErrorThen('Сперва выберите PAC-провайдера.', cb);
|
return clarifyThen('Сперва выберите PAC-провайдера.', cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
const pacProvider = this.getPacProvider(key);
|
const pacProvider = this.getPacProvider(key);
|
||||||
|
@ -536,7 +320,6 @@
|
||||||
|
|
||||||
const ipsErrorPromise = new Promise(
|
const ipsErrorPromise = new Promise(
|
||||||
(resolve, reject) => updatePacProxyIps(
|
(resolve, reject) => updatePacProxyIps(
|
||||||
pacProvider,
|
|
||||||
resolve
|
resolve
|
||||||
)
|
)
|
||||||
);
|
);
|
|
@ -43,17 +43,13 @@
|
||||||
|
|
||||||
chrome.tabs.onUpdated.addListener( onTabUpdate );
|
chrome.tabs.onUpdated.addListener( onTabUpdate );
|
||||||
|
|
||||||
const antiCensorRu = window.apis.antiCensorRu;
|
const updateTitle = function updateTitle(requestDetails, proxyHost, cb) {
|
||||||
|
|
||||||
const updateTitle = function updateTitle(requestDetails, cb) {
|
|
||||||
|
|
||||||
chrome.browserAction.getTitle(
|
chrome.browserAction.getTitle(
|
||||||
{tabId: requestDetails.tabId},
|
{tabId: requestDetails.tabId},
|
||||||
(title) => {
|
(title) => {
|
||||||
|
|
||||||
const ifTitleSetAlready = /\n/.test(title);
|
const ifTitleSetAlready = /\n/.test(title);
|
||||||
const proxyHost = antiCensorRu.getPacProvider()
|
|
||||||
.proxyIps[requestDetails.ip];
|
|
||||||
|
|
||||||
const hostname = new URL( requestDetails.url ).hostname;
|
const hostname = new URL( requestDetails.url ).hostname;
|
||||||
|
|
||||||
|
@ -131,8 +127,8 @@
|
||||||
|
|
||||||
const isProxiedAndInformed = function isProxiedAndInformed(requestDetails) {
|
const isProxiedAndInformed = function isProxiedAndInformed(requestDetails) {
|
||||||
|
|
||||||
if ( !(requestDetails.ip
|
const host = window.apis.ipToHost.get( requestDetails.ip );
|
||||||
&& antiCensorRu.isProxied( requestDetails.ip )) ) {
|
if (!host) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,7 +137,7 @@
|
||||||
previousUpdateTitleFinished = previousUpdateTitleFinished.then(
|
previousUpdateTitleFinished = previousUpdateTitleFinished.then(
|
||||||
() => new Promise(
|
() => new Promise(
|
||||||
(resolve) => {
|
(resolve) => {
|
||||||
const cb = () => updateTitle( requestDetails, resolve );
|
const cb = () => updateTitle( requestDetails, host, resolve );
|
||||||
return ifMainFrame
|
return ifMainFrame
|
||||||
? afterTabUpdated(requestDetails.tabId, cb) : cb();
|
? afterTabUpdated(requestDetails.tabId, cb) : cb();
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,9 +25,12 @@
|
||||||
"background": {
|
"background": {
|
||||||
"scripts": [
|
"scripts": [
|
||||||
"00-init-apis.js",
|
"00-init-apis.js",
|
||||||
"11-api-error-handlers.js",
|
"11-error-handlers-api.js",
|
||||||
"13-pac-kitchen.js",
|
"12-errors-lib.js",
|
||||||
"15-api-sync-pac-script-with-pac-provider.js",
|
"13-http-lib.js",
|
||||||
|
"14-ip-to-host-api.js",
|
||||||
|
"15-pac-kitchen-api.js",
|
||||||
|
"17-sync-pac-script-with-pac-provider-api.js",
|
||||||
"20-api-fixes.js",
|
"20-api-fixes.js",
|
||||||
"30-block-informer.js",
|
"30-block-informer.js",
|
||||||
"40-context-menus.js"
|
"40-context-menus.js"
|
||||||
|
|
|
@ -6,14 +6,29 @@
|
||||||
:root {
|
:root {
|
||||||
--ribbon-color: #4169e1; /* #1a6cc8 */
|
--ribbon-color: #4169e1; /* #1a6cc8 */
|
||||||
}
|
}
|
||||||
div {
|
div, section {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
header {
|
||||||
|
margin: 0 0 0.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
section header {
|
||||||
|
display: block;
|
||||||
|
border-bottom: 1px solid #558abb;
|
||||||
|
border-left: 5px solid #1048ac;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
height: 2em;
|
||||||
|
background-color: DodgerBlue;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
label {
|
label {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
ul {
|
ul, ol {
|
||||||
list-style-type: none;
|
list-style-type: none;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -53,9 +68,6 @@
|
||||||
#none:checked + label {
|
#none:checked + label {
|
||||||
color: red;
|
color: red;
|
||||||
}
|
}
|
||||||
#configs-panel > header {
|
|
||||||
margin: 0 0 0.4em;
|
|
||||||
}
|
|
||||||
.if-not-controlled {
|
.if-not-controlled {
|
||||||
display: none;
|
display: none;
|
||||||
color: red;
|
color: red;
|
||||||
|
@ -147,6 +159,39 @@
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
select#exceptions-select {
|
||||||
|
color: black;
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 0;
|
||||||
|
box-shadow: none;
|
||||||
|
text-shadow: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#flex-right {
|
||||||
|
flex-grow: 99;
|
||||||
|
}
|
||||||
|
#flex-right > * {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
#except-editor {
|
||||||
|
border-radius: 0 !important;
|
||||||
|
border-bottom: 0;
|
||||||
|
height: 1.6em !important;
|
||||||
|
min-height: 1.6em !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#flex-container {
|
||||||
|
position: relative;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#flex-container > * {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
@ -154,27 +199,61 @@
|
||||||
<span id="which-extension"></span>
|
<span id="which-extension"></span>
|
||||||
<hr style="border-color: red; border-style: solid;"/>
|
<hr style="border-color: red; border-style: solid;"/>
|
||||||
</span>
|
</span>
|
||||||
|
<section>
|
||||||
|
<header>PAC-скрипт</header>
|
||||||
<ul id="list-of-providers">
|
<ul id="list-of-providers">
|
||||||
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
|
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
|
||||||
</ul>
|
</ul>
|
||||||
<div style="white-space: nowrap">
|
<div style="white-space: nowrap">
|
||||||
Обновлялись: <span class="update-date">...</span>
|
Обновлялись: <span class="update-date">...</span>
|
||||||
</div>
|
</div>
|
||||||
<span id="status" style="will-change: contents">Загрузка...</span>
|
</section>
|
||||||
|
<section id="status" style="will-change: contents">Загрузка...</section>
|
||||||
|
<hr/>
|
||||||
|
<div id="exceptions">
|
||||||
|
<header>Проксировать этот сайт?</header>
|
||||||
|
<div id="flex-container">
|
||||||
|
<ul style="padding-right: 1em">
|
||||||
|
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site"/> <label for="this-auto">🔄︎ авто</label></li>
|
||||||
|
<li><input id="this-yes" type="radio" name="if-proxy-this-site"/> <label for="this-yes">✔ да</label></li>
|
||||||
|
<li><input id="this-no" type="radio" name="if-proxy-this-site"/> <label for="this-no">✘ нет</label></li>
|
||||||
|
<li><a href>Весь список</a></li>
|
||||||
|
</ul>
|
||||||
|
<div id="flex-right">
|
||||||
|
<input type="text" value="google.com" id="except-editor"/>
|
||||||
|
<select multiple id="exceptions-select">
|
||||||
|
<option class="sel">google.com</option>
|
||||||
|
<option class="sel">yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
<option>yandex.ru</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<hr/>
|
<hr/>
|
||||||
<div>
|
|
||||||
<ul id="pac-mods">
|
<ul id="pac-mods">
|
||||||
<li class="control-row">
|
<li class="control-row">
|
||||||
<input type="button" value="Применить" id="apply-mods" disabled/>
|
<input type="button" value="Применить" id="apply-mods" disabled/>
|
||||||
<a href id="reset-mods" class="link-button">К изначальным!</a>
|
<a href id="reset-mods" class="link-button">К изначальным!</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
<hr/>
|
<hr/>
|
||||||
<div id="configs-panel">
|
<div id="configs-panel">
|
||||||
<header>Я ❤️ уведомления:</header>
|
<header>Я ❤️ уведомления:</header>
|
||||||
<ul id="list-of-handlers">
|
<ul id="list-of-handlers"></ul>
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
<footer class="control-row">
|
<footer class="control-row">
|
||||||
<input type="button" value="Готово" class="close-button">
|
<input type="button" value="Готово" class="close-button">
|
||||||
|
|
|
@ -80,8 +80,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
|
|
||||||
const warning = warns
|
const warning = warns
|
||||||
.map(
|
.map(
|
||||||
(w) =>
|
(w) => w && w.message || ''
|
||||||
(w && (w.clarification && w.clarification.message || w.message) || '')
|
|
||||||
)
|
)
|
||||||
.filter( (m) => m )
|
.filter( (m) => m )
|
||||||
.map( (m) => '✘ ' + m )
|
.map( (m) => '✘ ' + m )
|
||||||
|
@ -89,18 +88,20 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
|
|
||||||
let message = '';
|
let message = '';
|
||||||
if (err) {
|
if (err) {
|
||||||
let clarification = err.clarification;
|
let wrapped = err.wrapped;
|
||||||
message = err.message || '';
|
message = err.message || '';
|
||||||
|
|
||||||
while( clarification ) {
|
while( wrapped ) {
|
||||||
message = (clarification && (clarification.message + ' ')) +
|
const deeperMsg = wrapped && wrapped.message;
|
||||||
message;
|
if (deeperMsg) {
|
||||||
clarification = clarification.prev;
|
message = message + ' > ' + deeperMsg;
|
||||||
|
}
|
||||||
|
wrapped = wrapped.wrapped;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
message = message.trim();
|
message = message.trim();
|
||||||
if (warning) {
|
if (warning) {
|
||||||
message += ' ' + warning;
|
message = message ? message + '<br/>' + warning : warning;
|
||||||
}
|
}
|
||||||
setStatusTo(
|
setStatusTo(
|
||||||
`<span style="color:red">
|
`<span style="color:red">
|
||||||
|
@ -121,11 +122,11 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const enableDisableInputs = function() {
|
const switchInputs = function(val) {
|
||||||
|
|
||||||
const inputs = document.querySelectorAll('input');
|
const inputs = document.querySelectorAll('input');
|
||||||
for ( let i = 0; i < inputs.length; i++ ) {
|
for ( let i = 0; i < inputs.length; i++ ) {
|
||||||
inputs[i].disabled = !inputs[i].disabled;
|
inputs[i].disabled = val === 'on' ? false : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -133,14 +134,17 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
|
const conduct = (beforeStatus, operation, afterStatus, onSuccess) => {
|
||||||
|
|
||||||
setStatusTo(beforeStatus);
|
setStatusTo(beforeStatus);
|
||||||
enableDisableInputs();
|
switchInputs('off');
|
||||||
operation((err, res, ...warns) => {
|
operation((err, res, ...warns) => {
|
||||||
|
|
||||||
|
warns = warns.filter( (w) => w );
|
||||||
if (err || warns.length) {
|
if (err || warns.length) {
|
||||||
|
backgroundPage.console.log('ERR', err, 'W', warns.length, 'w', warns);
|
||||||
showErrors(err, ...warns);
|
showErrors(err, ...warns);
|
||||||
} else {
|
} else {
|
||||||
setStatusTo(afterStatus);
|
setStatusTo(afterStatus);
|
||||||
}
|
}
|
||||||
enableDisableInputs();
|
switchInputs('on');
|
||||||
if (!err) {
|
if (!err) {
|
||||||
onSuccess && onSuccess(res);
|
onSuccess && onSuccess(res);
|
||||||
}
|
}
|
||||||
|
@ -148,6 +152,15 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const infoSign = function infoSign(tooltip) {
|
||||||
|
|
||||||
|
return `<div class="desc">
|
||||||
|
<span class="info-sign">🛈</span>
|
||||||
|
<div class="tooltip">${tooltip}</div>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
{
|
{
|
||||||
const ul = document.querySelector('#list-of-providers');
|
const ul = document.querySelector('#list-of-providers');
|
||||||
const _firstChild = ul.firstChild;
|
const _firstChild = ul.firstChild;
|
||||||
|
@ -161,11 +174,8 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
<input type="radio" name="pacProvider" id="${providerKey}">
|
<input type="radio" name="pacProvider" id="${providerKey}">
|
||||||
<label for="${providerKey}"> ${provider.label}</label>
|
<label for="${providerKey}"> ${provider.label}</label>
|
||||||
<a href class="link-button checked-radio-panel"
|
<a href class="link-button checked-radio-panel"
|
||||||
id="update-${providerKey}">[обновить]</a>
|
id="update-${providerKey}">[обновить]</a> ` +
|
||||||
<div class="desc">
|
infoSign(provider.desc);
|
||||||
<span class="info-sign">🛈</span>
|
|
||||||
<div class="tooltip">${provider.desc}</div>
|
|
||||||
</div>`;
|
|
||||||
li.querySelector('.link-button').onclick =
|
li.querySelector('.link-button').onclick =
|
||||||
() => {
|
() => {
|
||||||
conduct(
|
conduct(
|
||||||
|
@ -212,6 +222,26 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EXCEPTIONS PANEL
|
||||||
|
|
||||||
|
chrome.tabs.query({ active: true, currentWindow: true }, ([tab]) => {
|
||||||
|
|
||||||
|
/*
|
||||||
|
console.log(tab);
|
||||||
|
const opt = document.createElement('option');
|
||||||
|
opt.text =
|
||||||
|
opt.selected = true;
|
||||||
|
opt.style.backgroundColor = 'green !important';
|
||||||
|
opt.style.background = 'green !important';
|
||||||
|
|
||||||
|
const sl = document.getElementById('exceptions-select');
|
||||||
|
sl.insertBefore( opt, sl.firstChild );
|
||||||
|
*/
|
||||||
|
|
||||||
|
document.getElementById('except-editor').value = new URL(tab.url).hostname;
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
// PAC MODS PANEL
|
// PAC MODS PANEL
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -222,6 +252,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
const _firstChild = modPanel.firstChild;
|
const _firstChild = modPanel.firstChild;
|
||||||
const keyToLi = {};
|
const keyToLi = {};
|
||||||
const customProxyStringKey = 'customProxyStringRaw';
|
const customProxyStringKey = 'customProxyStringRaw';
|
||||||
|
const uiRaw = 'ui-proxy-string-raw';
|
||||||
pacKitchen.getConfigs().forEach( (conf) => {
|
pacKitchen.getConfigs().forEach( (conf) => {
|
||||||
|
|
||||||
const key = conf.key;
|
const key = conf.key;
|
||||||
|
@ -229,16 +260,12 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
const li = document.createElement('li');
|
const li = document.createElement('li');
|
||||||
li.className = 'info-row';
|
li.className = 'info-row';
|
||||||
keyToLi[key] = li;
|
keyToLi[key] = li;
|
||||||
console.log(key, conf.value);
|
|
||||||
li.innerHTML = `
|
li.innerHTML = `
|
||||||
<input type="checkbox" id="${iddy}" ${ conf.value ? 'checked' : '' }/>
|
<input type="checkbox" id="${iddy}" ${ conf.value ? 'checked' : '' }/>
|
||||||
<label for="${iddy}"> ${ conf.label }</label>`;
|
<label for="${iddy}"> ${ conf.label }</label>`;
|
||||||
|
|
||||||
if (key !== customProxyStringKey) {
|
if (key !== customProxyStringKey) {
|
||||||
li.innerHTML += `<div class="desc">
|
li.innerHTML += infoSign(conf.desc);
|
||||||
<span class="info-sign">🛈</span>
|
|
||||||
<div class="tooltip">${conf.desc}</div>
|
|
||||||
</div>`;
|
|
||||||
} else {
|
} else {
|
||||||
li.innerHTML += `<a href="${conf.url}" class="info-sign info-url">🛈</a><br/>
|
li.innerHTML += `<a href="${conf.url}" class="info-sign info-url">🛈</a><br/>
|
||||||
<textarea
|
<textarea
|
||||||
|
@ -246,7 +273,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
|
||||||
placeholder="SOCKS5 localhost:9050; # TOR Expert
|
placeholder="SOCKS5 localhost:9050; # TOR Expert
|
||||||
SOCKS5 localhost:9150; # TOR Browser
|
SOCKS5 localhost:9150; # TOR Browser
|
||||||
HTTPS foobar.com:3143;
|
HTTPS foobar.com:3143;
|
||||||
HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</textarea>`;
|
||||||
li.querySelector('textarea').onkeyup = function() {
|
li.querySelector('textarea').onkeyup = function() {
|
||||||
|
|
||||||
this.dispatchEvent(new Event('change', { 'bubbles': true }));
|
this.dispatchEvent(new Event('change', { 'bubbles': true }));
|
||||||
|
@ -270,12 +297,28 @@ HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
||||||
return configs;
|
return configs;
|
||||||
|
|
||||||
}, {});
|
}, {});
|
||||||
|
const taVal = keyToLi[customProxyStringKey].querySelector('textarea').value;
|
||||||
if (configs[customProxyStringKey]) {
|
if (configs[customProxyStringKey]) {
|
||||||
configs[customProxyStringKey] = keyToLi[customProxyStringKey].querySelector('textarea').value;
|
const ifValid = taVal
|
||||||
|
.replace(/#.*$/mg)
|
||||||
|
.split(/\s*[;\n\r]+\s*/g)
|
||||||
|
.every(
|
||||||
|
(str) =>
|
||||||
|
/^(?:DIRECT|(?:(?:HTTPS?|PROXY|SOCKS(?:4|5))\s+\S+))$/g
|
||||||
|
.test(str)
|
||||||
|
)
|
||||||
|
if (!ifValid) {
|
||||||
|
return showErrors(new TypeError(
|
||||||
|
'Неверный формат своих прокси. Свертесь с <a href="https://rebrand.ly/ac-own-proxy" data-in-bg="true">документацией</a>.'
|
||||||
|
))
|
||||||
|
}
|
||||||
|
configs[customProxyStringKey] = taVal;
|
||||||
|
} else {
|
||||||
|
localStorage.setItem(uiRaw, taVal);
|
||||||
}
|
}
|
||||||
conduct(
|
conduct(
|
||||||
'Применяем настройки...',
|
'Применяем настройки...',
|
||||||
(cb) => pacKitchen.keepCookedNow(configs, cb),
|
(cb) => pacKitchen.keepCookedNowAsync(configs, cb),
|
||||||
'Настройки применены.',
|
'Настройки применены.',
|
||||||
() => { document.getElementById('apply-mods').disabled = true; }
|
() => { document.getElementById('apply-mods').disabled = true; }
|
||||||
);
|
);
|
||||||
|
@ -284,7 +327,12 @@ HTTPS 11.22.33.44:8080;">${conf.value || ''}</textarea>`;
|
||||||
|
|
||||||
document.getElementById('reset-mods').onclick = () => {
|
document.getElementById('reset-mods').onclick = () => {
|
||||||
|
|
||||||
|
const ifSure = backgroundPage.confirm('Сбросить все модификации PAC-скрипта?');
|
||||||
|
if (!ifSure) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
pacKitchen.resetToDefaultsVoid();
|
pacKitchen.resetToDefaultsVoid();
|
||||||
|
backgroundPage.apis.ipToHost.resetToDefaultsVoid();
|
||||||
window.close();
|
window.close();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user