diff --git a/extensions/chromium/runet-censorship-bypass/README.md b/extensions/chromium/runet-censorship-bypass/README.md index 888109a..7e3e365 100644 --- a/extensions/chromium/runet-censorship-bypass/README.md +++ b/extensions/chromium/runet-censorship-bypass/README.md @@ -1,30 +1,20 @@ -# Dev +# Install -Linting JS: `npm run lint` +``` +npm instll +cd src/extension-common/pages/options/ +npm install +cd - +npm start +use your build/extension-beta +``` -# О расширении +# Release -Обход интернет-цензуры в России пока что не является преступлением. - -Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ -к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным -сайтам, случайно заблокированным в силу разных причин. - -Проксирует только заблокированные сайты, оставляя нетронутыми все остальные. - -Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org. - -Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки. -Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку. - -Синяя лента – кампания фонда EFF в защиту свобод слова, прессы и союзов. - -Если расширение не работает: https://git.io/vgDDj - -Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia -Группа в 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 +1. `vim src/extension-common/pages/options/src/components/App.js` +2. Change github link there. +3. `npm run release` +4. Change back: `vim src/extension-common/pages/options/src/components/App.js` +5. `vim src/templates-data.js` and bump version. +6. Commit bumped version. +7. Merge development to production (usually after deployment and testing and many patches). diff --git a/extensions/chromium/runet-censorship-bypass/description.md b/extensions/chromium/runet-censorship-bypass/description.md new file mode 100644 index 0000000..6f28dbb --- /dev/null +++ b/extensions/chromium/runet-censorship-bypass/description.md @@ -0,0 +1,26 @@ +# О расширении + +Обход интернет-цензуры в России пока что не является преступлением. + +Расширение позволяет обходить блокировки РосКомНадзора, давая вам доступ +к библиотекам, энциклопедиям, сайтам оппозиционеров, а также к неповинным +сайтам, случайно заблокированным в силу разных причин. + +Проксирует только заблокированные сайты, оставляя нетронутыми все остальные. + +Устанавливает PAC-скрипт, работающий через сервера anticenz.org и antizapret.prostovpn.org. + +Обновляет PAC-скрипт каждые 4 часа, что составляет примерно 7MB трафика в сутки. +Также расширение постоянно потребляет ~15MB памяти для информирования о блокировках через иконку. + +Синяя лента – кампания фонда EFF в защиту свобод слова, прессы и союзов. + +Если расширение не работает: https://git.io/vgDDj + +Антицензура на Реддите: https://www.reddit.com/r/anticensorship_russia +Группа в 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 diff --git a/extensions/chromium/runet-censorship-bypass/gulpfile.js b/extensions/chromium/runet-censorship-bypass/gulpfile.js index 89f6afa..1d751a6 100644 --- a/extensions/chromium/runet-censorship-bypass/gulpfile.js +++ b/extensions/chromium/runet-censorship-bypass/gulpfile.js @@ -56,52 +56,31 @@ const contexts = require('./src/templates-data').contexts; const excFolder = (name) => [`!./src/**/${name}`, `!./src/**/${name}/**/*`]; const excluded = [ ...excFolder('test') , ...excFolder('node_modules'), ...excFolder('src') ]; -const commonWoTests = ['./src/extension-common/**/*', ...excluded]; const miniDst = './build/extension-mini'; const fullDst = './build/extension-full'; const betaDst = './build/extension-beta'; +const firefoxDst = './build/extension-firefox'; -gulp.task('_cp-common', ['clean'], function(cb) { +const commonSrc = './src/extension-common/**/*';; +const miniSrc = './src/extension-mini/**/*'; +const fullSrc = './src/extension-full/**/*'; +const firefoxSrc = './src/extension-firefox/**/*'; - let fins = 0; - const intheend = () => { - if (++fins === 2) { - cb(); - } - }; +const joinSrc = (...args) => [...args, ...excluded]; - gulp.src(commonWoTests) - //.pipe(changed(miniDst)) - .pipe(templatePlugin(contexts.mini)) - .pipe(gulp.dest(miniDst)) - .on('end', intheend); +gulp.task('_cp-mini', function(cb) { - 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]) + gulp.src(joinSrc(commonSrc, miniSrc)) //.pipe(changed(miniDst)) .pipe(templatePlugin(contexts.mini)) .pipe(gulp.dest(miniDst)) .on('end', cb); }); -gulp.task('_cp-full', ['_cp-common'], function(cb) { +gulp.task('_cp-full', function(cb) { - gulp.src(['./src/extension-full/**/*', ...excluded]) + gulp.src(joinSrc(commonSrc, fullSrc)) //.pipe(changed(fullDst)) .pipe(templatePlugin(contexts.full)) .pipe(gulp.dest(fullDst)) @@ -109,9 +88,19 @@ gulp.task('_cp-full', ['_cp-common'], function(cb) { }); -gulp.task('_cp-beta', ['_cp-common'], function(cb) { +gulp.task('_cp-firefox', function(cb) { - gulp.src(['./src/extension-full/**/*', ...excluded]) + gulp.src(joinSrc(commonSrc, fullSrc, firefoxSrc)) + //.pipe(changed(fullDst)) + .pipe(templatePlugin(contexts.firefox)) + .pipe(gulp.dest(firefoxDst)) + .on('end', cb); + +}); + +gulp.task('_cp-beta', function(cb) { + + gulp.src(joinSrc(commonSrc, fullSrc)) //.pipe(changed(fullDst)) .pipe(templatePlugin(contexts.beta)) .pipe(gulp.dest(betaDst)) @@ -119,5 +108,6 @@ gulp.task('_cp-beta', ['_cp-common'], function(cb) { }); -gulp.task('build:all', ['_cp-mini', '_cp-full', '_cp-beta']); +gulp.task('build:all', ['_cp-mini', '_cp-full', '_cp-beta', '_cp-firefox']); gulp.task('build:beta', ['_cp-beta']); +gulp.task('build:firefox', ['_cp-firefox']); diff --git a/extensions/chromium/runet-censorship-bypass/package.json b/extensions/chromium/runet-censorship-bypass/package.json index a05f423..68eea3a 100644 --- a/extensions/chromium/runet-censorship-bypass/package.json +++ b/extensions/chromium/runet-censorship-bypass/package.json @@ -8,7 +8,8 @@ "gulp": "gulp", "test": "mocha --recursive ./src/**/test/*", "subpages": "cd ./src/extension-common/pages/options/ && npm run build && cd -", - "start": "npm run subpages && npm run gulp build:beta", + "subpages:dev": "cd ./src/extension-common/pages/options/ && npm run build:dev:nocomp && cd -", + "start": "npm run subpages:dev && npm run gulp build:beta", "release": "npm run subpages && npm run gulp build:all" }, "author": "Ilya Ig. Petrov", diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/00-init-apis.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/00-init-apis.js index 1b1566c..ce40fcf 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/00-init-apis.js +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/00-init-apis.js @@ -241,6 +241,9 @@ const compareVersions = (a, b) => versionToInt(a) - versionToInt(b); window.apis = { + platform: { + ifFirefox: navigator.userAgent.toLowerCase().includes('firefox'), + }, version: { ifMini: false, build: chrome.runtime.getManifest().version.replace(/\d+\.\d+\./g, ''), @@ -249,4 +252,43 @@ }, }; + // Shims for FireFox + + if (!chrome.proxy.settings) { + + const ffxStore = window.utils.createStorage('firefox-only'); + + + chrome.proxy.settings = { + get: (_, cb) => { + + let currentSettings = ffxStore('proxySettings') || {}; + currentSettings.levelOfControl = 'controlled_by_this_extension'; // May be lie, but this field is required. + cb && cb(currentSettings); + + }, + onChange: { + addListener: () => {}, + }, + set: (details, cb) => { + + browser.proxy.unregister(); + browser.proxy.register('./default.pac.js'); + + + // browser.proxy.onProxyError.addListener((...err) => { console.log('ERROR IN PAC:', ...err) }); + + browser.runtime.sendMessage(details, {toProxyScript: true}); + ffxStore('proxySettings', details); + cb && cb(); + + }, + }; + const proxySettings = ffxStore('proxySettings'); + if (proxySettings) { + chrome.proxy.settings.set(proxySettings); + } + + } + } diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/11-error-handlers-api.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/11-error-handlers-api.js index 96d3263..9090c55 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/11-error-handlers-api.js +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/11-error-handlers-api.js @@ -8,6 +8,9 @@ const errorJsonReplacer = function errorJsonReplacer(key, value) { // fooWindow.ErrorEvent !== barWindow.ErrorEvent + if (value === window) { + return; // STUPID, because other window object may be passed. + } if (!( value && value.constructor && ['Error', 'Event'].some( (suff) => value.constructor.name.endsWith(suff) @@ -114,20 +117,22 @@ isControllable(details) { - this.ifControllable = window.utils.areSettingsControllableFor(details); + if (details) { + this.ifControllable = window.utils.areSettingsControllableFor(details); - if (this.ifControllable) { - this.ifControlled = window.utils.areSettingsControlledFor(details); - } else { - this.ifControlled = false; - } + if (this.ifControllable) { + this.ifControlled = window.utils.areSettingsControlledFor(details); + } else { + this.ifControlled = false; + } - if (this.ifControlled) { - chrome.browserAction.setIcon( {path: './icons/default-128.png'} ); - } else { - chrome.browserAction.setIcon({ - path: './icons/default-grayscale-128.png', - }); + if (this.ifControlled) { + chrome.browserAction.setIcon( {path: './icons/default-128.png'} ); + } else { + chrome.browserAction.setIcon({ + path: './icons/default-grayscale-128.png', + }); + } } return this.ifControllable; @@ -136,7 +141,9 @@ isControlled(details) { - this.isControllable(details); + if (details) { + this.isControllable(details); + } return this.ifControlled; }, @@ -175,16 +182,15 @@ const message = errOrMessage.message || errOrMessage.toString(); chrome.notifications.create( id, - { + Object.assign({ title: title, message: message, contextMessage: context, - requireInteraction: ifSticky, type: 'basic', iconUrl: './icons/' + icon, appIconMaskUrl: './icons/default-mask-128.png', isClickable: true, - } + }, window.apis.platform.ifFirefox ? {} : { requireInteraction: ifSticky }), ); }, @@ -253,7 +259,11 @@ error: "net::ERR_PAC_SCRIPT_FAILED", fatal: false, */ - const ifConFail = details.error === 'net::ERR_PROXY_CONNECTION_FAILED'; + const ifConFail = [ + 'net::ERR_TUNNEL_CONNECTION_FAILED', + 'net::ERR_PROXY_CONNECTION_FAILED', + ].includes(details.error); + if (ifConFail) { // Happens if you return neither prixies nor "DIRECT". // Ignore it. @@ -262,7 +272,7 @@ console.warn('PAC ERROR', details); // TOOD: add "view pac script at this line" button. handlers.mayNotify('pac-error', 'Ошибка PAC!', - details.error + '\n' + details.details, + (details.error || details.message /* Firefox */) + '\n' + details.details, {icon: 'pac-error-128.png'} ); diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/35-pac-kitchen-api.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/35-pac-kitchen-api.js index 64e2c03..1d0ad68 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/35-pac-kitchen-api.js +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/35-pac-kitchen-api.js @@ -317,11 +317,11 @@ return pacMods.ifNoMods ? pacData : pacData + `${ kitchenStartsMark } /******/ -/******/;+function(global) { +/******/;(function(global) { /******/ "use strict"; /******/ /******/ const originalFindProxyForURL = FindProxyForURL; -/******/ global.FindProxyForURL = function(url, host) { +/******/ const tmp = function(url, host) { /******/ ${ function() { @@ -423,7 +423,7 @@ ${ pacMods.filteredCustomsString pacMods.ifUsePacScriptProxies ) { return res + ` -/******/ return pacScriptProxies + directIfAllowed;`; +/******/ return (pacScriptProxies + directIfAllowed) || "DIRECT";`; } return res + ` @@ -451,9 +451,15 @@ ${ pacMods.filteredCustomsString }() } - }; +/******/ }; -}(this);`; +/******/ if (global) { +/******/ global.FindProxyForURL = tmp; +/******/ } else { +/******/ FindProxyForURL = tmp; +/******/ } + +/*****/})(this);`; }, diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/default.pac.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/default.pac.js new file mode 100644 index 0000000..6b4603b --- /dev/null +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/default.pac.js @@ -0,0 +1,17 @@ +var coolFind = () => {}; +this.FindProxyForURL = function (...args) { + return coolFind(...args); +}; + +const dnsResolve = this.dnsResolve || (() => null); // Welcome to hell! Someone forgot dns. + +browser.runtime.onMessage.addListener((details) => { + const pacData = + details && details.value && details.value.pacScript && details.value.pacScript.data; + if (!pacData) { + throw new Error('Never install empty PAC scripts!'); + } + coolFind = (function() { eval(pacData); return FindProxyForURL; })(); + +}); + diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/manifest.tmpl.json b/extensions/chromium/runet-censorship-bypass/src/extension-common/manifest.tmpl.json index ad52cb9..6e1cb73 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/manifest.tmpl.json +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/manifest.tmpl.json @@ -27,6 +27,7 @@ ${persistent} "scripts": [ "00-init-apis.js" + ${scripts_0x} , "11-error-handlers-api.js" , "12-errors-lib.js" , "13-http-lib.js" @@ -45,5 +46,7 @@ "options_ui": { "page": "/pages/options/index.html", "chrome_style": false - } + }, + + "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'" } diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/debug/index.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/debug/index.js index ef0e255..6157146 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/debug/index.js +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/debug/index.js @@ -1,60 +1,63 @@ 'use strict'; -const setStatusTo = (msg) => document.getElementById('status').innerHTML = msg; +chrome.runtime.getBackgroundPage((bgWin) => { -const red = (text) => '' + text + ''; + const setStatusTo = (msg) => document.getElementById('status').innerHTML = msg; -const editor = window.ace.edit('editor'); -editor.getSession().setOptions({ - mode: 'ace/mode/javascript', - useSoftTabs: true, -}); - -chrome.proxy.settings.onChange.addListener( - (details) => setStatusTo(red( details.levelOfControl + '!') ) -); - -function _read() { - - chrome.proxy.settings.get({}, (details) => { - - let control = details.levelOfControl; - if (control.startsWith('controlled_by_other')) { - control = red(control); - } - setStatusTo(control); - console.log(details); - const pac = details.value.pacScript; - const data = pac && pac.data || 'PAC скрипт не установлен.'; - editor.setValue( data ); + const red = (text) => '' + text + ''; + const editor = window.ace.edit('editor'); + editor.getSession().setOptions({ + mode: 'ace/mode/javascript', + useSoftTabs: true, }); -} + bgWin.chrome.proxy.settings.onChange.addListener( + (details) => setStatusTo(red( details.levelOfControl + '!') ) + ); -document.querySelector('#read-button').onclick = _read; + function _read() { -document.querySelector('#save-button').onclick = () => { + bgWin.chrome.proxy.settings.get({}, (details) => { + + let control = details.levelOfControl; + if (control.startsWith('controlled_by_other')) { + control = red(control); + } + setStatusTo(control); + console.log(details); + const pac = details.value.pacScript; + const data = pac && pac.data || 'PAC скрипт не установлен.'; + editor.setValue( data ); + + }); + + } + + document.querySelector('#read-button').onclick = _read; + + document.querySelector('#save-button').onclick = () => { + + const config = { + mode: 'pac_script', + pacScript: { + mandatory: false, + data: editor.getValue(), + }, + }; + bgWin.chrome.proxy.settings.set( {value: config}, () => alert('Saved!') ); - const config = { - mode: 'pac_script', - pacScript: { - mandatory: false, - data: editor.getValue(), - }, }; - 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...'); + _read(); - alert('Cleared! Reading...'); - _read(); + }); - }); - -}; + }; +}) diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/package-lock.json b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/package-lock.json index 849d0e2..c29d856 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/package-lock.json +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/package-lock.json @@ -1205,10 +1205,21 @@ "integrity": "sha512-H1b0LNa197L/y2eBcVSQxpldkgzK15HU3xG1hCn0xJ4rxRSo6n78/fabBqyuUYMA1CtVa1Z7WnAVa9FKvlFecQ==", "dev": true, "requires": { + "inferno": "3.8.0", "inferno-shared": "3.8.0", "inferno-vnode-flags": "3.8.0" }, "dependencies": { + "inferno": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/inferno/-/inferno-3.8.0.tgz", + "integrity": "sha512-6s/hAYOrKNB0a8FDNHTJlz13d9uup7fVpvfd+2XPzxE69h8WgtMl+CSTIxHkW8RuEcc+hFAqi0ScsXVkMor5pw==", + "dev": true, + "requires": { + "inferno-shared": "3.8.0", + "inferno-vnode-flags": "3.8.0" + } + }, "inferno-vnode-flags": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/inferno-vnode-flags/-/inferno-vnode-flags-3.8.0.tgz", @@ -1223,11 +1234,22 @@ "integrity": "sha512-OOCSFxgnQUYvT8bhkE7OearfQi4NXevHVE49rExHhbf6m7U/AW5uQ/Ji8+BmX//sQY4YjQIvZnD/t1O+UEEBgA==", "dev": true, "requires": { + "inferno": "3.8.0", "inferno-component": "3.8.0", "inferno-shared": "3.8.0", "inferno-vnode-flags": "3.8.0" }, "dependencies": { + "inferno": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/inferno/-/inferno-3.8.0.tgz", + "integrity": "sha512-6s/hAYOrKNB0a8FDNHTJlz13d9uup7fVpvfd+2XPzxE69h8WgtMl+CSTIxHkW8RuEcc+hFAqi0ScsXVkMor5pw==", + "dev": true, + "requires": { + "inferno-shared": "3.8.0", + "inferno-vnode-flags": "3.8.0" + } + }, "inferno-vnode-flags": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/inferno-vnode-flags/-/inferno-vnode-flags-3.8.0.tgz", diff --git a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/src/components/PacChooser.js b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/src/components/PacChooser.js index 894c146..47de8ec 100644 --- a/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/src/components/PacChooser.js +++ b/extensions/chromium/runet-censorship-bypass/src/extension-common/pages/options/src/components/PacChooser.js @@ -63,7 +63,7 @@ export default function getPacChooser(theState) { this.updatePac = function updatePac(onSuccess) { props.funs.conduct( 'Обновляем...', - (cb) => props.apis.antiCensorRu.syncWithPacProviderAsync(cb), + (cb) => theState.apis.antiCensorRu.syncWithPacProviderAsync(cb), 'Обновлено.', onSuccess ); @@ -75,7 +75,7 @@ export default function getPacChooser(theState) { getCurrentProviderId() { - return this.props.apis.antiCensorRu.getCurrentPacProviderKey() || 'none'; + return theState.apis.antiCensorRu.getCurrentPacProviderKey() || 'none'; } @@ -94,7 +94,7 @@ export default function getPacChooser(theState) { const pacKey = event.target.id; if ( pacKey === ( - this.props.apis.antiCensorRu.getCurrentPacProviderKey() || 'none' + theState.apis.antiCensorRu.getCurrentPacProviderKey() || 'none' ) ) { return false; @@ -102,7 +102,7 @@ export default function getPacChooser(theState) { if (pacKey === 'none') { this.props.funs.conduct( 'Отключение...', - (cb) => this.props.apis.antiCensorRu.clearPacAsync(cb), + (cb) => theState.apis.antiCensorRu.clearPacAsync(cb), 'Отключено.', () => this.setState({ chosenPacName: 'none' }), checkChosenProvider @@ -110,7 +110,7 @@ export default function getPacChooser(theState) { } else { this.props.funs.conduct( 'Установка...', - (cb) => this.props.apis.antiCensorRu.installPacAsync(pacKey, cb), + (cb) => theState.apis.antiCensorRu.installPacAsync(pacKey, cb), 'PAC-скрипт установлен.', checkChosenProvider ); @@ -127,7 +127,7 @@ export default function getPacChooser(theState) { {props.flags.ifInsideOptionsPage && (
PAC-скрипт:
)}