Compare commits

..

No commits in common. "development" and "1.5" have entirely different histories.

58 changed files with 7914 additions and 25134 deletions

12
.github/FUNDING.yml vendored
View File

@ -1,12 +0,0 @@
# These are supported funding model platforms
github: # ilyaigpetrov
patreon: # Replace with a single Patreon username
open_collective:
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: https://github.com/anticensority/runet-censorship-bypass/wiki/Поддержать

0
.gitignore vendored Normal file → Executable file
View File

78
README.md Normal file → Executable file
View File

@ -1,71 +1,35 @@
If you __unstar__, please, [leave us a note](https://github.com/anticensority/runet-censorship-bypass/issues) why you do so. # [Maintainer Needed! Нужен разработчик!](https://github.com/anticensorship-russia/chromium-extension/issues/2)
[d1]: https://img.shields.io/badge/Поддержать-❤-green.svg Also, if you __unstar__, please, [leave us a note](https://github.com/anticensorship-russia/chromium-extension/issues) why you do so.
[d2]: https://github.com/anticensority/runet-censorship-bypass/wiki/Поддержать
[![Поддержать][d1]][d2] # Russian Anti-Censorship on PAC-Scripts
[![Backers on Open Collective](https://opencollective.com/anticensority/backers/badge.svg)](#backers)
[![Sponsors on Open Collective](https://opencollective.com/anticensority/sponsors/badge.svg)](#sponsors)
# Russian Anticensorship on PAC-Scripts This repo contains:
This repo contains an extension for Chromium and FireFox that helps to bypass censorship in Russia: [WebStore](https://chrome.google.com/webstore/detail/npgcnondjocldhldegnakemclmfkngch) 1. Chrome Extension to bypass censorship in Russia:
| [Sources](./extensions/chromium/runet-censorship-bypass). [WebStore](https://chrome.google.com/webstore/detail/npgcnondjocldhldegnakemclmfkngch)
This extension uses pac scripts, one of which (anticensority) is generated by this [pac-generator]. | [Sources](https://github.com/ilyaigpetrov/anti-censorship-russia/tree/master/extensions/chromium/minimalistic-pac-setter)
2. Proof of concept PAC-script generator based on https://github.com/zapret-info/z-i
3. ~~PAC-scripts performance analyses of scripts generated~~ (doesn't take parse time into account)
4. Based on the research of step 3 [the final PAC-generator][pac-generator] was written as a Google App Script in JavaScript which is triggered every two hours to generate and publish PAC-script on Google Drive (don't use direct URL without extension, please, URL will be periodically changed to counter abuse).
[pac-generator]: https://github.com/anticensority/pac-script-generator [pac-generator]: https://script.google.com/d/1RlqqfUmYNpEhekySfOqdzJ8L4eV1GsHYDjPD1DexxEW0RcGvuCSQlWa0/edit?usp=sharing
## Install / Установка
1. [Chrome Web Store](https://chrome.google.com/webstore/detail/обход-блокировок-рунета/npgcnondjocldhldegnakemclmfkngch)
2. [Chrome Web Store (MINI)](https://chrome.google.com/webstore/detail/обход-блокировок-рунета-м/gnknjnebjldmkpmlhjipalimhjofpgho)
3. [Microsoft Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/обход-блокировок-рунета/ajgpnodjpffiagcfmifildjpoaeiobfh)
4. [Microsoft Edge Add-ons (MINI)](https://microsoftedge.microsoft.com/addons/detail/обход-блокировок-рунета-м/cjppllmpmkpjfchbaoebeneghcbmlibj)
5. [FireFox Add-ons](https://addons.mozilla.org/ru/firefox/addon/обход-блокировок-рунета/).
6. Opera: сначала [установщик расширений из WebStore](https://addons.opera.com/ru/extensions/details/install-chrome-extensions/) (от команды Opera), затем см. пункты 1 и 2 выше.
7. Пакеты для автономной (offline) установки / Packages for offline installation: https://github.com/anticensority/runet-censorship-bypass/releases.
Из-за блокировок адресов Google расширение может не устанавливаться из WebStore. Подробности и способы установки см. https://github.com/anticensority/runet-censorship-bypass/wiki/Автономная-установка-расширения.
## Why I do This ## Why I do This
See [my arguments against censorship (ru)](https://github.com/anticensority/runet-censorship-bypass/wiki/Почему-мы-это-делаем%3F-Аргументы-против-цензуры). I believe __information mustn't be blocked based on political or other subjective views__.
Looking at how Russian government [distorts TV](https://therussianreader.wordpress.com/2015/11/22/russian-truckers-strike-dagestan/) and blocks [critics of Putin](https://www.reuters.com/article/us-russia-internet-idUSBREA2C21L20140313), My maxim is _"Your freedom ends when it starts to confine the freedom of others"_.
See [my other arguments against censorship (ru)](https://gist.github.com/ilyaigpetrov/9452b93ef3d7dd3d8cc2)
Looking at how Russian government [distorts TV](https://therussianreader.wordpress.com/2015/11/22/russian-truckers-strike-dagestan/) and blocks [critics of Putin](http://www.reuters.com/article/2014/03/13/us-russia-internet-idUSBREA2C21L20140313),
I decided to write an anti-censorship extension for Chromium before they strike me first. I decided to write an anti-censorship extension for Chromium before they strike me first.
## How it Works ## How it Works
0. PAC script is a JavaScript file, triggered on every URL request, which tells the browser which proxy to use if any for this particular URL. 0. PAC script is a JavaScript file, triggered on every URL request, which says browser which proxy to use if any for this particular URL.
1. The Chrome Extension sets the PAC-script in browser settings and keeps it synced with the PAC script on the server. It offers Antizapret (hosted on a dedicated server) or Anticensority (hosted on GitHub) built-in PAC scripts for the user choice. 1. The Chrome Extension sets PAC script in browser settings and keeps it synced with PAC script on the server (offering Antizapret (hosted on a dedicated server) or Anticensority (hosted on GitHub + CloudFlare)).
2. On every request the PAC script checks if the host is blocked or if its IP is blocked. 2. On every request PAC script checks if host is blocked or if its IP is blocked.
3. If an address is blocked, the PAC script returns the proxy server to the browser. The Antizapret PAC script uses its own proxy servers and the Anticensority PAC-script uses local Tor. 3. If address is blocked PAC script returns proxy server to the browser, both Antizapret and Anticensority use Antizapret proxy servers.
4. PAC scripts on servers are updated periodically from https://github.com/zapret-info/z-i. 4. PAC scripts on servers are updated periodically from https://github.com/zapret-info/z-i.
## Contributors
This project exists thanks to all the people who contribute.
<a href="https://github.com/anticensority/runet-censorship-bypass/graphs/contributors"><img src="https://opencollective.com/anticensority/contributors.svg?width=890&button=false?force" /></a>
## Backers
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/anticensority#backer)]
<a href="https://opencollective.com/anticensority#backers" target="_blank"><img src="https://opencollective.com/anticensority/backers.svg?width=890"></a>
## Sponsors
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. [[Become a sponsor](https://opencollective.com/anticensority#sponsor)]
<a href="https://opencollective.com/anticensority/sponsor/0/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/1/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/2/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/3/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/4/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/5/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/6/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/7/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/8/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/anticensority/sponsor/9/website" target="_blank"><img src="https://opencollective.com/anticensority/sponsor/9/avatar.svg"></a>

View File

@ -1,33 +1,30 @@
# Install # Dev
Tested on: Linting JS: `npm run lint`
NodeJS: v16.1.0. # О расширении
NPM: 7.11.2.
OS: Linux Mint 20 Xfce Edition.
``` Обход интернет-цензуры в России пока что не является преступлением.
npm install
cd src/extension-common/pages/options/
npm install
cd -
# For debugging: Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ
npm start к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным
# Use your build/extension-beta сайтам, случайно заблокированным в силу разных причин.
# For production: Проксирует только заблокированные сайты, оставляя нетронутыми все остальные.
npm start
# Use your build/extension-full or build/extension-mini
```
# For Reviewers Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org.
See ./src/extension-common/FOR_REVIEWERS.md. Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки.
Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку.
# Release Instructions Синяя лента кампания фонда EFF в защиту свобод слова, прессы и союзов.
1. `npm run release` Если расширение не работает: https://git.io/vgDDj
2. `vim src/templates-data.js` and bump version.
3. Commit bumped version. Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia
4. Merge development to production (usually after deployment and testing and many patches). Группа в G+: https://goo.gl/Lh0Cjh
История изменений: https://github.com/ilyaigpetrov/anti-censorship-russia/releases
# Дополнительно
Иконка синей ленты: http://www.iconsdb.com/icon-sets/cardboard-blue-icons/ribbon-12-icon.html

View File

@ -1,25 +0,0 @@
# О расширении
Обход интернет-цензуры в России пока что не является преступлением.
Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ
к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным
сайтам, случайно заблокированным в силу разных причин.
Проксирует только заблокированные сайты, оставляя нетронутыми все остальные.
Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org.
Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки.
Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку.
Синяя лента кампания фонда EFF в защиту свобод слова, прессы и союзов.
Если расширение не работает: https://git.io/vgDDj
Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia
История изменений: https://github.com/anticensority/runet-censorship-bypass/releases
# Дополнительно
Иконка синей ленты: http://www.iconsdb.com/icon-sets/cardboard-blue-icons/ribbon-12-icon.html

View File

@ -1 +1 @@
grep -r "$@" ./*.js ./src --exclude-dir=vendor --exclude-dir=node_modules --exclude-dir=dist grep -r $@ ./src --exclude-dir=vendor --exclude-dir=node_modules

View File

@ -3,7 +3,7 @@
const gulp = require('gulp'); const gulp = require('gulp');
const del = require('del'); const del = require('del');
const through = require('through2'); const through = require('through2');
const PluginError = require('plugin-error'); const PluginError = require('gulp-util').PluginError;
const changed = require('gulp-changed'); const changed = require('gulp-changed');
const PluginName = 'Template literals'; const PluginName = 'Template literals';
@ -29,7 +29,7 @@ const templatePlugin = (context) => through.obj(function(file, encoding, cb) {
}, { keys: [], values: [] }); }, { keys: [], values: [] });
try { try {
file.contents = Buffer.from( file.contents = new Buffer(
(new Function(...keys, 'return `' + String(file.contents) + '`;'))(...values) (new Function(...keys, 'return `' + String(file.contents) + '`;'))(...values)
); );
} catch(e) { } catch(e) {
@ -43,65 +43,81 @@ const templatePlugin = (context) => through.obj(function(file, encoding, cb) {
}); });
gulp.task('default', ['build']);
const clean = function(cb) { gulp.task('clean', function(cb) {
del.sync('./build'); //return del.sync('./build');
return cb(); return cb();
}; });
const contexts = require('./src/templates-data').contexts; const contexts = require('./src/templates-data').contexts;
const excFolder = (name) => [`!./src/**/${name}`, `!./src/**/${name}/**/*`]; const excFolder = (name) => [`!./src/**/${name}`, `!./src/**/${name}/**/*`];
const excluded = [ ...excFolder('test') , ...excFolder('node_modules'), ...excFolder('src') ]; const excluded = [ ...excFolder('test') , ...excFolder('node_modules'), ...excFolder('src') ];
const commonWoTests = ['./src/extension-common/**/*', ...excluded];
const miniDst = './build/extension-mini'; const miniDst = './build/extension-mini';
const fullDst = './build/extension-full'; const fullDst = './build/extension-full';
const betaDst = './build/extension-beta'; const betaDst = './build/extension-beta';
const firefoxDst = './build/extension-firefox';
const commonSrc = './src/extension-common/**/*';; gulp.task('_cp-common', ['clean'], function(cb) {
const miniSrc = './src/extension-mini/**/*';
const fullSrc = './src/extension-full/**/*';
const firefoxSrc = './src/extension-firefox/**/*';
const joinSrc = (...args) => [...args, ...excluded]; let fins = 0;
const intheend = () => {
if (++fins === 2) {
cb();
}
};
const copyMini = function(cb) { gulp.src(commonWoTests)
//.pipe(changed(miniDst))
.pipe(templatePlugin(contexts.mini))
.pipe(gulp.dest(miniDst))
.on('end', intheend);
gulp.src(joinSrc(commonSrc, miniSrc)) gulp.src(commonWoTests)
//.pipe(changed(fullDst))
.pipe(templatePlugin(contexts.full))
.pipe(gulp.dest(fullDst))
.on('end', intheend);
gulp.src(commonWoTests)
//.pipe(changed(fullDst))
.pipe(templatePlugin(contexts.beta))
.pipe(gulp.dest(betaDst))
.on('end', intheend);
});
gulp.task('_cp-mini', ['_cp-common'], function(cb) {
gulp.src(['./src/extension-mini/**/*', ...excluded])
//.pipe(changed(miniDst)) //.pipe(changed(miniDst))
.pipe(templatePlugin(contexts.mini)) .pipe(templatePlugin(contexts.mini))
.pipe(gulp.dest(miniDst)) .pipe(gulp.dest(miniDst))
.on('end', cb); .on('end', cb);
}; });
const copyFull = function(cb) { gulp.task('_cp-full', ['_cp-common'], function(cb) {
gulp.src(joinSrc(commonSrc, fullSrc)) gulp.src(['./src/extension-full/**/*', ...excluded])
//.pipe(changed(fullDst)) //.pipe(changed(fullDst))
.pipe(templatePlugin(contexts.full)) .pipe(templatePlugin(contexts.full))
.pipe(gulp.dest(fullDst)) .pipe(gulp.dest(fullDst))
.on('end', cb); .on('end', cb);
}; });
const copyBeta = function(cb) { gulp.task('_cp-beta', ['_cp-common'], function(cb) {
gulp.src(joinSrc(commonSrc, fullSrc)) gulp.src(['./src/extension-full/**/*', ...excluded])
//.pipe(changed(fullDst)) //.pipe(changed(fullDst))
.pipe(templatePlugin(contexts.beta)) .pipe(templatePlugin(contexts.beta))
.pipe(gulp.dest(betaDst)) .pipe(gulp.dest(betaDst))
.on('end', cb); .on('end', cb);
}; });
const buildAll = gulp.series(clean, gulp.parallel(copyMini, copyFull, copyBeta)); gulp.task('build:all', ['_cp-mini', '_cp-full', '_cp-beta']);
const buildBeta = copyBeta; gulp.task('build', ['_cp-full']);
module.exports = {
default: buildAll,
buildAll,
buildBeta,
};

File diff suppressed because it is too large Load Diff

View File

@ -3,29 +3,27 @@
"version": "0.0.19", "version": "0.0.19",
"description": "Development tools for chromium extension", "description": "Development tools for chromium extension",
"scripts": { "scripts": {
"postinstall": "node --use_strict -e \"const fs = require('fs'), path = 'node_modules/_project-root'; fs.unlink(path, ()=> fs.symlinkSync('..', path, 'dir'));\"",
"lint": "eslint ./src/**/*.js --ignore-pattern vendor", "lint": "eslint ./src/**/*.js --ignore-pattern vendor",
"gulp": "gulp", "gulp": "gulp",
"test": "mocha --recursive ./src/**/test/*", "test": "mocha --recursive ./src/**/test/*",
"subpages": "cd ./src/extension-common/pages/options/ && npm run build && cd -", "subpages": "cd ./src/extension-common/pages/options/ && npm run build && cd -",
"subpages:dev": "cd ./src/extension-common/pages/options/ && npm run build:dev:nocomp && cd -", "start": "npm run subpages && npm run gulp",
"start": "npm run release", "beta": "npm run subpages && npm run gulp build:all"
"release": "npm run subpages && npx gulp -- buildAll"
}, },
"author": "Ilya Ig. Petrov", "author": "Ilya Ig. Petrov",
"license": "GPLv3", "license": "GPLv3",
"devDependencies": { "devDependencies": {
"chai": "^4.3.0", "chai": "^3.5.0",
"eslint": "^7.19.0", "eslint": "^3.15.0",
"eslint-config-google": "^0.14.0", "eslint-config-google": "^0.7.1",
"gulp-changed": "^4.0.2", "gulp-changed": "^3.1.0",
"mocha": "^8.2.1", "mocha": "^3.3.0",
"sinon-chrome": "^3.0.1", "sinon-chrome": "^2.2.1"
"symlink-to": "^0.0.4"
}, },
"dependencies": { "dependencies": {
"del": "^6.0.0", "del": "^2.2.2",
"gulp": "^4.0.2", "gulp": "^3.9.1",
"plugin-error": "^1.0.1", "through2": "^2.0.3"
"through2": "^4.0.2"
} }
} }

View File

@ -1,7 +1,5 @@
'use strict'; 'use strict';
console.log('Extension started.');
{ {
const IF_DEBUG = true; const IF_DEBUG = true;
@ -41,18 +39,15 @@ console.log('Extension started.');
}, },
lastError: undefined,
checkChromeError() { checkChromeError() {
// Chrome API calls your cb in a context different from the point of API // Chrome API calls your cb in a context different from the point of API
// method invokation. // method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError || self.lastError; const err = chrome.runtime.lastError || chrome.extension.lastError;
if (!err) { if (!err) {
return; return;
} }
console.warn('API returned error:', err); console.warn('API returned error:', err);
delete self.lastError;
return new Error(err.message); // Add stack. return new Error(err.message); // Add stack.
}, },
@ -76,19 +71,6 @@ console.log('Extension started.');
}, },
getOrDie(cb = self.mandatory()) {
return self.chromified((err, ...args) => {
if (err) {
throw err;
}
cb(...args);
});
},
getProp(obj, path = self.mandatory()) { getProp(obj, path = self.mandatory()) {
const props = path.split('.'); const props = path.split('.');
@ -158,32 +140,6 @@ console.log('Extension started.');
}, },
promisedLocalStorage: {
get(key) {
return new Promise((resolve) => (
chrome.storage.local.get(
key,
window.utils.getOrDie((storage) => resolve(key ? storage[key] : storage)),
)
));
},
set(items) {
return new Promise((resolve) => (
chrome.storage.local.set(items, resolve)
));
},
remove(keys) {
return new Promise((resolve) => (
chrome.storage.local.remove(keys, resolve)
));
},
clear() {
return new Promise((resolve) => (
chrome.storage.local.clear(resolve)
));
},
},
/* /*
* Possible values for levelOfControl: * Possible values for levelOfControl:
* *
@ -212,7 +168,7 @@ console.log('Extension started.');
searchSettingsForUrl(niddle) { searchSettingsForUrl(niddle) {
return 'chrome://settings/?search=' + (chrome.i18n.getMessage(niddle) || niddle); return 'chrome://settings/search#' + (chrome.i18n.getMessage(niddle) || niddle);
}, },
@ -220,86 +176,19 @@ console.log('Extension started.');
return chrome.i18n.getMessage('noControl') + return chrome.i18n.getMessage('noControl') +
` <a href="${ this.searchSettingsForUrl('proxy') }"> ` <a href="${ this.searchSettingsForUrl('proxy') }">
${ chrome.i18n.getMessage('WhichQ') } ${ chrome.i18n.getMessage('which') }
</a>`; </a>`;
}, },
}, },
parseProxyScheme(proxyAsStringRaw) {
const proxyAsString = proxyAsStringRaw.trim();
const [type] = proxyAsString.split(/\s+/);
/*
if (!/^[a-zA-Z0-9]+$/.test(type)) {
throw new Error(`${type} is not a proxy type!`);
}
JS has no code blocks in RE, seems safe to omit this check.
*/
const typeRe = new RegExp(`^${type}\\s+`, 'g');
const crededAddr = proxyAsString.replace(typeRe, '');
let parts;
parts = crededAddr.split('@');
let [creds, addr] = [parts.slice(0, -1).join('@'), parts[parts.length - 1]];
const [hostname, port] = addr.split(':');
parts = creds.split(':')
const username = parts[0];
const password = parts.slice(1).join(':');
return {
type,
username,
password,
hostname,
port,
creds,
}
},
openAndFocus(url) {
chrome.tabs.create(
{url: url},
(tab) => chrome.windows.update(tab.windowId, {focused: true})
);
},
}; };
const max = 2**16;
const versionToArray = (v) => [ ...v.split('.'), 0, 0, 0].slice(0,4);
const versionToInt = (v) => versionToArray(v)
.reverse()
.reduce((acc, vv, i) => acc + parseInt(vv)*(max**i), 0);
const compareVersions = (a, b) => versionToInt(a) - versionToInt(b);
const ifFirefox = navigator.userAgent.toLowerCase().includes('firefox');
let give;
const promise = !ifFirefox ? Promise.resolve() : new Promise((resolve) => {
give = resolve;
});
window.apis = { window.apis = {
consent: {
promise,
give,
},
platform: {
ifFirefox,
},
version: { version: {
ifMini: false, ifMini: false,
build: chrome.runtime.getManifest().version.replace(/\d+\.\d+\./g, ''), build: chrome.runtime.getManifest().version.replace(/\d+\.\d+\./g, ''),
isLeq: (a, b) => compareVersions(a, b) <= 0,
}, },
}; };

View File

@ -8,9 +8,6 @@
const errorJsonReplacer = function errorJsonReplacer(key, value) { const errorJsonReplacer = function errorJsonReplacer(key, value) {
// fooWindow.ErrorEvent !== barWindow.ErrorEvent // fooWindow.ErrorEvent !== barWindow.ErrorEvent
if (value === window) {
return; // STUPID, because other window object may be passed.
}
if (!( value && value.constructor if (!( value && value.constructor
&& ['Error', 'Event'].some( && ['Error', 'Event'].some(
(suff) => value.constructor.name.endsWith(suff) (suff) => value.constructor.name.endsWith(suff)
@ -53,6 +50,15 @@
}; };
const openAndFocus = function openAndFocus(url) {
chrome.tabs.create(
{url: url},
(tab) => chrome.windows.update(tab.windowId, {focused: true})
);
};
const ifPrefix = 'if-on-'; const ifPrefix = 'if-on-';
const extName = chrome.runtime.getManifest().name; const extName = chrome.runtime.getManifest().name;
const extVersion = window.apis.version.build; const extVersion = window.apis.version.build;
@ -66,12 +72,10 @@
const errors = err ? {[type]: err} : this.idToError; const errors = err ? {[type]: err} : this.idToError;
const json = JSON.stringify(errors, errorJsonReplacer, 0); const json = JSON.stringify(errors, errorJsonReplacer, 0);
window.utils.openAndFocus( openAndFocus(
'https://anticensority.github.io/error/?json=' + encodeURIComponent(json) + 'http://rebrand.ly/ac-error/?json=' + encodeURIComponent(json) +
(type ? '&type=' + encodeURIComponent(type) : '') + (type ? '&type=' + encodeURIComponent(type) : '') +
'&version=' + chrome.runtime.getManifest().version + '&version=' + chrome.runtime.getManifest().version
'&useragent=' + encodeURIComponent(navigator.userAgent) +
'&platform=' + encodeURIComponent(navigator.platform),
); );
}, },
@ -110,7 +114,6 @@
isControllable(details) { isControllable(details) {
if (details) {
this.ifControllable = window.utils.areSettingsControllableFor(details); this.ifControllable = window.utils.areSettingsControllableFor(details);
if (this.ifControllable) { if (this.ifControllable) {
@ -126,7 +129,6 @@
path: './icons/default-grayscale-128.png', path: './icons/default-grayscale-128.png',
}); });
} }
}
return this.ifControllable; return this.ifControllable;
@ -134,9 +136,7 @@
isControlled(details) { isControlled(details) {
if (details) {
this.isControllable(details); this.isControllable(details);
}
return this.ifControlled; return this.ifControlled;
}, },
@ -148,9 +148,7 @@
timeouted( timeouted(
(details) => { (details) => {
if (details) {
this.isControllable(details); this.isControllable(details);
}
cb(); cb();
} }
@ -177,15 +175,16 @@
const message = errOrMessage.message || errOrMessage.toString(); const message = errOrMessage.message || errOrMessage.toString();
chrome.notifications.create( chrome.notifications.create(
id, id,
Object.assign({ {
title: title, title: title,
message: message, message: message,
contextMessage: context, contextMessage: context,
requireInteraction: ifSticky,
type: 'basic', type: 'basic',
iconUrl: './icons/' + icon, iconUrl: './icons/' + icon,
appIconMaskUrl: './icons/default-mask-128.png', appIconMaskUrl: './icons/default-mask-128.png',
isClickable: true, isClickable: true,
}, window.apis.platform.ifFirefox ? {} : { requireInteraction: ifSticky }), }
); );
}, },
@ -233,7 +232,7 @@
chrome.notifications.clear(notId); chrome.notifications.clear(notId);
if(notId === 'no-control') { if(notId === 'no-control') {
return window.utils.openAndFocus( return openAndFocus(
window.utils.messages.searchSettingsForUrl('proxy') window.utils.messages.searchSettingsForUrl('proxy')
); );
} }
@ -243,7 +242,7 @@
handlers.installListenersOn(window, 'BG'); handlers.installListenersOn(window, 'BG');
(chrome.proxy.onProxyError || chrome.proxy.onError).addListener( timeouted( (details) => { chrome.proxy.onProxyError.addListener( timeouted( (details) => {
if (!handlers.ifControlled) { if (!handlers.ifControlled) {
return; return;
@ -254,11 +253,7 @@
error: "net::ERR_PAC_SCRIPT_FAILED", error: "net::ERR_PAC_SCRIPT_FAILED",
fatal: false, fatal: false,
*/ */
const ifConFail = [ const ifConFail = details.error === 'net::ERR_PROXY_CONNECTION_FAILED';
'net::ERR_TUNNEL_CONNECTION_FAILED',
'net::ERR_PROXY_CONNECTION_FAILED',
].includes(details.error);
if (ifConFail) { if (ifConFail) {
// Happens if you return neither prixies nor "DIRECT". // Happens if you return neither prixies nor "DIRECT".
// Ignore it. // Ignore it.
@ -267,7 +262,7 @@
console.warn('PAC ERROR', details); console.warn('PAC ERROR', details);
// TOOD: add "view pac script at this line" button. // TOOD: add "view pac script at this line" button.
handlers.mayNotify('pac-error', 'Ошибка PAC!', handlers.mayNotify('pac-error', 'Ошибка PAC!',
(details.error || details.message /* Firefox */) + '\n' + details.details, details.error + '\n' + details.details,
{icon: 'pac-error-128.png'} {icon: 'pac-error-128.png'}
); );
@ -275,14 +270,14 @@
chrome.proxy.settings.onChange.addListener( timeouted( (details) => { chrome.proxy.settings.onChange.addListener( timeouted( (details) => {
console.log('Proxy settings changed:', details.levelOfControl); console.log('Proxy settings changed.', details);
const noCon = 'no-control'; const noCon = 'no-control';
const ifWasControllable = handlers.ifControllable; const ifWasControllable = handlers.ifControllable;
if ( !handlers.isControllable(details) && ifWasControllable ) { if ( !handlers.isControllable(details) && ifWasControllable ) {
handlers.mayNotify( handlers.mayNotify(
noCon, noCon,
chrome.i18n.getMessage('noControl'), chrome.i18n.getMessage('noControl'),
chrome.i18n.getMessage('WhichQ'), chrome.i18n.getMessage('which'),
{icon: 'no-control-128.png', ifSticky: false} {icon: 'no-control-128.png', ifSticky: false}
); );
} else { } else {

View File

@ -69,7 +69,7 @@
); );
} }
console.log('GETed with success:', url.substr(0, 100), Date.now() - start); console.log('GETed with success:', url, Date.now() - start);
textCb(); textCb();
}, },
@ -78,31 +78,6 @@
}, },
head(url, cb = mandatory()) {
const start = Date.now();
fetch(url, {cache: 'no-store', method: 'HEAD'}).then(
(res) => {
const status = res.status;
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
return cb(
errorsLib.clarify(
res,
'Получен ответ с неудачным HTTP-кодом ' + status + '.'
)
);
}
console.log('HEADed with success:', url, Date.now() - start);
cb();
},
errorsLib.clarifyThen(checkCon, cb)
);
},
}; };
} }

View File

@ -1,64 +0,0 @@
'use strict';
if (window.apis.platform.ifFirefox) {
const prefix = 'firefox-only';
const originalSet = chrome.proxy.settings.set.bind( chrome.proxy.settings );
chrome.proxy.settings.set = function(details, cb) {
const pac = window.utils.getProp(details, 'value.pacScript') || {};
if (!(pac && pac.data)) {
return originalSet(details, cb);
}
const blob = new Blob([pac.data], { type : 'application/x-ns-proxy-autoconfig' });
const blobUrl = URL.createObjectURL(blob);
originalSet({
value: {
proxyType: 'autoConfig',
autoConfigUrl: blobUrl,
},
}, window.utils.chromified( async (err) => {
if (err) {
window.utils.lastError = err;
cb();
return;
}
await window.utils.promisedLocalStorage.set({ [`${prefix}-pac-data`]: pac.data });
cb();
}));
};
const originalGet = chrome.proxy.settings.get.bind( chrome.proxy.settings );
chrome.proxy.settings.get = function(details, cb) {
originalGet(details, window.utils.chromified(async (err, originalDetails) => {
if (err) {
window.utils.lastError = err;
cb(originalDetails);
return;
}
let pacData = await window.utils.promisedLocalStorage.get(`${prefix}-pac-data`);
if (!pacData || !Object.keys(pacData).length) {
cb(originalDetails);
return;
}
cb(Object.assign(
originalDetails,
{
value: {
mode: 'pac_script',
pacScript: {
data: pacData,
},
},
}
));
}));
};
const originalClear = chrome.proxy.settings.clear.bind( chrome.proxy.settings );
chrome.proxy.settings.clear = async function(details, cb) {
await window.utils.promisedLocalStorage.remove(`${prefix}-pac-data`);
originalClear(details, cb);
};
}

View File

@ -12,57 +12,6 @@
const ifIncontinence = 'if-incontinence'; const ifIncontinence = 'if-incontinence';
const modsKey = 'mods'; const modsKey = 'mods';
let proxyHostToCredsList = {};
const ifAuthSupported = chrome.webRequest && chrome.webRequest.onAuthRequired && !window.apis.version.ifMini;
if (ifAuthSupported) {
const requestIdToTries = {};
chrome.webRequest.onAuthRequired.addListener(
(details) => {
if (!details.isProxy) {
return {};
}
const proxyHost = `${details.challenger.host}:${details.challenger.port}`;
const credsList = proxyHostToCredsList[proxyHost];
if (!credsList) {
return {}; // No creds found for this proxy.
}
const requestId = details.requestId;
const tries = requestIdToTries[requestId] || 0;
if (tries > credsList.length) {
return {}; // All creds for this proxy were tried already.
}
requestIdToTries[requestId] = tries + 1;
return {
authCredentials: credsList[tries],
};
},
{urls: ['<all_urls>']},
['blocking'],
);
const forgetRequestId = (details) => {
delete requestIdToTries[details.requestId];
};
chrome.webRequest.onCompleted.addListener(
forgetRequestId,
{urls: ['<all_urls>']},
);
chrome.webRequest.onErrorOccurred.addListener(
forgetRequestId,
{urls: ['<all_urls>']},
);
}
const getDefaultConfigs = () => ({// Configs user may mutate them and we don't care! const getDefaultConfigs = () => ({// Configs user may mutate them and we don't care!
ifProxyHttpsUrlsOnly: { ifProxyHttpsUrlsOnly: {
@ -79,7 +28,7 @@
}, },
ifProhibitDns: { ifProhibitDns: {
dflt: false, dflt: false,
label: 'запретить определение по IP/DNS', label: 'запретить опредление по IP/DNS',
desc: 'Пытается запретить скрипту использовать DNS, без которого определение блокировки по IP работать не будет (т.е. будет разблокироваться меньше сайтов). Используйте, чтобы получить прирост в производительности или если вам кажется, что мы проксируем слишком много сайтов. Запрет действует только для скрипта, браузер и др.программы продолжат использование DNS.', desc: 'Пытается запретить скрипту использовать DNS, без которого определение блокировки по IP работать не будет (т.е. будет разблокироваться меньше сайтов). Используйте, чтобы получить прирост в производительности или если вам кажется, что мы проксируем слишком много сайтов. Запрет действует только для скрипта, браузер и др.программы продолжат использование DNS.',
order: 2, order: 2,
}, },
@ -101,19 +50,12 @@
dflt: false, dflt: false,
category: 'ownProxies', category: 'ownProxies',
label: 'использовать СВОЙ локальный Tor', label: 'использовать СВОЙ локальный Tor',
desc: 'Установите <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Расширение-и-TOR">Tor</a> на свой компьютер и используйте его как прокси-сервер. <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Расширение-и-TOR#важно">ВАЖНО</a>.', desc: 'Установите <a href="https://rebrand.ly/ac-tor">Tor</a> на свой компьютер и используйте его как прокси-сервер. <a href="https://rebrand.ly/ac-tor">ВАЖНО</a>',
order: 5, order: 5,
}, },
ifUseLocalWarp: {
dflt: false,
category: 'ownProxies',
label: 'использовать WARP как прокси',
desc: 'Использовать СВОЙ локальный CloudFlare WARP (<a href="https://one.one.one.one">https://one.one.one.one</a>) в качестве прокси.',
order: 5.5,
},
exceptions: { exceptions: {
dflt: null,
category: 'exceptions', category: 'exceptions',
dflt: null,
}, },
ifMindExceptions: { ifMindExceptions: {
dflt: true, dflt: true,
@ -122,31 +64,13 @@
desc: 'Учитывать сайты, добавленные вручную. Только для своих прокси-серверов! Без своих прокси работать не будет.', desc: 'Учитывать сайты, добавленные вручную. Только для своих прокси-серверов! Без своих прокси работать не будет.',
order: 6, order: 6,
}, },
whitelist: {
dflt: [],
category: 'exceptions',
},
ifMindWhitelist: {
dflt: false,
category: 'exceptions',
label: 'Ограничиться только <a href="../exceptions/index.html">белым списком</a>',
desc: 'Разрешить расширению работать только с адресами из белого списка.',
order: 6.5,
},
customProxyStringRaw: { customProxyStringRaw: {
dflt: '', dflt: '',
category: 'ownProxies', category: 'ownProxies',
label: 'использовать СВОИ прокси', label: 'использовать СВОИ прокси',
url: 'https://github.com/anticensority/runet-censorship-bypass/wiki/Свои-прокси-в-расширении', url: 'https://rebrand.ly/ac-own-proxy',
order: 7, order: 7,
}, },
ifUseOwnProxiesOnlyForOwnSites: {
dflt: false,
category: 'ownProxies',
label: 'СВОИ прокси только для СВОИХ сайтов',
desc: 'Не использовать СВОИ прокси для всех сайтов из PAC-скрипта, а только для добавленных вручную исключений.',
order: 7.5,
},
ifProxyMoreDomains: { ifProxyMoreDomains: {
ifDisabled: true, ifDisabled: true,
dflt: false, dflt: false,
@ -155,14 +79,6 @@
desc: 'Проксировать особые домены. Необходима поддержка со стороны СВОИХ прокси.', desc: 'Проксировать особые домены. Необходима поддержка со стороны СВОИХ прокси.',
order: 8, order: 8,
}, },
replaceDirectWith: {
ifDisabled: true,
dflt: false,
category: 'ownProxies',
label: 'подменять DIRECT на',
desc: 'Использовать в PAC-скрипте указанную строку для запросов напрямую (вместо директивы DIRECT). Данная строка не проверяется на требования к шифрованию связи до прокси! Строка должна соответствовать формату возвращаемого значения PAC-скрипта, который подобен <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Свои-прокси-в-расширении#формат">формату своих прокси</a>.',
order: 9,
},
}); });
@ -175,15 +91,16 @@
return acc; return acc;
}, {}); }, {});
}; };
const getCurrentConfigs = function getCurrentConfigs(ifRaw = false) { const getCurrentConfigs = function getCurrentConfigs() {
const oldMods = kitchenState(modsKey); const oldMods = kitchenState(modsKey);
if (ifRaw) { /*if (oldMods) {
// No migration! // No migration!
return oldMods; return oldMods;
} }*/
// Client may expect mods.included and mods.excluded! // Client may expect mods.included and mods.excluded!
// On first install they are not defined. // On first install they are not defined.
@ -226,73 +143,34 @@
.every((dProp) => { .every((dProp) => {
const ifDflt = ( const ifDflt = (
!( !(dProp in mods) ||
dProp in mods && Boolean(configs[dProp].dflt) === Boolean(mods[dProp])
Boolean(configs[dProp].dflt) !== Boolean(mods[dProp])
)
); );
const ifMods = configs[dProp].ifDfltMods; // If default value implies PAC-script modification. const ifMods = configs[dProp].ifDfltMods;
return ifDflt ? !ifMods : ifMods; return ifDflt ? !ifMods : ifMods;
}); });
const self = {}; const self = {};
const gdft = getDefaults(); Object.assign(self, getDefaults(), mods);
Object.assign(self, gdft, mods);
self.ifNoMods = ifNoMods; self.ifNoMods = ifNoMods;
let customProxyArray = []; let customProxyArray = [];
if (self.customProxyStringRaw) { if (self.customProxyStringRaw) {
customProxyArray = self.customProxyStringRaw customProxyArray = self.customProxyStringRaw
.replace(/#.*$/mg, '') // Strip comments. .replace(/#.*$/mg, '') // Strip comments.
.split( /(?:\s*(?:;\r?\n)+\s*)+/g ) .split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g )
.map( (p) => p.trim() ) .map( (p) => p.trim() )
.filter( (p) => p && /\s+/g.test(p) ); // At least one space is required. .filter( (p) => p && /\s+/g.test(p) );
if (self.ifUseSecureProxiesOnly) { if (self.ifUseSecureProxiesOnly) {
customProxyArray = customProxyArray.filter( (pStr) => /^HTTPS\s/.test(pStr) ); customProxyArray = customProxyArray.filter( (pStr) => /^HTTPS\s/.test(pStr) );
} }
} }
if (self.ifUseLocalWarp) {
self.warpPoints = ['SOCKS5 localhost:40000', 'HTTPS localhost:40000'];
customProxyArray.push(...self.warpPoints);
}
if (self.ifUseLocalTor) { if (self.ifUseLocalTor) {
self.torPoints = ['SOCKS5 localhost:9150', 'SOCKS5 localhost:9050']; self.torPoints = ['SOCKS5 localhost:9150', 'SOCKS5 localhost:9050'];
customProxyArray.push(...self.torPoints); customProxyArray.push(...self.torPoints);
} }
// Hanlde protected proxies in customProxyArray.
const protectedProxies = [];
customProxyArray = customProxyArray.map((proxyScheme) => {
if (proxyScheme.includes('@')) {
const proxy = window.utils.parseProxyScheme(proxyScheme);
protectedProxies.push(proxy);
return `${proxy.type} ${proxy.hostname}:${proxy.port}`;
}
return proxyScheme;
});
if (!ifAuthSupported && protectedProxies.length) {
return [new Error('Запароленные прокси не поддерживаются в данной версии/платформе!')];
}
proxyHostToCredsList = {};
protectedProxies.forEach(({ hostname, port, username, password }) => {
proxyHostToCredsList[`${hostname}:${port}`] =
proxyHostToCredsList[`${hostname}:${port}`] || [];
const tries = proxyHostToCredsList[`${hostname}:${port}`];
tries.push({
username: username || '',
password: password || '',
});
});
self.filteredCustomsString = ''; self.filteredCustomsString = '';
if (customProxyArray.length) { if (customProxyArray.length) {
self.customProxyArray = customProxyArray; self.customProxyArray = customProxyArray;
@ -320,8 +198,7 @@
self.included = []; self.included = [];
self.excluded = []; self.excluded = [];
for(const host of Object.keys(self.exceptions)) { for(const host of Object.keys(self.exceptions)) {
const ifProxy = self.exceptions[host] || false; if (self.exceptions[host]) {
if (ifProxy) {
self.included.push(host); self.included.push(host);
} else { } else {
self.excluded.push(host); self.excluded.push(host);
@ -330,9 +207,9 @@
['included', 'excluded'].forEach((who) => { ['included', 'excluded'].forEach((who) => {
self[who] = self[who] self[who] = self[who]
.map( (domain) => domain.split('').reverse() ) .map( (s) => s.split('').reverse() )
.sort() .sort()
.map( (rDomain) => rDomain.reverse().join('') ); .map( (a) => a.reverse().join('') );
}); });
if (self.included.length && !self.filteredCustomsString) { if (self.included.length && !self.filteredCustomsString) {
@ -348,50 +225,29 @@
window.apis.pacKitchen = { window.apis.pacKitchen = {
getPacMods: getCurrentConfigs, getPacMods: getCurrentConfigs,
getPacModsRaw: () => getCurrentConfigs(true),
getOrderedConfigs: getOrderedConfigsForUser, getOrderedConfigs: getOrderedConfigsForUser,
cook(pacData, pacMods = mandatory()) { cook(pacData, pacMods = mandatory()) {
pacData = pacData.replace(
new RegExp(kitchenStartsMark + '[\\s\\S]*$', 'g'),
''
);
/a/.test('a'); // GC RegExp.input and friends.
return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark } return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark }
/******/ /******/
/******/;(function(global) { /******/;+function(global) {
/******/ "use strict"; /******/ "use strict";
/******/ /******/
/******/ const originalFindProxyForURL = FindProxyForURL; /******/ const originalFindProxyForURL = FindProxyForURL;
/******/ let tmp = function(url, host) { /******/ global.FindProxyForURL = function(url, host) {
/******/ const dotHost = '.' + host; /******/
${ ${
function() { function() {
let generatedPac = `
/******/ if (${pacMods.ifMindWhitelist && pacMods.whitelist.length}) {
/******/ const ifWhitelisted =
/******/ ${JSON.stringify(pacMods.whitelist)}.some((whiteHost) => {
/******/ const ifWild = whiteHost.startsWith('*');
/******/ if (ifWild) {
/******/ return dotHost.endsWith(whiteHost.substr(1));
/******/ }
/******/ return host === whiteHost;
/******/ })
/******/ if (!ifWhitelisted) {
/******/ return 'DIRECT';
/******/ }
/******/ }`;
generatedPac += pacMods.ifProhibitDns ? ` let res = pacMods.ifProhibitDns ? `
/******/ /******/
/******/ global.dnsResolve = function(host) { return null; }; /******/ global.dnsResolve = function(host) { return null; };
/******/ /******/
/******/` : ''; /******/` : '';
if (pacMods.ifProxyHttpsUrlsOnly) { if (pacMods.ifProxyHttpsUrlsOnly) {
generatedPac += ` res += `
/******/ /******/
/******/ if (!url.startsWith("https")) { /******/ if (!url.startsWith("https")) {
/******/ return "DIRECT"; /******/ return "DIRECT";
@ -401,7 +257,7 @@
} }
if (pacMods.ifUseLocalTor) { if (pacMods.ifUseLocalTor) {
generatedPac += ` res += `
/******/ /******/
/******/ if (host.endsWith(".onion")) { /******/ if (host.endsWith(".onion")) {
/******/ return "${pacMods.torPoints.join('; ')}"; /******/ return "${pacMods.torPoints.join('; ')}";
@ -409,25 +265,25 @@
/******/ /******/
/******/ `; /******/ `;
} }
generatedPac += ` res += `
/******/ /******/
/******/ const directIfAllowed = ${pacMods.ifProxyOrDie ? '""/* Not allowed. */' : '"DIRECT"'}; /******/ const directIfAllowed = ${pacMods.ifProxyOrDie ? '""/* Not allowed. */' : '"; DIRECT"'};
/******/`; /******/`;
if (pacMods.filteredCustomsString) { if (pacMods.filteredCustomsString) {
generatedPac += ` res += `
/******/ /******/
/******/ const filteredCustomProxies = "${pacMods.filteredCustomsString}"; /******/ const filteredCustomProxies = "; ${pacMods.filteredCustomsString}";
/******/`; /******/`;
} }
const ifIncluded = pacMods.included && pacMods.included.length; const ifIncluded = pacMods.included && pacMods.included.length;
const ifExcluded = pacMods.excluded && pacMods.excluded.length; const ifExcluded = pacMods.excluded && pacMods.excluded.length;
const ifManualExceptions = ifIncluded || ifExcluded; const ifManualExceptions = ifIncluded || ifExcluded;
let finalExceptions = {}; const finalExceptions = {};
if (pacMods.ifProxyMoreDomains) { if (pacMods.ifProxyMoreDomains) {
finalExceptions = pacMods.moreDomains.reduce((acc, tld) => { pacMods.moreDomains.reduce((acc, tld) => {
acc['*.' + tld] = true; acc[tld] = true;
return acc; return acc;
}, finalExceptions); }, finalExceptions);
@ -438,33 +294,21 @@
const ifExceptions = Object.keys(finalExceptions).length; const ifExceptions = Object.keys(finalExceptions).length;
if (ifExceptions) { if (ifExceptions) {
generatedPac += ` res += `
/******/ /******/
/******/ /* EXCEPTIONS START */ /******/ /* EXCEPTIONS START */
// TODO: handle wildcards. /******/ const dotHost = '.' + host;
/******/ const isHostInDomain = (domain, ifWild) => { /******/ const isHostInDomain = (domain) => dotHost.endsWith('.' + domain);
if (ifWild) { /******/ const domainReducer = (maxWeight, [domain, ifIncluded]) => {
return dotHost.endsWith(domain.substr(1));
}
return domain === host;
}
/******/ const domainReducer = (maxWeight, [domain, ifProxy]) => {
/******/ /******/
const ifWild = domain.startsWith('*.'); /******/ if (!isHostInDomain(domain)) {
/******/ if (!isHostInDomain(domain, ifWild)) {
/******/ return maxWeight; /******/ return maxWeight;
/******/ } /******/ }
let len = domain.length; /******/ const newWeightAbs = domain.length;
if (ifWild) {
len = len === 0 ? len : (len - 2)*2 - 1;
} else {
len = len*2;
}
/******/ const newWeightAbs = len;
/******/ if (newWeightAbs < Math.abs(maxWeight)) { /******/ if (newWeightAbs < Math.abs(maxWeight)) {
/******/ return maxWeight; /******/ return maxWeight;
/******/ } /******/ }
/******/ return newWeightAbs*(ifProxy ? 1 : -1); /******/ return newWeightAbs*(ifIncluded ? 1 : -1);
/******/ /******/
/******/ }; /******/ };
/******/ /******/
@ -476,30 +320,27 @@
/******/ } /******/ }
/******/ // Always proxy it! /******/ // Always proxy it!
${ pacMods.filteredCustomsString ${ pacMods.filteredCustomsString
? `/******/ return filteredCustomProxies + "; " + directIfAllowed;` ? `/******/ return filteredCustomProxies + directIfAllowed;`
: '/******/ /* No custom proxies -- continue. */' : '/******/ /* No custom proxies -- continue. */'
} }
/******/ } /******/ }
/******/ /* EXCEPTIONS END */ /******/ /* EXCEPTIONS END */
`; `;
} }
generatedPac += ` res += `
/******/ const pacScriptProxies = originalFindProxyForURL(url, host)${ /******/ const pacScriptProxies = originalFindProxyForURL(url, host)${
/******/ pacMods.ifProxyOrDie /******/ pacMods.ifProxyOrDie ? '.replace(/DIRECT/g, "")' : ' + directIfAllowed'
? '.replace(/DIRECT/g, "")'
: ' + "; " + directIfAllowed'
};`; };`;
if( if(
!pacMods.ifUseSecureProxiesOnly && !pacMods.ifUseSecureProxiesOnly &&
!pacMods.filteredCustomsString && !pacMods.filteredCustomsString &&
pacMods.ifUsePacScriptProxies pacMods.ifUsePacScriptProxies
) { ) {
return generatedPac + ` return res + `
/******/ return [pacScriptProxies, directIfAllowed] /******/ return pacScriptProxies + directIfAllowed;`;
.filter((p) => p).join("; ") || "DIRECT";`;
} }
return generatedPac + ` return res + `
/******/ let pacProxyArray = pacScriptProxies.split(/(?:\\s*;\\s*)+/g).filter( (p) => p ); /******/ let pacProxyArray = pacScriptProxies.split(/(?:\\s*;\\s*)+/g).filter( (p) => p );
/******/ const ifNoProxies = pacProxyArray${pacMods.ifProxyOrDie ? '.length === 0' : '.every( (p) => /^DIRECT$/i.test(p) )'}; /******/ const ifNoProxies = pacProxyArray${pacMods.ifProxyOrDie ? '.length === 0' : '.every( (p) => /^DIRECT$/i.test(p) )'};
/******/ if (ifNoProxies) { /******/ if (ifNoProxies) {
@ -507,10 +348,6 @@ ${ pacMods.filteredCustomsString
/******/ return "DIRECT"; /******/ return "DIRECT";
/******/ } /******/ }
/******/ return ` + /******/ return ` +
((pacMods.filteredCustomsString && !pacMods.ifUseOwnProxiesOnlyForOwnSites)
? 'filteredCustomProxies + "; " + '
: ''
) +
function() { function() {
if (!pacMods.ifUsePacScriptProxies) { if (!pacMods.ifUsePacScriptProxies) {
@ -521,57 +358,16 @@ ${ pacMods.filteredCustomsString
filteredPacExp = filteredPacExp =
'pacProxyArray.filter( (pStr) => /^HTTPS\\s/.test(pStr) ).join("; ")'; 'pacProxyArray.filter( (pStr) => /^HTTPS\\s/.test(pStr) ).join("; ")';
} }
return filteredPacExp + ' + "; " + '; return filteredPacExp + ' + ';
}() + 'directIfAllowed;'; // Without DIRECT you will get 'PROXY CONN FAILED' pac-error. }() + `${pacMods.filteredCustomsString ? 'filteredCustomProxies + ' : ''}directIfAllowed;`; // Without DIRECT you will get 'PROXY CONN FAILED' pac-error.
}() }()
} }
/******/ }; };
${
!pacMods.replaceDirectWith
? ''
: `
/******/ const oldTmp = tmp;
/******/ tmp = function(url, host) {
/******/ const ip = dnsResolve(host);
/******/ if (ip) {
/******/ const ipInt = convert_addr(ip);
/******/ if([
/******/ /* Reserved networks: https://en.wikipedia.org/wiki/Reserved_IP_addresses#IPv4 */
/******/ [-16777216, 0 ], // ['0.0.0.0' , '255.0.0.0' ],
/******/ [-16777216, 167772160 ], // ['10.0.0.0' , '255.0.0.0' ],
/******/ [-4194304, 1681915904 ], // ['100.64.0.0' , '255.192.0.0'],
/******/ [-16777216, 2130706432 ], // ['127.0.0.0' , '255.0.0.0' ],
/******/ [-65536, -1442971648], // ['169.254.0.0', '255.255.0.0'],
/******/ [-1048576, -1408237568], // ['172.16.0.0', '255.240.0.0'],
/******/ [-256, -1073741824], // ['192.0.0.0' , '255.255.255.0'],
/******/ [-256, -1073741312], // ['192.0.2.0' , '255.255.255.0'],
/******/ [-256, -1067949312], // ['192.88.99.0' , '255.255.255.0'],
/******/ [-65536, -1062731776], // ['192.168.0.0', '255.255.0.0'],
/******/ [-131072, -971898880 ], // ['198.18.0.0', '255.254.0.0'],
/******/ [-256, -969710592 ], // ['198.51.100.0', '255.255.255.0'],
/******/ [-256, -889163520 ], // ['203.0.113.0', '255.255.255.0'],
/******/ [-268435456, -536870912 ], // ['224.0.0.0', '240.0.0.0'],
/******/ [-268435456, -268435456 ], // ['240.0.0.0', '240.0.0.0'],
/******/ [-1, -1 ], // ['255.255.255.255' , '255.255.255.255'],
/******/ ].some(([netMask, maskedNet]) => (ipInt & netMask) === maskedNet)
/******/ ) {
/******/ return "DIRECT";
/******/ }
/******/ }
/******/ return oldTmp.call(this, url, host).replace(/(;|^)\\s*DIRECT\\s*(?=;|$)/g, "$1${pacMods.replaceDirectWith}");
/******/ };
`
}
/******/ if (global) {
/******/ global.FindProxyForURL = tmp;
/******/ } else {
/******/ FindProxyForURL = tmp;
/******/ }
/*****/})(this);`; }(this);`;
}, },
@ -586,15 +382,21 @@ ${
details details
? resolve(details) ? resolve(details)
: chrome.proxy.settings.get({}, timeouted(resolve) ), : chrome.proxy.settings.get({}, timeouted(resolve) )
).then( (details) => { ).then( (details) => {
if ( if (
details && details.levelOfControl === 'controlled_by_this_extension' details.levelOfControl === 'controlled_by_this_extension'
) { ) {
const pac = window.utils.getProp(details, 'value.pacScript'); const pac = window.utils.getProp(details, 'value.pacScript');
if (pac && pac.data) { if (pac && pac.data) {
// Delete old kitchen modifications.
pac.data = pac.data.replace(
new RegExp(kitchenStartsMark + '[\\s\\S]*$', 'g'),
''
);
/a/.test('a'); // GC RegExp.input and friends.
return chrome.proxy.settings.set(details, chromified(cb)); return chrome.proxy.settings.set(details, chromified(cb));
} }
} }
@ -611,7 +413,7 @@ ${
checkIncontinence(details) { checkIncontinence(details) {
if ( kitchenState(ifIncontinence) ) { if ( kitchenState(ifIncontinence) ) {
this.setNowAsync(details, (err) => { if (err) { throw err; } }); // TODO: suppress? this.setNowAsync(details, () => {/* Swallow. */});
} }
}, },
@ -646,14 +448,9 @@ ${
return cb(null, res, ...accWarns); return cb(null, res, ...accWarns);
} }
const newHosts = (pacMods.customProxyArray || []).map( (ps) => ps.split(/\s+/)[1] ); const newHosts = (pacMods.customProxyArray || []).map( (ps) => ps.split(/\s+/)[1] );
window.utils.fireRequest( window.utils.fireRequest('ip-to-host-replace-all', newHosts, (err, res, ...moreWarns) => cb( err, res, ...accWarns.concat(moreWarns) ));
'ip-to-host-replace-all',
newHosts,
(err, res, ...moreWarns) =>
cb(err, res, ...accWarns, ...moreWarns),
);
}, }
); );
}, },
@ -673,21 +470,27 @@ ${
const originalSet = chrome.proxy.settings.set.bind( chrome.proxy.settings ); const originalSet = chrome.proxy.settings.set.bind( chrome.proxy.settings );
chrome.proxy.settings.set = function(details, cb) { chrome.proxy.settings.set = function(details, cb) {
const pac = window.utils.getProp(details, 'value.pacScript'); const pac = window.utils.getProp(details, 'value.pacScript');
if (!(pac && pac.data)) { if (!(pac && pac.data)) {
return originalSet(details, window.utils.timeouted(cb)); return originalSet(details, cb);
} }
const pacMods = getCurrentConfigs(); const pacMods = getCurrentConfigs();
pac.data = pacKitchen.cook( pac.data, pacMods ); pac.data = pacKitchen.cook( pac.data, pacMods );
originalSet({value: details.value}, window.utils.chromified((err) => { originalSet({value: details.value}, (/* No args. */) => {
if (!err) {
kitchenState(ifIncontinence, null); kitchenState(ifIncontinence, null);
}
window.utils.lastError = err;
cb && cb(); cb && cb();
})); });
}; };
pacKitchen.checkIncontinence();
chrome.proxy.settings.onChange.addListener(
timeouted(
pacKitchen.checkIncontinence.bind(pacKitchen)
)
);
} // Private namespace ends. } // Private namespace ends.

View File

@ -19,25 +19,14 @@
*/ */
/*
Due to History
- *Async suffix usually means that function requires a cb.
It may not be related to async (returning a Promise).
This naming is confusing and should be reconsidered.
*/
{ // Private namespace starts. { // Private namespace starts.
const ifRu = chrome.i18n.getMessage('@@ui_locale').startsWith('ru');
console.log('Russian?', ifRu);
const mandatory = window.utils.mandatory; const mandatory = window.utils.mandatory;
const throwIfError = window.utils.throwIfError; const throwIfError = window.utils.throwIfError;
const chromified = window.utils.chromified; const chromified = window.utils.chromified;
const timeouted = window.utils.timeouted; const timeouted = window.utils.timeouted;
const clarifyThen = window.apis.errorsLib.clarifyThen; const clarifyThen = window.apis.errorsLib.clarifyThen;
const clarify = window.apis.errorsLib.clarify;
const Warning = window.apis.errorsLib.Warning; const Warning = window.apis.errorsLib.Warning;
const httpLib = window.apis.httpLib; const httpLib = window.apis.httpLib;
@ -60,70 +49,8 @@
}; };
const tryPromiseSeveralTimesAsync = (createPromise, timeoutsInSec) =>
createPromise().then(
(res) => Promise.resolve(res),
(err) => {
console.log('Promise failed, are there any retries?');
const outSec = timeoutsInSec.shift();
if (outSec === undefined) {
console.log('No retries left.');
return Promise.reject(err);
}
console.log('Retrying in', outSec, 'sec');
/*
const alarmName = 'try-promise=several-times-async';
const res = new Promise((resolve) => {
chrome.alarms.onAlarm.addListener((alarmInfo) => {
if (alarmInfo.name === alarmName) {
console.log('Time to retry.');
resolve(tryPromiseSeveralTimesAsync(createPromise, timeoutsInSec));
}
});
});
chrome.alarms.create(alarmName, { delayInMinutes: outSec/60 });
return res;
*/
return new Promise((resolve) =>
window.setTimeout(() => resolve(tryPromiseSeveralTimesAsync(createPromise, timeoutsInSec)), outSec*1000),
);
},
);
const doWithoutProxyAsync = (createPromise) => new Promise((resolve, reject) => {
console.log('Doing without proxy...');
chrome.proxy.settings.get({}, chromified((getErr, settings) => {
if (getErr) {
reject(getErr);
return;
}
const ifWeAreInControl = window.utils.areSettingsControlledFor(settings);
if (!ifWeAreInControl) {
resolve(createPromise());
return;
}
delete settings.levelOfControl;
const setProxyAsync = () => new Promise((setResolve, setReject) => {
console.log('Restoring chrome proxy settings...');
chrome.proxy.settings.set(
settings,
chromified((err) => err ? setReject(err) : setResolve()),
);
});
console.log('Clearing chrome proxy settings...');
chrome.proxy.settings.clear({}, chromified((clearErr) => {
if (clearErr) {
reject(clearErr);
return;
}
createPromise().then((actionResult) => setProxyAsync().then(() => resolve(actionResult)), reject);
}));
}));
});
const setPacAsync = function setPacAsync( const setPacAsync = function setPacAsync(
pacData = mandatory(), cb = throwIfError, pacData = mandatory(), cb = throwIfError
) { ) {
const config = { const config = {
@ -137,13 +64,6 @@
chrome.proxy.settings.set( {value: config}, chromified((err) => { chrome.proxy.settings.set( {value: config}, chromified((err) => {
if (err) { if (err) {
if (err.message === 'proxy.settings requires private browsing permission.') {
clarifyThen(
chrome.i18n.getMessage('AllowExtensionToRunInPrivateWindows'),
cb,
)(err);
return;
}
return cb(err); return cb(err);
} }
handlers.updateControlState( () => { handlers.updateControlState( () => {
@ -152,16 +72,17 @@
console.warn('Failed, other extension is in control.'); console.warn('Failed, other extension is in control.');
return cb( return cb(
new Error( window.utils.messages.whichExtensionHtml() ), new Error( window.utils.messages.whichExtensionHtml() )
); );
} }
console.log('Successfuly set PAC in proxy settings.'); console.log('Successfuly set PAC in proxy settings..');
cb(); cb();
}); });
})); }));
}; };
const updatePacProxyIps = function updatePacProxyIps( const updatePacProxyIps = function updatePacProxyIps(
@ -170,86 +91,75 @@
cb = asyncLogGroup( cb = asyncLogGroup(
'Getting IPs for PAC hosts...', 'Getting IPs for PAC hosts...',
cb, cb
); );
window.utils.fireRequest('ip-to-host-update-all', cb); window.utils.fireRequest('ip-to-host-update-all', cb);
}; };
const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync( const setPacScriptFromProviderAsync = function setPacScriptFromProviderAsync(
provider, lastModifiedStr, ifUnattended = mandatory(), cb = throwIfError, provider, lastModifiedStr = mandatory(), cb = throwIfError
) { ) {
const pacUrl = provider.pacUrls[0]; const pacUrl = provider.pacUrls[0];
cb = asyncLogGroup( cb = asyncLogGroup(
'Getting PAC script from provider...', pacUrl, 'Getting PAC script from provider...', pacUrl,
cb, cb
); );
const warnings = []; httpLib.ifModifiedSince(pacUrl, lastModifiedStr, (err, newLastModifiedStr) => {
const originalCb = cb;
cb = (err, res, ...warns) => originalCb(err, res, ...warns, ...warnings);
const addWarning = (wText) => { warnings.push(new Warning(wText)) };
if (provider.distinctKey === 'Anticensority') { if (!newLastModifiedStr) {
const res = {lastModified: lastModifiedStr};
const pacMods = window.apis.pacKitchen.getPacMods(); const ifWasEverModified = lastModifiedStr !== new Date(0).toUTCString();
if (!pacMods.filteredCustomsString) { if (ifWasEverModified) {
addWarning( return cb(
ifRu null, res,
? \` new Warning(
Не найдено СВОИХ прокси. Этот PAC-скрипт 'Ваш PAC-скрипт не нуждается в обновлении. Его дата: ' +
работает только со <a href="https://git.io/ac-own-proxy">СВОИМИ прокси</a> lastModifiedStr
(по умолчанию будет использоваться локальный <a href="https://git.io/ac-tor">Tor</a>). )
\`
: \`
Couldn't find OWN proxies. This PAC-script
works only with <a href="https://git.io/ac-own-proxy">OWN proxies</a>
(by default local <a href="https://git.io/ac-tor">Tor</a> will be used).
\`,
); );
} }
} }
doWithoutProxyAsync(
// Employ all urls, the latter are fallbacks for the former. // Employ all urls, the latter are fallbacks for the former.
() => { const pacDataPromise = provider.pacUrls.reduce(
const tryAllUrlsAsync = () => provider.pacUrls.reduce(
(promise, url) => promise.catch( (promise, url) => promise.catch(
() => new Promise( () => new Promise(
(resolve, reject) => httpLib.get( (resolve, reject) => httpLib.get(
url, url,
(newErr, pacData) => (newErr, pacData) => newErr ? reject(newErr) : resolve(pacData)
newErr ? reject(newErr) : resolve(pacData), )
)
), ),
), Promise.reject()
),
Promise.reject(),
); );
return (ifUnattended
? tryPromiseSeveralTimesAsync(tryAllUrlsAsync, [20, 40, 60]) pacDataPromise.then(
: tryAllUrlsAsync()
).catch(
(err) => Promise.reject(clarify(
err,
chrome.i18n.getMessage('FailedToDownloadPacScriptFromAddresses') + ': [ '
+ provider.pacUrls.join(' , ') + ' ].',
)),
);
},
).then(
(pacData) => { (pacData) => {
setPacAsync( setPacAsync(
pacData, pacData,
(err, res) => cb( (err, res) => cb(
err, err,
Object.assign(res || {}, {lastModified: lastModifiedStr}), Object.assign(res || {}, {lastModified: newLastModifiedStr})
), )
); );
}, },
cb,
clarifyThen(
'Не удалось скачать PAC-скрипт с адресов: [ '
+ provider.pacUrls.join(' , ') + ' ].',
cb
)
); );
});
}; };
window.apis.antiCensorRu = { window.apis.antiCensorRu = {
@ -258,45 +168,20 @@
pacProviders: { pacProviders: {
Антизапрет: { Антизапрет: {
// Distinct keys are needed if you want to check if a given label: 'Антизапрет',
// provider is this or that (distinct it from others). desc: \`Альтернативный PAC-скрипт от стороннего разработчика.
distinctKey: 'Antizapret', Работает быстрее, но охватывает меньше сайтов.
label: chrome.i18n.getMessage('Antizapret'), Блокировка определяется по доменному имени,
desc: ifRu <br/> <a href="https://antizapret.prostovpn.org">Страница проекта</a>.\`,
? \`Основной PAC-скрипт от автора проекта «Антизапрет».
Охватывет меньше сайтов.
Блокировка определяется по доменному имени и при необходимости по IP.
<br/> <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/PAC-скрипты:-различия">Сравнение PAC-скриптов</a>.
\`
: \`The main PAC-script from the author of project "Antizapret"\.
Covers fewer sites.
Block is detected based on a domain name and, if necessary, on an IP.
<br/> <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/PAC-скрипты:-различия">Comparison of PAC-scripts (ru)</a>.
\`,
order: 0, order: 0,
pacUrls: [ pacUrls: ['https://antizapret.prostovpn.org/proxy.pac'],
'https://e.cen.rodeo:8443/proxy.pac',
'https://antizapret.prostovpn.org:8443/proxy.pac',
'https://antizapret.prostovpn.org:18443/proxy.pac',
'https://antizapret.prostovpn.org/proxy.pac',
],
}, },
Антицензорити: { Антицензорити: {
distinctKey: 'Anticensority', label: 'Антицензорити',
label: chrome.i18n.getMessage('Anticensority'), desc: \`Основной PAC-скрипт от автора расширения.
desc: ifRu Работает медленней, но охватывает больше сайтов.
? \`Альтернативный PAC-скрипт от автора расширения. Блокировка определятся по доменному имени или IP адресу.<br/>
Охватывает больше сайтов. <a href="https://rebrand.ly/ac-anticensority">Страница проекта</a>.\`,
Блокировка определятся по доменному имени или IP адресу.
Подходит для провайдеров, блокирующих все сайты на одном IP.
<br/> <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/PAC-скрипты:-различия">Сравнение PAC-скриптов</a>.
\`
: \`Alternative PAC-script from the author of this extension.
Covers more sites.
Block is detected based on a domain name and on an IP address.
Better fits providers that block all sites on one IP.
<br/> <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/PAC-скрипты:-различия">Comparison of PAC-scripts (ru)</a>.
\`,
order: 1, order: 1,
/* /*
@ -305,17 +190,28 @@
Version: 0.17 Version: 0.17
*/ */
pacUrls: ${JSON.stringify(anticensorityPacUrls, null, 2)}, pacUrls: ${JSON.stringify(anticensorityPacUrls, null, 2)},
/*[
// First official, shortened:
'https://rebrand.ly/ac-chrome-anticensority-pac',
// Second official, Cloud Flare with caching:
'https://anticensority.tk/generated-pac-scripts/anticensority.pac',
// GitHub.io (anticensority):
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x67\x69\x74\x68\x75\x62\x2e\x69\x6f\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63',
// GitHub repo (anticensority):
'\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\x69\x74\x79\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',
// Old, deprecated:
'https://anticensorship-russia.tk/generated-pac-scripts/anticensority.pac',
// Google Drive (0.17, anticensority):
'\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\x32\x6d\x68\x42\x67\x46\x6e\x66\x34\x70\x45\x4c\x56\x6c\x47\x4e\x54\x42\x45\x4d\x58\x4e\x6d\x52\x58\x63',
],*/
}, },
onlyOwnSites: { onlyOwnSites: {
distinctKey: 'onlyOwnSites', label: 'Только свои сайты и свои прокси',
label: chrome.i18n.getMessage('Only_own_sites_and_only_own_proxies'), desc: 'Проксируются только добавленные вручную сайты через СВОИ вручную добавленные прокси или через локальный Tor.',
desc: ifRu
? 'Проксируются только добавленные вручную адреса через СВОИ вручную добавленные прокси или через локальный Tor.'
: 'Only added manually urls are proxied via your OWN manually added proxies or via Tor.',
order: 99, order: 99,
pacUrls: [ pacUrls: [
'data:application/x-ns-proxy-autoconfig,' + escape('function FindProxyForURL(){ return "DIRECT"; }'), 'data:application/x-ns-proxy-autoconfig,' + escape('function FindProxyForURL(){ return "DIRECT"; }'),
], ]
} }
}, },
@ -325,18 +221,12 @@
}, },
_currentPacProviderKey: 'Антизапрет', _currentPacProviderKey: 'Антицензорити',
/* Is it the first time extension installed? /* Is it the first time extension installed?
Do something, e.g. initiate PAC sync. Do something, e.g. initiate PAC sync.
*/ */
ifFirstInstall: false, ifFirstInstall: false,
/* We have .lastPacUpdateStamp and ._currentPacProviderLastModified.
LastModified is received from a server, we kind of don't trust it,
just use it for cache and maybe show to the user.
UpdateStamp is got from client and we base our timers on it,
malicious server can't interfere with it.
*/
lastPacUpdateStamp: 0, lastPacUpdateStamp: 0,
setTitle() { setTitle() {
@ -344,12 +234,12 @@
const upDate = new Date(this.lastPacUpdateStamp).toLocaleString('ru-RU') const upDate = new Date(this.lastPacUpdateStamp).toLocaleString('ru-RU')
.replace(/:\\d+$/, '').replace(/\\.\\d{4}/, ''); .replace(/:\\d+$/, '').replace(/\\.\\d{4}/, '');
chrome.browserAction.setTitle({ chrome.browserAction.setTitle({
title: \`\${chrome.i18n.getMessage('Updated')} \${upDate} | \${chrome.i18n.getMessage('Version')} \${window.apis.version.build}\`, title: \`Обновлялись \${upDate} | Версия \${window.apis.version.build}\`,
}); });
}, },
_currentPacProviderLastModified: 0, _currentPacProviderLastModified: 0, // Not initialized.
getLastModifiedForKey(key = mandatory()) { getLastModifiedForKey(key = mandatory()) {
@ -382,7 +272,7 @@
setCurrentPacProviderKey( setCurrentPacProviderKey(
newKey = mandatory(), newKey = mandatory(),
lastModified = new Date().toUTCString(), lastModified = new Date().toUTCString()
) { ) {
this.mustBeKey(newKey); this.mustBeKey(newKey);
@ -419,28 +309,27 @@
} }
} }
chrome.storage.local.remove( chrome.storage.local.clear(
'antiCensorRu',
() => chrome.storage.local.set( () => chrome.storage.local.set(
{ antiCensorRu: onlySettable }, onlySettable,
chromified(cb), chromified(cb)
), )
); );
}, },
syncWithPacProviderAsync(opts = {}, cb = throwIfError) { syncWithPacProviderAsync(
const optsDefaults = Object.freeze({ key: this.getCurrentPacProviderKey(), ifUnattended: false }); key = this.currentPacProvierKey, cb = throwIfError) {
if( typeof(opts) === 'function' ) {
cb = opts; if( typeof(key) === 'function' ) {
opts = {}; cb = key;
key = this.getCurrentPacProviderKey();
} }
let { key, ifUnattended } = { ...optsDefaults, ...opts };
cb = asyncLogGroup('Syncing with PAC provider ' + key + '...', cb); cb = asyncLogGroup('Syncing with PAC provider ' + key + '...', cb);
if (key === null) { if (key === null) {
// No pac provider set. // No pac provider set.
return clarifyThen(chrome.i18n.getMessage('ChoosePacProviderFirstD'), cb); return clarifyThen('Сперва выберите PAC-провайдера.', cb);
} }
const pacProvider = this.getPacProvider(key); const pacProvider = this.getPacProvider(key);
@ -449,7 +338,6 @@
(resolve, reject) => setPacScriptFromProviderAsync( (resolve, reject) => setPacScriptFromProviderAsync(
pacProvider, pacProvider,
this.getLastModifiedForKey(key), this.getLastModifiedForKey(key),
ifUnattended,
(err, res, ...warns) => { (err, res, ...warns) => {
if (!err) { if (!err) {
@ -466,37 +354,24 @@
) )
); );
const updateIpsAsync = () => new Promise( const ipsErrorPromise = new Promise(
(resolve, reject) => updatePacProxyIps( (resolve, reject) => updatePacProxyIps(
(err, res, ...warns) => { resolve
if (err) { )
reject([err, ...warns]);
return;
}
resolve([err, res, ...warns]);
},
),
); );
const ipsPromise = !ifUnattended Promise.all([pacSetPromise, ipsErrorPromise]).then(
? updateIpsAsync() ([[pacErr, pacRes, ...pacWarns], ipsErr]) => {
: tryPromiseSeveralTimesAsync(updateIpsAsync, [20, 40, 60]);
Promise.all([pacSetPromise, ipsPromise]).then( if (pacErr && ipsErr) {
([[pacErr, pacRes, ...pacWarns], [ipsErr, ipsRes, ...ipsWarns]]) => {
if (pacErr) {
return cb(pacErr, pacRes); return cb(pacErr, pacRes);
} }
const warns = pacWarns; const warns = pacWarns;
if (ipsErr) { if (ipsErr) {
warns.push(ipsErr); warns.push(ipsErr);
} }
if (ipsWarns.length) {
warns.push(...ipsWarns);
}
this.pushToStorageAsync( this.pushToStorageAsync(
(pushErr) => cb(pacErr || pushErr, null, ...warns), (pushErr) => cb(pacErr || pushErr, null, ...warns)
); );
}, },
@ -523,7 +398,7 @@
console.log( console.log(
'Next PAC update is scheduled on', 'Next PAC update is scheduled on',
new Date(nextUpdateMoment).toLocaleString('ru-RU'), new Date(nextUpdateMoment).toLocaleString('ru-RU')
); );
chrome.alarms.create( chrome.alarms.create(
@ -546,7 +421,7 @@
throw new Error('Key must be defined.'); throw new Error('Key must be defined.');
} }
if (this.currentProviderKey !== key) { if (this.currentProviderKey !== key) {
return this.syncWithPacProviderAsync({ key }, cb); return this.syncWithPacProviderAsync(key, cb);
} }
console.log(key + ' already installed.'); console.log(key + ' already installed.');
cb(); cb();
@ -566,11 +441,11 @@
} }
this.setCurrentPacProviderKey(null); this.setCurrentPacProviderKey(null);
this.pushToStorageAsync( this.pushToStorageAsync(
() => handlers.updateControlState(cb), () => handlers.updateControlState(cb)
); );
}), })
), )
); );
}, },
@ -578,25 +453,12 @@
}; };
// ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE // ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE
(async () => { chrome.storage.local.get(null, chromified( async (err, oldStorage) => {
let ifConsentGiven = await window.utils.promisedLocalStorage.get('ifConsentGiven');
if (!ifConsentGiven) { if (err) {
window.utils.openAndFocus('/pages/consent/index.html'); throw err;
await window.apis.consent.promise;
ifConsentGiven = true
await window.utils.promisedLocalStorage.set({ ifConsentGiven });
} }
let oldAntiCensorRu = await window.utils.promisedLocalStorage.get('antiCensorRu') || {};
const otherKeys = [
'pac-kitchen-if-incontinence',
'pac-kitchen-mods',
'ip-to-host',
'handlers-pac-error',
'handlers-ext-error',
'handlers-no-control',
];
/* /*
Event handlers that ALWAYS work (even if installation is not done Event handlers that ALWAYS work (even if installation is not done
or failed). or failed).
@ -611,9 +473,9 @@
if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) { if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) {
console.log( console.log(
'Periodic PAC update triggered:', 'Periodic PAC update triggered:',
new Date().toLocaleString('ru-RU'), new Date().toLocaleString('ru-RU')
); );
antiCensorRu.syncWithPacProviderAsync({ ifUnattended: true }, () => { /* Swallow. */ }); antiCensorRu.syncWithPacProviderAsync(() => {/* swallow */});
} }
}) })
@ -630,29 +492,28 @@
console.log('Keep cooked...'); console.log('Keep cooked...');
await new Promise((resolve) => window.apis.pacKitchen.keepCookedNowAsync(resolve)); await new Promise((resolve) => window.apis.pacKitchen.keepCookedNowAsync(resolve));
//console.log('Storage on init:', oldAntiCensorRu); console.log('Storage on init:', oldStorage);
antiCensorRu.ifFirstInstall = Object.keys(oldAntiCensorRu).length === 0; antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
if (antiCensorRu.ifFirstInstall) { if (antiCensorRu.ifFirstInstall) {
// INSTALL // INSTALL
console.log('Installing...'); console.log('Installing...');
handlers.switch('on', 'ext-error'); handlers.switch('on', 'ext-error');
chrome.runtime.openOptionsPage(); return chrome.runtime.openOptionsPage();
return;
} }
// LAUNCH, RELOAD, UPDATE // LAUNCH, RELOAD, UPDATE
// Use old or migrate to default. // Use old or migrate to default.
antiCensorRu._currentPacProviderKey = antiCensorRu._currentPacProviderKey =
oldAntiCensorRu._currentPacProviderKey || null; oldStorage._currentPacProviderKey || null;
antiCensorRu.lastPacUpdateStamp = antiCensorRu.lastPacUpdateStamp =
oldAntiCensorRu.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp; oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
antiCensorRu._currentPacProviderLastModified = antiCensorRu._currentPacProviderLastModified =
oldAntiCensorRu._currentPacProviderLastModified oldStorage._currentPacProviderLastModified
|| antiCensorRu._currentPacProviderLastModified; || antiCensorRu._currentPacProviderLastModified;
console.log( console.log(
'Last PAC update was on', 'Last PAC update was on',
new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU'), new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU')
); );
@ -664,45 +525,34 @@
Better on each launch then on each pull. Better on each launch then on each pull.
*/ */
await new Promise(async (resolve) => { await new Promise((resolve) => {
const ifUpdating = antiCensorRu.version !== oldAntiCensorRu.version; const ifUpdating = antiCensorRu.version !== oldStorage.version;
if (!ifUpdating) { if (!ifUpdating) {
// LAUNCH, RELOAD, ENABLE // LAUNCH, RELOAD, ENABLE
antiCensorRu.pacProviders = oldAntiCensorRu.pacProviders; antiCensorRu.pacProviders = oldStorage.pacProviders;
console.log('Extension launched, reloaded or enabled.'); console.log('Extension launched, reloaded or enabled.');
return resolve(); return resolve();
} }
// UPDATE & MIGRATION // UPDATE & MIGRATION
console.log('Updating from ', oldStorage.version, 'to', antiCensorRu.version);
const key = antiCensorRu._currentPacProviderKey;
if (oldStorage.version === ' 0.0.1.2') {
if (key !== null && key !== 'onlyOwnSites') {
antiCensorRu._currentPacProviderKey = 'Антицензорити';
}
}
const ifUpdatedCb = () => antiCensorRu.pushToStorageAsync(() => { antiCensorRu.pushToStorageAsync(() => {
console.log('Extension updated.'); console.log('Extension updated.');
resolve(); resolve();
}); });
console.log('Updating from', oldAntiCensorRu.version, 'to', antiCensorRu.version);
try {
if (window.apis.version.isLeq(oldAntiCensorRu.version, '0.0.1.62')) {
window.apis.antiCensorRu.pacProviders['Антизапрет'].pacUrls = [
'https://e.cen.rodeo:8443/proxy.pac',
'https://antizapret.prostovpn.org:8443/proxy.pac',
'https://antizapret.prostovpn.org:18443/proxy.pac',
'https://antizapret.prostovpn.org/proxy.pac',
];
console.log('Successfully updated to 0.0.1.63.');
}
} catch (e) {
// Log update error.
console.log('UPDATE ERROR:');
console.error(e);
}
ifUpdatedCb();
}); });
if (antiCensorRu.getPacProvider()) { if (antiCensorRu.getPacProvider()) {
@ -725,6 +575,6 @@
* Add storage.lastPacUpdateStamp. * Add storage.lastPacUpdateStamp.
**/ **/
})(); }));
} }

View File

@ -13,7 +13,7 @@
order: 0, order: 0,
}, },
googleCache: { hostTracker: {
title: 'Из кэша Google', title: 'Из кэша Google',
getUrl: (blockedUrl) => 'http://webcache.googleusercontent.com/search?q=cache:' + blockedUrl, getUrl: (blockedUrl) => 'http://webcache.googleusercontent.com/search?q=cache:' + blockedUrl,
order: 1, order: 1,
@ -27,19 +27,19 @@
otherUnblock: { otherUnblock: {
title: 'Разблокировать по-другому', title: 'Разблокировать по-другому',
getUrl: (blockedUrl) => ('https://anticensority.github.io/unblock#' + blockedUrl), getUrl: (blockedUrl) => ('https://rebrand.ly/ac-unblock#' + blockedUrl),
order: 3, order: 3,
}, },
antizapretInfo: { antizapretInfo: {
title: 'Сайт в реестре блокировок?', title: 'Сайт в реестре блокировок?',
getUrl: (blockedUrl) => 'https://reestr.rublacklist.net/?q=' + new URL(blockedUrl).hostname, getUrl: (blockedUrl) => 'https://antizapret.info/index.php?search=' + new URL(blockedUrl).hostname,
order: 4, order: 4,
}, },
support: { support: {
title: 'Документация / Помощь / Поддержка', title: 'Документация / Помощь / Поддержка',
getUrl: (blockedUrl) => 'https://github.com/anticensority/runet-censorship-bypass/wiki', getUrl: (blockedUrl) => 'https://rebrand.ly/ac-wiki',
order: 99, order: 99,
}, },

View File

@ -1,48 +0,0 @@
# For Reviewers
## Prerequirements
* You need a globally installed `gulp-cli@3.0.0`.
See https://gulpjs.com/docs/en/getting-started/quick-start#install-the-gulp-command-line-utility.
* Node v21.7.3
* NPM 10.5.0
## Steps
Steps to reproduce the same zip:
```
npm ci
cd src/extension-common/pages/options/
npm ci
cd -
npm start
# See ./build/extension-full
cd ./build/extension-full
zip -r runet-censorship-bypass-full.zip ./*
```
## Minified Files
### Ace Editor
https://ace.c9.io -> https://github.com/ajaxorg/ace -> Building Ace -> "The ace-builds repository endeavours to maintain the latest build" -> https://github.com/ajaxorg/ace-builds/ -> Select tag of 1.2.5, open https://github.com/ajaxorg/ace-builds/tree/v1.2.5/src-min and download the files you want to check.
```
mkdir downloaded
cd downloaded
wget https://raw.githubusercontent.com/ajaxorg/ace-builds/v1.2.5/src-min/ace.js
wget https://raw.githubusercontent.com/ajaxorg/ace-builds/v1.2.5/src-min/ext-searchbox.js
wget https://raw.githubusercontent.com/ajaxorg/ace-builds/v1.2.5/src-min/mode-javascript.js
wget https://raw.githubusercontent.com/ajaxorg/ace-builds/v1.2.5/src-min/worker-javascript.js
downloaded$ for i in ./*; do md5sum "$i"; done
2b9a1157bb3ba711a0402b6751d9ac71 ./ace.js
1f73efaff2853571af0e701c5e9a15ee ./ext-searchbox.js
e5eebd85c4e66667c28f124e6a07e3ed ./mode-javascript.js
f0d1342102d16ab7abe319b2683d10ea ./worker-javascript.js
```
## PAC-Script AntiZapret
https://antizapret.prostovpn.org/proxy.pac (old) and https://e.cen.rodeo:8443/proxy.pac (new) are generated by https://bitbucket.org/anticensority/antizapret-pac-generator-light/, reviewers may find justifications related to this PAC-script in that repo.

View File

@ -0,0 +1 @@
Files of this directory must be copied into final build without modifications.

View File

@ -3,7 +3,7 @@
"message": "Runet Censorship Bypass${nameSuffixEn}" "message": "Runet Censorship Bypass${nameSuffixEn}"
}, },
"extDesc": { "extDesc": {
"message": "Circumvent Russian Internet Censorship: https://git.io/ac-wiki" "message": "Circumvent Russian Internet Censorship: https://rebrand.ly/ac-wiki"
}, },
"proxy": { "proxy": {
"message": "proxy" "message": "proxy"
@ -11,133 +11,7 @@
"noControl": { "noControl": {
"message": "Other extension controls proxy!" "message": "Other extension controls proxy!"
}, },
"WhichQ": { "which": {
"message": "Which?" "message": "Which?"
},
"update": {
"message": "update"
},
"UpdatingDDD": {
"message": "Updating..."
},
"UpdatedD": {
"message": "Updated."
},
"DisablingDDD": {
"message": "Disabling..."
},
"DisabledD": {
"message": "Disabled."
},
"InstallingDDD": {
"message": "Installing..."
},
"PacScriptWasInstalledD": {
"message": "PAC-script was installed."
},
"Version": {
"message": "Version"
},
"FullVersion": {
"message": "Full version"
},
"VersionForSlowMachines": {
"message": "Version for slow machines"
},
"FailedToDownloadPacScriptFromAddresses": {
"message": "Failed to download PAC-script from addresses"
},
"ChoosePacProviderFirstD": {
"message": "Choose PAC-provider first."
},
"ProblemsQ": {
"message": "Problems?"
},
"Finish": {
"message": "OK"
},
"Disable": {
"message": "Disable"
},
"Only_own_sites_and_only_own_proxies": {
"message": "Only own sites and only own proxies"
},
"Antizapret": {
"message": "Antizapret"
},
"Anticensority": {
"message": "Anticensority"
},
"PAC_script": {
"message": "PAC-script"
},
"Exceptions": {
"message": "Exceptions"
},
"Own_proxies": {
"message": "Own proxies"
},
"Modifiers": {
"message": "Modifiers"
},
"Notifications": {
"message": "Notifications"
},
"Error": {
"message": "Error"
},
"Non_critical_error": {
"message": "Non-critical error. Don't worry: it works"
},
"Donate": {
"message": "Donate"
},
"Updated": {
"message": "Updated"
},
"ago": {
"message": "ago"
},
"never": {
"message": "never"
},
"ms": {
"message": "ms"
},
"s": {
"message": "s"
},
"min": {
"message": "min"
},
"h": {
"message": "h"
},
"d": {
"message": "d"
},
"w": {
"message": "w"
},
"m": {
"message": "m"
},
"ProxyTheDomainNameBelowQ": {
"message": "Proxy the domain name below?"
},
"auto": {
"message": "auto"
},
"yes": {
"message": "yes"
},
"no": {
"message": "no"
},
"noOwnProxiesError": {
"message": "Proxying of OWN sites is possible only via OWN proxies. No own proxies found that satisfy your requirements."
},
"AllowExtensionToRunInPrivateWindows": {
"message": "For the extension to work it is required to allow it to run in private windows, see <a href='https://github.com/anticensority/runet-censorship-bypass/wiki/Как-разрешить-запуск-расширения-в-приватных-окнах-|-How-to-allow-extension-to-run-in-private-windows'>a HOWTO</a>."
} }
} }

View File

@ -3,7 +3,7 @@
"message": "Обход блокировок Рунета${nameSuffixRu}" "message": "Обход блокировок Рунета${nameSuffixRu}"
}, },
"extDesc": { "extDesc": {
"message": "Обход интернет-цензуры в России: https://git.io/ac-wiki" "message": "Обход интернет-цензуры в России: https://rebrand.ly/ac-wiki"
}, },
"proxy": { "proxy": {
"message": "прокси" "message": "прокси"
@ -11,133 +11,7 @@
"noControl": { "noControl": {
"message": "Другое расширение контролирует настройки прокси!" "message": "Другое расширение контролирует настройки прокси!"
}, },
"WhichQ": { "which": {
"message": "Какое?" "message": "Какое?"
},
"update": {
"message": "обновить"
},
"UpdatingDDD": {
"message": "Обновляем..."
},
"UpdatedD": {
"message": "Обновлено."
},
"DisablingDDD": {
"message": "Отключение..."
},
"DisabledD": {
"message": "Отключено."
},
"InstallingDDD": {
"message": "Установка..."
},
"PacScriptWasInstalledD": {
"message": "PAC-скрипт установлен."
},
"Version": {
"message": "Версия"
},
"FullVersion": {
"message": "Полная версия"
},
"VersionForSlowMachines": {
"message": "Версия для слабых машин"
},
"FailedToDownloadPacScriptFromAddresses": {
"message": "Не удалось скачать PAC-скрипт с адресов"
},
"ChoosePacProviderFirstD": {
"message": "Сперва выберите PAC-провайдера."
},
"ProblemsQ": {
"message": "Проблемы?"
},
"Finish": {
"message": "Готово"
},
"Disable": {
"message": "Отключить"
},
"Only_own_sites_and_only_own_proxies": {
"message": "Только свои сайты и свои прокси"
},
"Antizapret": {
"message": "Антизапрет"
},
"Anticensority": {
"message": "Антицензорити"
},
"PAC_script": {
"message": "PAC-скрипт"
},
"Exceptions": {
"message": "Исключения"
},
"Own_proxies": {
"message": "Свои прокси"
},
"Modifiers": {
"message": "Модификаторы"
},
"Notifications": {
"message": "Уведомления"
},
"Error": {
"message": "Ошибка"
},
"Non_critical_error": {
"message": "Некритичная ошибка. Всё хорошо, продолжаем работу"
},
"Donate": {
"message": "Поддержать"
},
"Updated": {
"message": "Обновлялись"
},
"ago": {
"message": "назад"
},
"never": {
"message": "никогда"
},
"ms": {
"message": "мс"
},
"s": {
"message": "с"
},
"min": {
"message": "мин"
},
"h": {
"message": "ч"
},
"d": {
"message": "дн"
},
"w": {
"message": " недель"
},
"m": {
"message": " месяцев"
},
"ProxyTheDomainNameBelowQ": {
"message": "Проксировать указанное доменное имя?"
},
"auto": {
"message": "авто"
},
"yes": {
"message": "да"
},
"no": {
"message": "нет"
},
"noOwnProxiesError": {
"message": "Проксировать СВОИ сайты можно только при наличии СВОИХ прокси. Нет своих прокси, удовлетворяющих вашим требованиям."
},
"AllowExtensionToRunInPrivateWindows": {
"message": "Для работы расширения необходимо разрешить запуск в приватных окнах, см. <a href='https://github.com/anticensority/runet-censorship-bypass/wiki/Как-разрешить-запуск-расширения-в-приватных-окнах-|-How-to-allow-extension-to-run-in-private-windows'>инструкции</a>."
} }
} }

View File

@ -6,15 +6,15 @@
"description": "__MSG_extDesc__", "description": "__MSG_extDesc__",
"version": "0.0.${version}", "version": "0.0.${version}",
"icons": { "icons": {
"128": "icons/default-128.png" "128": "/icons/default-128.png"
}, },
"author": "anticensority+owners@googlegroups.com", "author": "ilyaigpetrov@gmail.com",
"homepage_url": "https://github.com/anticensorship-russia/chromium-extension",
"permissions": [ "permissions": [
"proxy" "proxy"
, "alarms" , "alarms"
, "storage" , "storage"
, "unlimitedStorage"
, "<all_urls>" , "<all_urls>"
, "tabs" , "tabs"
, "contextMenus" , "contextMenus"
@ -22,21 +22,14 @@
${extra_permissions} ${extra_permissions}
], ],
"minimum_chrome_version": "55.0.0.0", "minimum_chrome_version": "55.0.0.0",
"browser_specific_settings": {
"gecko": {
"strict_min_version": "91.1.0"
}
},
"background": { "background": {
${persistent} ${persistent}
"scripts": [ "scripts": [
"00-init-apis.js" "00-init-apis.js"
${scripts_0x}
, "11-error-handlers-api.js" , "11-error-handlers-api.js"
, "12-errors-lib.js" , "12-errors-lib.js"
, "13-http-lib.js" , "13-http-lib.js"
, "15-firefox-proxy-settings.js"
${scripts_2x} ${scripts_2x}
, "35-pac-kitchen-api.js" , "35-pac-kitchen-api.js"
, "37-sync-pac-script-with-pac-provider-api.js" , "37-sync-pac-script-with-pac-provider-api.js"
@ -45,7 +38,6 @@
, "75-context-menus.js" , "75-context-menus.js"
] ]
}, },
"browser_action": { "browser_action": {
"default_title": "Этот сайт благословлён | Версия ${version + versionSuffix}", "default_title": "Этот сайт благословлён | Версия ${version + versionSuffix}",
"default_popup": "/pages/options/index.html" "default_popup": "/pages/options/index.html"
@ -54,5 +46,4 @@
"page": "/pages/options/index.html", "page": "/pages/options/index.html",
"chrome_style": false "chrome_style": false
} }
} }

View File

@ -1,108 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<link rel="icon" type="image/x-icon" href="./ribbon-128.ico">
<meta charset="utf-8">
<title>Согласие на сбор данных / Data Collection Consent</title>
<style>
p, ol { font-size: 16px }
</style>
</head>
<body>
<div style="margin: 0 auto; max-width: 35rem; text-align: justify; font-family: sans-serif">
<img src="./ribbon-128.png">
<h1>Согласие на сбор данных / Data Collection Consent</h1>
<p>
Этот документ написан на двух языках. Английский предпочтительней.
</p>
<p>
This document is written in two languages. English is preferable.
</p>
<p>
Привет! На связи команда разработчиков браузерного расширения «Обход блокировок Рунета».
Вы либо только что установили расширение, либо только что обновились до новой версии.
По новым правилам мы <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Зачем-нам-согласие-со-сбором-данных%3F">должны</a>
получить от вас согласие на сбор ваших данных и передачу их
третьим лицам.
</p>
<p>
Hi! It's "Runet Censorship Bypass" browser extension developers team.
You have just installed the extension or just updated to a new version.
According to the new requirements we <a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Зачем-нам-согласие-со-сбором-данных%3F">have to</a> get your consent before collecting your data and
sharing it with 3rd parties.
</p>
<p>
Сообщаем вам, что после первого запуска расширения и при дальнейшем его использовании
настроенные в нём PAC-скрипты и прокси-сервера могут собирать или уже* собирают
некоторые ваши данные. Какие именно, зависит от выбранного вами поставщика этих ресурсов,
так что рекомендуем ознакомиться с их соответствующей политикой конфиденциальности (Privacy
Policy).
<br>
* Если вы установили и пользовалсись расширением уже некоторое время. Этого экрана согласия не
было в старых версиях.
</p>
<p>
We inform you that starting from the first launch of the extension and on further usage chosen
PAC-scripts and proxy-servers may collect or are already* collecting some of your data. Which
exactly depends on the chosen provider of these resources so we recommend you to get
acquainted with their corresponding Privacy Policy.
<br>
* If you have installed and have been using the extension for some time already. This consent
screen wasn't shown in the old versions.
</p>
<p>
При первом запуске и по умолчанию будут использоваться PAC-скрипт и встроенные в него
прокси-сервера, предоставляемые <a href="https://antizapret.prostovpn.org:8443">проектом
"АнтиЗапрет"</a>, — политику конфиденциальности этого решения см. в
<a href="https://antizapret.prostovpn.org:8443/faq.html">FAQ</a> под заголовком "Какие данные
собирает сервис и каким образом они используются?".
</p>
<p>
On the first launch and by default the PAC-script and its built-in proxy-servers provided by
<a href="https://antizapret.prostovpn.org:8443">project "AntiZapret" (RU)</a> will be used, —
see its Privacy Policy in
<a href="https://antizapret.prostovpn.org:8443/faq.html">the FAQ (RU)</a> under the title
"Какие данные собирает сервис и каким образом они используются?". Its translation to EN is
present in the Privacy Policy of the extension.
</p>
<p>
Политику конфиденциальности самого расширения см.
<a
href="https://github.com/anticensority/runet-censorship-bypass/wiki/Privacy-Policy-|-Политика-конфиденциальности"
>здесь (EN)</a>.
</p>
<p>
See the Privacy Policy of this extension
<a
href="https://github.com/anticensority/runet-censorship-bypass/wiki/Privacy-Policy-|-Политика-конфиденциальности"
>here</a>.
</p>
<p>
В расширении представлены кнопки / There are these buttons in the extension:
</p>
<ol>
<li>
"Через Google Translate" / "Via Google Translate"
</li><li>
"Из кэша Google" / "From Google Cache"
</li><li>
"Из архива archive.org" / "From archive.org archive"
</li><li>
"Разблокировать по-другому" / "Unblock another way"
</li><li>
"Сайт в реестре блокировок?" / "Is site in the registry of blockings?"
</li>
</ol>
<p>
Все эти кнопки передают URL-адрес текущей вкладки в соответствующие службы. /
All these buttons share URL-address of the current tab with corresponding services.
</p>
<button id="agreeBtn">Разрешаю собирать заявленные данные / Allow claimed data collection
</button>
<button id="rejectBtn">Нет, удалите расширение / No, delete this extension
</button>
</div>
<script src="./index.js"></script>
<script src="../lib/keep-links-clickable.js"></script>
</body>
</html>

View File

@ -1,14 +0,0 @@
'use strict';
chrome.runtime.getBackgroundPage( (backgroundPage) =>
backgroundPage.apis.errorHandlers.installListenersOn(
window, 'CONSENT', () => {
agreeBtn.onclick = () => {
backgroundPage.apis.consent.give();
window.close();
}
rejectBtn.onclick = () =>
chrome.management.uninstallSelf();
},
),
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,7 +1,5 @@
'use strict'; 'use strict';
chrome.runtime.getBackgroundPage((bgWin) => {
const setStatusTo = (msg) => document.getElementById('status').innerHTML = msg; const setStatusTo = (msg) => document.getElementById('status').innerHTML = msg;
const red = (text) => '<span style="color: red">' + text + '</span>'; const red = (text) => '<span style="color: red">' + text + '</span>';
@ -12,19 +10,20 @@ chrome.runtime.getBackgroundPage((bgWin) => {
useSoftTabs: true, useSoftTabs: true,
}); });
bgWin.chrome.proxy.settings.onChange.addListener( chrome.proxy.settings.onChange.addListener(
(details) => setStatusTo(red( details.levelOfControl + '!') ) (details) => setStatusTo(red( details.levelOfControl + '!') )
); );
function _read() { function _read() {
bgWin.chrome.proxy.settings.get({}, (details) => { chrome.proxy.settings.get({}, (details) => {
let control = details.levelOfControl; let control = details.levelOfControl;
if (control.startsWith('controlled_by_other')) { if (control.startsWith('controlled_by_other')) {
control = red(control); control = red(control);
} }
setStatusTo(control); setStatusTo(control);
console.log(details);
const pac = details.value.pacScript; const pac = details.value.pacScript;
const data = pac && pac.data || 'PAC скрипт не установлен.'; const data = pac && pac.data || 'PAC скрипт не установлен.';
editor.setValue( data ); editor.setValue( data );
@ -44,13 +43,13 @@ chrome.runtime.getBackgroundPage((bgWin) => {
data: editor.getValue(), data: editor.getValue(),
}, },
}; };
bgWin.chrome.proxy.settings.set( {value: config}, () => alert('Saved!') ); chrome.proxy.settings.set( {value: config}, () => alert('Saved!') );
}; };
document.querySelector('#clear-button').onclick = () => { document.querySelector('#clear-button').onclick = () => {
bgWin.chrome.proxy.settings.clear({}, () => { chrome.proxy.settings.clear({}, () => {
alert('Cleared! Reading...'); alert('Cleared! Reading...');
_read(); _read();
@ -59,4 +58,3 @@ chrome.runtime.getBackgroundPage((bgWin) => {
}; };
})

View File

@ -17,9 +17,7 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
# Сначала идёт список проксируемых сайтов, # Сначала идёт список проксируемых сайтов,
# затем ==== на отдельной строке, # затем ==== на отдельной строке,
# затем исключённые сайты. # затем исключённые сайты.
# После ещё одной строки с ==== идёт белый список.
# Сортировка с конца строки. # Сортировка с конца строки.
# Адреса со звёздочками поддерживаются: *.kasparov.ru, например.
# ПРОКСИРОВАТЬ: # ПРОКСИРОВАТЬ:
@ -28,16 +26,7 @@ ${(mods.included || []).join('\n')}
=============================== ===============================
# НЕ ПРОКСИРОВАТЬ: # НЕ ПРОКСИРОВАТЬ:
${(mods.excluded || []).join('\n')} ${(mods.excluded || []).join('\n')}`;
===============================
# БЕЛЫЙ СПИСОК
# Разрешить расширению работать только с этими адресами:
${(mods.whitelist || []).join('\n')}
`.trim();
status.innerText = 'Успешно загружено!'; status.innerText = 'Успешно загружено!';
@ -46,7 +35,7 @@ ${(mods.whitelist || []).join('\n')}
saveBtn.onclick = function() { saveBtn.onclick = function() {
let [proxyList, dontProxyList, whitelist] = editor.value let [proxyList, dontProxyList] = editor.value
.trim() .trim()
.replace(/#.*/g, '') .replace(/#.*/g, '')
.split(/=+/g) .split(/=+/g)
@ -56,14 +45,12 @@ ${(mods.whitelist || []).join('\n')}
.filter((host) => host) .filter((host) => host)
) )
dontProxyList = dontProxyList || []; dontProxyList = dontProxyList || [];
whitelist = whitelist || [];
const exceptions = {}; const exceptions = {};
proxyList.forEach((host) => (exceptions[host] = true)); proxyList.forEach((host) => (exceptions[host] = true));
dontProxyList.forEach((host) => (exceptions[host] = false)); dontProxyList.forEach((host) => (exceptions[host] = false));
const mods = backgroundPage.apis.pacKitchen.getPacMods(); const mods = backgroundPage.apis.pacKitchen.getPacMods();
mods.exceptions = exceptions; mods.exceptions = exceptions;
mods.whitelist = whitelist;
backgroundPage.apis.pacKitchen.keepCookedNowAsync(mods, (err) => { backgroundPage.apis.pacKitchen.keepCookedNowAsync(mods, (err) => {
if (!err) { if (!err) {
status.innerText = 'Успешно сохранено!'; status.innerText = 'Успешно сохранено!';

View File

@ -16,7 +16,7 @@ Use only if really required because of performance penalty.
const location = ln.href; const location = ln.href;
ln.onclick = function() { ln.onclick = function() {
chrome.tabs.create({active: this.dataset.inBg === "false", url: location}); chrome.tabs.create({active: !this.dataset.inBg, url: location});
return false; return false;
}; };

View File

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html style="visibility: hidden; will-change: contents, visibility"> <html style="display: none; will-change: contents, display">
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<title>Настройки</title> <title>Настройки</title>

View File

@ -1,27 +1,25 @@
{ {
"name": "options-page-builder", "name": "hello-react",
"version": "1.0.0", "version": "1.0.0",
"main": "index.js", "main": "index.js",
"license": "GPLv3", "license": "MIT",
"devDependencies": { "devDependencies": {
"babel-cli": "^6.26.0", "babel-cli": "^6.24.1",
"babel-loader": "^7.1.5", "babel-loader": "^7.0.0",
"babel-plugin-dynamic-import-webpack": "^1.1.0", "babel-plugin-dynamic-import-webpack": "^1.0.1",
"babel-preset-flow": "^6.23.0", "babel-preset-flow": "^6.23.0",
"babel-preset-react": "^6.24.1", "babel-preset-react": "^6.24.1",
"concat-stream": "^1.6.2", "concat-stream": "^1.6.0",
"csjs-inject": "^1.0.1", "csjs-inject": "^1.0.1",
"flow-bin": "^0.45.0", "flow-bin": "^0.45.0",
"gulp": "^4.0.2",
"inferno": "~3.2.0", "inferno": "~3.2.0",
"inferno-component": "^3.10.1", "inferno-component": "^3.1.2",
"inferno-create-element": "^3.10.1", "inferno-create-element": "^3.1.2",
"webpack": "^5.82.0", "webpack": "^2.5.1"
"webpack-cli": "^5.0.2"
}, },
"scripts": { "scripts": {
"check": "flow status", "check": "flow status",
"build:prod": "webpack --node-env=\"'production'\" --env=prod", "build:prod": "webpack --define process.env.NODE_ENV=\"'production'\" --env=prod",
"build:dev:nocomp": "NODE_ENV=development webpack --define process.env.NODE_ENV=\"'development'\" --env=dev", "build:dev:nocomp": "NODE_ENV=development webpack --define process.env.NODE_ENV=\"'development'\" --env=dev",
"build:dev": "NODE_ENV=development webpack --debug --define process.env.NODE_ENV=\"'development'\" --output-pathinfo --env=dev", "build:dev": "NODE_ENV=development webpack --debug --define process.env.NODE_ENV=\"'development'\" --output-pathinfo --env=dev",
"gulp": "cd .. && npm run gulp", "gulp": "cd .. && npm run gulp",
@ -29,6 +27,6 @@
"start": "cd .. && npm start" "start": "cd .. && npm start"
}, },
"dependencies": { "dependencies": {
"babel-plugin-inferno": "^3.5.1" "babel-plugin-inferno": "^3.2.0"
} }
} }

View File

@ -17,15 +17,12 @@ export default function getApp(theState) {
constructor(props) { constructor(props) {
super(props); super(props);
const hash = window.location.hash.substr(1);
const sanitizedUrl = theState.flags.ifOpenedUnsafely ? { hash: '', search: '' } : window.location; const hashParams = new URLSearchParams(hash);
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);
@ -50,10 +47,7 @@ export default function getApp(theState) {
this.setStatusTo( this.setStatusTo(
<ol style="list-style-type: initial;"> <ol style="list-style-type: initial;">
{newsArr {newsArr.map(([title, url]) => (<li><a href={url}>{title}</a></li>))}
.map(([title, url]) => (<li><a href={url}>{title}</a></li>))
.reverse() // News order.
}
</ol> </ol>
); );
@ -65,16 +59,16 @@ 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 statusFromUrl = this.state.searchParams.get('status'); const statusFromHash = this.state.hashParams.get('status');
if (statusFromUrl) { if (statusFromHash) {
return this.setStatusTo(statusFromUrl); return this.setStatusTo(statusFromHash);
} }
const comDate = localStorage[uiComDate]; const comDate = localStorage[uiComDate];
const query = comDate ? `?since=${comDate}` : ''; const query = comDate ? `?since=${comDate}` : '';
const oldEtag = localStorage[uiComEtag]; const oldEtag = localStorage[uiComEtag];
const headers = { const headers = {
'User-Agent': 'https://github.com/anticensority/runet-censorship-bypass', 'User-Agent': 'anticensorship-russia',
}; };
if (oldEtag) { if (oldEtag) {
Object.assign(headers, { Object.assign(headers, {
@ -85,9 +79,8 @@ export default function getApp(theState) {
headers: new Headers(headers), headers: new Headers(headers),
}; };
// I comment and uncomment this variable manually before release or build: //const ghUrl = `https://api.github.com/repos/anticensority/chromium-extension/issues/10/comments${query}`;
const ghUrl = `https://api.github.com/repos/anticensority/chromium-extension/issues/10/comments${query}`; const ghUrl = `https://api.github.com/repos/anticensority/for-testing/issues/1/comments${query}`;
// const ghUrl = `https://api.github.com/repos/anticensority/for-testing/issues/1/comments${query}`;
const [error, comments, etag] = await fetch( const [error, comments, etag] = await fetch(
ghUrl, ghUrl,
@ -230,10 +223,7 @@ export default function getApp(theState) {
this.setStatusTo( this.setStatusTo(
(<span> (<span>
<span style="color:red"> <span style="color:red">
{err {err ? <span><span class="emoji">🔥</span> Ошибка!</span> : 'Некритичная oшибка.'}
? <span><span class="emoji">🔥</span> {chrome.i18n.getMessage('Error')}!</span>
: `${chrome.i18n.getMessage('Non_critical_error')}.`
}
</span> </span>
<br/> <br/>
<span style="font-size: 0.9em; color: darkred" dangerouslySetInnerHTML={{__html: messageHtml}}></span> <span style="font-size: 0.9em; color: darkred" dangerouslySetInnerHTML={{__html: messageHtml}}></span>
@ -266,6 +256,7 @@ export default function getApp(theState) {
this.setStatusTo(beforeStatus); this.setStatusTo(beforeStatus);
this.switchInputs('off'); this.switchInputs('off');
operation((err, res, ...warns) => { operation((err, res, ...warns) => {
warns = warns.filter( (w) => w ); warns = warns.filter( (w) => w );
if (err || warns.length) { if (err || warns.length) {
this.showErrors(err, ...warns); this.showErrors(err, ...warns);

View File

@ -4,7 +4,7 @@ export default function getApplyMods(theState) {
const resetMods = function resetMods(props) { const resetMods = function resetMods(props) {
const ifSure = props.bgWindow.confirm('Сбросить все модификаторы и ИСКЛЮЧЕНИЯ?'); const ifSure = props.bgWindow.confirm('Сбросиь все модификаторы и ИСКЛЮЧЕНИЯ?');
if (!ifSure) { if (!ifSure) {
return false; return false;
} }

View File

@ -75,18 +75,13 @@ export default function getExcEditor(theState) {
constructor(props) { constructor(props) {
super(props); super(props);
const trimmedInputValueOrSpace =
props.currentTab &&
props.currentTab.url &&
!props.currentTab.url.startsWith('chrome')
? '*.' + (new URL(props.currentTab.url).hostname.replace(/^www\./g, ''))
: '';
const pacMods = props.apis.pacKitchen.getPacMods(); const pacMods = props.apis.pacKitchen.getPacMods();
this.state = { this.state = {
trimmedInputValueOrSpace, trimmedInputValueOrSpace:
props.currentTab && !props.currentTab.url.startsWith('chrome') ? new URL(props.currentTab.url).hostname : '',
sortedListOfOptions: this.modsToOpts(pacMods), sortedListOfOptions: this.modsToOpts(pacMods),
hostToIfHidden: {}, isHostHidden: {}
}; };
this.handleRadioClick = this.handleRadioClick.bind(this); this.handleRadioClick = this.handleRadioClick.bind(this);
this.handleInputOrClick = this.handleInputOrClick.bind(this); this.handleInputOrClick = this.handleInputOrClick.bind(this);
@ -96,11 +91,11 @@ export default function getExcEditor(theState) {
hideAllOptions() { hideAllOptions() {
this.setState({ this.setState({
hostToIfHidden: this.state.sortedListOfOptions.reduce( isHostHidden: this.state.sortedListOfOptions.reduce(
(hostToIfHidden, [excHost]) => { (isHostHidden, [excHost]) => {
hostToIfHidden[excHost] = true; isHostHidden[excHost] = true;
return hostToIfHidden; return isHostHidden;
}, },
{}), {}),
@ -110,7 +105,7 @@ export default function getExcEditor(theState) {
isHostValid(host) { isHostValid(host) {
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])$/; 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)) { if(!ValidHostnameRegex.test(host)) {
this.props.funs.showErrors(new TypeError('Должно быть только доменное имя, без протокола, порта и пути. Попробуйте ещё раз.')); this.props.funs.showErrors(new TypeError('Должно быть только доменное имя, без протокола, порта и пути. Попробуйте ещё раз.'));
return false; return false;
@ -142,7 +137,7 @@ export default function getExcEditor(theState) {
case 'this-no': case 'this-no':
if (ifYesClicked && !pacMods.filteredCustomsString) { if (ifYesClicked && !pacMods.filteredCustomsString) {
this.props.funs.showErrors( new TypeError( this.props.funs.showErrors( new TypeError(
chrome.i18n.getMessage('noOwnProxiesError'), 'Проксировать СВОИ сайты можно только при наличии СВОИХ прокси. Нет своих прокси, удовлетворяющих вашим требованиям.'
)); ));
return false; return false;
} }
@ -210,9 +205,7 @@ export default function getExcEditor(theState) {
const ifInit = !event; const ifInit = !event;
const currentHost = ifTriangleClicked ? '' : (trimmedInput || (ifInit ? '' : ' ')); const currentHost = ifTriangleClicked ? '' : (trimmedInput || (ifInit ? '' : ' '));
setInputValue(currentHost); setInputValue(currentHost);
this.setState({ this.setState({trimmedInputValueOrSpace: currentHost});
trimmedInputValueOrSpace: currentHost,
});
// Episode 2. // Episode 2.
@ -281,7 +274,7 @@ export default function getExcEditor(theState) {
})(); })();
this.setState({ this.setState({
hostToIfHidden: hidden, isHostHidden: hidden,
sortedListOfOptions: options, sortedListOfOptions: options,
}); });
@ -300,10 +293,10 @@ export default function getExcEditor(theState) {
return ( return (
<section style="padding-bottom: 1em;"> <section style="padding-bottom: 1em;">
<div>{chrome.i18n.getMessage('ProxyTheDomainNameBelowQ')}</div> <div>Проксировать указанный сайт?</div>
<div id="exc-address-container"> <div id="exc-address-container">
<div id="exc-address" class={inputProxyingState !== undefined ? ( inputProxyingState === true ? scopedCss.ifYes : scopedCss.ifNo ) : ''}> <div id="exc-address" class={inputProxyingState !== undefined ? ( inputProxyingState === true ? scopedCss.ifYes : scopedCss.ifNo ) : ''}>
<input placeholder="*.navalny.com" list="exc-list" id="exc-editor" <span>*.</span><input placeholder="navalny.com" list="exc-list" id="exc-editor"
value={this.state.trimmedInputValueOrSpace} value={this.state.trimmedInputValueOrSpace}
ref={(inputNode) => { this.rawInput = inputNode; }} ref={(inputNode) => { this.rawInput = inputNode; }}
onKeyDown={this.handleKeyDown.bind(this)} onKeyDown={this.handleKeyDown.bind(this)}
@ -323,10 +316,9 @@ export default function getExcEditor(theState) {
// 1. Option's value may be changed to hide it from the tooltip. // 1. Option's value may be changed to hide it from the tooltip.
// 2. Space is used in matching so even an empty input (replaced with space) has tooltip with prompts. // 2. Space is used in matching so even an empty input (replaced with space) has tooltip with prompts.
const ifProxy = excState;
return <option return <option
value={ this.state.hostToIfHidden[excHost] ? '\n' : excHost + ' ' } value={ this.state.isHostHidden[excHost] ? '\n' : excHost + ' ' }
label={ ifProxy === true ? labelIfProxied : (ifProxy === false ? labelIfNotProxied : labelIfAuto) }/> label={ excState === true ? labelIfProxied : (excState === false ? labelIfNotProxied : labelIfAuto) }/>
}) })
} }
@ -335,19 +327,19 @@ export default function getExcEditor(theState) {
<li><input id="this-auto" type="radio" checked name="if-proxy-this-site" onClick={this.handleRadioClick}/>{' '} <li><input id="this-auto" type="radio" checked name="if-proxy-this-site" onClick={this.handleRadioClick}/>{' '}
<label for="this-auto">{/*<span class="emoji">🔄(looks fat)</span>*/}<svg <label for="this-auto">{/*<span class="emoji">🔄(looks fat)</span>*/}<svg
class="icon" class="icon"
style="position: relative; top: 0.15em;"><use xlink:href="#iconLoopRound"></use></svg>&nbsp;{chrome.i18n.getMessage('auto')}</label> style="position: relative; top: 0.15em;"><use xlink:href="#iconLoopRound"></use></svg>&nbsp;авто</label>
</li> </li>
<li> <li>
<input id="this-yes" type="radio" name="if-proxy-this-site" checked={inputProxyingState === true} onClick={this.handleRadioClick}/> <input id="this-yes" type="radio" name="if-proxy-this-site" checked={inputProxyingState === true} onClick={this.handleRadioClick}/>
{' '}<label for="this-yes"> {' '}<label for="this-yes">
<span <span
class="emoji____buggy" class="emoji____buggy"
></span>&nbsp;{chrome.i18n.getMessage('yes')} ></span>&nbsp;да
</label> </label>
</li> </li>
<li> <li>
<input id="this-no" type="radio" name="if-proxy-this-site" checked={inputProxyingState === false} onClick={this.handleRadioClick}/> <input id="this-no" type="radio" name="if-proxy-this-site" checked={inputProxyingState === false} onClick={this.handleRadioClick}/>
{' '}<label for="this-no"><span class="emoji"></span>&nbsp;{chrome.i18n.getMessage('no')}</label></li> {' '}<label for="this-no"><span class="emoji"></span>&nbsp;нет</label></li>
</ol> </ol>
</section> </section>
); );

View File

@ -67,7 +67,7 @@ export default function getExceptions(theState) {
<InfoLi <InfoLi
type="checkbox" type="checkbox"
conf={{ conf={{
label: '<span>Собирать <a data-in-bg="false" href="../errors-to-exc/index.html">последние ошибки</a> сайтов</span>', label: '<span>Собирать <a href="../errors-to-exc/index.html">последние ошибки</a> сайтов</span>',
key: 'lookupLastErrors', key: 'lookupLastErrors',
desc: 'Собирать последние ошибки в запросах, чтобы вручную добавлять избранные из них в исключения.', desc: 'Собирать последние ошибки в запросах, чтобы вручную добавлять избранные из них в исключения.',
}} }}

View File

@ -1,7 +1,7 @@
import Inferno from 'inferno'; import Inferno from 'inferno';
import css from 'csjs-inject'; import css from 'csjs-inject';
export default function getFooter(theState) { export default function getFooter() {
const scopedCss = css` const scopedCss = css`
@ -17,22 +17,23 @@ export default function getFooter(theState) {
`; `;
return (props) => ( return function (props) {
return (
<div class="horPadded"> <div class="horPadded">
<section class={scopedCss.statusRow}> <section class={scopedCss.statusRow}>
<div class={scopedCss.status} style="will-change: contents"> <div clss={scopedCss.status} style="will-change: contents">{props.status}</div>
{typeof(props.status) === 'string' ? <div dangerouslySetInnerHTML={{ __html: props.status }}></div> : props.status}
</div>
</section> </section>
<footer class={scopedCss.controlRow + ' horFlex nowrap'}> <footer class={scopedCss.controlRow + ' horFlex nowrap'}>
<input type="button" value={chrome.i18n.getMessage('Finish')} disabled={props.ifInputsDisabled} style={{ display: theState.flags.ifInsideEdgeOptionsPage ? 'none' : 'initial' }} onClick={() => window.close()} /> <input type="button" value="Готово" disabled={props.ifInputsDisabled} onClick={() => window.close()} />
<a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Поддержать">{chrome.i18n.getMessage('Donate')}</a> <a href="../troubleshoot/index.html">
<a data-in-bg="false" href="../troubleshoot/index.html"> Проблемы?
{chrome.i18n.getMessage('ProblemsQ')}
</a> </a>
</footer> </footer>
</div> </div>
); );
}; };
};

View File

@ -25,9 +25,6 @@ export default function getInfoLi() {
.infoRow { .infoRow {
position: relative; position: relative;
} }
.infoRow a {
text-decoration: underline;
}
.infoRow > input[type="checkbox"] { .infoRow > input[type="checkbox"] {
position: relative; position: relative;
top: -0.08em; top: -0.08em;
@ -52,7 +49,7 @@ export default function getInfoLi() {
position: absolute; position: absolute;
white-space: initial; white-space: initial;
word-break: initial; word-break: initial;
/* top: 100%; Commented to get rid of bug when tooltip is placed below InfoLi children fields. */ top: 100%;
left: 0; left: 0;
right: 1em; right: 1em;
z-index: 1; z-index: 1;

View File

@ -7,10 +7,8 @@ export default function getLastUpdateDate(theState) {
componentWillMount() { componentWillMount() {
this.onStorageChangedHandler = (changes) => { this.onStorageChangedHandler = (changes) =>
const ac = changes.antiCensorRu; changes.lastPacUpdateStamp.newValue && this.forceUpdate();
return ac && ac.newValue && ac.newValue.lastPacUpdateStamp && this.forceUpdate();
};
chrome.storage.onChanged.addListener( this.onStorageChangedHandler ); chrome.storage.onChanged.addListener( this.onStorageChangedHandler );
@ -24,17 +22,17 @@ export default function getLastUpdateDate(theState) {
getDate(antiCensorRu) { getDate(antiCensorRu) {
let dateForUser = chrome.i18n.getMessage('never'); let dateForUser = 'никогда';
if( antiCensorRu.lastPacUpdateStamp ) { if( antiCensorRu.lastPacUpdateStamp ) {
let diff = Date.now() - antiCensorRu.lastPacUpdateStamp; let diff = Date.now() - antiCensorRu.lastPacUpdateStamp;
let units = chrome.i18n.getMessage('ms'); let units = 'мс';
const gauges = [ const gauges = [
[1000, chrome.i18n.getMessage('s')], [1000, 'с'],
[60, chrome.i18n.getMessage('min')], [60, 'мин'],
[60, chrome.i18n.getMessage('h')], [60, 'ч'],
[24, chrome.i18n.getMessage('d')], [24, 'дн'],
[7, chrome.i18n.getMessage('w')], [7, ' недель'],
[4, chrome.i18n.getMessage('m')], [4, ' месяцев'],
]; ];
for(const g of gauges) { for(const g of gauges) {
const diffy = Math.floor(diff / g[0]); const diffy = Math.floor(diff / g[0]);
@ -43,10 +41,10 @@ export default function getLastUpdateDate(theState) {
diff = diffy; diff = diffy;
units = g[1]; units = g[1];
} }
dateForUser = diff + units + ' ' + chrome.i18n.getMessage('ago'); dateForUser = diff + units + ' назад';
} }
return { return {
text: `${dateForUser} / ${antiCensorRu.pacUpdatePeriodInMinutes/60}${chrome.i18n.getMessage('h')}`, text: `${dateForUser} / ${antiCensorRu.pacUpdatePeriodInMinutes/60}ч`,
title: new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU'), title: new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU'),
}; };
@ -55,7 +53,7 @@ export default function getLastUpdateDate(theState) {
render(props) { render(props) {
const date = this.getDate(props.apis.antiCensorRu); const date = this.getDate(props.apis.antiCensorRu);
return (<div>{chrome.i18n.getMessage('Updated')}: <span class="updateDate" title={date.title}>{ date.text }</span></div>); return (<div>Обновлялись: <span class="updateDate" title={date.title}>{ date.text }</span></div>);
} }

View File

@ -31,7 +31,6 @@ export default function getMain(theState) {
const Notifications = getNotifications(theState); const Notifications = getNotifications(theState);
const checksName = 'pacMods'; const checksName = 'pacMods';
let selection = [0, 0]; // TODO: dirty hack but seems ok.
return class Main extends Component { return class Main extends Component {
@ -145,43 +144,23 @@ export default function getMain(theState) {
return createElement(TabPanel, Object.assign({}, props, { return createElement(TabPanel, Object.assign({}, props, {
tabs: [ tabs: [
{ {
label: chrome.i18n.getMessage('PAC_script'), label: 'PAC-скрипт',
content: createElement(PacChooser, props), content: createElement(PacChooser, props),
key: 'pacScript', key: 'pacScript',
}, },
{ {
label: chrome.i18n.getMessage('Exceptions'), label: 'Исключения',
content: createElement(Exceptions, props), content: createElement(Exceptions, props),
key: 'exceptions', key: 'exceptions',
}, },
{ {
label: chrome.i18n.getMessage('Own_proxies'), label: 'Свои прокси',
content: createElement( content: createElement(
ModList, ModList,
Object.assign({}, props, { Object.assign({}, props, {
orderedConfigs: this.state.catToOrderedMods['ownProxies'], orderedConfigs: this.state.catToOrderedMods['ownProxies'],
childrenOfMod: { childrenOfMod: {
customProxyStringRaw: ProxyEditor, customProxyStringRaw: ProxyEditor,
replaceDirectWith: ({ conf, onNewValue, ifInputsDisabled }) =>
(<input
style="width: 100%; margin: 0.5em 0"
disabled={ifInputsDisabled}
value={conf.value || ''}
onInput={(event) => {
const t = event.target;
selection = [t.selectionStart, t.selectionEnd];
onNewValue(true, t.value);
}}
ref={(input) => {
if (input) {
input.focus();
input.selectionStart = selection[0];
input.selectionEnd = selection[1];
}
}}
/>),
}, },
name: checksName, name: checksName,
}, modsHandlers) }, modsHandlers)
@ -189,7 +168,7 @@ export default function getMain(theState) {
key: 'ownProxies', key: 'ownProxies',
}, },
{ {
label: chrome.i18n.getMessage('Modifiers'), label: 'Модификаторы',
content: createElement( content: createElement(
ModList, ModList,
Object.assign({}, props, { Object.assign({}, props, {
@ -204,7 +183,7 @@ export default function getMain(theState) {
key: 'applyMods', key: 'applyMods',
}, },
{ {
label: chrome.i18n.getMessage('Notifications'), label: 'Уведомления',
content: createElement(Notifications, props), content: createElement(Notifications, props),
key: 'notifications', key: 'notifications',
}, },

View File

@ -55,14 +55,7 @@ export default function getModList(theState) {
const child = ifMayHaveChild && this.state.checks[index] const child = ifMayHaveChild && this.state.checks[index]
&& createElement( && createElement(
props.childrenOfMod[conf.key], props.childrenOfMod[conf.key],
Object.assign( Object.assign({}, props, {conf, onNewValue: (ifValid, newValue) => this.handleNewValue(ifValid, confMeta, newValue)})
{},
props,
{
conf,
onNewValue: (ifValid, newValue) => this.handleNewValue(ifValid, confMeta, newValue),
},
)
); );
return (<InfoLi return (<InfoLi

View File

@ -55,16 +55,16 @@ export default function getPacChooser(theState) {
constructor(props) { constructor(props) {
super(props); super();
this.state = { this.state = {
chosenPacName: 'none', chosenPacName: 'none',
}; };
this.updatePac = function updatePac(onSuccess) { this.updatePac = function updatePac(onSuccess) {
props.funs.conduct( props.funs.conduct(
chrome.i18n.getMessage('UpdatingDDD'), 'Обновляем...',
(cb) => theState.apis.antiCensorRu.syncWithPacProviderAsync(cb), (cb) => props.apis.antiCensorRu.syncWithPacProviderAsync(cb),
chrome.i18n.getMessage('UpdatedD'), 'Обновлено.',
onSuccess onSuccess
); );
}; };
@ -75,7 +75,7 @@ export default function getPacChooser(theState) {
getCurrentProviderId() { getCurrentProviderId() {
return theState.apis.antiCensorRu.getCurrentPacProviderKey() || 'none'; return this.props.apis.antiCensorRu.getCurrentPacProviderKey() || 'none';
} }
@ -94,24 +94,24 @@ export default function getPacChooser(theState) {
const pacKey = event.target.id; const pacKey = event.target.id;
if ( if (
pacKey === ( pacKey === (
theState.apis.antiCensorRu.getCurrentPacProviderKey() || 'none' this.props.apis.antiCensorRu.getCurrentPacProviderKey() || 'none'
) )
) { ) {
return false; return false;
} }
if (pacKey === 'none') { if (pacKey === 'none') {
this.props.funs.conduct( this.props.funs.conduct(
chrome.i18n.getMessage('DisablingDDD'), 'Отключение...',
(cb) => theState.apis.antiCensorRu.clearPacAsync(cb), (cb) => this.props.apis.antiCensorRu.clearPacAsync(cb),
chrome.i18n.getMessage('DisabledD'), 'Отключено.',
() => this.setState({ chosenPacName: 'none' }), () => this.setState({ chosenPacName: 'none' }),
checkChosenProvider checkChosenProvider
); );
} else { } else {
this.props.funs.conduct( this.props.funs.conduct(
chrome.i18n.getMessage('InstallingDDD'), 'Установка...',
(cb) => theState.apis.antiCensorRu.installPacAsync(pacKey, cb), (cb) => this.props.apis.antiCensorRu.installPacAsync(pacKey, cb),
chrome.i18n.getMessage('PacScriptWasInstalledD'), 'PAC-скрипт установлен.',
checkChosenProvider checkChosenProvider
); );
} }
@ -124,10 +124,10 @@ export default function getPacChooser(theState) {
const iddyToCheck = this.getCurrentProviderId(); const iddyToCheck = this.getCurrentProviderId();
return ( return (
<div> <div>
{props.flags.ifInsideOptionsPage && (<header>{chrome.i18n.getMessage('PAC_script')}:</header>)} {props.flags.ifInsideOptionsPage && (<header>PAC-скрипт:</header>)}
<ul> <ul>
{ {
[...theState.apis.antiCensorRu.getSortedEntriesForProviders(), {key: 'none', label: chrome.i18n.getMessage('Disable')}].map((provConf) => [...props.apis.antiCensorRu.getSortedEntriesForProviders(), {key: 'none', label: 'Отключить'}].map((provConf) =>
(<InfoLi (<InfoLi
onClick={this.radioClickHandler} onClick={this.radioClickHandler}
conf={provConf} conf={provConf}
@ -135,7 +135,7 @@ export default function getPacChooser(theState) {
name="pacProvider" name="pacProvider"
checked={iddyToCheck === provConf.key} checked={iddyToCheck === provConf.key}
ifInputsDisabled={props.ifInputsDisabled} ifInputsDisabled={props.ifInputsDisabled}
nodeAfterLabel={<a href="" class={scopedCss.updateButton} onClick={this.updateClickHandler}>[{chrome.i18n.getMessage('update')}]</a>} nodeAfterLabel={<a href="" class={scopedCss.updateButton} onClick={this.updateClickHandler}>[обновить]</a>}
/>) />)
) )
} }
@ -145,10 +145,10 @@ export default function getPacChooser(theState) {
<div class={scopedCss.fullLineHeight}> <div class={scopedCss.fullLineHeight}>
{ {
props.flags.ifMini props.flags.ifMini
? (<a class={scopedCss.otherVersion + ' emoji'} href="https://github.com/anticensority/runet-censorship-bypass/wiki/Различные-версии-расширения" ? (<a class={scopedCss.otherVersion + ' emoji'} href="https://rebrand.ly/ac-versions"
title={chrome.i18n.getMessage("FullVersion")}>🏋</a>) title="Полная версия">🏋</a>)
: (<a class={scopedCss.otherVersion + ' emoji'} href="https://github.com/anticensority/runet-censorship-bypass/wiki/Различные-версии-расширения" : (<a class={scopedCss.otherVersion + ' emoji'} href="https://rebrand.ly/ac-versions"
title={chrome.i18n.getMessage("VersionForSlowMachines")}>🐌</a>) title="Версия для слабых машин">🐌</a>)
} }
</div> </div>
</div> </div>
@ -159,7 +159,7 @@ export default function getPacChooser(theState) {
componentDidMount() { componentDidMount() {
if (theState.apis.antiCensorRu.ifFirstInstall) { if (this.props.apis.antiCensorRu.ifFirstInstall) {
this.updatePac(); this.updatePac();
} }

View File

@ -64,7 +64,7 @@ export default function getProxyEditor(theState) {
{ {
text-align: center; text-align: center;
} }
table.editor tr.proxyRow input[name="crededHostname"] { table.editor tr.proxyRow input[name="hostname"] {
padding: 0; padding: 0;
} }
@ -158,14 +158,8 @@ export default function getProxyEditor(theState) {
return true; return true;
}; };
const splitBySemi = (proxyString) => proxyString const splitBySemi = (proxyString) => proxyString.replace(/#.*$/mg, '').trim().split(/\s*;\s*/g).filter((s) => s);
.replace(/#.*$/mg, '') const joinBySemi = (strs) => strs.join(';\n') + ';';
.trim()
.split(/\s*;\r?\n\s*/g)
.map((s) => s.trim())
.filter((s) => s);
const joinBySemi = (strs) => strs.join(';\n');
const normalizeProxyString = (str) => joinBySemi(splitBySemi(str)); const normalizeProxyString = (str) => joinBySemi(splitBySemi(str));
const PROXY_TYPE_LABEL_PAIRS = [['PROXY', 'PROXY/HTTP'],['HTTPS'],['SOCKS4'],['SOCKS5'],['SOCKS']]; const PROXY_TYPE_LABEL_PAIRS = [['PROXY', 'PROXY/HTTP'],['HTTPS'],['SOCKS4'],['SOCKS5'],['SOCKS']];
@ -223,11 +217,11 @@ export default function getProxyEditor(theState) {
}, {}); }, {});
const type = that.state.selectedNewType; const type = that.state.selectedNewType;
const crededHostname = elements.newHostname; const hostname = elements.newHostname;
const port = elements.newPort; const port = elements.newPort;
const newValue = `${that.props.proxyStringRaw};\n${type} ${crededHostname}:${port}` const newValue = `${that.props.proxyStringRaw}; ${type} ${hostname}:${port}`
.trim().replace(/(\s*;\n\s*)+/, ';\n'); .trim().replace(/(\s*;\s*)+/, '; ');
that.props.setProxyStringRaw(true, newValue); that.props.setProxyStringRaw(true, newValue);
} }
@ -328,17 +322,10 @@ export default function getProxyEditor(theState) {
</tr> </tr>
{/* ADD NEW PROXY ENDS. */} {/* ADD NEW PROXY ENDS. */}
{ {
splitBySemi(this.props.proxyStringRaw).map((proxyAsStringRaw, index) => { splitBySemi(this.props.proxyStringRaw).map((proxyAsString, index) => {
const proxyAsString = proxyAsStringRaw.trim();
const {
type,
creds,
hostname,
port,
} = theState.utils.parseProxyScheme(proxyAsString);
const [type, addr] = proxyAsString.trim().split(/\s+/);
const [hostname, port] = addr.split(':');
return ( return (
<tr class={scopedCss.proxyRow}> <tr class={scopedCss.proxyRow}>
<td> <td>
@ -348,7 +335,7 @@ export default function getProxyEditor(theState) {
>X</button> >X</button>
</td> </td>
<td>{type}</td> <td>{type}</td>
<td><input value={`${creds && `${creds}@`}${hostname}`} name="crededHostname" readonly/></td> <td><input value={hostname} name="hostname" readonly/></td>
<td>{port}</td> <td>{port}</td>
<td> <td>
<button type="button" disabled={props.ifInputsDisabled} <button type="button" disabled={props.ifInputsDisabled}
@ -363,7 +350,6 @@ export default function getProxyEditor(theState) {
} }
</tbody> </tbody>
</table> </table>
<a href="https://github.com/anticensority/runet-censorship-bypass/wiki/Прокси-и-пароль">Запароленные прокси?</a>
</form> </form>
); );
} }
@ -404,34 +390,28 @@ export default function getProxyEditor(theState) {
const errors = splitBySemi(this.state.stashedExports) const errors = splitBySemi(this.state.stashedExports)
.map((proxyAsString) => { .map((proxyAsString) => {
const { const [rawType, addr, ...rest] = proxyAsString.split(/\s+/);
type, if (rest && rest.length) {
creds, return new Error(
hostname, `"${rest.join(', ')}" кажется мне лишним. Вы забыли ";"?`
port, );
username, }
password,
} = theState.utils.parseProxyScheme(proxyAsString);
const crededAddr = `${creds ? `${creds}@` : ''}${hostname}:${port}`;
const knownTypes = PROXY_TYPE_LABEL_PAIRS.map(([type, label]) => type); const knownTypes = PROXY_TYPE_LABEL_PAIRS.map(([type, label]) => type);
if( !knownTypes.includes(type.toUpperCase()) ) { if( !knownTypes.includes(rawType.toUpperCase()) ) {
return new Error( return new Error(
`Неверный тип ${type}. Известные типы: ${knownTypes.join(', ')}.` `Неверный тип ${rawType}. Известные типы: ${knownTypes.join(', ')}.`
); );
} }
if (!(crededAddr && /^(?:.+@)?[^:]+:\d+$/.test(crededAddr))) { if (!(addr && /^[^:]+:\d+$/.test(addr))) {
return new Error( return new Error(
`Адрес прокси "${crededAddr || ''}" не соответствует формату "<опц_логин>:<опц_пароль>@омен_или_IP>:<порт_из_цифр>".` `Адрес прокси "${addr || ''}" не соответствует формату "омен_или_IP>:<порт_из_цифр>".`
); );
} }
if (password && !username) { const [hostname, rawPort] = addr.split(':');
return new Error('Вашему пользователю не хватает имени?'); const port = parseInt(rawPort);
} if (port < 0 || port > 65535) {
const portInt = parseInt(port);
if (portInt < 0 || portInt > 65535) {
return new Error( return new Error(
`Порт "${port}" должен быть целым числом от 0 до 65535.` `Порт "${rawPort}" должен быть целым числом от 0 до 65535.`
); );
} }
return false; return false;
@ -515,7 +495,7 @@ PROXY foobar.com:8080; # Not HTTP!`.trim()}
value={ value={
this.state.stashedExports !== false this.state.stashedExports !== false
? this.state.stashedExports ? this.state.stashedExports
: (this.props.proxyStringRaw || '').replace(/\s*;\n\s*/g, ';\n') : (this.props.proxyStringRaw || '').replace(/\s*;\s*/g, ';\n')
} }
/></td> /></td>
</tr> </tr>
@ -534,12 +514,10 @@ PROXY foobar.com:8080; # Not HTTP!`.trim()}
return proxyStringRaw return proxyStringRaw
.replace(/#.*$/mg, '') // Strip comments. .replace(/#.*$/mg, '') // Strip comments.
.replace(/[^\S\r\n]*DIRECT[^\S\r\n]*/g, '') // Remove DIRECT from old versions. .replace(/[^\S\r\n]*DIRECT[^\S\r\n]*/g, '') // Remove DIRECT from old versions.
/*
.split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g ) .split( /(?:[^\S\r\n]*(?:;|\r?\n)+[^\S\r\n]*)+/g )
.map( (p) => p.trim() ) .map( (p) => p.trim() )
.filter((p) => p) .filter((p) => p)
.join(';\n'); .join(';\n');
*/
}; };

View File

@ -54,7 +54,7 @@ export default function getTabPannel({ flags, baseCss }) {
.navLabels { .navLabels {
background-color: var(--cr-grey-panel); background-color: var(--cr-grey-panel);
text-align: center; text-align: center;
min-width: 25em; min-width: 24em;
} }
.navLabels li label { .navLabels li label {
display: inline-block; display: inline-block;

View File

@ -4,7 +4,7 @@ export default function append(document, { flags }) {
document.querySelector('style').innerHTML = ` document.querySelector('style').innerHTML = `
/* GLOBAL VARIABLES */ /* GLOBAL VARIABLES */
body { :root {
--ribbon-color: #4169e1; --ribbon-color: #4169e1;
--blue-bg: dodgerblue; --blue-bg: dodgerblue;
--default-grey: #bfbfbf; --default-grey: #bfbfbf;
@ -12,13 +12,13 @@ export default function append(document, { flags }) {
--cr-icon-selected: #d7d7d7; --cr-icon-selected: #d7d7d7;
--cr-popup-border: #bababa; --cr-popup-border: #bababa;
--cr-grey-panel: #f2f2f2; --cr-grey-panel: #f2f2f2;
${ flags.ifInsideOptionsPage ? '' : 'max-width: 24em;' }
} }
/* BASE ELEMENTS */ /* BASE ELEMENTS */
body { body {
margin: 0; margin: 0;
max-width: 25em;
} }
a, a:visited { a, a:visited {
color: var(--ribbon-color); color: var(--ribbon-color);
@ -27,11 +27,9 @@ export default function append(document, { flags }) {
a:hover { a:hover {
text-decoration: underline; text-decoration: underline;
} }
/*
label { label {
user-select: none; user-select: none;
} }
*/
div, section, header, ul, ol { div, section, header, ul, ol {
margin: 0; margin: 0;
padding: 0; padding: 0;

View File

@ -9,13 +9,7 @@ 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;
@ -35,26 +29,15 @@ chrome.runtime.getBackgroundPage( (bgWindow) =>
// IF INSIDE OPTIONS TAB // IF INSIDE OPTIONS TAB
const currentTab = await new Promise( const currentTab = await new Promise(
(resolve) => winChrome.tabs.query( (resolve) => chrome.tabs.query(
{active: true, currentWindow: true}, {active: true, currentWindow: true},
([tab]) => resolve(tab), ([tab]) => resolve(tab)
) )
); );
// winChrome.runtime.sendMessage({ currentTab, eventName: 'POPUP_OPENED' });
theState.flags.ifInsideOptionsPage = !(currentTab && currentTab.url) || /.*:\/\/extensions\/\?options=/g.test(currentTab.url) || currentTab.url.startsWith('about:addons');
theState.flags.ifInsideEdgeOptionsPage = theState.flags.ifInsideOptionsPage && currentTab && currentTab.url && currentTab.url.startsWith('edge://');
theState.flags.ifInsideOptionsPage = !currentTab || currentTab.url.startsWith('chrome://extensions/?options=');
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);
@ -66,13 +49,7 @@ chrome.runtime.getBackgroundPage( (bgWindow) =>
); );
// READY TO RENDER // READY TO RENDER
const show = () => { document.documentElement.style.visibility = 'initial'; }; document.documentElement.style.display = 'initial';
if (theState.flags.ifInsideOptionsPage) {
show();
} else {
setTimeout(show, 200); // Mac bug: https://bugs.chromium.org/p/chromium/issues/detail?id=428044
}
} }
) )

View File

@ -5,31 +5,17 @@
<title>Устранение проблем</title> <title>Устранение проблем</title>
</head> </head>
<body> <body>
<h1>Устранение проблем</h1> <h1>Устранение проблем</h1>
<ol> <ol>
<li><a data-in-bg="false" href class="view-errors">Детали последних ошибок</a></li> <li><a href id="view-errors">Детали последних ошибок</a></li>
<li><a data-in-bg="false" href class="reset-settings">Сбросить настройки</a></li> <li><a href id="reset-settings">Сбросить настройки</a></li>
<li><a data-in-bg="false" href="https://github.com/anticensority/runet-censorship-bypass/wiki/Если-расширение-не-работает" target="_blank">Файл самопомощи</a></li> <li><a href="https://rebrand.ly/ac-support" target="_blank">Файл самопомощи</a></li>
<li><a data-in-bg="false" href="https://groups.google.com/g/anticensority">Напишите нам!</a></li> <li><a href="https://rebrand.ly/ac-contact">Напишите нам!</a></li>
</ol> </ol>
<h2>Для продвинутых</h2> <h2>Для продвинутых</h2>
<ol> <ol>
<li><a data-in-bg="false" href="https://github.com/anticensority/runet-censorship-bypass/wiki/Как-прочитать-логи%3F-%28для-продвинутых%29">Как прочитать логи?</a></li> <li><a href="https://rebrand.ly/ac-logs">Как прочитать логи?</a></li>
<li><a data-in-bg="false" href="../debug/index.html">Отладка PAC-скрипта</a></li> <li><a href="../debug/index.html">Отладка PAC-скрипта</a></li>
</ol>
<hr/>
<h1>Troubleshooting</h1>
<ol>
<li><a data-in-bg="false" href class="view-errors">Details of last errors</a></li>
<li><a data-in-bg="false" href class="reset-settings">Reset settings</a></li>
<li><a data-in-bg="false" href="https://github.com/anticensority/runet-censorship-bypass/wiki/Если-расширение-не-работает" target="_blank">Troubleshoot guide (ru)</a></li>
<li><a data-in-bg="false" href="https://groups.google.com/g/anticensority">Write to us!</a></li>
</ol>
<h2>Advanced</h2>
<ol>
<li><a data-in-bg="false" href="https://github.com/anticensority/runet-censorship-bypass/wiki/Как-прочитать-логи%3F-%28для-продвинутых%29">How to read logs?</a></li>
<li><a data-in-bg="false" href="../debug/index.html">PAC-script debugging</a></li>
</ol> </ol>
<script src="./index.js"></script> <script src="./index.js"></script>
<script src="../lib/keep-links-clickable.js"></script> <script src="../lib/keep-links-clickable.js"></script>

View File

@ -4,22 +4,15 @@ chrome.runtime.getBackgroundPage( (backgroundPage) =>
backgroundPage.apis.errorHandlers.installListenersOn( backgroundPage.apis.errorHandlers.installListenersOn(
window, 'TRBL', () => { window, 'TRBL', () => {
document.querySelectorAll('.reset-settings').forEach((el) => { document.getElementById('reset-settings').onclick = () => {
el.onclick = () => {
backgroundPage.localStorage.clear(); backgroundPage.localStorage.clear();
chrome.storage.local.clear( () => chrome.runtime.reload() ); chrome.storage.local.clear( () => chrome.runtime.reload() );
}; };
});
document.querySelectorAll('.view-errors').forEach((el) => { document.getElementById('view-errors').onclick = () =>
el.onclick = () =>
backgroundPage.apis.errorHandlers.viewError('all'); backgroundPage.apis.errorHandlers.viewError('all');
});
}, })
),
); );

View File

@ -1,10 +1,10 @@
'use strict'; 'use strict';
const Storage = require('symlink-to/project-root/tools/sinon-storage'); const Storage = require('_project-root/tools/sinon-storage');
const Chai = require('chai'); const Chai = require('chai');
const Mocha = require('mocha'); const Mocha = require('mocha');
const CachelessRequire = require('symlink-to/project-root/tools/cacheless-require')(module); const CachelessRequire = require('_project-root/tools/cacheless-require')(module);
Mocha.describe('window.apis.pacKitchen', function () { Mocha.describe('window.apis.pacKitchen', function () {

View File

@ -3,7 +3,7 @@
const Chai = require('chai'); const Chai = require('chai');
const Mocha = require('mocha'); const Mocha = require('mocha');
const CachelessRequire = require('symlink-to/project-root/tools/cacheless-require')(module); const CachelessRequire = require('_project-root/tools/cacheless-require')(module);
Mocha.describe('window.utils', function () { Mocha.describe('window.utils', function () {

View File

@ -69,24 +69,27 @@
const reinit = function reinit() { const reinit = function reinit() {
privates._strToHostObj = [ // Defaults.
/* Please, don't use proxies directly (without PAC-script). */ const _antizapret = {
'n.thenewone.lol', /* Don't use directly, please.
's.thenewone.lol', Encoded to counter abuse. */
// antizapret.prostovpn.org: 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',
'proxy.antizapret.prostovpn.org',
'proxy-ssl.antizapret.prostovpn.org',
'proxy-nossl.antizapret.prostovpn.org',
'proxy-fbtw-ssl.antizapret.prostovpn.org',
].reduce((acc, hostname) => Object.assign(acc, { [hostname]: { host: hostname }}), {
// Defaults:
localhost: { host: 'localhost' },
});
privates._ipToHostObj = {
'127.0.0.1': { host: 'localhost' },
'0.0.0.0': { host: 'localhost' },
}; };
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. // Persisted.
const ipToHost = _state(ip2host); const ipToHost = _state(ip2host);
@ -112,7 +115,7 @@
const getIpsFor = function getIpsFor(host, cb = mandatory()) { const getIpsFor = function getIpsFor(host, cb = mandatory()) {
if (host.trim() === 'localhost') { if (host.trim() === 'localhost') {
return cb(null, ['127.0.0.1', '0.0.0.0', '::1']); return cb(null, ['127.0.0.1', '::1']);
} }
const types = [1, 28]; const types = [1, 28];
const promises = types.map( const promises = types.map(
@ -234,11 +237,12 @@
getIpsFor(hostStr, (err, ips, ...warns) => { getIpsFor(hostStr, (err, ips, ...warns) => {
console.log('Got IPs + err?:', ips, err); console.log('Got IPs + err?:', ips, err);
if (err) { if (!err) {
reject([err, null, ...warns]);
return;
}
resolveIps(ips); resolveIps(ips);
} else {
reject([err, null, ...warns]);
}
}); });
}).then( }).then(
@ -260,9 +264,7 @@
_updateAllAsync(cb = mandatory()) { _updateAllAsync(cb = mandatory()) {
const hostArr = Object.keys(privates._strToHostObj) const hostArr = Object.keys(privates._strToHostObj);
.filter((hostStr) => hostStr !== 'localhost');
console.log('Update all:', hostArr); console.log('Update all:', hostArr);
const promises = hostArr.map( const promises = hostArr.map(
@ -275,18 +277,29 @@
); );
Promise.all( promises ).then( (cbsRes) => { Promise.all( promises ).then( (cbsRes) => {
let ipErrors = cbsRes.map( ([err]) => err ).filter( (err) => err ); const errors = cbsRes.map( ([err]) => err ).filter( (err) => err );
let warns = []; let newError;
if (ipErrors.length) { const ifAllErrors = cbsRes.length === errors.length;
warns = [clarify( if (errors.length) {
ipErrors, if (ifAllErrors) {
newError = errors.shift();
} else {
newError = errors;
}
newError = clarify(
newError,
'Не удалось получить один или несколько IP адресов для' + 'Не удалось получить один или несколько IP адресов для' +
' прокси-серверов. Иконка для уведомления об обходе' + ' прокси-серверов. Иконка для уведомления об обходе' +
' блокировок может не отображаться.' ' блокировок может не отображаться.'
)]; );
} else {} if (ifAllErrors) {
cb(null, null, ...warns); return cb(newError);
}
}
cb(null, null, newError);
}); });
}, },
_replaceAllAsync(hostArr = mandatory(), cb) { _replaceAllAsync(hostArr = mandatory(), cb) {

View File

@ -2,137 +2,7 @@
{ {
const timeouted = window.utils.timeouted; chrome.webNavigation.onErrorOccurred.addListener((details) => {
const isProxied = (requestDetails) => false;
const isProxySideError = (details) =>
/* About !main_frame: Main frame websocket errors are followed by webnavigation errors
which chrome-internals code resets the state of the popup.
*/
details.error === 'net::ERR_TUNNEL_CONNECTION_FAILED' && details.type !== 'main_frame' && isProxied(details) ||
details.error === 'NS_ERROR_CONNECTION_REFUSED' && Boolean(details.proxyInfo);
const urlToA = (url) => new URL(url).host.link(
encodeURIComponent(url),
);
const isProxyErrorHandledAsync = async (details) => {
if (!isProxySideError(details)) {
return;
}
let fromPageHref = '';
let toUrlHref = '';
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)}`;
}
toUrlHref = new URL(details.url).href;
youMayReportHtml = ` Вы можете <b>${'сообщить об ошибке'.link(
encodeURIComponent(
'/pages/report-proxy-error/index.html?' +
new URLSearchParams({
fromPageHref,
requestFailedTo: toUrlHref,
}),
),
)}</b> администратору прокси.`;
} catch(e) {
/* For malformed urls. */
console.log('Error handling malformed URLs:', details);
const msg = `Error handling malformed URLs: ${JSON.stringify(details, null, 2)}`;
throw new TypeError(msg);
}
// Service workers have tabId = -1, get active tubId for them.
const tabId = details.tabId < 0
? await new Promise((resolve) => chrome.tabs.query(
{ active: true },
([tab]) => resolve(tab.id)),
)
: details.tabId;
const [oldPopup, oldText, oldColor] = await new Promise((resolve) =>
chrome.browserAction.getPopup({ tabId }, (oldPopup) =>
chrome.browserAction.getBadgeText({ tabId }, (oldText) =>
chrome.browserAction.getBadgeBackgroundColor({ tabId }, (oldColor) => resolve([
oldPopup,
oldText,
oldColor,
])),
),
)
);
const popupPrefix = chrome.runtime.getURL(`/pages/options/index.html?status=<span style="color: red">🔥 Прокси-сервер отказался обслуживать запрос к%20`);
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);
const restoringHandler = timeouted((eventDetails) => {
if(eventDetails && tabId !== ((eventDetails.currentTab || eventDetails).id || eventDetails.tabId)) {
return;
}
clearInterval(timer);
chrome.browserAction.setPopup({ tabId, popup: oldPopup});
chrome.browserAction.setBadgeBackgroundColor({ tabId, color: oldColor});
chrome.browserAction.setBadgeText({ tabId, text: oldText});
chrome.runtime.onMessage.removeListener(restoringHandler);
chrome.tabs.onRemoved.removeListener(restoringHandler);
chrome.tabs.onReplaced.removeListener(restoringHandler);
chrome.webNavigation.onBeforeNavigate.removeListener(restoringHandler);
});
chrome.runtime.onMessage.addListener(restoringHandler);
chrome.tabs.onRemoved.addListener(restoringHandler);
chrome.tabs.onReplaced.addListener(restoringHandler); // When does it happen?
chrome.webNavigation.onBeforeNavigate.addListener(restoringHandler);
return true;
};
chrome.webNavigation.onErrorOccurred.addListener(timeouted(async (details) => {
const tabId = details.tabId; const tabId = details.tabId;
if ( !(details.frameId === 0 && tabId >= 0) || if ( !(details.frameId === 0 && tabId >= 0) ||
@ -142,16 +12,13 @@
].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?status=Правый клик по иконке — меню инструментов!#tab=exceptions', popup: './pages/options/index.html#tab=exceptions&status=Правый клик по иконке — меню инструментов!',
}); });
chrome.browserAction.setBadgeBackgroundColor({ window.chrome.browserAction.setBadgeBackgroundColor({
tabId, tabId,
color: '#4285f4', color: '#4285f4',
}); });
@ -160,10 +27,6 @@
text: '●●●', text: '●●●',
}); });
})); });
chrome.webRequest.onErrorOccurred.addListener(
timeouted(isProxyErrorHandledAsync),
{urls: ['<all_urls>']},
);
} }

View File

@ -9,6 +9,8 @@
to "loading". to "loading".
So if you set a title earlier it may be cleared by browser. So if you set a title earlier it may be cleared by browser.
It pertains not only to page refesh but to newly opened pages too. It pertains not only to page refesh but to newly opened pages too.
Also on loosing title see:
https://github.com/ilyaigpetrov/repository-for-chrome-bugs/blob/master/browserAction-title-lost-after-setting/background.js
Crazy parallel Chrome. Crazy parallel Chrome.
**/ **/
@ -41,7 +43,7 @@
const setRedBadge = (opts) => { const setRedBadge = (opts) => {
chrome.browserAction.setBadgeBackgroundColor({ window.chrome.browserAction.setBadgeBackgroundColor({
color: '#db4b2f', color: '#db4b2f',
}); });
chrome.browserAction.setBadgeText(opts); chrome.browserAction.setBadgeText(opts);
@ -57,7 +59,7 @@
if (err) { if (err) {
// E.g., no tab with such id happens. // E.g., no tab with such id happens.
// Because requestDetails may be stale. // Because requestDetails may be stale.
console.log('Notifier error ignored (this is normal, it happens):', err); console.log('Notifier error ignored:', err);
return cb(); return cb();
} }
const ifTitleSetAlready = /\n/.test(title); const ifTitleSetAlready = /\n/.test(title);
@ -140,23 +142,6 @@
if (!host) { if (!host) {
return; return;
} }
{
/*
If we fetch a resource from a proxy address it is almost never proxied and
shouldn't be shown.
Think about localhost as a proxy and a user working with a web site on localhost.
*/
/*
Host is constructed from hostname and port. Hostname never contains port,
it is an ip or a domain name. See hostname and host
in `new URL('https://localhost:8080')`.
*/
const hostnameFromUrl = new URL(requestDetails.url).hostname;
const hostnameFromProxy = new URL(`https://${host}`).hostname;
if (hostnameFromUrl === requestDetails.ip || hostnameFromUrl === hostnameFromProxy) {
return;
}
}
const ifMainFrame = requestDetails.type === 'main_frame'; const ifMainFrame = requestDetails.type === 'main_frame';

View File

@ -1,32 +0,0 @@
<!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>

View File

@ -1,39 +0,0 @@
'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)}`;
});
},
),
);

View File

@ -1,30 +1,31 @@
'use strict'; 'use strict';
const pacUrls = [
// GitHub.io (anticensority), cached:
'https://anticensority.github.io/generated-pac-scripts/anticensority.pac',
// GitHub repo (anticensority), cached:
'https://raw.githubusercontent.com/anticensority/generated-pac-scripts/master/anticensority.pac',
];
const commonContext = { const commonContext = {
version: '1.66', version: '1.5',
anticensorityPacUrls: [ anticensorityPacUrls: [
...pacUrls, // First official, shortened:
], 'https://rebrand.ly/ac-chrome-anticensority-pac',
// Second official, Cloud Flare with caching:
'https://anticensority.tk/generated-pac-scripts/anticensority.pac',
// GitHub.io (anticensority):
'\x68\x74\x74\x70\x73\x3a\x2f\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x67\x69\x74\x68\x75\x62\x2e\x69\x6f\x2f\x67\x65\x6e\x65\x72\x61\x74\x65\x64\x2d\x70\x61\x63\x2d\x73\x63\x72\x69\x70\x74\x73\x2f\x61\x6e\x74\x69\x63\x65\x6e\x73\x6f\x72\x69\x74\x79\x2e\x70\x61\x63',
// GitHub repo (anticensority):
'\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\x69\x74\x79\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',
// Old, deprecated:
'https://anticensorship-russia.tk/generated-pac-scripts/anticensority.pac',
// Google Drive (0.17, anticensority):
'\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\x32\x6d\x68\x42\x67\x46\x6e\x66\x34\x70\x45\x4c\x56\x6c\x47\x4e\x54\x42\x45\x4d\x58\x4e\x6d\x52\x58\x63',
]
}; };
exports.contexts = {}; exports.contexts = {};
const extra_permissions = ', "webRequest", "webRequestBlocking", "webNavigation"';
exports.contexts.full = Object.assign({}, commonContext, { exports.contexts.full = Object.assign({}, commonContext, {
versionSuffix: '', versionSuffix: '',
nameSuffixEn: '', nameSuffixEn: '',
nameSuffixRu: '', nameSuffixRu: '',
extra_permissions, extra_permissions: ', "webRequest", "webNavigation"',
persistent: '', persistent: '',
scripts_0x: '',
scripts_2x: ', "20-ip-to-host-api.js"', scripts_2x: ', "20-ip-to-host-api.js"',
scripts_8x: ', "80-error-menu.js", "83-last-errors.js", "85-block-informer.js"', scripts_8x: ', "80-error-menu.js", "83-last-errors.js", "85-block-informer.js"',
}); });
@ -35,34 +36,18 @@ exports.contexts.mini = Object.assign({}, commonContext, {
nameSuffixRu: ' МИНИ', nameSuffixRu: ' МИНИ',
extra_permissions: '', extra_permissions: '',
persistent: '"persistent": false,', persistent: '"persistent": false,',
scripts_0x: '',
scripts_2x: ', "20-for-mini-only.js"', scripts_2x: ', "20-for-mini-only.js"',
scripts_8x: '', scripts_8x: '',
}); });
exports.contexts.firefox = Object.assign({}, commonContext, {
versionSuffix: '',
nameSuffixEn: '',
nameSuffixRu: '',
extra_permissions,
persistent: '',
scripts_0x: ', "01-chrome-proxy-settings.js"',
scripts_2x: ', "20-ip-to-host-api.js"',
scripts_8x: ', "80-error-menu.js", "83-last-errors.js", "85-block-informer.js"',
});
exports.contexts.beta = Object.assign({}, commonContext, { exports.contexts.beta = Object.assign({}, commonContext, {
anticensorityPacUrls: [ anticensorityPacUrls: ['https://rebrand.ly/ac-beta-pac'],
'https://raw.githubusercontent.com/anticensority/for-testing/master/anticensority.pac', version: '1.5',
'https://anticensority.github.io/for-testing/anticensority.pac',
],
version: '1.14',
versionSuffix: '', versionSuffix: '',
nameSuffixEn: ' FOR TESTING', nameSuffixEn: ' FOR TESTING',
nameSuffixRu: ' ДЛЯ ТЕСТОВ', nameSuffixRu: ' ДЛЯ ТЕСТОВ',
extra_permissions, extra_permissions: ', "webRequest", "webNavigation"',
persistent: '', persistent: '',
scripts_0x: '',
scripts_2x: ', "20-ip-to-host-api.js"', scripts_2x: ', "20-ip-to-host-api.js"',
scripts_8x: ', "80-error-menu.js", "83-last-errors.js", "85-block-informer.js"', scripts_8x: ', "80-error-menu.js", "83-last-errors.js", "85-block-informer.js"',
}); });

File diff suppressed because it is too large Load Diff

17
package.json Normal file → Executable file
View File

@ -2,16 +2,17 @@
"name": "subjective-good-is-evil", "name": "subjective-good-is-evil",
"version": "1.0.0", "version": "1.0.0",
"description": "", "description": "",
"main": "index.js",
"scripts": { "scripts": {
"postinstall": "opencollective postinstall" "generate": "cd pac-generator && npm start",
"prestart": "npm run generate",
"start": "npm run bench",
"bench": "cd ./pac-performance-analyses/benchmark && dnx run ../../pac-generator/generated-PACs",
"test": "rm -r pac-generator/generated-PACs"
}, },
"author": "anticensority+owners@googlegroups.com", "author": "ilyaigpetrov",
"license": "ISC", "license": "ISC",
"dependencies": { "devDependencies": {
"opencollective": "^1.0.3" "http-server": "^0.8.5"
},
"collective": {
"type": "opencollective",
"url": "https://opencollective.com/anticensority"
} }
} }