Add exceptions w/o working UI yet

This commit is contained in:
Ilya Ig. Petrov 2017-01-26 10:11:09 +00:00
parent ff5c51af11
commit 2dacd14f57
5 changed files with 468 additions and 206 deletions

View File

@ -18,7 +18,7 @@
const _match = function _match(ipRe, str) {
let m = (str.match(ipRe) || []).filter( (c) => c );
const m = (str.match(ipRe) || []).filter( (c) => c );
const port = m.length > 1 ? m.pop() : false;
return { ifMatched: m.length, port: port };
@ -55,17 +55,19 @@
const privates = {};
const _createHostObj = function _addHostObj(hostStr) {
return (privates._strToHostObj[hostStr] = { host: hostStr });
}
const _getHostObj = function _getHostObj(hostStr) {
let hostObj = privates._strToHostObj[hostStr];
if (!hostObj) {
hostObj = privates._strToHostObj[hostStr] = { host: hostStr };
}
return hostObj;
return privates._strToHostObj[hostStr] || _createHostObj(hostStr);
};
const init = function init() {
const reinit = function reinit() {
// Defaults.
const _antizapret = {
@ -100,7 +102,7 @@
};
init();
reinit();
const getIpsFor = function getIpsFor(host, cb = mandatory()) {
@ -200,7 +202,15 @@
resetToDefaultsVoid() {
_state(ip2host, null);
init();
reinit();
},
_purgeIpsForVoid(hostStr) {
for(const ip of Object.keys(privates._ipToHostObj)) {
delete privates._ipToHostObj[ip];
}
},
@ -210,6 +220,7 @@
console.log('IPS', ips);
if (!err) {
this._purgeIpsForVoid(hostStr);
// Object may be shared, string can't.
const hostObj = _getHostObj(hostStr);
for(const ip of ips) {
@ -222,14 +233,9 @@
},
_replaceAllAsync(hostArr = mandatory(), cb) {
updateAllAsync(cb = mandatory()) {
if (typeof(hostArr) === 'function') {
cb = hostArr;
hostArr = Object.keys(privates._strToHostObj);
}
this.resetToDefaultsVoid();
const hostArr = Object.keys(privates._strToHostObj);
const promises = hostArr.map(
(hostStr) => new Promise( (resolve) => this._addAsync(hostStr, (...args) => resolve(args) ) )
@ -261,6 +267,22 @@
},
_replaceAllAsync(hostArr = mandatory(), cb) {
if (typeof(hostArr) === 'function') {
cb = hostArr;
hostArr = Object.keys(privates._strToHostObj);
}
this.resetToDefaultsVoid();
for(const hostStr of hostArr) {
_createHostObj(hostStr);
}
this.updateAllAsync(cb);
},
replaceAllAsync(addrArr, cb = mandatory()) {
console.log('Replacing...');
@ -282,12 +304,6 @@
},
updateAllAsync(cb = mandatory()) {
this._replaceAllAsync(cb);
},
get(ip) {
const tmp = privates._ipToHostObj[ip];

View File

@ -10,6 +10,7 @@
const kitchenState = window.utils.createStorage('pac-kitchen-');
const ifIncontinence = 'if-incontinence';
// Don't keep objects in defaults or at least freeze them!
const configs = {
ifProxyHttpsUrlsOnly: {
@ -24,23 +25,35 @@
desc: 'Шифровать соединение до прокси от провайдера. Провайдер всё же сможет видеть адреса (но не содержимое) проксируемых ресурсов из протокола DNS.',
index: 1,
},
ifProhibitDns: {
dflt: false,
label: 'запретить опредление по IP/DNS',
desc: 'Пытается запретить скрипту использовать DNS, без которого определение блокировки по IP работать не будет. Используйте, если вам кажется, что мы проксируем слишком много сайтов.',
index: 2,
},
ifUsePacScriptProxies: {
dflt: true,
label: 'использовать прокси PAC-скрипта',
desc: 'Использовать прокси от авторов PAC-скрипта.',
index: 2,
index: 3,
},
ifUseLocalTor: {
dflt: false,
label: 'использовать свой локальный TOR',
desc: 'Установите TOR на свой компьютер и используйте его как прокси. <a href="https://rebrand.ly/ac-tor">ВАЖНО</a>',
index: 3,
label: 'использовать СВОЙ локальный TOR',
desc: 'Установите <a href="https://ru.wikipedia.org/wiki/Tor">TOR</a> на свой компьютер и используйте его как прокси. <a href="https://rebrand.ly/ac-tor">ВАЖНО</a>',
index: 4,
},
exceptions: {
dflt: null,
label: 'учитывать исключения',
desc: 'Учитывать сайты, добавленные вручную. Только для своих прокси! Без своих прокси работать не будет.',
index: 5,
},
customProxyStringRaw: {
dflt: '',
label: 'использовать свои прокси',
label: 'использовать СВОИ прокси',
url: 'https://rebrand.ly/ac-own-proxy',
index: 4,
index: 6,
},
};
@ -59,21 +72,18 @@
const getCurrentConfigs = function getCurrentConfigs() {
const mods = kitchenState('mods');
if (!mods) {
return null;
}
return new PacModifiers(mods);
return new PacModifiers(mods || {});
};
const getOrderedConfigsForUser = function getOrderedConfigs() {
const pacMods = getCurrentConfigs() || {};
const pacMods = getCurrentConfigs();
return Object.keys(configs).reduce((arr, key) => {
const conf = configs[key]
arr[conf.index] = conf;
conf.value = (key in pacMods) ? pacMods[key] : conf.dflt;
conf.value = pacMods[key];
conf.key = key;
return arr;
@ -89,7 +99,8 @@
const ifAllDefaults =
Object.keys(defaults)
.every(
(prop) => !(prop in mods) || Boolean(defaults[prop]) === Boolean(mods[prop])
(prop) => !(prop in mods)
|| Boolean(defaults[prop]) === Boolean(mods[prop])
);
Object.assign(this, defaults, mods);
@ -121,13 +132,31 @@
this.filteredCustomsString = '';
}
if (this.exceptions) {
this.included = [];
this.excluded = [];
for(const host of Object.keys(this.exceptions)) {
if (this.exceptions[host]) {
this.included.push(host)
} else {
this.excluded.push(host);
}
}
if (this.included && !this.filteredCustomsString) {
throw new TypeError(
'Проксировать свои сайты можно только через свои прокси. Нет ни одного своего прокси, удовлетворяющего вашим требованиям!'
);
}
}
}
};
window.apis.pacKitchen = {
getConfigs: getOrderedConfigsForUser,
getPacMods: getCurrentConfigs,
getOrderedConfigs: getOrderedConfigsForUser,
cook(pacData, pacMods = mandatory()) {
@ -139,22 +168,42 @@
global.FindProxyForURL = function(url, host) {
${function() {
let res = '';
let res = pacMods.ifProhibitDns ? `
global.dnsResolve = function(host) { return null; };
` : '';
if (pacMods.ifProxyHttpsUrlsOnly) {
res = `
res += `
if (!url.startsWith("https")) {
return "DIRECT";
}
`;
}
if (pacMods.included && pacMods.included.length) {
res += `
if ( ${JSON.stringify(pacMods.included)}.some( (included) => host.endsWith(included) ) ) {
return "${pacMods.filteredCustomsString}; DIRECT";
}
`;
}
if (pacMods.excluded && pacMods.excluded.length) {
res += `
if ( ${JSON.stringify(pacMods.excluded)}.some( (excluded) => host.endsWith(excluded) ) ) {
return "DIRECT";
}
`;
}
if(
!pacMods.ifUseSecureProxiesOnly &&
!pacMods.filteredCustomsString &&
pacMods.ifUsePacScriptProxies
) {
return res + `
return originalFindProxyForURL(url, host);`;
}
return originalFindProxyForURL(url, host);
`;
}
return res + `
@ -296,9 +345,7 @@
return originalSet(details, cb);
}
const pacMods = getCurrentConfigs();
if (pacMods) {
pac.data = pacKitchen.cook( pac.data, pacMods );
}
originalSet({ value: details.value }, (/* No args. */) => {
kitchenState(ifIncontinence, null);

View File

@ -125,11 +125,11 @@
let previousUpdateTitleFinished = Promise.resolve();
const isProxiedAndInformed = function isProxiedAndInformed(requestDetails) {
const tryProxyAndInform = function tryProxyAndInform(requestDetails) {
const host = window.apis.ipToHost.get( requestDetails.ip );
if (!host) {
return false;
return;
}
const ifMainFrame = requestDetails.type === 'main_frame';
@ -144,27 +144,27 @@
)
);
return true;
};
const isInsideTabWithIp = function isInsideTabWithIp(requestDetails) {
return requestDetails.tabId !== -1 && requestDetails.ip;
};
chrome.webRequest.onResponseStarted.addListener(
(requestDetails) => isInsideTabWithIp(requestDetails)
&& isProxiedAndInformed(requestDetails),
{urls: ['<all_urls>']}
);
const onRequest = function onRequest(requestDetails) {
chrome.webRequest.onErrorOccurred.addListener(
(requestDetails) =>
isInsideTabWithIp(requestDetails)
&& isProxiedAndInformed(requestDetails),
const ifInsideTabWithIp = requestDetails.tabId !== -1 && requestDetails.ip;
if (ifInsideTabWithIp) {
tryProxyAndInform(requestDetails);
}
};
for(const eventName of ['onResponseStarted', 'onErrorOccurred']) {
chrome.webRequest[eventName].addListener(
onRequest,
{urls: ['<all_urls>']}
);
}
}

View File

@ -6,23 +6,25 @@
:root {
--ribbon-color: #4169e1; /* #1a6cc8 */
}
div, section {
body {
margin-left: 0;
margin-right: 0;
}
.main-padded {
margin: 0 17px;
}
div, section, header {
margin: 0;
padding: 0;
}
header {
margin: 0 0 0.4em;
.valign-parent {
display: flex;
align-items: center;
}
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;
section header h4 {
margin: 0 0 0 0.7em;
}
label {
@ -46,6 +48,14 @@
cursor: pointer;
}
hr {
border-width: 1px 0 0 0;
margin: 0 0 0.6em 0;
padding: 0;
}
/* COMMON */
.link-button, .link-button:visited {
color: #0000EE;
text-decoration: none;
@ -54,27 +64,30 @@
text-decoration: underline;
}
.checked-radio-panel {
visibility: hidden;
}
input:checked ~ .checked-radio-panel {
visibility: visible;
}
hr {
border-width: 1px 0 0 0;
margin: 0.6em 0;
padding: 0;
}
#none:checked + label {
color: red;
}
.if-not-controlled {
display: none;
color: red;
}
li.info-row {
/* PAC PROVIDER */
.update-button {
visibility: hidden;
}
input:checked ~ .update-button {
visibility: inherit;
}
#none:checked + label {
color: red;
}
/* INFO SIGNS */
.info-row {
display: table;
width: 100%;
position: relative;
}
.info-sign {
@ -84,11 +97,15 @@
margin-left: 0.1em;
}
.info-url {
float: right;
text-decoration: none;
line-height: initial;
vertical-align: bottom !important;
float: right;
text-align: right;
line-height: normal !important;
vertical-align: top !important;
}
/* Source: https://jsfiddle.net/greypants/zgCb7/ */
.desc {
display: table-cell;
@ -137,16 +154,76 @@
left: 75%;
width: calc(25% + 0.6em);
}
#custom-proxy-string-raw ~ textarea {
/* PAC MODS & EXCEPTIONS */
#mods-custom-proxy-string-raw ~ textarea {
width: 100%;
height: 7em;
margin-top: 0.3em;
font-size: 0.9em;
}
#custom-proxy-string-raw:not(:checked) ~ textarea {
#mods-custom-proxy-string-raw:not(:checked) ~ textarea {
display: none;
}
#this-yes:disabled + label {
color: grey;
text-decoration: line-through;
}
/* EXCEPTIONS */
#right-flexed-editor {
flex-grow: 99;
max-height: 100%;
display: flex;
flex-direction: column;
}
#right-flexed-editor > * {
width: 100%;
}
#except-editor {
border-radius: 0 !important;
border-bottom: 0;
max-height: 1.6em !important;
min-height: 1.6em !important;
flex-grow: 99;
}
#bottom-flexed-editor {
flex-grow: 99;
position: relative;
}
select#exceptions-select {
color: black;
background: transparent;
border-radius: 0;
box-shadow: none;
text-shadow: none;
padding: 0;
position: absolute;
top: 0;
bottom: 0;
left: 0;
width: 100%;
}
#exc-flex-container {
position: relative;
display: flex;
}
#exc-flex-container > * {
flex-grow: 1;
padding: 0;
margin: 0;
}
/* CONTROL RAW = BUTTON + LINK */
.control-row {
display: table;
width: 100%;
@ -160,107 +237,194 @@
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;
.main-nav {
display: flex;
flex-direction: column;
counter-reset: line;
}
#flex-container {
position: relative;
padding: 0;
margin: 0;
display: flex;
/* ACCORDION */
.accordion-radio {
display: none;
}
#flex-container > * {
flex-grow: 1;
.accordion-title {
position: relative;
height: 1.8em;
border-bottom: 1px solid #558abb;
}
.accordion-title label:before {
content: counter(line) '.';
counter-increment: line;
padding-left: 0.2em;
}
.accordion-title label {
background-color: transparent;
color: black;
padding-left: 5px;
}
.accordion-radio:checked + section {
order: 0;
}
#status {
padding: 0 0.3em 1em;
}
.status {
order: 2;
/*border-left: 5px solid DodgerBlue;*/
margin-bottom: 0.5em;
}
.accordion-radio:checked + section .accordion-title {
margin-top: 0.3em; /* Short gap. */
}
.accordion-radio:checked + section .accordion-title label,
.accordion-title:hover label
{
background-color: DodgerBlue;
color: white;
padding-left: 0;
border-left: 5px solid #1048ac;
}
.accordion-title label {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
.accordion-radio:not(:checked) + section .accordion-content {
/* Hide, but preclude width resizes. */
height: 0px !important;
line-height: 0px !important;
padding: 0;
margin: 0;
display: block;
visibility: hidden;
transform:scaleY(0.5);
}
.accordion-radio:not(:checked) + section .accordion-content * {
margin-top: 0 !important;
margin-bottom: 0 !important;
}
.accordion-radio + section .accordion-content .acc-padded {
margin: 0.6em 0 1em 0.5em;
}
</style>
</head>
<body>
<span class="if-not-controlled">
<span id="which-extension"></span>
<section class="if-not-controlled">
<div id="which-extension" class="main-padded" style="margin-bottom: 1em"></div>
<hr style="border-color: red; border-style: solid;"/>
</span>
</section>
<nav class="main-padded main-nav">
<input type="radio" name="accordion" id="acc-pac-provider" class="accordion-radio" checked/>
<section>
<header>PAC-скрипт</header>
<header class='accordion-title'>
<label for="acc-pac-provider" class="valign-parent"><h4>PAC-скрипт</h4></label>
</header>
<div class="accordion-content">
<div class="acc-padded">
<ul id="list-of-providers">
<li><input type="radio" name="pacProvider" id="none" checked> <label for="none">Отключить</label></li>
</ul>
<div style="white-space: nowrap">
Обновлялись: <span class="update-date">...</span>
</div>
</div>
</div>
</section>
<section id="status" style="will-change: contents">Загрузка...</section>
<hr/>
<div id="exceptions">
<header>Проксировать этот сайт?</header>
<div id="flex-container">
<input type="radio" name="accordion" id="acc-exceptions" class="accordion-radio"/>
<section id="exceptions">
<header class='accordion-title'>
<label for="acc-exceptions" class="valign-parent"><h4>Проксировать этот сайт?</h4></label>
</header>
<div class="accordion-content">
<div class="acc-padded" id="exc-flex-container">
<ul style="padding-right: 1em">
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site"/> <label for="this-auto">🔄&#xFE0E; авто</label></li>
<li><input id="this-yes" type="radio" name="if-proxy-this-site"/> <label for="this-yes">&nbsp;да</label></li>
<li><input id="this-no" type="radio" name="if-proxy-this-site"/> <label for="this-no">&nbsp;нет</label></li>
<li><a href>Весь список</a></li>
</ul>
<div id="flex-right">
<div id="right-flexed-editor">
<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 id="bottom-flexed-editor">
&nbsp;
<select multiple id="exceptions-select"></select>
</div>
</div>
</div>
<hr/>
</div>
</section>
<input type="radio" name="accordion" id="acc-pac-mods" class="accordion-radio"/>
<section>
<header class='accordion-title'>
<label for="acc-pac-mods" class="valign-parent"><h4>Модификаторы PAC-скрипта</h4></label>
</header>
<div class="accordion-content">
<div class="acc-padded">
<ul id="pac-mods">
<li class="control-row">
<input type="button" value="Применить" id="apply-mods" disabled/>
<a href id="reset-mods" class="link-button">К изначальным!</a>
</li>
</ul>
<hr/>
<div id="configs-panel">
<header>Я ❤️ уведомления:</header>
</div>
</div>
</section>
<input type="radio" name="accordion" id="acc-notifications" class="accordion-radio"/>
<section id="configs-panel">
<header class='accordion-title'>
<label for="acc-notifications" class="valign-parent"><h4>Уведомления</h4></label>
</header>
<div class="accordion-content">
<div class="acc-padded">
<ul id="list-of-handlers"></ul>
</div>
</div>
</section>
</nav>
<hr/>
<div class="main-padded">
<div id="status" style="will-change: contents">Загрузка...</div>
<footer class="control-row">
<input type="button" value="Готово" class="close-button">
<a href="../troubleshoot/index.html" class="link-button">
Проблемы?
</a>
</footer>
</div>
<script src="./index.js"></script>
<script src="../lib/keep-links-clickable.js"></script>
</body>

View File

@ -173,7 +173,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
li.innerHTML = `
<input type="radio" name="pacProvider" id="${providerKey}">
<label for="${providerKey}"> ${provider.label}</label>
&nbsp;<a href class="link-button checked-radio-panel"
&nbsp;<a href class="link-button update-button"
id="update-${providerKey}">[обновить]</a> ` +
infoSign(provider.desc);
li.querySelector('.link-button').onclick =
@ -224,39 +224,74 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
// EXCEPTIONS PANEL
{
const pacKitchen = backgroundPage.apis.pacKitchen;
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
{
const pacKitchen = backgroundPage.apis.pacKitchen;
const pacMods = pacKitchen.getPacMods();
const exc = pacMods.exceptions || {};
const addOption = function addOption(host) {
const opt = document.createElement('option');
opt.text = host;
document.getElementById('exceptions-select').add(opt);
}
for(const host of Object.keys(exc).sort()) {
addOption(host);
}
document.getElementById('this-yes').onclick = function() {
const pacMods = pacKitchen.getPacMods();
if (!pacMods.filteredCustomsString) {
showErrors( new TypeError(
'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси (см.«Модификаторы»).'
));
return false;
}
const host = document.getElementById('except-editor').value;
const ValidHostnameRegex = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
if(!ValidHostnameRegex.test(host)) {
showErrors(new TypeError('Должно быть только доменное имя, без протокола и пути. Попробуйте ещё раз.'));
return false;
}
pacMods.exceptions = pacMods.exceptions || {};
pacMods.exceptions[host] = true;
pacKitchen.keepCookedNowAsync(
pacMods,
(err) => err
? showErrors(err)
: addOption(host)
);
}
}
// PAC MODS PANEL
const modPanel = document.getElementById('pac-mods');
const _firstChild = modPanel.firstChild;
const keyToLi = {};
const customProxyStringKey = 'customProxyStringRaw';
const uiRaw = 'ui-proxy-string-raw';
pacKitchen.getConfigs().forEach( (conf) => {
for(const conf of pacKitchen.getOrderedConfigs()) {
const key = conf.key;
const iddy = conf.key.replace(/([A-Z])/g, (_, p) => '-' + p.toLowerCase());
const iddy = 'mods-' + conf.key.replace(/([A-Z])/g, (_, p) => '-' + p.toLowerCase());
const li = document.createElement('li');
li.className = 'info-row';
keyToLi[key] = li;
@ -283,7 +318,7 @@ HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</tex
modPanel.insertBefore( li, _firstChild );
});
};
document.getElementById('apply-mods').onclick = () => {
const configs = Object.keys(keyToLi).reduce( (configs, key) => {
@ -298,7 +333,7 @@ HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</tex
}, {});
const taVal = keyToLi[customProxyStringKey].querySelector('textarea').value;
if (configs[customProxyStringKey]) {
if (configs[customProxyStringKey] !== false) {
const ifValid = taVal
.replace(/#.*$/mg)
.split(/\s*[;\n\r]+\s*/g)
@ -329,7 +364,7 @@ HTTPS 11.22.33.44:8080;">${conf.value || localStorage.getItem(uiRaw) || ''}</tex
const ifSure = backgroundPage.confirm('Сбросить все модификации PAC-скрипта?');
if (!ifSure) {
return;
return false;
}
pacKitchen.resetToDefaultsVoid();
backgroundPage.apis.ipToHost.resetToDefaultsVoid();