mirror of
https://github.com/anticensority/runet-censorship-bypass.git
synced 2024-11-24 02:13:43 +03:00
Handle TUNN CONN FAILED, sanitize optioins page url params, fix #50
This commit is contained in:
parent
1eb005f085
commit
719d6eac9c
File diff suppressed because it is too large
Load Diff
|
@ -17,12 +17,15 @@ export default function getApp(theState) {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
||||||
super(props);
|
super(props);
|
||||||
const hash = window.location.hash.substr(1);
|
|
||||||
const hashParams = new URLSearchParams(hash);
|
const sanitizedUrl = theState.flags.ifOpenedUnsafely ? { hash: '', search: '' } : window.location;
|
||||||
|
const hashParams = new URLSearchParams(sanitizedUrl.hash.substr(1));
|
||||||
|
const searchParams = new URLSearchParams(sanitizedUrl.search.substr(1));
|
||||||
this.state = {
|
this.state = {
|
||||||
status: 'Загрузка...',
|
status: 'Загрузка...',
|
||||||
ifInputsDisabled: false,
|
ifInputsDisabled: false,
|
||||||
hashParams: hashParams,
|
hashParams,
|
||||||
|
searchParams,
|
||||||
};
|
};
|
||||||
|
|
||||||
this.setStatusTo = this.setStatusTo.bind(this);
|
this.setStatusTo = this.setStatusTo.bind(this);
|
||||||
|
@ -62,9 +65,9 @@ export default function getApp(theState) {
|
||||||
const uiComEtag = 'ui-last-comments-etag';
|
const uiComEtag = 'ui-last-comments-etag';
|
||||||
const uiLastNewsArr = 'ui-last-news-arr';
|
const uiLastNewsArr = 'ui-last-news-arr';
|
||||||
|
|
||||||
const statusFromHash = this.state.hashParams.get('status');
|
const statusFromUrl = this.state.searchParams.get('status');
|
||||||
if (statusFromHash) {
|
if (statusFromUrl) {
|
||||||
return this.setStatusTo(statusFromHash);
|
return this.setStatusTo(statusFromUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
const comDate = localStorage[uiComDate];
|
const comDate = localStorage[uiComDate];
|
||||||
|
|
|
@ -17,24 +17,22 @@ export default function getFooter() {
|
||||||
|
|
||||||
`;
|
`;
|
||||||
|
|
||||||
return function (props) {
|
return (props) => (
|
||||||
|
<div class="horPadded">
|
||||||
|
<section class={scopedCss.statusRow}>
|
||||||
|
<div class={scopedCss.status} style="will-change: contents">
|
||||||
|
{typeof(props.status) === 'string' ? <div dangerouslySetInnerHTML={{ __html: props.status }}></div> : props.status}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
return (
|
<footer class={scopedCss.controlRow + ' horFlex nowrap'}>
|
||||||
<div class="horPadded">
|
<input type="button" value={chrome.i18n.getMessage('Finish')} disabled={props.ifInputsDisabled} onClick={() => window.close()} />
|
||||||
<section class={scopedCss.statusRow}>
|
<a href="https://rebrand.ly/ac-donate">{chrome.i18n.getMessage('Donate')}</a>
|
||||||
<div clss={scopedCss.status} style="will-change: contents">{props.status}</div>
|
<a data-in-bg="false" href="../troubleshoot/index.html">
|
||||||
</section>
|
{chrome.i18n.getMessage('ProblemsQ')}
|
||||||
|
</a>
|
||||||
<footer class={scopedCss.controlRow + ' horFlex nowrap'}>
|
</footer>
|
||||||
<input type="button" value={chrome.i18n.getMessage('Finish')} disabled={props.ifInputsDisabled} onClick={() => window.close()} />
|
</div>
|
||||||
<a href="https://rebrand.ly/ac-donate">{chrome.i18n.getMessage('Donate')}</a>
|
);
|
||||||
<a data-in-bg="false" href="../troubleshoot/index.html">
|
|
||||||
{chrome.i18n.getMessage('ProblemsQ')}
|
|
||||||
</a>
|
|
||||||
</footer>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,13 @@ import getApp from './components/App';
|
||||||
chrome.runtime.getBackgroundPage( (bgWindow) =>
|
chrome.runtime.getBackgroundPage( (bgWindow) =>
|
||||||
bgWindow.apis.errorHandlers.installListenersOn(
|
bgWindow.apis.errorHandlers.installListenersOn(
|
||||||
window, 'PUP', async() => {
|
window, 'PUP', async() => {
|
||||||
|
/*
|
||||||
|
`Extension context invalidated` error is thrown if `window.closed` is true and call to
|
||||||
|
`window.chrome.i18n` or other `window.chrome` api happens. Use bgWindow.chrome instead.
|
||||||
|
Use winChrome for tab-related calls like winChrome.tabs.getCurrent.
|
||||||
|
*/
|
||||||
|
window.winChrome = window.chrome;
|
||||||
|
window.chrome = bgWindow.chrome;
|
||||||
let theState;
|
let theState;
|
||||||
{
|
{
|
||||||
const apis = bgWindow.apis;
|
const apis = bgWindow.apis;
|
||||||
|
@ -29,15 +35,23 @@ chrome.runtime.getBackgroundPage( (bgWindow) =>
|
||||||
// IF INSIDE OPTIONS TAB
|
// IF INSIDE OPTIONS TAB
|
||||||
|
|
||||||
const currentTab = await new Promise(
|
const currentTab = await new Promise(
|
||||||
(resolve) => chrome.tabs.query(
|
(resolve) => winChrome.tabs.query(
|
||||||
{active: true, currentWindow: true},
|
{active: true, currentWindow: true},
|
||||||
([tab]) => resolve(tab)
|
([tab]) => resolve(tab),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
theState.flags.ifInsideOptionsPage = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=') || currentTab.url.startsWith('about:addons');
|
theState.flags.ifInsideOptionsPage = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=') || currentTab.url.startsWith('about:addons');
|
||||||
theState.currentTab = currentTab;
|
theState.currentTab = currentTab;
|
||||||
|
|
||||||
|
// If opened not via popup and not via options modal.
|
||||||
|
// E.g., if opened via copy-pasting an URL into the address bar from somewhere.
|
||||||
|
// If browser is not Chrome (Opera, e.g.) then options page may be opened in a separate tab
|
||||||
|
// and then you will get a false positive.
|
||||||
|
theState.flags.ifOpenedUnsafely = Boolean(await new Promise(
|
||||||
|
(resolve) => winChrome.tabs.getCurrent(resolve),
|
||||||
|
));
|
||||||
|
|
||||||
// STATE DEFINED, COMPOSE.
|
// STATE DEFINED, COMPOSE.
|
||||||
|
|
||||||
appendGlobalCss(document, theState);
|
appendGlobalCss(document, theState);
|
||||||
|
|
|
@ -2,7 +2,88 @@
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
||||||
chrome.webNavigation.onErrorOccurred.addListener((details) => {
|
const proxySideErrors = [
|
||||||
|
'net::ERR_TUNNEL_CONNECTION_FAILED',
|
||||||
|
];
|
||||||
|
|
||||||
|
const urlToA = (url) => new URL(url).host.link(
|
||||||
|
encodeURIComponent(url),
|
||||||
|
);
|
||||||
|
|
||||||
|
const isProxyErrorHandledAsync = async (details) => {
|
||||||
|
|
||||||
|
if (!proxySideErrors.includes(details.error) || details.type === 'main_frame') {
|
||||||
|
// Main frame websocket errors are followed by webnavigation errors
|
||||||
|
// which chrome-internals code resets the state of the popup.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let fromPageHref = '';
|
||||||
|
let fromPageHtml = '';
|
||||||
|
let youMayReportHtml = '';
|
||||||
|
const initiator = details.initiator !== 'null' && details.initiator;
|
||||||
|
try {
|
||||||
|
if (initiator) {
|
||||||
|
fromPageHref = new URL(initiator).href; // Sanitize: only urls, not other stuff.
|
||||||
|
fromPageHtml = ` со страницы ${urlToA(fromPageHref)}`;
|
||||||
|
}
|
||||||
|
youMayReportHtml = ` Вы можете <b>${'сообщить об ошибке'.link(
|
||||||
|
encodeURIComponent(
|
||||||
|
'/pages/report-proxy-error/index.html?' +
|
||||||
|
new URLSearchParams({
|
||||||
|
fromPageHref,
|
||||||
|
requestFailedTo: new URL(details.url).href,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)}</b> администратору прокси.`;
|
||||||
|
} catch(e) {
|
||||||
|
/* Suppress for malformed urls. */
|
||||||
|
console.log('Error handling malformed URLs:', details);
|
||||||
|
}
|
||||||
|
|
||||||
|
const tabId = details.tabId;
|
||||||
|
const popupPrefix = chrome.runtime.getURL(`/pages/options/index.html?status=<span style="color: red">🔥 Прокси-сервер отказался обслуживать запрос к `);
|
||||||
|
const oldPopup = await new Promise((resolve) => chrome.browserAction.getPopup({ tabId }, resolve));
|
||||||
|
if (decodeURIComponent(oldPopup).startsWith(popupPrefix)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const popup = `${popupPrefix}${urlToA(details.url)}${fromPageHtml}</span>. Это могло быть намеренно или по ошибке.${youMayReportHtml}#tab=exceptions`;
|
||||||
|
|
||||||
|
chrome.browserAction.setPopup({
|
||||||
|
tabId,
|
||||||
|
popup,
|
||||||
|
});
|
||||||
|
|
||||||
|
chrome.browserAction.setBadgeBackgroundColor({
|
||||||
|
tabId,
|
||||||
|
color: 'red',
|
||||||
|
});
|
||||||
|
chrome.browserAction.setBadgeText({
|
||||||
|
tabId,
|
||||||
|
text: '❗',
|
||||||
|
});
|
||||||
|
let limit = 5;
|
||||||
|
let ifOnTurn = true;
|
||||||
|
let ifError = false;
|
||||||
|
const flip = () => {
|
||||||
|
|
||||||
|
if (!ifOnTurn && !--limit || ifError) {
|
||||||
|
clearInterval(timer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chrome.browserAction.setBadgeText({
|
||||||
|
tabId,
|
||||||
|
text: ifOnTurn ? '❗' : '',
|
||||||
|
}, () => {
|
||||||
|
ifError = chrome.runtime.lastError;
|
||||||
|
});
|
||||||
|
ifOnTurn = !ifOnTurn;
|
||||||
|
};
|
||||||
|
flip();
|
||||||
|
const timer = setInterval(flip, 500);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
chrome.webNavigation.onErrorOccurred.addListener(async (details) => {
|
||||||
|
|
||||||
const tabId = details.tabId;
|
const tabId = details.tabId;
|
||||||
if ( !(details.frameId === 0 && tabId >= 0) ||
|
if ( !(details.frameId === 0 && tabId >= 0) ||
|
||||||
|
@ -12,13 +93,16 @@
|
||||||
].includes(details.error) ) {
|
].includes(details.error) ) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (await isProxyErrorHandledAsync(details)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
chrome.browserAction.setPopup({
|
chrome.browserAction.setPopup({
|
||||||
tabId,
|
tabId,
|
||||||
popup: './pages/options/index.html#tab=exceptions&status=Правый клик по иконке — меню инструментов!',
|
popup: './pages/options/index.html?status=Правый клик по иконке — меню инструментов!#tab=exceptions',
|
||||||
});
|
});
|
||||||
|
|
||||||
window.chrome.browserAction.setBadgeBackgroundColor({
|
chrome.browserAction.setBadgeBackgroundColor({
|
||||||
tabId,
|
tabId,
|
||||||
color: '#4285f4',
|
color: '#4285f4',
|
||||||
});
|
});
|
||||||
|
@ -29,4 +113,9 @@
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
chrome.webRequest.onErrorOccurred.addListener(
|
||||||
|
isProxyErrorHandledAsync,
|
||||||
|
{urls: ['<all_urls>']},
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
const setRedBadge = (opts) => {
|
const setRedBadge = (opts) => {
|
||||||
|
|
||||||
window.chrome.browserAction.setBadgeBackgroundColor({
|
chrome.browserAction.setBadgeBackgroundColor({
|
||||||
color: '#db4b2f',
|
color: '#db4b2f',
|
||||||
});
|
});
|
||||||
chrome.browserAction.setBadgeText(opts);
|
chrome.browserAction.setBadgeText(opts);
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Сообщить об ошибке прокси-сервера</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<style>
|
||||||
|
:root {
|
||||||
|
font-size: 1.3em;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<h1>Сообщить об ошибке прокси-сервера</h1>
|
||||||
|
Перешлите администратору вашего прокси следующее:
|
||||||
|
<fieldset>
|
||||||
|
<pre id="errorInfo"></pre>
|
||||||
|
</fieldset>
|
||||||
|
Вот известные нам электронные адреса для популярных прокси-серверов (кликните по email для открытия шаблона письма):
|
||||||
|
<ol>
|
||||||
|
<li>
|
||||||
|
Только если вы используете <a
|
||||||
|
href="https://antizapret.prostovpn.org">Антизапрет</a>:
|
||||||
|
<a
|
||||||
|
href="mailto:antizapret@prostovpn.org">antizapret@prostovpn.org</a>.
|
||||||
|
</li>
|
||||||
|
</ol>
|
||||||
|
<script src="./index.js"></script>
|
||||||
|
<script src="../lib/keep-links-clickable.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,39 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
chrome.runtime.getBackgroundPage( (bgWindow) =>
|
||||||
|
bgWindow.apis.errorHandlers.installListenersOn(
|
||||||
|
window, 'PRERR', () => {
|
||||||
|
|
||||||
|
const params = new URLSearchParams(location.search.substr(1));
|
||||||
|
const requestFailedTo = params.get('requestFailedTo');
|
||||||
|
const fromPageHref = params.get('fromPageHref') || requestFailedTo;
|
||||||
|
|
||||||
|
const acr = bgWindow.apis.antiCensorRu;
|
||||||
|
const pacKey = acr.getCurrentPacProviderKey();
|
||||||
|
const pacModTime = acr.getLastModifiedForKey(pacKey);
|
||||||
|
|
||||||
|
const errorReport = `
|
||||||
|
Your proxy blocked the following request:
|
||||||
|
* Request was from page: ${fromPageHref}
|
||||||
|
* To address: ${requestFailedTo}
|
||||||
|
* Used PAC-script: ${pacKey}
|
||||||
|
* Its Last-Modified HTTP-header: ${pacModTime}
|
||||||
|
I think it's a mistake! Could you, please, take action to fix it.
|
||||||
|
Thank you!
|
||||||
|
|
||||||
|
Ваш прокси-сервер заблокировал следующий запрос:
|
||||||
|
* Запрос был со страницы: ${fromPageHref}
|
||||||
|
* Адрес запроса: ${requestFailedTo}
|
||||||
|
* Мой PAC-скрипт: ${pacKey}
|
||||||
|
* Его HTTP-заголовок Last-Modified: ${pacModTime}
|
||||||
|
Я думаю, это произошло по ошибке! Пожалуйста, примите действия для её исправления.
|
||||||
|
Спасибо!
|
||||||
|
`.trim();
|
||||||
|
errorInfo.innerText = errorReport;
|
||||||
|
document.querySelectorAll('a[href^="mailto:"]').forEach((a) => {
|
||||||
|
|
||||||
|
a.href = `${a.href}?subject=${encodeURIComponent(new URL(requestFailedTo).hostname)} TUNNEL_CONNECTION_FAILED&body=${encodeURIComponent(errorReport)}`;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
Loading…
Reference in New Issue
Block a user