Restyle, scope vars/names out of window

This commit is contained in:
Ilya Ig. Petrov 2016-11-20 04:58:49 -08:00
parent 05c6deadf8
commit 1ef480acd1
4 changed files with 497 additions and 483 deletions

View File

@ -13,518 +13,526 @@
use chrome.runtime.getBackgroundPage(..), use chrome.runtime.getBackgroundPage(..),
extension.getBackgroundPage is deprecated extension.getBackgroundPage is deprecated
*/ */
{ // Private namespace starts.
window.antiCensorRu = { window.antiCensorRu = {
version: chrome.runtime.getManifest().version, version: chrome.runtime.getManifest().version,
pacProviders: { pacProviders: {
Антизапрет: { Антизапрет: {
pacUrl: 'https://antizapret.prostovpn.org/proxy.pac', pacUrl: 'https://antizapret.prostovpn.org/proxy.pac',
proxyHosts: ['proxy.antizapret.prostovpn.org'], proxyHosts: ['proxy.antizapret.prostovpn.org'],
proxyIps: { proxyIps: {
'195.123.209.38': 'proxy.antizapret.prostovpn.org', '195.123.209.38': 'proxy.antizapret.prostovpn.org',
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org' '2a02:27ac::10': 'proxy.antizapret.prostovpn.org'
}
},
Антиценз: {
pacUrl: 'https://config.anticenz.org/proxy.pac',
proxyHosts: ['gw2.anticenz.org'],
proxyIps: {
'5.196.220.114': 'gw2.anticenz.org'
}
},
Обаа_свитчах: {
//pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14
pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15
proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'],
proxyIps: {
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
'5.196.220.114': 'gw2.anticenz.org'
}
} }
}, },
Антиценз: {
pacUrl: 'https://config.anticenz.org/proxy.pac', _currentPacProviderKey: 'Оба_и_на_свитчах',
proxyHosts: ['gw2.anticenz.org'],
proxyIps: { get currentPacProviderKey() { return this._currentPacProviderKey },
'5.196.220.114': 'gw2.anticenz.org' set currentPacProviderKey(newKey) {
}
if (newKey && !this.pacProviders[newKey])
throw new IllegalArgumentException('No provider for key:' + newKey);
this._currentPacProviderKey = newKey;
}, },
Обаа_свитчах: {
//pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0akpCOURNS2VCTmc', // 0.14
pacUrl: 'https://drive.google.com/uc?export=download&id=0B-ZCVSvuNWf0bzNUR2F4RF8wOU0', // 0.15
proxyHosts: ['proxy.antizapret.prostovpn.org', 'gw2.anticenz.org'],
proxyIps: {
'195.123.209.38': 'proxy.antizapret.prostovpn.org',
'2a02:27ac::10': 'proxy.antizapret.prostovpn.org',
'5.196.220.114': 'gw2.anticenz.org'
}
}
},
_currentPacProviderKey: 'Оба_и_на_свитчах', get pacProvider() { return this.pacProviders[this.currentPacProviderKey] },
get currentPacProviderKey() { return this._currentPacProviderKey }, /*
set currentPacProviderKey(newKey) { Is it the first time extension installed? Do something, e.g. initiate PAC sync.
*/
ifFirstInstall: false,
lastPacUpdateStamp: 0,
if (newKey && !this.pacProviders[newKey]) _periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта Антизапрет',
throw new IllegalArgumentException('No provider for key:' + newKey);
this._currentPacProviderKey = newKey;
},
get pacProvider() { return this.pacProviders[this.currentPacProviderKey] }, pushToStorage(cb) {
/* console.log('Pushing to storage...');
Is it the first time extension installed? Do something, e.g. initiate PAC sync.
*/
ifFirstInstall: false,
lastPacUpdateStamp: 0,
_periodicUpdateAlarmReason: 'Периодичное обновление PAC-скрипта Антизапрет', // Copy only settable properties (except functions).
const onlySettable = {};
pushToStorage(cb) { for(const key of Object.keys(this)) {
if (Object.getOwnPropertyDescriptor(this, key).writable && typeof(this[key]) !== 'function') {
console.log('Pushing to storage...'); onlySettable[key] = this[key];
// Copy only settable properties (except functions).
const onlySettable = {};
for(const key of Object.keys(this)) {
if (Object.getOwnPropertyDescriptor(this, key).writable && typeof(this[key]) !== 'function') {
onlySettable[key] = this[key];
}
}
return chrome.storage.local.clear(
() => chrome.storage.local.set(
onlySettable,
chromified(cb, onlySettable)
)
);
},
pullFromStorage(cb) {
chrome.storage.local.get(null, (storage) => {
const err = checkChromeError();
if (!err) {
console.log('Pulled from storage:', storage);
for(const key of Object.keys(storage)) {
this[key] = storage[key];
} }
} }
console.log('Synced with storage, any callback?', !!cb); return chrome.storage.local.clear(
return cb && cb(err, storage); () => chrome.storage.local.set(
onlySettable,
chromified(cb, onlySettable)
)
);
},
pullFromStorage(cb) {
chrome.storage.local.get(null, (storage) => {
const err = checkChromeError();
if (!err) {
console.log('Pulled from storage:', storage);
for(const key of Object.keys(storage)) {
this[key] = storage[key];
}
}
console.log('Synced with storage, any callback?', !!cb);
return cb && cb(err, storage);
});
},
syncWithPacProvider(cb) {
cb = asyncLogGroup('Syncing with PAC provider...', cb);
if (!this.pacProvider) {
return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}});
}
const pacSetPromise = new Promise(
(resolve, reject) => setPacScriptFromProvider(
this.pacProvider,
(err, res) => {
if (!err) {
this.lastPacUpdateStamp = Date.now();
this.ifFirstInstall = false;
this.setAlarms();
}
return resolve([err, res]);
}
)
);
const ipsPromise = new Promise(
(resolve, reject) => updatePacProxyIps(
this.pacProvider,
(ipsError) => {
if (ipsError && ipsError.clarification) {
ipsError.clarification.ifNotCritical = true;
}
return resolve([ipsError]);
}
)
);
Promise.all([pacSetPromise, ipsPromise]).then(
([[pacErr, pacRes], [ipsErr]]) => {
if (pacErr && ipsErr) {
return cb(pacErr, pacRes);
}
return cb(pacErr || ipsErr);
this.pushToStorage(
(pushErr) => cb(pacErr || ipsErr || pushErr, pacRes)
);
}
);
},
_pacUpdatePeriodInMinutes: 4*60,
setAlarms() {
let nextUpdateMoment = this.lastPacUpdateStamp + this._pacUpdatePeriodInMinutes*60*1000;
const now = Date.now();
if (nextUpdateMoment < now) {
nextUpdateMoment = now;
}
console.log( 'Next PAC update is scheduled on', new Date(nextUpdateMoment).toLocaleString('ru-RU') );
chrome.alarms.create(
this._periodicUpdateAlarmReason,
{
when: nextUpdateMoment,
periodInMinutes: this._pacUpdatePeriodInMinutes
}
);
return nextUpdateMoment === now; // ifAlarmTriggered. May be changed in the future.
},
installPac(key, cb) {
console.log('Installing PAC');
if(typeof(key) === 'function') {
cb = key;
key = undefined;
}
if(key) {
this.currentPacProviderKey = key;
}
return this.syncWithPacProvider(cb);
},
clearPac(cb) {
cb = asyncLogGroup('Cearing alarms and PAC...', cb);
chrome.alarms.clearAll(
() => chrome.proxy.settings.clear(
{},
() => {
const err = checkChromeError();
if (err) {
return cb(err);
}
this.currentPacProviderKey = undefined;
return this.pushToStorage(cb);
}
)
);
}
};
// ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE
chrome.storage.local.get(null, (oldStorage) => {
console.log('Init on storage:', oldStorage);
antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
if (!antiCensorRu.ifFirstInstall) {
// LAUNCH, RELOAD, UPDATE
antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey;
antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') );
}
chrome.alarms.onAlarm.addListener(
(alarm) => {
if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) {
console.log('Periodic PAC update triggered:', new Date().toLocaleString('ru-RU'));
antiCensorRu.syncWithPacProvider(/* Swallows errors. */);
}
}
);
console.log('Alarm listener installed. We won\'t miss any PAC update.');
window.addEventListener('online', () => {
console.log('We are online, checking periodic updates...');
antiCensorRu.setAlarms();
}); });
},
syncWithPacProvider(cb) { if (antiCensorRu.ifFirstInstall) {
// INSTALL
cb = asyncLogGroup('Syncing with PAC provider...', cb); console.log('Installing...');
if (!this.pacProvider) { return chrome.runtime.openOptionsPage();
return cb({clarification:{message:'Сперва выберите PAC-провайдера.'}});
} }
const pacSetPromise = new Promise( if (!antiCensorRu.pacProvider) {
(resolve, reject) => setPacScriptFromProvider( return console.log('No PAC provider set. Do nothing.');
this.pacProvider, /*
(err, res) => { In case of UPDATE:
1. new providers will still be shown.
if (!err) { 2. new version won't be pushed to storage
this.lastPacUpdateStamp = Date.now(); */
this.ifFirstInstall = false;
this.setAlarms();
}
return resolve([err, res]);
}
)
);
const ipsPromise = new Promise(
(resolve, reject) => updatePacProxyIps(
this.pacProvider,
(ipsError) => {
if (ipsError && ipsError.clarification) {
ipsError.clarification.ifNotCritical = true;
}
return resolve([ipsError]);
}
)
);
Promise.all([pacSetPromise, ipsPromise]).then(
([[pacErr, pacRes], [ipsErr]]) => {
if (pacErr && ipsErr) {
return cb(pacErr, pacRes);
}
return cb(pacErr || ipsErr);
this.pushToStorage(
(pushErr) => cb(pacErr || ipsErr || pushErr, pacRes)
);
}
);
},
_pacUpdatePeriodInMinutes: 4*60,
setAlarms() {
let nextUpdateMoment = this.lastPacUpdateStamp + this._pacUpdatePeriodInMinutes*60*1000;
const now = Date.now();
if (nextUpdateMoment < now)
nextUpdateMoment = now;
console.log( 'Next PAC update is scheduled on', new Date(nextUpdateMoment).toLocaleString('ru-RU') );
chrome.alarms.create(
this._periodicUpdateAlarmReason,
{
when: nextUpdateMoment,
periodInMinutes: this._pacUpdatePeriodInMinutes
}
);
return nextUpdateMoment === now; // ifAlarmTriggered. May be changed in the future.
},
installPac(key, cb) {
console.log('Installing PAC');
if(typeof(key) === 'function') {
cb = key;
key = undefined;
} }
if(key) { /*
this.currentPacProviderKey = key; 1. There is no way to check that chrome.runtime.onInstalled wasn't fired except timeout.
Otherwise we could put storage migration code only there.
2. We have to check storage for migration before using it.
Better on each launch then on each pull.
*/
const ifAlarmTriggered = antiCensorRu.setAlarms();
if (antiCensorRu.version === oldStorage.version) {
// LAUNCH, RELOAD, ENABLE
antiCensorRu.pacProviders = oldStorage.pacProviders;
return console.log('Extension launched, reloaded or enabled.');
} }
return this.syncWithPacProvider(cb); // UPDATE & MIGRATION
console.log('Extension updated.');
}, if (!ifAlarmTriggered) {
antiCensorRu.pushToStorage(/* Swallows errors. */);
clearPac(cb) {
cb = asyncLogGroup('Cearing alarms and PAC...', cb);
chrome.alarms.clearAll(
() => chrome.proxy.settings.clear(
{},
() => {
const err = checkChromeError();
if (err) {
return cb(err);
}
this.currentPacProviderKey = undefined;
return this.pushToStorage(cb);
}
)
);
}
};
// ON EACH LAUNCH, STARTUP, RELOAD, UPDATE, ENABLE
chrome.storage.local.get(null, (oldStorage) => {
console.log('Init on storage:', oldStorage);
antiCensorRu.ifFirstInstall = Object.keys(oldStorage).length === 0;
if (!antiCensorRu.ifFirstInstall) {
// LAUNCH, RELOAD, UPDATE
antiCensorRu._currentPacProviderKey = oldStorage._currentPacProviderKey;
antiCensorRu.lastPacUpdateStamp = oldStorage.lastPacUpdateStamp || antiCensorRu.lastPacUpdateStamp;
console.log( 'Last PAC update was on', new Date(antiCensorRu.lastPacUpdateStamp).toLocaleString('ru-RU') );
}
chrome.alarms.onAlarm.addListener(
(alarm) => {
if (alarm.name === antiCensorRu._periodicUpdateAlarmReason) {
console.log('Periodic PAC update triggered:', new Date().toLocaleString('ru-RU'));
antiCensorRu.syncWithPacProvider(/* Swallows errors. */);
}
} }
);
console.log('Alarm listener installed. We won\'t miss any PAC update.');
window.addEventListener('online', () => {
console.log('We are online, checking periodic updates...');
antiCensorRu.setAlarms();
/*
History of Changes to Storage (Migration Guide)
-----------------------------------------------
Version 0.0.0.10
* Added this.version
* PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname}
Version 0.0.0.8-9
* Changed storage.ifNotInstalled to storage.ifFirstInstall
* Added storage.lastPacUpdateStamp
**/
}); });
if (antiCensorRu.ifFirstInstall) { function asyncLogGroup(...args) {
// INSTALL
console.log('Installing...'); const cb = args.pop() || (() => {});
return chrome.runtime.openOptionsPage(); console.group.apply(console, args);
return function(...cbArgs) {
console.groupEnd();
console.log('Group finished.');
return cb.apply(this, cbArgs);
}
} }
if (!antiCensorRu.pacProvider) { function checkChromeError(betterStack) {
return console.log('No PAC provider set. Do nothing.');
// Chrome API calls your cb in a context different from the point of API method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError || null;
if (err) {
const args = ['API returned error:', err];
if (betterStack) {
args.push('\n' + betterStack);
}
console.warn.apply(console, args);
}
return err;
} }
/* function chromified(cb, ...replaceArgs) {
1. There is no way to check that chrome.runtime.onInstalled wasn't fired except timeout.
Otherwise we could put storage migration code only there.
2. We have to check storage for migration before using it.
Better on each launch then on each pull.
*/
const ifAlarmTriggered = antiCensorRu.setAlarms(); const stack = (new Error()).stack;
// Take error first callback and covert it to chrome api callback.
return function(...args) {
if (replaceArgs.length) {
args = replaceArgs;
}
const err = checkChromeError(stack);
return cb && cb.call(this, err, ...args);
};
if (antiCensorRu.version === oldStorage.version) {
// LAUNCH, RELOAD, ENABLE
antiCensorRu.pacProviders = oldStorage.pacProviders;
return console.log('Extension launched, reloaded or enabled.');
} }
// UPDATE & MIGRATION function httpGet(url, cb) {
console.log('Extension updated.');
if (!ifAlarmTriggered) { const start = Date.now();
updatePacProxyIps( return fetch(url).then(
antiCensorRu.pacProvider, (res) => {
(ipsError) => ipsError ? console.error('Error updating IPs:', ipsError) : antiCensorRu.pushToStorage(/* Swallows errors. */)
const textCb =
(err) => cb && res.text()
.then( (text) => cb(err, text), cb );
const status = res.status;
if ( !( status >= 200 && status < 300 || status === 304 ) ) {
res.clarification = {message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.'};
return textCb(res);
}
console.log('GETed with success:', url, Date.now() - start);
return textCb();
},
(err) => {
err.clarification = {message: 'Что-то не так с сетью, проверьте соединение.'};
return cb && cb(err);
}
); );
} }
/* function getOneDnsRecord(args, cb) {
History of Changes to Storage (Migration Guide)
-----------------------------------------------
Version 0.0.0.10
* Added this.version
* PacProvider.proxyIps changed from {ip -> Boolean} to {ip -> hostname}
Version 0.0.0.8-9
* Changed storage.ifNotInstalled to storage.ifFirstInstall
* Added storage.lastPacUpdateStamp
**/
});
function asyncLogGroup(...args) { // args: { host:..., type: 'AAAA', filter: ['AAAA'] }
if (!(args.host && args.type && cb)) {
const cb = args.pop() || (() => {}); throw new Error('Wrong args:' + host + ',' + type);
console.group.apply(console, args);
return function(...cbArgs) {
console.groupEnd();
console.log('Group finished.');
return cb.apply(this, cbArgs);
}
}
function checkChromeError(betterStack) {
// Chrome API calls your cb in a context different from the point of API method invokation.
const err = chrome.runtime.lastError || chrome.extension.lastError || null;
if (err) {
const args = ['API returned error:', err];
if (betterStack) {
args.push('\n' + betterStack);
} }
console.warn.apply(console, args);
}
return err;
} const type2str = {
// https://en.wikipedia.org/wiki/List_of_DNS_record_types
// A, AAAA may be localized (github, e.g.), but you may use ANY
1: 'A', // IPv4
28: 'AAAA', // IPv6
2: 'NS',
5: 'CNAME', // Synonyms, returned by server together with A/AAAA.
255: 'ANY' // Deprecated on some servers, not recommended
};
function chromified(cb, ...replaceArgs) { httpGet(
'https://dns.google.com/resolve?type=' + args.type + '&name=' + args.host,
const stack = (new Error()).stack; (err, res) => {
// Take error first callback and covert it to chrome api callback. if (res) {
return function(...args) { try {
res = JSON.parse(res);
if (replaceArgs.length) { console.log('Json parsed.');
args = replaceArgs; if (err || res.Status) {
} const msg = ['Answer', 'Comment', 'Status']
const err = checkChromeError(stack); .filter( (prop) => res[ prop ] )
return cb && cb.call(this, err, ...args); .map( (prop) => prop + ': ' + JSON.stringify( res[ prop ] ) )
.join(', \n');
}; err.clarification.message += ' Сервер (json): ' + msg;
err.data = err.data || res;
} }
else {
function httpGet(url, cb) { res = res.Answer || [];
for (const record of res) {
const start = Date.now(); record.type = type2str[ record.type ];
return fetch(url).then( }
(res) => { if ( args.filter ) {
res = res.filter( (record) => args.filter.indexOf( record.type ) > -1 );
const textCb = }
(err) => cb && res.text() }
.then( (text) => cb(err, text), cb ); }
const status = res.status; catch(e) {
if ( !( status >= 200 && status < 300 || status === 304 ) ) { err = e || err || {clarification:{message:''}};
res.clarification = {message: 'Получен ответ с неудачным HTTP-кодом ' + status + '.'}; err.clarification = err.clarification || { message: '' };
return textCb(res); err.clarification.message += ' Сервер (текст): '+ res;
} err.clarification.message.trim();
console.log('GETed with success:', url, Date.now() - start);
return textCb();
},
(err) => {
err.clarification = {message: 'Что-то не так с сетью, проверьте соединение.'};
return cb && cb(err);
}
);
}
function getOneDnsRecord(args, cb) {
// args: { host:..., type: 'AAAA', filter: ['AAAA'] }
if (!(args.host && args.type && cb)) {
throw new Error('Wrong args:' + host + ',' + type);
}
const type2str = {
// https://en.wikipedia.org/wiki/List_of_DNS_record_types
// A, AAAA may be localized (github, e.g.), but you may use ANY
1: 'A', // IPv4
28: 'AAAA', // IPv6
2: 'NS',
5: 'CNAME', // Synonyms, returned by server together with A/AAAA.
255: 'ANY' // Deprecated on some servers, not recommended
};
httpGet(
'https://dns.google.com/resolve?type=' + args.type + '&name=' + args.host,
(err, res) => {
if (res) {
try {
res = JSON.parse(res);
console.log('Json parsed.');
if (err || res.Status) {
const msg = ['Answer', 'Comment', 'Status']
.filter( (prop) => res[ prop ] )
.map( (prop) => prop + ': ' + JSON.stringify( res[ prop ] ) )
.join(', \n');
err.clarification.message += ' Сервер (json): ' + msg;
err.data = err.data || res; err.data = err.data || res;
} }
else {
res = res.Answer || [];
for (const record of res) {
record.type = type2str[ record.type ];
}
if ( args.filter ) {
res = res.filter( (record) => args.filter.indexOf( record.type ) > -1 );
}
}
}
catch(e) {
err = e || err || {clarification:{message:''}};
err.clarification = err.clarification || { message: '' };
err.clarification.message += ' Сервер (текст): '+ res;
err.clarification.message.trim();
err.data = err.data || res;
} }
return cb( err, res );
} }
return cb( err, res ); );
}
);
};
function getDnsRecords(args, cb) {
/*
Example of input:
{
// Required
host: 'proxy.navalny.cia.gov',
// Optional
types: {
string: ['A', 'AAAA'], // <- Default. Makes one request per each type.
filter: ['A', 'AAAA'], // <- Default. E.g., you want to get rid of CNAME type from response.
}
}
Exmple of answer from google:
"Answer":
[
{
"name": "apple.com.", // Always matches name in the Question section
"type": 1, // A - Standard DNS RR type
"TTL": 3599, // Record's time-to-live in seconds
"data": "17.178.96.59" // Data for A - IP address as text
},
...
Exmple of output:
The same as google, but types _may be_ canonical strings ('AAAA', 'A')
**/
if ( !args.host.length ) {
throw new Error('args.host is required: ' + args.host);
}
args.types = Object.assign({
string: ['A', 'AAAA'],
filter: ['A', 'AAAA']
}, args.types);
const promises = args.types.string.map(
(type) => new Promise( (resolve, reject) =>
getOneDnsRecord({ host: args.host, type: type, filter: args.types.filter }, (err, res) => err ? reject(err) : resolve(res) )
)
);
Promise.all(promises).then( (answers) => cb( null, [].concat.apply([], answers) ), cb );
}
const getIpDnsRecords = (host, cb) => getDnsRecords({ host: host }, cb);
function updatePacProxyIps(provider, cb) {
cb = asyncLogGroup('Getting IP for '+ provider.proxyHosts.join(', ') +'...', cb);
let failure = {
clarification: {message:'Не удалось получить один или несколько IP адресов для прокси-серверов. Иконка для уведомления об обходе блокировок может не отображаться.'},
errors: {}
}; };
let i = 0;
provider.proxyHosts.map(
(proxyHost) => getIpDnsRecords(
proxyHost,
(err, records) => {
if (!err) { function getDnsRecords(args, cb) {
provider.proxyIps = provider.proxyIps || {};
records.forEach( (ans) => provider.proxyIps[ ans.data ] = proxyHost ); /*
} Example of input:
else { {
failure.errors[proxyHost] = err; // Required
host: 'proxy.navalny.cia.gov',
// Optional
types: {
string: ['A', 'AAAA'], // <- Default. Makes one request per each type.
filter: ['A', 'AAAA'], // <- Default. E.g., you want to get rid of CNAME type from response.
}
} }
Exmple of answer from google:
"Answer":
[
{
"name": "apple.com.", // Always matches name in the Question section
"type": 1, // A - Standard DNS RR type
"TTL": 3599, // Record's time-to-live in seconds
"data": "17.178.96.59" // Data for A - IP address as text
},
...
Exmple of output:
The same as google, but types _may be_ canonical strings ('AAAA', 'A')
**/
if ( ++i === provider.proxyHosts.length ) { if ( !args.host.length ) {
failure = Object.keys(failure.errors).length ? failure : null; throw new Error('args.host is required: ' + args.host);
return cb(failure, provider.proxyIps);
}
}
)
);
}
function setPacScriptFromProvider(provider, cb) {
cb = asyncLogGroup('Getting pac script from provider...', provider.pacUrl, cb);
httpGet(
provider.pacUrl,
(err, pacData) => {
if (err) {
err.clarification = {
message: 'Не удалось скачать PAC-скрипт с адреса: ' + provider.pacUrl + '.',
prev: err.clarification
};
return cb(err);
}
const config = {
mode: 'pac_script',
pacScript: {
mandatory: false,
data: pacData
}
};
console.log('Setting chrome proxy settings...');
chrome.proxy.settings.set( {value: config}, chromified(cb) );
} }
); args.types = Object.assign({
string: ['A', 'AAAA'],
filter: ['A', 'AAAA']
}, args.types);
const promises = args.types.string.map(
(type) => new Promise( (resolve, reject) =>
getOneDnsRecord({ host: args.host, type: type, filter: args.types.filter }, (err, res) => err ? reject(err) : resolve(res) )
)
);
Promise.all(promises).then( (answers) => cb( null, [].concat.apply([], answers) ), cb );
}
const getIpDnsRecords = (host, cb) => getDnsRecords({ host: host }, cb);
function updatePacProxyIps(provider, cb) {
cb = asyncLogGroup('Getting IP for '+ provider.proxyHosts.join(', ') +'...', cb);
let failure = {
clarification: {message:'Не удалось получить один или несколько IP адресов для прокси-серверов. Иконка для уведомления об обходе блокировок может не отображаться.'},
errors: {}
};
let i = 0;
provider.proxyHosts.map(
(proxyHost) => getIpDnsRecords(
proxyHost,
(err, records) => {
if (!err) {
provider.proxyIps = provider.proxyIps || {};
records.forEach( (ans) => provider.proxyIps[ ans.data ] = proxyHost );
}
else {
failure.errors[proxyHost] = err;
}
if ( ++i === provider.proxyHosts.length ) {
failure = Object.keys(failure.errors).length ? failure : null;
return cb(failure, provider.proxyIps);
}
}
)
);
}
function setPacScriptFromProvider(provider, cb) {
cb = asyncLogGroup('Getting pac script from provider...', provider.pacUrl, cb);
httpGet(
provider.pacUrl,
(err, pacData) => {
if (err) {
err.clarification = {
message: 'Не удалось скачать PAC-скрипт с адреса: ' + provider.pacUrl + '.',
prev: err.clarification
};
return cb(err);
}
const config = {
mode: 'pac_script',
pacScript: {
mandatory: false,
data: pacData
}
};
console.log('Setting chrome proxy settings...');
chrome.proxy.settings.set( {value: config}, chromified(cb) );
}
);
}
} }
window.addEventListener('error', (err) => { window.addEventListener('error', (err) => {

View File

@ -18,9 +18,9 @@ window.chrome.browserAction.setBadgeBackgroundColor({
window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP! window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
+function() { {
var _tabCallbacks = {}; const _tabCallbacks = {};
function afterTabUpdated(tabId, cb) { function afterTabUpdated(tabId, cb) {
if (_tabCallbacks[tabId]) if (_tabCallbacks[tabId])
@ -42,7 +42,7 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
} }
chrome.webRequest.onErrorOccurred.addListener( chrome.webRequest.onErrorOccurred.addListener(
requestDetails => (requestDetails) =>
isInsideTabWithIp(requestDetails) && isInsideTabWithIp(requestDetails) &&
( (
isProxiedAndInformed(requestDetails) || requestDetails.type === 'main_frame' && ( window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip ) isProxiedAndInformed(requestDetails) || requestDetails.type === 'main_frame' && ( window.tabWithError2ip[requestDetails.tabId] = requestDetails.ip )
@ -50,9 +50,9 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
{ urls: ['<all_urls>'] } { urls: ['<all_urls>'] }
); );
chrome.tabs.onRemoved.addListener( tabId => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } ); chrome.tabs.onRemoved.addListener( (tabId) => { onTabUpdate(tabId); delete window.tabWithError2ip[tabId] } );
var previousUpdateTitleFinished = Promise.resolve(); let previousUpdateTitleFinished = Promise.resolve();
function isProxiedAndInformed(requestDetails) { function isProxiedAndInformed(requestDetails) {
@ -62,12 +62,12 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
return false; return false;
} }
var ifMainFrame = requestDetails.type === 'main_frame'; const ifMainFrame = requestDetails.type === 'main_frame';
previousUpdateTitleFinished = previousUpdateTitleFinished.then( previousUpdateTitleFinished = previousUpdateTitleFinished.then(
() => new Promise( () => new Promise(
resolve => { (resolve) => {
var cb = () => updateTitle( requestDetails, resolve ); const cb = () => updateTitle( requestDetails, resolve );
return ifMainFrame ? afterTabUpdated(requestDetails.tabId, cb) : cb(); return ifMainFrame ? afterTabUpdated(requestDetails.tabId, cb) : cb();
} }
) )
@ -79,15 +79,16 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
chrome.browserAction.getTitle( chrome.browserAction.getTitle(
{ tabId: requestDetails.tabId }, { tabId: requestDetails.tabId },
title => { (title) => {
var ifTitleSetAlready = /\n/.test(title);
var proxyHost = window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ];
var hostname = new URL( requestDetails.url ).hostname; const ifTitleSetAlready = /\n/.test(title);
const proxyHost = window.antiCensorRu.pacProvider.proxyIps[ requestDetails.ip ];
var ifShouldUpdateTitle = false; const hostname = new URL( requestDetails.url ).hostname;
var indent = ' ';
var proxyTitle = 'Прокси:'; let ifShouldUpdateTitle = false;
const indent = ' ';
const proxyTitle = 'Прокси:';
if (!ifTitleSetAlready) { if (!ifTitleSetAlready) {
title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost; title = 'Разблокированы:\n'+ indent + hostname +'\n'+ proxyTitle +'\n'+ indent + proxyHost;
@ -98,7 +99,8 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
text: ifMainFrame ? '1' : '%1' text: ifMainFrame ? '1' : '%1'
}); });
} else { }
else {
const hostsProxiesPair = title.split(proxyTitle); const hostsProxiesPair = title.split(proxyTitle);
if (hostsProxiesPair[1].indexOf(proxyHost) === -1) { if (hostsProxiesPair[1].indexOf(proxyHost) === -1) {
@ -110,10 +112,11 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle); title = title.replace(proxyTitle, indent + hostname +'\n'+ proxyTitle);
ifShouldUpdateTitle = true; ifShouldUpdateTitle = true;
var _cb = cb; const _cb = cb;
cb = () => chrome.browserAction.getBadgeText( cb = () => chrome.browserAction.getBadgeText(
{tabId: requestDetails.tabId}, {tabId: requestDetails.tabId},
result => { (result) => {
chrome.browserAction.setBadgeText( chrome.browserAction.setBadgeText(
{ {
tabId: requestDetails.tabId, tabId: requestDetails.tabId,
@ -121,27 +124,30 @@ window.tabWithError2ip = {}; // For errors only: Error? -> Check this IP!
} }
); );
return _cb(); return _cb();
} }
); );
} }
} }
if (ifShouldUpdateTitle) if (ifShouldUpdateTitle) {
chrome.browserAction.setTitle({ chrome.browserAction.setTitle({
title: title, title: title,
tabId: requestDetails.tabId tabId: requestDetails.tabId
}); });
}
return cb(); return cb();
} }
); );
} }
} }
chrome.webRequest.onResponseStarted.addListener( chrome.webRequest.onResponseStarted.addListener(
requestDetails => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails), (requestDetails) => isInsideTabWithIp(requestDetails) && isProxiedAndInformed(requestDetails),
{ urls: ['<all_urls>'] } { urls: ['<all_urls>'] }
); );
}(); }

View File

@ -5,7 +5,7 @@
const createMenuLinkEntry = (title, tab2url) => chrome.contextMenus.create({ const createMenuLinkEntry = (title, tab2url) => chrome.contextMenus.create({
title: title, title: title,
contexts: ['browser_action'], contexts: ['browser_action'],
onclick: (menuInfo, tab) => Promise.resolve( tab2url( tab ) ).then( url => chrome.tabs.create({url: url}) ) onclick: (menuInfo, tab) => Promise.resolve( tab2url( tab ) ).then( (url) => chrome.tabs.create({url: url}) )
}); });
createMenuLinkEntry( 'Сайт доступен из-за границы? Is up?', (tab) => 'http://isup.me/'+ new URL(tab.url).hostname ); createMenuLinkEntry( 'Сайт доступен из-за границы? Is up?', (tab) => 'http://isup.me/'+ new URL(tab.url).hostname );

View File

@ -1,7 +1,7 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"name": "Обход блокировок Рунета", "name": "Обход блокировок Рунета 0.15",
"description": "Аргументы против цензуры: https://git.io/vEkI9", "description": "Аргументы против цензуры: https://git.io/vEkI9",
"version": "0.0.0.15", "version": "0.0.0.15",
"icons": { "icons": {