diff --git a/.eslintrc.js b/.eslintrc.js
new file mode 100644
index 00000000..6746346b
--- /dev/null
+++ b/.eslintrc.js
@@ -0,0 +1,20 @@
+module.exports = {
+ env: {
+ es2021: true,
+ browser: true,
+ webextensions: true,
+ node: true,
+ jquery: true,
+ },
+ extends: [
+ 'google',
+ ],
+ parserOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ },
+ rules: {
+ 'require-jsdoc': 'off',
+ 'max-len': ['error', {'code': 100}],
+ },
+};
diff --git a/.eslintrc.json b/.eslintrc.json
deleted file mode 100644
index b211b9d3..00000000
--- a/.eslintrc.json
+++ /dev/null
@@ -1,17 +0,0 @@
-{
- "env": {
- "browser": true,
- "webextensions": true,
- "node": true,
- "jquery": true,
- "es2021": true
- },
- "extends": [
- "eslint:recommended"
- ],
- "parserOptions": {
- "ecmaVersion": "latest",
- "sourceType": "module"
- },
- "rules": {}
-}
\ No newline at end of file
diff --git a/Procfile b/Procfile
deleted file mode 100644
index 4ebe101d..00000000
--- a/Procfile
+++ /dev/null
@@ -1,2 +0,0 @@
-web: node server/server.js --nolog
-
diff --git a/README.md b/README.md
index 509334d1..8e7f6416 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,6 @@
# Unblock Youku
-This project contains two parts (closely related with shared JavaScript code).
-
-1. A _Google Chrome extension_ helping users access their web services while travelling outside mainland China. You can find this extension on Chrome Web Store at [http://uku.im/chrome](http://uku.im/chrome).
-
-2. A Node.js based _backend server_ that supports the redirect mode of the Chrome extension. [![Build Status](https://travis-ci.org/uku/Unblock-Youku.svg?branch=master)](https://travis-ci.org/uku/Unblock-Youku)
+ A Chrome extension helping users access their web services while travelling outside mainland China. You can find this extension on Chrome Web Store at [http://uku.im/chrome](http://uku.im/chrome).
## Disclaimer
diff --git a/chrome/config.js b/chrome/config.js
deleted file mode 100644
index 20b1b0e7..00000000
--- a/chrome/config.js
+++ /dev/null
@@ -1,258 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-/*global setup_header: false, setup_proxy: false */
-/*global clear_redirect: false, clear_header: false, clear_proxy: false */
-/*global ga_report_event: false, ga_report_ratio: false, ga_report_error: false */
-"use strict";
-
-// ====== Constant and Variable Settings ======
-var unblock_youku = unblock_youku || {}; // namespace
-
-// Default proxy server settings
-unblock_youku.default_proxy_server_proc = 'HTTPS';
-unblock_youku.default_proxy_server_addr = 'secure.uku.im:8443';
-unblock_youku.backup_proxy_server_proc = 'HTTPS';
-unblock_youku.backup_proxy_server_addr = 'secure.uku.im:993';
-
-
-unblock_youku.ip_addr = new_random_ip();
-console.log('generated random ip addr: ' + unblock_youku.ip_addr);
-
-
-// ====== Configuration Functions ======
-function set_mode_name(mode_name, callback) {
- if (typeof callback === 'undefined') {
- var err_msg = 'missing callback function in set_mode_name()';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
-
- if (mode_name === 'off') {
- set_storage('unblock_youku_mode', mode_name, callback);
- } else {
- set_storage('unblock_youku_mode', 'normal', callback);
- }
-}
-
-function get_mode_name(callback) {
- if (typeof callback === 'undefined') {
- var err_msg = 'missing callback function in get_mode_name()';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
-
- get_storage('unblock_youku_mode', function(current_mode) {
- if (typeof current_mode === 'undefined' || current_mode !== 'off') {
- set_mode_name('normal', function () {
- callback('normal');
- });
- } else {
- callback(current_mode);
- }
- });
-}
-
-function clear_mode_settings(mode_name) {
- switch (mode_name) {
- case 'off':
- console.log('cleared settings for off');
- break;
- case 'lite':
- clear_header();
- clear_redirect();
- console.log('cleared settings for lite');
- break;
- case 'normal':
- clear_header();
- clear_redirect();
- clear_proxy();
- console.log('cleared settings for normal');
- break;
- default:
- var err_msg = 'clear_mode_settings: should never come here';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- break;
- }
-
- console.log('cleared the settings for the mode: ' + mode_name);
-}
-
-function setup_mode_settings(mode_name) {
- switch (mode_name) {
- case 'off':
- chrome.browserAction.setBadgeText({ text: 'OFF' });
- chrome.browserAction.setTitle({ title: 'Unblock Youku has been turned off.' });
- change_browser_icon('off');
- break;
- case 'lite':
- set_mode_name('normal', function() {
- console.log('migrate lite mode to normal mode');
- });
- // fall through
- case 'normal':
- setup_header();
- setup_proxy();
- chrome.browserAction.setBadgeText({ text: '' });
- change_browser_icon('normal');
- break;
- default:
- var err_msg = 'setup_mode_settings: should never come here';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- break;
- }
-
- console.log('initialized the settings for the mode: ' + mode_name);
-}
-
-function change_mode(new_mode_name) {
- set_mode_name(new_mode_name, function() {});
- // the storage change listener would take care about the setting changes
-}
-
-function change_browser_icon(option) {
- function _change_browser_icon(option) {
- var today = new Date();
- var y = today.getFullYear();
- var d = today.getDate();
- var m = today.getMonth() + 1;
-
- // hard-coded spring festival dates
- var is_spring = false;
- switch (y) {
- case 2023: // Jan. 22, 2023
- is_spring = m === 1;
- break;
- case 2024: // Feb. 10, 2024
- is_spring = m === 2;
- break;
- case 2025: // Jan. 29, 2025
- is_spring = m === 1;
- break;
- case 2026: // Feb. 17, 2026
- is_spring = m === 2;
- break;
- case 2027: // Feb. 7, 2027
- is_spring = m === 2;
- break;
- }
- if (is_spring) {
- chrome.browserAction.setIcon({path: 'chrome/icons/icon19spring.png'});
- chrome.browserAction.setTitle({title: 'Happy Spring Festival! (Unblock Youku ' + unblock_youku.version + ')'});
- return;
- }
-
- // christmas
- if (m === 12 && d >= 15) {
- chrome.browserAction.setIcon({path: 'chrome/icons/icon19xmas.png'});
- chrome.browserAction.setTitle({title: 'Merry Christmas! (Unblock Youku ' + unblock_youku.version + ')'});
- return;
- }
-
- if (option === 'off') {
- chrome.browserAction.setIcon({path: 'chrome/icons/icon19gray.png'});
- return;
- }
-
- chrome.browserAction.setIcon({path: 'chrome/icons/icon19.png'});
- chrome.browserAction.setTitle({title: 'Unblock Youku ' + unblock_youku.version});
- }
-
- // check chrome.storage before changing icons
- // the mode should already be set in previous get_mode_name()
- get_storage('unblock_youku_mode', function(current_mode) {
- if (typeof current_mode !== 'undefined') {
- _change_browser_icon(option);
- } else {
- var err_msg = 'chrome.storage has some problems';
- console.log(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
- });
-}
-
-
-// Settings are changed asynchronously
-function storage_monitor(changes, area) {
- console.log('storage changes: ' + JSON.stringify(changes));
-
- if (typeof changes.unblock_youku_mode !== 'undefined') {
- var mode_change = changes.unblock_youku_mode;
-
- // doesn't run if it's first time to migrate the old settings
- if (typeof mode_change.oldValue !== 'undefined' && typeof mode_change.newValue !== 'undefined') {
- clear_mode_settings(mode_change.oldValue);
- setup_mode_settings(mode_change.newValue);
- ga_report_event('Change Mode', mode_change.oldValue + ' -> ' + mode_change.newValue);
- }
- }
-
- if (typeof changes.custom_proxy_server !== 'undefined') {
- var proxy_server_change = changes.custom_proxy_server;
- if (typeof proxy_server_change.newValue !== 'undefined'
- && typeof proxy_server_change.newValue.proc !== 'undefined'
- && typeof proxy_server_change.newValue.addr !== 'undefined') {
- localStorage.custom_proxy_server_proc = proxy_server_change.newValue.proc;
- localStorage.custom_proxy_server_addr = proxy_server_change.newValue.addr;
- } else {
- if (typeof localStorage.custom_proxy_server_proc !== 'undefined') {
- localStorage.removeItem('custom_proxy_server_proc');
- }
- if (typeof localStorage.custom_proxy_server_addr !== 'undefined') {
- localStorage.removeItem('custom_proxy_server_addr');
- }
- }
- }
-}
-
-
-function setup_storage_monitor() {
- if (!chrome.storage.onChanged.hasListener(storage_monitor)) {
- chrome.storage.onChanged.addListener(storage_monitor);
- console.log('storage_monitor is set');
- } else {
- var err_msg = 'storage_monitor is already there!';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
-}
-
-
-// ====== Initialization ======
-document.addEventListener("DOMContentLoaded", function() {
- setup_storage_monitor();
-
- unblock_youku.version = chrome.runtime.getManifest().version;
-
- // the latest version to show NEW on the icon; it's usually a big update with new features
- unblock_youku.lastest_new_version = '3.0.0.1';
- get_storage('previous_new_version', function(version) {
- // previous_new_version will be set by the popup page once the page is opened
- if (typeof version === 'undefined' || version !== unblock_youku.lastest_new_version) {
- chrome.browserAction.setBadgeText({text: 'NEW'});
- }
- });
-
- get_mode_name(function(current_mode_name) {
- setup_mode_settings(current_mode_name);
-
- ga_report_ratio('Init Mode', current_mode_name);
- ga_report_ratio('Version', unblock_youku.version);
- });
-});
diff --git a/chrome/donation.js b/chrome/donation.js
deleted file mode 100644
index aa16dd5d..00000000
--- a/chrome/donation.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-'use strict';
-
-
-function create_donation_tab() {
- const donation_url = chrome.i18n.getMessage('donation_url');
- chrome.tabs.create({
- url: donation_url,
- });
-}
-
-chrome.runtime.onInstalled.addListener(function(details) {
- if (details.reason === 'install') {
- create_donation_tab();
- }
-});
diff --git a/chrome/header.js b/chrome/header.js
deleted file mode 100644
index b42cb588..00000000
--- a/chrome/header.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-/*global unblock_youku: false */
-/*global ga_report_error: false */
-"use strict";
-
-
-function header_modifier(details) {
- console.log('modify headers of ' + details.url);
-
- details.requestHeaders.push({
- name: 'X-Forwarded-For',
- value: unblock_youku.ip_addr
- }, {
- name: 'Client-IP',
- value: unblock_youku.ip_addr
- });
-
- return {requestHeaders: details.requestHeaders};
-}
-
-
-function setup_header() {
- if (!chrome.webRequest.onBeforeSendHeaders.hasListener(header_modifier)) {
- chrome.webRequest.onBeforeSendHeaders.addListener(
- header_modifier,
- {
- urls: unblock_youku.header_urls
- },
- ['requestHeaders', 'blocking']
- );
- console.log('header_modifier is set');
- } else {
- var err_msg = 'header_modifer is already there!';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
-}
-
-
-function clear_header() {
- if (chrome.webRequest.onBeforeSendHeaders.hasListener(header_modifier)) {
- chrome.webRequest.onBeforeSendHeaders.removeListener(header_modifier);
- console.log('header_modifier is removed');
- } else {
- var err_msg = 'header_modifier is not there!';
- console.error(err_msg);
- ga_report_error('Unexpected Error', err_msg);
- }
-}
diff --git a/chrome/pages/LICENSE.txt b/chrome/pages/LICENSE.txt
deleted file mode 100644
index 811d471e..00000000
--- a/chrome/pages/LICENSE.txt
+++ /dev/null
@@ -1 +0,0 @@
-The libraries, including Bootstrap and jQuery, contained in this folder are under their own licenses.
diff --git a/chrome/pages/js/analytics.js b/chrome/pages/js/analytics.js
deleted file mode 100644
index ae1b7973..00000000
--- a/chrome/pages/js/analytics.js
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Copyright (C) 2012 - 2014 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-
-/*jslint browser: true */
-
-var unblock_youku = unblock_youku || {};
-
-
-(function() {
- "use strict";
- if (typeof localStorage.uuid === 'undefined') {
- // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
- unblock_youku.uuid = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
- var r = Math.random()*16|0;
- var v = c === 'x' ? r : (r&0x3|0x8);
- return v.toString(16);
- });
- localStorage.uuid = unblock_youku.uuid;
- } else {
- unblock_youku.uuid = localStorage.uuid;
- }
-}());
-
-
-function ga_collect_data(type, data) {
- "use strict";
- var xhr = new XMLHttpRequest();
- xhr.open('POST', 'https://www.google-analytics.com/collect', true);
- var payload = 'v=1&'
- + 'tid=UA-30726750-8&'
- + 'cid='+ unblock_youku.uuid + '&'
- + 'aip=1&' // anonymize IP
- + 't=' + type + '&'
- + data;
- xhr.send(payload);
-}
-
-
-function ga_report_event(event_name, event_desc, collection_rate) {
- "use strict";
- if (typeof collection_rate !== 'undefined' && Math.random() > collection_rate) {
- // reduce data points
- return;
- }
-
- var data = 'ec=' + encodeURIComponent(event_name) + '&'
- + 'ea=' + encodeURIComponent(event_desc);
- ga_collect_data('event', data);
-}
-
-
-function ga_report_ratio(ratio_name, ratio_value) {
- ga_report_event(ratio_name, ratio_value, 0.1);
- // ga_report_event(ratio_name, ratio_value, 1.0);
-}
-
-
-function ga_report_timeout(timeout_type, timeout_server) {
- ga_report_event(timeout_type, timeout_server, 0.1);
-}
-
-
-function ga_report_error(error_name, error_desc) {
- "use strict";
- // var data = 'exd=' + encodeURIComponent(error_name) + '&'
- // + 'exf=' + encodeURIComponent(error_desc);
- // ga_collect_data('exception', data);
- ga_report_event(error_name, error_desc);
-}
-
-
-
-// see http://goo.gl/QLJu6 and http://goo.gl/aNH3H
-window.onerror = function(message, file, line) {
- "use strict";
- var msg = file + '(' + line + '): ' + message;
- // console.error(msg);
- ga_report_error('Unknown Error', msg);
-};
diff --git a/chrome/pages/js/options-proxy.js b/chrome/pages/js/options-proxy.js
deleted file mode 100644
index d1afc14d..00000000
--- a/chrome/pages/js/options-proxy.js
+++ /dev/null
@@ -1,174 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-var background = background || chrome.extension.getBackgroundPage();
-var default_proxy_server_proc = background.unblock_youku.default_proxy_server_proc;
-var default_proxy_server_addr = background.unblock_youku.default_proxy_server_addr;
-
-function show_proxy_message(type, content) {
- "use strict";
- var alert_type = 'info';
- if (type === 'success' || type === 'warning') {
- alert_type = type; // success, info, or warning
- }
-
- $('#proxy_message').html('
× ' + content + '
');
-}
-
-function show_proxy_error(content) {
- "use strict";
- $('#proxy_message').html('× ' + content + '
');
-}
-
-function show_proxy_test_message(type, content) {
- "use strict";
- var alert_type = 'info';
- if (type === 'success' || type === 'warning' || type === 'danger') {
- alert_type = type; // success, info, or warning
- }
-
- $('#proxy_test_message').html('× ' + content + '
');
-}
-
-function remove_custom_proxy_server(callback) {
- "use strict";
- // Change localStorage first to make sure it contains the correct values before updating PAC
- background.localStorage.removeItem('custom_proxy_server_proc');
- background.localStorage.removeItem('custom_proxy_server_addr');
- background.remove_storage('custom_proxy_server', callback);
-}
-
-function set_custom_proxy_server(server_proc, server_addr, callback) {
- "use strict";
- background.localStorage.custom_proxy_server_proc = server_proc;
- background.localStorage.custom_proxy_server_addr = server_addr;
- background.set_storage("custom_proxy_server", {
- proc : server_proc,
- addr : server_addr
- }, callback);
-}
-
-function get_custom_proxy_server(callback) {
- "use strict";
- background.get_storage('custom_proxy_server', function(server_info) {
- if (typeof server_info === 'undefined'
- || typeof server_info.proc === 'undefined'
- || typeof server_info.addr === 'undefined') {
- callback(/*custom_enabled=*/false, default_proxy_server_proc, default_proxy_server_addr);
- } else {
- callback(/*custom_enabled=*/true, server_info.proc, server_info.addr);
- }
- });
-}
-
-function test_custom_proxy_server(callback) {
- "use strict";
- // TODO: Change this URL to a new one. It stopped working a while ago.
- var test_url = 'http://ipservice.163.com/isFromMainland';
- show_proxy_test_message('info', 'Testing connection & Unblock...');
- $.get(test_url, function(data) {
- if (data === 'true') {
- show_proxy_test_message('success', 'Unblock OK.');
- } else {
- show_proxy_test_message('danger', 'Unblock test failed! Invalid config or server not located in mainland China.');
- }
- }).error(function() {
- show_proxy_test_message('danger', 'Unblock test failed! Invalid config or server not working properly.');
- });
-}
-
-$(document).ready(function() {
- "use strict";
- get_custom_proxy_server(function(custom_enabled, server_proc, server_addr) {
- $('#custom_proxy_proc option').each(function(idx, d) {
- if (d.value === server_proc) {
- d.selected = true;
- }
- });
- $('#custom_proxy_addr').val(server_addr);
- if (custom_enabled === true) {
- $('#custom_proxy_proc').attr('disabled', true);
- $('#custom_proxy_addr').attr('disabled', true);
- $('#custom_proxy_enable').attr('disabled', true);
- background.get_mode_name(function(current_mode) {
- if (current_mode === 'normal') {
- show_proxy_message('info', 'Status: Enabled');
- } else {
- show_proxy_message('warning', 'Status: Enabled. But current mode is not proxy mode.');
- }
- });
- } else {
- show_proxy_message('info', 'Status: NOT Enabled');
- $('#custom_proxy_reset').attr('disabled', true);
- }
- });
-
- $('#custom_proxy_enable').click(function() {
- var custom_proxy_proc = $('#custom_proxy_proc').val();
- var custom_proxy_addr = $('#custom_proxy_addr').val();
- $('#custom_proxy_proc').attr('disabled', true);
- $('#custom_proxy_addr').attr('disabled', true);
- $('#custom_proxy_enable').attr('disabled', true);
- set_custom_proxy_server(custom_proxy_proc, custom_proxy_addr, function() {
- // switch to proxy mode and force refresh pac proxy
- background.get_mode_name(function(current_mode) {
- if (current_mode === 'normal') {
- background.setup_proxy();
- $('#custom_proxy_reset').attr('disabled', false);
- show_proxy_message('info', 'Enabled custom proxy server.');
- // test_custom_proxy_server();
- } else {
- background.set_mode_name('normal', function() {
- $('#custom_proxy_reset').attr("disabled", false);
- show_proxy_message('warning', 'Enabled custom proxy server, and changed to proxy mode.');
- // test_custom_proxy_server();
- });
- }
- });
- });
- });
-
- $('#custom_proxy_reset').click(function() {
- $("#custom_proxy_reset").attr('disabled', true);
- remove_custom_proxy_server(function() {
- // switch to proxy mode and force refresh pac proxy
- background.get_mode_name(function(current_mode) {
- if (current_mode === 'normal') {
- background.setup_proxy();
- $('#custom_proxy_proc').attr('disabled', false);
- $('#custom_proxy_addr').attr('disabled', false);
- $('#custom_proxy_enable').attr('disabled', false);
- show_proxy_message('warning', 'Reset custom proxy server.');
- // test_custom_proxy_server();
- } else {
- background.set_mode_name('normal', function() {
- $('#custom_proxy_proc').attr('disabled', false);
- $('#custom_proxy_addr').attr('disabled', false);
- $('#custom_proxy_enable').attr('disabled', false);
- show_proxy_message('warning', 'Reset custom proxy server, and changed to proxy mode.');
- // test_custom_proxy_server();
- });
- }
- });
- });
- });
-
- $('#form_custom_proxy_server').submit(function(event) {
- // prevent the default action of submitting a form
- event.preventDefault();
- });
-});
diff --git a/chrome/pages/js/popup.js b/chrome/pages/js/popup.js
deleted file mode 100644
index c068f441..00000000
--- a/chrome/pages/js/popup.js
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-
-function set_i18n_text() {
- "use strict";
- var get_msg = chrome.i18n.getMessage;
-
- $('div#support strong').html(get_msg('support_title'));
- $('p#support_message').html(get_msg('support_message'));
- $('a#support_link').attr('href', get_msg('donation_url'));
- $('button#support_button').html(get_msg('support_button'));
-
- $('div#social strong').html(get_msg('social_title'));
-
- $('div#mode_select strong').html(get_msg('mode_select'));
-
- $('span.mode_off_name').html(get_msg('mode_off'));
- $('span.mode_off_desc').html(get_msg('mode_off_description'));
- $('span.mode_normal_name').html(get_msg('mode_normal'));
- $('span.mode_normal_desc').html(get_msg('mode_normal_description'));
-
- $('div#faq').html(get_msg('faq'));
- $('div#feedback').html(get_msg('feedback'));
- $('div#rating').html(get_msg('rating'));
-}
-
-$(document).ready(function() {
- "use strict";
- set_i18n_text();
-
- var background = chrome.extension.getBackgroundPage();
-
- // set default button display
- background.get_mode_name(function(current_mode_name) {
- switch (current_mode_name) {
- case 'off':
- $('label#off').addClass('active');
- break;
- default:
- $('label#normal').addClass('active');
- break;
- }
- });
-
-
- chrome.browserAction.setBadgeText({text: ''}); // clear the text NEW
- background.get_storage('previous_new_version', function(version) {
- if (typeof version === 'undefined' || version !== background.unblock_youku.lastest_new_version) {
- background.set_storage('previous_new_version', background.unblock_youku.lastest_new_version);
- }
- });
-
- $('div#version small').html('Unblock Youku v' + background.unblock_youku.version);
-
- // button actions
- $('input#input_off').change(function() {
- console.log('to change mode to off');
- background.change_mode('off');
- });
- $('input#input_normal').change(function() {
- console.log('to change mode to normal');
- background.change_mode('normal');
- });
-
- // enable tooltip
- $('#tooltip').tooltip();
-});
-
diff --git a/chrome/proxy.js b/chrome/proxy.js
deleted file mode 100644
index b60430ff..00000000
--- a/chrome/proxy.js
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-/*jshint devel:true, globalstrict: true */
-/*global chrome: false, unblock_youku: false, new_sogou_proxy_addr: false, urls2pac: false, get_mode_name: false */
-/*global ga_report_timeout: false, ga_report_error: false, ga_report_event: false, localStorage: false */
-"use strict";
-
-function setup_proxy() {
- function setup_pac_data(proxy_prot_1, proxy_addr_1,
- proxy_prot_2, proxy_addr_2) {
- var pac_data = urls2pac(
- unblock_youku.chrome_proxy_bypass_urls,
- unblock_youku.chrome_proxy_urls,
- proxy_addr_1, proxy_prot_1,
- proxy_addr_2, proxy_prot_2);
- // console.log(pac_data);
- var proxy_config = {
- mode: 'pac_script',
- pacScript: {
- data: pac_data
- }
- };
- chrome.proxy.settings.set(
- {
- value: proxy_config,
- scope: 'regular'
- },
- function() {}
- );
- }
-
-
- console.group('to set up proxy');
-
- var proxy_server_proc = unblock_youku.default_proxy_server_proc;
- var proxy_server_addr = unblock_youku.default_proxy_server_addr;
- var backup_proxy_server_proc = unblock_youku.backup_proxy_server_proc;
- var backup_proxy_server_addr = unblock_youku.backup_proxy_server_addr;
-
- if (typeof localStorage.custom_proxy_server_proc !== 'undefined' &&
- typeof localStorage.custom_proxy_server_addr !== 'undefined') {
- proxy_server_proc = localStorage.custom_proxy_server_proc;
- proxy_server_addr = localStorage.custom_proxy_server_addr;
- backup_proxy_server_proc = localStorage.custom_proxy_server_proc;
- backup_proxy_server_addr = localStorage.custom_proxy_server_addr;
- }
-
- setup_pac_data(proxy_server_proc, proxy_server_addr,
- backup_proxy_server_proc, backup_proxy_server_addr);
- console.log('using the proxy server: ' + proxy_server_proc + ' ' + proxy_server_addr);
- ga_report_event('Proxy Server Selection', proxy_server_proc + ' ' + proxy_server_addr, 0.1);
-
- console.groupEnd();
-}
-
-
-function clear_proxy() {
- var proxy_config = {
- mode: 'system'
- };
-
- chrome.proxy.settings.set(
- {
- value: proxy_config,
- scope: 'regular'
- },
- function() {}
- );
-
- console.log('proxy is removed (changed to system setting)');
-}
diff --git a/chrome/redirect.js b/chrome/redirect.js
deleted file mode 100644
index 2f6af78f..00000000
--- a/chrome/redirect.js
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-/*global string_starts_with: false */
-"use strict";
-
-
-function http_redirector(details) {
- console.log('original url: ' + details.url);
- if (details.url.slice(-15) === 'crossdomain.xml') {
- console.log('directly pass');
- return {};
- }
- var redirect_url = null;
-
- // special treatment for play.baidu
- if (details.url.slice(0, 41) === 'http://play.baidu.com/data/music/songlink') {
- redirect_url = 'http://play.baidu.com/data/cloud/songlink' + details.url.slice(41);
- console.log('redirect url: ' + redirect_url);
- return {redirectUrl: redirect_url};
- }
-
- var backend_server;
- if (typeof localStorage.custom_redirect_server === 'undefined') {
- backend_server = unblock_youku.actual_redirect_server;
- } else {
- backend_server = localStorage.custom_redirect_server;
- }
-
- if (string_starts_with(details.url, 'http://')) {
- redirect_url = 'http://' + backend_server + '/http/' + details.url.substring('http://'.length);
- } else if (string_starts_with(details.url, 'https://')) {
- redirect_url = 'http://' + backend_server + '/https/' + details.url.substring('https://'.length);
- }
- console.log('redirect url: ' + redirect_url);
-
- if (redirect_url !== null) {
- return {redirectUrl: redirect_url};
- }
- return {};
-}
-
-
-function clear_redirect() {
- if (chrome.webRequest.onBeforeRequest.hasListener(http_redirector)) {
- chrome.webRequest.onBeforeRequest.removeListener(http_redirector);
- console.log('http_redirector is removed');
- } else {
- console.log('http_redirector is not there!');
- }
-}
diff --git a/chrome/storage.js b/chrome/storage.js
deleted file mode 100644
index 6856e79d..00000000
--- a/chrome/storage.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-/*global chrome: false */
-"use strict";
-
-// have to use a callback function
-function get_storage(key, callback) {
- chrome.storage.sync.get(key, function(items) {
- if (typeof items !== 'undefined' && items.hasOwnProperty(key)) {
- callback(items[key]);
- } else {
- callback();
- }
- });
-}
-
-
-function set_storage(key, value, callback) {
- var obj = {};
- obj[key] = value; // can't just use {key: value}
- chrome.storage.sync.set(obj, callback);
-}
-
-
-function remove_storage(key, callback) {
- chrome.storage.sync.remove(key, callback);
-}
\ No newline at end of file
diff --git a/chrome/icons/icon128.png b/icons/icon128.png
similarity index 100%
rename from chrome/icons/icon128.png
rename to icons/icon128.png
diff --git a/chrome/icons/icon16.png b/icons/icon16.png
similarity index 100%
rename from chrome/icons/icon16.png
rename to icons/icon16.png
diff --git a/chrome/icons/icon19.png b/icons/icon19.png
similarity index 100%
rename from chrome/icons/icon19.png
rename to icons/icon19.png
diff --git a/chrome/icons/icon19gray.png b/icons/icon19gray.png
similarity index 100%
rename from chrome/icons/icon19gray.png
rename to icons/icon19gray.png
diff --git a/chrome/icons/icon19heart.png b/icons/icon19heart.png
similarity index 100%
rename from chrome/icons/icon19heart.png
rename to icons/icon19heart.png
diff --git a/chrome/icons/icon19spring.png b/icons/icon19spring.png
similarity index 100%
rename from chrome/icons/icon19spring.png
rename to icons/icon19spring.png
diff --git a/chrome/icons/icon19xmas.png b/icons/icon19xmas.png
similarity index 100%
rename from chrome/icons/icon19xmas.png
rename to icons/icon19xmas.png
diff --git a/chrome/icons/icon48.png b/icons/icon48.png
similarity index 100%
rename from chrome/icons/icon48.png
rename to icons/icon48.png
diff --git a/manifest.json b/manifest.json
index 64285a94..e568f042 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,71 +1,32 @@
{
"name": "Unblock Youku",
- "version": "3.10.0",
- "manifest_version": 2,
- "minimum_chrome_version": "80.0",
+ "version": "4.0.0",
+ "manifest_version": 3,
+ "minimum_chrome_version": "96.0",
+ "default_locale": "en",
"description": "__MSG_description__",
"icons": {
- "16": "chrome/icons/icon16.png",
- "48": "chrome/icons/icon48.png",
- "128": "chrome/icons/icon128.png"
+ "16": "icons/icon16.png",
+ "48": "icons/icon48.png",
+ "128": "icons/icon128.png"
},
- "permissions": [
- "proxy",
- "webRequest",
- "webRequestBlocking",
- "storage",
- "http://*/*",
- "https://*/*"
- ],
"background": {
- "scripts": [
- "shared/urls.js",
- "shared/tools.js",
- "chrome/pages/js/analytics.js",
- "chrome/storage.js",
- "chrome/header.js",
- "chrome/proxy.js",
- "chrome/redirect.js",
- "chrome/config.js",
- "chrome/donation.js"
- ]
+ "service_worker": "src/background.mjs",
+ "type": "module"
},
- "browser_action": {
+ "action": {
"default_title": "Unblock Youku isn't working properly! Visit http://uku.im/faq for solutions.",
- "default_icon": "chrome/icons/icon19gray.png",
- "default_popup": "chrome/pages/popup.html"
+ "default_icon": "icons/icon19gray.png",
+ "default_popup": "src/popup.html"
},
- "options_page": "chrome/pages/options.html",
- "content_scripts": [
- {
- "matches": ["http://www.tudou.com/*"],
- "js": ["chrome/content/tudou.js"],
- "run_at": "document_end",
- "all_frames": true
- }, {
- "matches": ["http://play.baidu.com/*"],
- "css": ["chrome/content/play.baidu.css"],
- "all_frames": true
- }, {
- "matches": ["http://music.163.com/*"],
- "js": ["chrome/content/music.163.js"],
- "run_at": "document_end",
- "all_frames": true
- }, {
- "matches": [
- "http://unblockcn.com/*",
- "http://*.unblockcn.com/*",
- "http://unblockyouku.cn/*",
- "http://*.unblockyouku.cn/*",
- "http://724sky.com/*",
- "http://*.724sky.com/*",
- "http://tieba.baidu.com/f?kw=unblockcn*",
- "http://*.tieba.baidu.com/f?kw=unblockcn*"
- ],
- "js": ["chrome/content/unblockcn.js"],
- "run_at": "document_end",
- "all_frames": true
- }
+ "options_page": "src/options.html",
+ "permissions": [
+ "proxy",
+ "declarativeNetRequestWithHostAccess",
+ "storage"
],
- "default_locale": "en"
+ "host_permissions": [
+ "http://*/*",
+ "https://*/*"
+ ]
}
diff --git a/package.json b/package.json
index 258e8058..25698086 100644
--- a/package.json
+++ b/package.json
@@ -1,26 +1,15 @@
{
- "name": "ub.uku.js",
- "version": "0.0.1",
- "dependencies": {
- "colors": "*",
- "nomnom": "1.8.x",
- "request": "2.83.x",
- "uglify-js": "2.7.x",
- "validator": "9.1.x"
- },
- "scripts": {
- "start": "node server/server.js"
- },
+ "license": "AGPL-3.0",
"engines": {
- "node": "8.9.x",
- "npm": "5.5.x"
+ "node": "18.x",
+ "npm": "8.x"
},
- "repository": {
- "type": "git",
- "url": "https://github.com/zhuzhuor/Unblock-Youku.git"
- },
- "license": "AGPL-3.0",
+ "type": "module",
"devDependencies": {
- "eslint": "^8.18.0"
+ "eslint": "^8.18.0",
+ "eslint-config-google": "^0.14.0",
+ "eslint-plugin-import": "^2.26.0",
+ "eslint-plugin-n": "^15.2.3",
+ "eslint-plugin-promise": "^6.0.0"
}
}
diff --git a/server/server.js b/server/server.js
deleted file mode 100755
index 15e1833e..00000000
--- a/server/server.js
+++ /dev/null
@@ -1,269 +0,0 @@
-#!/usr/bin/env node
-
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-
-var net = require('net');
-var url = require('url');
-var util = require('util');
-var domain = require('domain');
-var cluster = require('cluster');
-var http = require('http');
-http.globalAgent.maxSockets = Infinity;
-
-// mikeal/request might have a bug
-// always obtain TLS errors when proxying over https
-process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0';
-
-var colors = require('colors');
-var request = require('request');
-var validator = require('validator');
-
-var shared_tools = require('../shared/tools');
-var server_utils = require('./utils');
-
-
-function check_url_format(str) {
- if (!validator.isURL(str, {
- protocols: ['http', 'https'],
- require_tld: true,
- require_protocol: true
- })) {
- return 'Input Error: Invalid URL format'.red;
- }
-// return; // return nothing
-}
-
-var opts = require("nomnom")
- // .help('Run a redirect server for the Chrome extension')
- .option('nolog', {
- flag: true,
- help: 'Do not print request logs'
- })
- .option('proxy', {
- type: 'string',
- metavar: 'HTTP://IP_ADDRESS:PORT',
- help: 'Send requests through a remote proxy',
- callback: check_url_format
- })
- .option('bak_proxy', {
- type: 'string',
- help: 'Set another proxy server for backup',
- metavar: 'HTTP://IP_ADDRESS:PORT',
- callback: check_url_format
- })
- .option('pac_proxy', {
- type: 'string',
- help: 'Use a different proxy in the PAC file',
- metavar: 'HTTP://IP_ADDRESS:PORT',
- callback: check_url_format
- })
- .parse();
-// console.log(opts);
-
-var local_port = process.env.PORT || 8888;
-if ((!opts.proxy) && process.env.PROXY_ADDR) {
- opts.proxy = process.env.PROXY_ADDR;
-}
-if ((!opts.bak_proxy) && process.env.BAK_PROXY_ADDR) {
- opts.bak_proxy = process.env.BAK_PROXY_ADDR;
-}
-if (!opts.pac_proxy) {
- if (process.env.PAC_PROXY_ADDR) {
- opts.pac_proxy = process.env.PAC_PROXY_ADDR;
- } else if (opts.proxy) {
- opts.pac_proxy = opts.proxy;
- }
-}
-
-//opts.proxy = 'http://abc.com:8888';
-
-if ((opts.proxy && (check_url_format(opts.proxy) !== undefined))
- || (opts.bak_proxy && (check_url_format(opts.bak_proxy) !== undefined))
- || (opts.pac_proxy && (check_url_format(opts.pac_proxy) !== undefined))) {
- console.error('ENV Error: Invalid URL format'.red);
- process.exit(400);
-}
-//console.log(opts);
-
-var pac_file_content = null;
-var pac_proxy_obj = null;
-if (opts.pac_proxy) {
- pac_proxy_obj = url.parse(opts.pac_proxy);
- if (!pac_proxy_obj.host || !pac_proxy_obj.protocol) {
- console.error('Invalid URL format parsed'.red);
- process.exit(401);
- }
- pac_file_content = server_utils.generate_pac_file(pac_proxy_obj.host, pac_proxy_obj.protocol);
-} else {
- pac_file_content = 'function FindProxyForURL(url, host) {return "DIRECT";}';
-}
-// console.log(pac_file_content);
-
-
-function http_req_handler(client_request, client_response) {
- if (!opts.nolog) {
- console.log(
- '[ub.uku.js] '
- + client_request.connection.remoteAddress + ': '
- + client_request.method + ' ' + client_request.url.underline);
- }
-
- if ((client_request.url === '/proxy.pac' || client_request.url === '/pac.pac')
- || (!shared_tools.string_starts_with(client_request.url, '/proxy'))) {
- server_utils.static_responses(client_request, client_response, pac_file_content);
- }
-
- var target = server_utils.get_real_target(client_request.url);
- if (!target.host) {
- client_response.writeHead(403, {
- 'Cache-Control': 'public, max-age=14400'
- });
- client_response.end();
- return;
- }
-
- // access control
- if (!server_utils.is_valid_url(target.href)) {
- client_response.writeHead(403, {
- 'Cache-Control': 'public, max-age=14400'
- });
- client_response.end();
- return;
- }
-
-
- var proxy_request_headers = server_utils.filter_request_headers(client_request.headers);
- proxy_request_headers.Host = target.host;
- var proxy_request_options = {
- encoding: null, // disable auto `buffer.toString()` in package "request"
- url: target.href,
-// url: 'http://httpbin.org/status/400',
- method: client_request.method,
- headers: proxy_request_headers
- };
- if (opts.proxy) {
- proxy_request_options.proxy = opts.proxy;
- }
-
- function handle_proxy_request_error() {
- try {
- client_response.writeHead(500);
- client_response.end('Error occurred, sorry.');
- } catch (er) {
- console.error('[ub.uku.js] Error sending 500', er, client_request.url);
- }
- }
-
- function handle_proxy_response_data(response, payload) {
- var filtered_headers = server_utils.filter_response_headers(response.headers);
- client_response.writeHead(response.statusCode, filtered_headers);
- client_response.end(payload);
- }
-
- request(proxy_request_options, function(err, resp, body) {
- if (err) {
- console.error('[ub.uku.js] first proxy_request error: (' + err.code + ') ' + err.message, client_request.url);
- if (opts.bak_proxy) { // retry the request
- setTimeout(function() { // run it in 1s
- proxy_request_options.proxy = opts.bak_proxy;
- request(proxy_request_options, function(err, resp, body) {
- if (err) {
- console.error('[ub.uku.js] second proxy_request error: (' + err.code + ') ' + err.message, client_request.url, err.stack);
- handle_proxy_request_error();
-
- } else {
- handle_proxy_response_data(resp, body);
- }
- });
- }, 1000);
-
- } else {
- handle_proxy_request_error();
- }
-
- } else {
- handle_proxy_response_data(resp, body);
- }
- });
-}
-
-
-if (cluster.isMaster) {
- var num_CPUs = require('os').cpus().length;
- // num_CPUs = 1;
-
- var i;
- for (i = 0; i < num_CPUs; i++) {
- cluster.fork();
- // one note here
- // the fork() in nodejs is not the same as the fork() in C
- // fork() in nodejs will run the whole code from beginning
- // not from where it is invoked
- }
-
- cluster.on('listening', function(worker, addr_port) {
- // use ub.uku.js as keyword for searching in log files
- util.log('[ub.uku.js] Worker ' + worker.process.pid
- + ' is now connected to ' + addr_port.address + ':' + addr_port.port);
- });
-
- cluster.on('exit', function(worker, code, signal) {
- if (signal) {
- util.log('[ub.uku.js] Worker ' + worker.process.pid
- + ' was killed by signal: ' + signal);
- } else if (code !== 0) {
- console.error('[ub.uku.js] Worker ' + worker.process.pid
- + ' exited with error code: ' + code);
- // respawn a worker process when one dies
- cluster.fork();
- } else {
- console.error('[ub.uku.js] Worker ' + worker.process.pid + ' exited.');
- }
- });
-
- console.log('The redirect server is running...'.green);
- if (opts.proxy) {
- console.log(('Using the remote proxy: ' + opts.proxy).green);
- } else {
- console.log('Running locally without remote proxies.'.green);
- }
- if (opts.bak_proxy) {
- console.log(('Using the backup proxy: ' + opts.bak_proxy).green);
- }
- if (opts.pac_proxy) {
- console.log(('The PAC file uses the proxy: ' + opts.pac_proxy).green);
- }
-
-} else if (cluster.isWorker) {
- var ubuku_server = http.createServer();
- ubuku_server.on('request', http_req_handler);
-
- ubuku_server.listen(local_port, '0.0.0.0').on('error', function(err) {
- if (err.code === 'EADDRINUSE') {
- console.error('[ub.uku.js] Port number is already in use! Exiting now...');
- process.exit();
- }
- });
-}
-
-
-process.on('uncaughtException', function(err) {
- console.error('[ub.uku.js] Caught uncaughtException: ' + err, err.stack);
- process.exit(213);
-});
diff --git a/server/utils.js b/server/utils.js
deleted file mode 100644
index 5dfb9879..00000000
--- a/server/utils.js
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-'use strict';
-
-var url = require('url');
-var util = require('util');
-var http = require('http');
-var querystring = require('querystring');
-var uglify = require('uglify-js');
-
-var shared_urls = require('../shared/urls');
-var shared_tools = require('../shared/tools');
-
-var string_starts_with = shared_tools.string_starts_with;
-var to_title_case = shared_tools.to_title_case;
-
-
-function get_real_target(req_path) {
- var real_target = {};
- var real_url = null;
-
- if (string_starts_with(req_path, '/proxy/http/')) {
- real_url = 'http://' + req_path.substring('/proxy/http/'.length);
- } else if (string_starts_with(req_path, '/proxy/https/')) {
- real_url = 'https://' + req_path.substring('/proxy/https/'.length);
- } else {
- real_url = querystring.parse(url.parse(req_path).query).url;
- if (real_url) {
- // to use urlsafe_b64encode
- real_url = real_url.replace('-', '+').replace('_', '/');
-
- // fix possible padding errors
- // real_url += (new Array((4 - real_url.length % 4) % 4 + 1)).join('=');
- var i;
- for (i = 0; i < (4 - real_url.length % 4) % 4; i++) {
- real_url += '=';
- }
-
- var buf = new Buffer(real_url, 'base64');
- real_url = buf.toString();
- }
- }
-
- if (real_url) {
- real_target = url.parse(real_url);
- if (!real_target.port) {
- real_target.port = 80;
- }
- }
-
- return real_target;
-}
-
-
-function is_valid_url(target_url) {
- var i;
- for (i = 0; i < shared_urls.regex_crx_bypass_urls.length; i++) {
- if (shared_urls.regex_crx_bypass_urls[i].test(target_url)) {
- return false;
- }
- }
- for (i = 0; i < shared_urls.regex_crx_urls.length; i++) {
- if (shared_urls.regex_crx_urls[i].test(target_url)) {
- return true;
- }
- }
-
- return false;
-}
-
-
-function filter_request_headers(headers) {
- var ret_headers = {};
-
- var field;
- for (field in headers) {
- if (headers.hasOwnProperty(field)) {
- if (string_starts_with(field, 'proxy-')) {
- if (field === 'proxy-connection') {
- ret_headers.Connection = headers['proxy-connection'];
- }
- } else if (field === 'user-agent') {
- if (headers['user-agent'].indexOf('CloudFront') !== -1 ||
- headers['user-agent'].indexOf('CloudFlare') !== -1) {
- ret_headers['User-Agent'] = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) ' +
- 'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36';
- } else {
- ret_headers['User-Agent'] = headers['user-agent'];
- }
- } else if (field !== 'cookie' &&
- field !== 'via' &&
- (!string_starts_with(field, 'x-'))) {
- // in case some servers do not recognize lower-case headers, such as hacker news
- ret_headers[to_title_case(field)] = headers[field];
- }
- }
- }
-
- return ret_headers;
-}
-
-
-function filter_response_headers(headers) {
- var res_headers = {};
-
- var field;
- for (field in headers) {
- if (headers.hasOwnProperty(field)) {
- if (string_starts_with(field, 'proxy-')) {
- if (field === 'proxy-connection') {
- res_headers.Connection = headers['proxy-connection'];
- }
- } else if (field !== 'set-cookie' &&
- field !== 'cache-control' &&
- field !== 'expires' &&
- field !== 'pragma' &&
- field !== 'age' &&
- field !== 'via' &&
- field !== 'server' &&
- (!string_starts_with(field, 'x-'))) {
- res_headers[field] = headers[field];
- }
- }
- }
- res_headers['cache-control'] = 'public, max-age=3600';
- res_headers.server = '; DROP TABLE servertypes; --';
-
- return res_headers;
-}
-
-
-function static_responses(client_request, client_response, pac_file_content) {
- if (client_request.url === '/crossdomain.xml') {
- client_response.writeHead(200, {
- 'Content-Type': 'text/xml',
- 'Content-Length': '113',
- 'Cache-Control': 'public, max-age=2592000'
- });
- client_response.end('\n' +
- ' ');
- return;
- }
-
- if (client_request.url === '/status') {
- var status_text = 'OK';
-
- client_response.writeHead(200, {
- 'Content-Type': 'text/plain',
- 'Content-Length': status_text.length.toString(),
- 'Cache-Control': 'public, max-age=3600'
- });
- client_response.end(status_text);
- return;
- }
-
- if (client_request.url === '/proxy.pac' || client_request.url === '/pac.pac') {
- var content_type = 'application/x-ns-proxy-autoconfig';
- if (client_request.headers['user-agent'] !== undefined &&
- client_request.headers['user-agent'].indexOf('PhantomJS') !== -1) {
- content_type = 'text/plain';
- }
- client_response.writeHead(200, {
- 'Content-Type': content_type,
- 'Content-Length': pac_file_content.length.toString(),
- 'Cache-Control': 'public, max-age=14400'
- });
- client_response.end(pac_file_content);
- return;
- }
-
- if (client_request.url === '/favicon.ico') {
- client_response.writeHead(404, {
- 'Cache-Control': 'public, max-age=2592000'
- });
- client_response.end();
- return;
- }
-
- if (client_request.url === '/robots.txt') {
- client_response.writeHead(200, {
- 'Content-Type': 'text/plain',
- 'Content-Length': '25',
- 'Cache-Control': 'public, max-age=2592000'
- });
- client_response.end('User-agent: *\nDisallow: /');
- return;
- }
-
- if (client_request.url === '/regex') {
- var regex_list = shared_urls.produce_squid_regex_list(true /* for PAC proxy */);
- var regex_text = regex_list.join('\n');
-
- client_response.writeHead(200, {
- 'Content-Type': 'text/plain',
- 'Content-Length': regex_text.length.toString(),
- 'Cache-Control': 'public, max-age=3600'
- });
- client_response.end(regex_text);
- return;
- }
-
- if (client_request.url === '/chrome_regex') {
- var chrome_regex_list = shared_urls.produce_squid_regex_list(false /* for Chrome proxy */);
- var chrome_regex_text = chrome_regex_list.join('\n');
-
- client_response.writeHead(200, {
- 'Content-Type': 'text/plain',
- 'Content-Length': chrome_regex_text.length.toString(),
- 'Cache-Control': 'public, max-age=60'
- });
- client_response.end(chrome_regex_text);
- return;
- }
-
- client_response.writeHead(403, {
- 'Cache-Control': 'public, max-age=14400'
- });
- client_response.end();
-}
-
-
-function generate_pac_file(proxy_addr_port, proxy_protocol) {
- return '/*\n' +
- ' Installing/using this software, you agree that this software is\n' +
- ' only for study purposes and its authors and service providers \n' +
- ' take no responsibilities for any consequences.\n' +
- '*/\n' +
- uglify.minify(
- shared_tools.urls2pac(
- shared_urls.pac_bypass_urls,
- shared_urls.pac_urls,
- proxy_addr_port,
- proxy_protocol
- ),
- {fromString: true}
- ).code;
-}
-
-
-exports.get_real_target = get_real_target;
-exports.is_valid_url = is_valid_url;
-exports.filter_request_headers = filter_request_headers;
-exports.filter_response_headers = filter_response_headers;
-exports.static_responses = static_responses;
-exports.generate_pac_file = generate_pac_file;
diff --git a/shared/produce_regex.js b/shared/produce_regex.js
deleted file mode 100644
index 4fcb9fc3..00000000
--- a/shared/produce_regex.js
+++ /dev/null
@@ -1,9 +0,0 @@
-'use strict';
-
-const shared_urls = require('../shared/urls');
-
-const chrome_regex_list = shared_urls.produce_squid_regex_list(/* for_pac_server= */ false);
-const chrome_regex_text = chrome_regex_list.join('\n') + '\n';
-console.log(chrome_regex_text);
-
-process.exit(0);
\ No newline at end of file
diff --git a/shared/tools.js b/shared/tools.js
deleted file mode 100644
index 1463736a..00000000
--- a/shared/tools.js
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-
-function new_random_ip() {
- "use strict";
- var ip_addr = '220.181.111.';
- //ip_addr += Math.floor(Math.random() * 255) + '.';
- ip_addr += Math.floor(Math.random() * 254 + 1); // 1 ~ 254
- return ip_addr;
-}
-
-
-function string_starts_with(str, substr) {
- "use strict";
- return str.slice(0, substr.length) === substr;
-}
-
-
-function _parse_url(url_str) {
- "use strict";
- var protocol = null;
- if (string_starts_with(url_str, 'http://')) {
- url_str = url_str.slice('http://'.length);
- protocol = 'http';
- } else if (string_starts_with(url_str, 'https://')) {
- url_str = url_str.slice('https://'.length);
- protocol = 'https';
- } else {
- console.error('URL does not start with http:// or https://');
- return null;
- }
-
- var path_idx = url_str.indexOf('/');
- if (path_idx < 0) {
- path_idx = url_str.length;
- url_str += '/';
- }
- var colon_idx = url_str.indexOf(':'); // the colon before the optional port number
-
- var sep_idx = path_idx;
- if (colon_idx >= 0 && colon_idx < path_idx) {
- sep_idx = colon_idx;
- }
-
- return {
- protocol: protocol,
- // the parameter in FindProxyForURL only doesn't contain port numbers
- hostname: url_str.slice(0, sep_idx),
- portpath: url_str.slice(sep_idx)
- };
-}
-// console.log(_parse_url('http://test.com'));
-// console.log(_parse_url('http://test.com:123'));
-// console.log(_parse_url('http://test.com/path'));
-// console.log(_parse_url('http://test.com:123/path'));
-
-
-function gen_url_map(protocol, white_ulist, proxy_ulist) {
- "use strict";
- var url_map = {
- white: {
- any: []
- },
- proxy: {
- any: []
- }
- };
-
- function stringify(map_obj) {
- var white_map = map_obj.white;
- var proxy_map = map_obj.proxy;
-
- var res_str = [
- "{",
- " 'white': {"
- ].join("\n") + "\n";
- res_str += stringify_patterns(white_map);
-
- res_str += [
- " },",
- " 'proxy': {"
- ].join("\n") + "\n";
- res_str += stringify_patterns(proxy_map);
-
- res_str += [
- " }",
- "}"
- ].join("\n");
-
- return res_str;
- }
-
- function stringify_patterns(hostname_map) {
- var res_str = "";
- var i, patterns = null;
-
- for (var hostname in hostname_map) {
- if (hostname_map.hasOwnProperty(hostname)) {
- res_str += " '" + hostname + "': [";
- patterns = hostname_map[hostname];
-
- if (patterns.length === 0) {
- res_str += "],\n";
-
- } else {
- res_str += "\n";
- for (i = 0; i < patterns.length; i++) {
- res_str += " " + patterns[i] + ",\n";
- }
- res_str = res_str.slice(0, -2) + '\n'; // remove the last ,
- res_str += " ],\n";
- }
- }
- }
- res_str = res_str.slice(0, -2) + '\n'; // remove the last ,
-
- return res_str;
- }
-
- function add_patterns(map_obj, ulist) {
- var i, uobj, hostname, portpath;
- var key, val;
- for (i = 0; i < ulist.length; i++) {
- uobj = _parse_url(ulist[i]);
- if (uobj === null) {
- console.error('Invalid URL pattern: ' + ulist[i]);
- continue;
- }
-
- if (uobj.protocol === protocol) {
- hostname = uobj.hostname;
- portpath = uobj.portpath;
- if (hostname.indexOf('*') >= 0) {
- if (hostname.slice(1).indexOf('*') >= 0) { // * is only allowed to be the first char
- console.error('Invalid wildcard URL pattern: ' + ulist[i]);
- continue;
-
- } else {
- key = 'any';
- val = hostname + portpath; // host:port/path
- }
-
- } else {
- if (!map_obj.hasOwnProperty(hostname)) {
- map_obj[hostname] = [];
- }
- key = hostname;
- val = portpath; // only :port/path
- }
-
- val = val.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
- val = val.replace(/\*/g, '.*');
- val = val.replace(/^\.\*/i, '[^\/]*'); // if starts with *; should not be possible for :port or /path
- // val = new RegExp('^' + val + '$', 'i');
- val = '/^' + val + '$/i';
-
- if (val.slice(-5) === '.*$/i') {
- val = val.slice(0, -5) + '/i';
- }
-
- map_obj[key].push(val);
- } // if
- } // for
- }
-
-
- add_patterns(url_map.white, white_ulist);
- add_patterns(url_map.proxy, proxy_ulist);
-
- // console.log(stringify(url_map));
- return stringify(url_map);
-}
-
-
-function urls2pac(url_whitelist, url_list,
- proxy_server_1, proxy_protocol_1,
- proxy_server_2, proxy_protocol_2) {
- "use strict";
- var http_map_str = gen_url_map('http', url_whitelist, url_list);
- var https_map_str = gen_url_map('https', url_whitelist, url_list);
-
- if (typeof proxy_protocol_1 === 'undefined') {
- proxy_protocol_1 = 'PROXY';
- } else {
- proxy_protocol_1 = proxy_protocol_1.replace(/:/g,'');
- proxy_protocol_1 = proxy_protocol_1.replace(/\//g,'');
- proxy_protocol_1 = proxy_protocol_1.toUpperCase();
- if (proxy_protocol_1 === 'HTTP') {
- proxy_protocol_1 = 'PROXY';
- }
- }
-
- var _proxy_str = proxy_protocol_1 + " " + proxy_server_1 + "; ";
-
- if (typeof proxy_server_2 !== 'undefined') {
- if (typeof proxy_protocol_2 === 'undefined') {
- proxy_protocol_2 = 'PROXY';
- }
- proxy_protocol_2 = proxy_protocol_2.replace(/:/g,'');
- proxy_protocol_2 = proxy_protocol_2.replace(/\//g,'');
- proxy_protocol_2 = proxy_protocol_2.toUpperCase();
- if (proxy_protocol_2 === 'HTTP') {
- proxy_protocol_2 = 'PROXY';
- }
-
- _proxy_str += proxy_protocol_2 + " " + proxy_server_2 + "; ";
- }
-
- _proxy_str += "DIRECT;";
-
- return [
- "var _http_map = " + http_map_str + ";",
- "var _https_map = " + https_map_str + ";",
- "var _proxy_str = '" + _proxy_str + "';",
- "",
- "function _check_regex_list(regex_list, str) {",
- " if (str.slice(0, 4) === ':80/')",
- " str = str.slice(3);",
- " for (var i = 0; i < regex_list.length; i++)",
- " if (regex_list[i].test(str))",
- " return true;",
- " return false;",
- "}",
- "",
- "function _check_patterns(patterns, hostname, full_url, prot_len) {",
- " if (patterns.hasOwnProperty(hostname))",
- " if (_check_regex_list(patterns[hostname],",
- " full_url.slice(prot_len + hostname.length)))", // check only :port/path
- " return true;",
- " if (_check_regex_list(patterns.any,", // try our best to speed up the checking for non-proxied urls
- " full_url.slice(prot_len)))", // check hostname:port/path
- " return true;",
- " return false;",
- "}",
- "",
- "function _find_proxy(url_map, host, url, prot_len) {",
- " if (_check_patterns(url_map.white, host, url, prot_len))",
- " return 'DIRECT';",
- " if (_check_patterns(url_map.proxy, host, url, prot_len))",
- " return _proxy_str;",
- " return 'DIRECT';",
- "}",
- "",
- "function FindProxyForURL(url, host) {", // host doesn't contain port
- " var prot = url.slice(0, 6);",
- " if (prot === 'http:/')",
- " return _find_proxy(_http_map, host, url, 7);", // 'http://'.length
- " else if (prot === 'https:')",
- " return _find_proxy(_https_map, host, url, 8);", // 'https://'.length
- " return 'DIRECT';",
- "}"
- ].join("\n") + "\n";
-}
-
-
-// change host to Host, or user-agent to User-Agent
-function to_title_case(str) {
- "use strict";
- // just a little differnt from http://goo.gl/IGhfR
- return str.replace(/\w[^\-\s]*/g, function(txt) {
- return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
- });
-}
-
-
-var exports = exports || {};
-exports.new_random_ip = new_random_ip;
-exports.urls2pac = urls2pac;
-exports.string_starts_with = string_starts_with;
-exports.to_title_case = to_title_case;
-
-
-(function() {
- "use strict";
-
- if (typeof module !== 'undefined' && module.exports && require.main === module) {
- var shared_urls = require('./urls');
- var pac_content = urls2pac(shared_urls.pac_bypass_urls, shared_urls.pac_urls, 'localhost:8888');
- console.log(pac_content);
- }
-}());
diff --git a/shared/urls.js b/shared/urls.js
deleted file mode 100644
index 3a17eac0..00000000
--- a/shared/urls.js
+++ /dev/null
@@ -1,563 +0,0 @@
-/*
- * Copyright (C) 2012 - 2016 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-var unblock_youku = unblock_youku || {};
-
-/*
- * Note that the HTTPS URLs must NOT have any path portions.
- *
- * For example:
- * - Wrong: https://example.com/abc/*
- * - Right: https://example.com/*
- */
-
-
-// The HTTP headers of these URLs will be modified.
-unblock_youku.header_urls = [
- 'http://*.ssports.com/*',
- 'http://ssports.com/*',
- 'http://ssports.smgbb.cn/*',
- 'https://pstream.api.mgtv.com/*',
- 'http://kandian.com/player/getEpgInfo*',
-];
-
-// These URLs will go through a proxy.
-unblock_youku.chrome_proxy_urls = [
- 'https://dmd-fifajs-h5-ikuweb.youku.com/*',
- 'https://dmd-fifa-h5-ikuweb.youku.com/*',
- 'http://acs.youku.com/*',
- 'https://acs.youku.com/*',
- // 'http://v.youku.com/player/*',
- 'http://pl-ali.youku.com/*',
- 'http://list.youku.com/category/*',
- 'http://api.youku.com/player/*',
- 'http://play.youku.com/play/get.json*',
- 'http://play-dxk.youku.com/play/get.json*',
- 'http://play-ali.youku.com/play/get.json*',
- 'http://list.youku.com/show/module?*',
- 'http://list.youku.com/show/point?*',
- 'http://list.youku.com/show/episode?*',
- 'http://v.youku.com/page/playlist*',
- 'http://ups.youku.com/ups/get.json*',
- 'https://ups.youku.com/',
- 'https://ac.qq.com/*',
- 'http://player.aplus.pptv.com/*',
- 'http://api.aixifan.com/plays/*',
- 'http://www.acfun.cn/video/getVideo*',
- 'https://www.acfun.cn/*',
- // 'https://v.youku.com/*',
- // 'http://v2.tudou.com/*',
- 'http://video.tudou.com/v/*',
- 'http://www.tudou.com/a/*',
- 'http://www.tudou.com/v/*',
- 'http://www.tudou.com/outplay/goto/*',
- 'http://www.tudou.com/tvp/alist.action*',
- 'http://s.plcloud.music.qq.com/fcgi-bin/p.fcg*',
- 'http://i.y.qq.com/*/fcg-bin/*',
- 'http://i.y.qq.com/*/fcgi-bin/*',
- 'http://c.y.qq.com/*/fcg-bin/*',
- 'http://c.y.qq.com/*/fcgi-bin/*',
- 'https://c.y.qq.com/*',
- 'http://api.unipay.qq.com/cgi-bin/get_pay_info.fcg?*',
- 'https://api.unipay.qq.com/*',
- 'http://hot.vrs.sohu.com/*',
- 'https://hot.vrs.sohu.com/*',
- 'http://live.tv.sohu.com/live/player*',
- 'http://pad.tv.sohu.com/playinfo*',
- 'http://my.tv.sohu.com/play/m3u8version.do*',
- 'http://hot.vrs.letv.com/*',
- 'http://api.le.com/mms/out/video/*',
- 'http://player.pc.le.com/mms/out/video/*',
- 'http://player-pc.le.com/mms/out/video/*',
- // 'http://g3.letv.cn/*',
- 'http://data.video.qiyi.com/*',
- 'http://data.video.iqiyi.com/*',
- 'https://data.video.qiyi.com/*',
- 'https://data.video.iqiyi.com/*',
- // 'http://nl.rcd.iqiyi.com/apis/urc/*',
- 'http://cache.vip.qiyi.com/*',
- 'http://cache.video.qiyi.com/*',
- 'http://cache.vip.iqiyi.com/*',
- 'http://cache.video.iqiyi.com/*',
- 'https://cache.video.iqiyi.com/*',
- 'http://iplocation.geo.qiyi.com/cityjson*',
- 'http://iplocation.geo.iqiyi.com/cityjson*',
- 'http://*.cupid.iqiyi.com/*',
- 'http://v.api.hunantv.com/player/video*',
- 'http://mobile.api.hunantv.com/v5/video/getSource*',
- 'http://v.api.mgtv.com/player/video*',
- 'https://v.api.mgtv.com/*',
- 'http://pcweb.api.mgtv.com/player/video*',
- 'https://pcweb.api.mgtv.com/*',
- 'http://acc.music.qq.com/base/fcgi-bin/getsession*',
- 'http://182.254.116.117/*',
-
- 'http://api.appsdk.soku.com/*',
-
- 'http://app.bilibili.com/bangumi/*',
- 'http://bangumi.bilibili.com/*',
-
- 'http://122.72.82.31/*',
- 'http://211.151.158.155/*',
-
- // 'http://tt.video.qq.com/get*',
- // 'http://ice.video.qq.com/get*',
- // 'http://tjsa.video.qq.com/get*',
- // 'http://a10.video.qq.com/get*',
- // 'http://xyy.video.qq.com/get*',
- // 'http://vcq.video.qq.com/get*',
- // 'http://vsh.video.qq.com/get*',
- // 'http://vbj.video.qq.com/get*',
- // 'http://bobo.video.qq.com/get*',
- // 'http://flvs.video.qq.com/get*',
- // 'http://bkvv.video.qq.com/get*',
- // 'http://h5vv.video.qq.com/get*',
- 'http://*.video.qq.com/get*',
- 'http://*.video.qq.com/fcgi-bin/*',
- 'https://*.video.qq.com/*',
- 'http://info.zb.qq.com/?*',
- 'https://info.zb.qq.com/*',
- 'http://info.zb.video.qq.com/?*',
- 'https://info.zb.video.qq.com/*',
- 'http://qzs.qq.com/tencentvideo_v1/*',
- 'https://qzs.qq.com/*',
- 'http://vd.l.qq.com/*',
- 'https://vd.l.qq.com/*',
- 'http://vi.l.qq.com/*',
- 'https://vi.l.qq.com/*',
-
- 'http://dispatcher.video.sina.com.cn/*',
- 'http://geo.js.kankan.com/*',
- 'http://web-play.pptv.com/*',
- 'http://v.pptv.com/show/*',
- 'https://ppi.api.pptv.com/*',
- 'http://web-play.pplive.cn/*',
- 'http://tools.aplusapi.pptv.com/get_ppi?*',
- 'http://live.pptv.com/api/subject_list?*',
- // 'http://c1.pptv.com/*',
- 'http://dyn.ugc.pps.tv/*',
- 'http://v.pps.tv/ugc/ajax/aj_html5_url.php*',
- 'http://inner.kandian.com/*',
- 'http://ipservice.163.com/*',
- 'http://so.open.163.com/open/info.htm*',
- 'http://zb.s.qq.com/*',
- 'https://zb.s.qq.com/*',
- 'http://ip.kankan.com/*',
- 'http://vxml.56.com/json/*',
-
- 'http://music.sina.com.cn/yueku/intro/*',
- //'http://ting.baidu.com/data/music/songlink*',
- //'http://ting.baidu.com/data/music/songinfo*',
- //'http://ting.baidu.com/song/*/download*',
- 'http://music.sina.com.cn/radio/port/webFeatureRadioLimitList.php*',
- 'http://play.baidu.com/data/music/songlink*',
-
- 'http://v.iask.com/v_play.php*',
- 'http://v.iask.com/v_play_ipad.cx.php*',
- 'http://tv.weibo.com/player/*',
- 'http://wtv.v.iask.com/*.m3u8*',
- 'http://wtv.v.iask.com/mcdn.php',
- 'http://video.sina.com.cn/interface/l/u/getFocusStatus.php*',
- 'http://wtv.v.iask.com/player/ovs1_idc_list.php*',
-
- //'http://kandian.com/player/getEpgInfo*', // !!!
- //'http://cdn.kandian.com/*',
- 'http://www.yinyuetai.com/insite/*',
- 'http://www.yinyuetai.com/main/get-*',
- 'http://www.kugou.com/interface/geoip/*',
- 'http://www.kuwo.cn/yy/PlayCheckIp?callback=checkIpCallback&_=*',
- 'http://antiserver.kuwo.cn/anti.s?*',
- 'http://ipcheck.kuwo.cn/ip_check.kuwo*',
- 'http://*.dpool.sina.com.cn/iplookup*',
- 'http://api.letv.com/streamblock*',
- 'http://api.letv.com/mms/out/video/play*',
- 'http://api.www.letv.com/mms/out/video/playJson?*',
- 'http://*.letv.com/mms/out/video/play*',
- 'http://api.letv.com/mms/out/common/geturl*',
- 'http://api.letv.com/geturl*',
- 'http://api.letv.com/api/geturl*',
- 'http://st.live.letv.com/live/*',
- 'http://live.gslb.letv.com/gslb?*',
- 'http://live.g3proxy.lecloud.com/gslb?*',
- 'http://api.live.letv.com/crossdomain.xml',
- 'http://static.itv.letv.com/api*',
- 'http://ip.apps.cntv.cn/js/player.do*',
- 'http://vdn.apps.cntv.cn/api/get*',
- 'http://vdn.live.cntv.cn/api2/*',
- 'http://cctv1.vtime.cntv.cloudcdn.net/cache/*',
- 'http://cctv5.vtime.cntv.cloudcdn.net/cache/*',
- 'http://cctv5plus.vtime.cntv.cloudcdn.net/cache/*',
- 'http://cctv13.vtime.cntv.cloudcdn.net/cache/*',
- 'http://sports1pull.live.wscdns.com/live/aoyun2',
- 'http://vip.sports.cntv.cn/check.do*',
- 'http://vip.sports.cntv.cn/play.do*',
- 'http://vip.sports.cntv.cn/servlets/encryptvideopath.do*',
- 'http://211.151.157.15/*',
-
- 'http://www.tudou.com/programs/view/*',
- 'http://www.tudou.com/albumplay/*',
- 'http://www.tudou.com/listplay/*',
-
- 'http://www.youku.com/show_page/*',
- 'http://v.youku.com/v_show/*',
- 'http://www.soku.com/search_video/*',
- 'http://search.api.3g.youku.com/*',
- 'http://search.api.3g.tudou.com/*',
- "http://*.api.tv.itc.cn/*",
- "http://api.tv.sohu.com/*",
- 'http://ac.qq.com/Comic*',
- 'http://ac.qq.com/Jump*',
- "http://live.api.hunantv.com/pc/getSourceById*",
- "http://mobile.api.hunantv.com/*",
- 'http://www.qie.tv/*',
- 'http://www.bilibili.com/video/*',
- 'https://www.bilibili.com/*',
- 'http://api.bilibili.com/*',
- 'https://api.bilibili.com/*',
- 'http://interface.bilibili.com/*',
- 'https://interface.bilibili.com/*',
- 'http://bangumi.bilibili.com/api/*',
- 'https://bangumi.bilibili.com/*',
- // 'http://live-play.acgvideo.com/live/*',
- 'http://m10.music.126.net/*', //for the testing of netease music
-
- 'http://douban.fm/*',
- 'https://douban.fm/*',
- 'http://www.xiami.com/*',
- 'http://lixian.xunlei.com/*',
- 'http://lixian.vip.xunlei.com/*',
- 'http://dynamic.cloud.vip.xunlei.com/*',
- 'http://cloud.vip.xunlei.com/*',
- // 'http://vod.lixian.xunlei.com/*',
- 'http://www.iqiyi.com/dongman/',
- 'https://www.iqiyi.com/',
-
- // LETV https://github.com/Unblocker/Unblock-Youku/issues/590
- "http://36.110.222.105/*",
- "http://36.110.222.119/*",
- "http://36.110.222.146/*",
- "http://36.110.222.156/*",
- "http://123.125.89.6/*",
- "http://123.125.89.101/*",
- "http://123.125.89.102/*",
- "http://123.125.89.103/*",
- "http://123.125.89.157/*",
- "http://123.125.89.159/*",
- "http://123.126.32.134/*",
- "http://123.59.122.75/*",
- "http://123.59.122.76/*",
- "http://123.59.122.77/*",
- "http://123.59.122.104/*",
- "http://111.206.208.36/*",
- "http://111.206.208.37/*",
- "http://111.206.208.38/*",
- "http://111.206.208.61/*",
- "http://111.206.208.62/*",
- "http://111.206.208.163/*",
- "http://111.206.208.164/*",
- "http://111.206.208.166/*",
- "http://111.206.211.145/*",
- "http://111.206.211.146/*",
- "http://111.206.211.147/*",
- "http://111.206.211.148/*",
- "http://111.206.211.129/*",
- "http://111.206.211.130/*",
- "http://111.206.211.131/*",
- "http://220.181.153.113/*",
- "http://14.152.77.32/*",
- "http://14.152.77.26/*",
- "http://14.152.77.25/*",
- "http://14.152.77.22/*",
- "http://183.232.229.22/*",
- "http://183.232.229.21/*",
- "http://183.232.229.25/*",
- "http://183.232.229.32/*",
- "http://115.182.200.51/*",
- "http://115.182.200.50/*",
- "http://115.182.200.54/*",
- "http://115.182.200.53/*",
- "http://115.182.200.52/*",
- "http://115.182.63.51/*",
- "http://115.182.63.93/*",
- "http://*.letv.cn/vod/v2/*",
- "http://ark.letv.com/s*",
- "http://search.lekan.letv.com/*",
-
- // 'http://live.video.sina.com.cn/room/*',
- // 'http://edge.v.iask.com/*', // may be large files
-
- 'http://pay.youku.com/buy/redirect.html*',
- 'http://pay.tudou.com/buy/redirect.html*',
- 'http://aid.video.qq.com/fcgi-bin/userip?*',
- 'http://aidbak.video.qq.com/fcgi-bin/userip?*',
- 'http://pay.video.qq.com/fcgi-bin/paylimit*',
- 'http://paybak.video.qq.com/fcgi-bin/paylimit*',
- 'https://*.l.qq.com/*',
- 'http://chrome.2345.com/dianhua/index.php?m=call&f=check&*',
-
- 'http://music.163.com/eapi/*',
-
- // 'http://play.baidu.com/*',
- // 'http://zhangmenshiting.baidu.com/*',
- // 'http://music.baidu.com/box*',
- // 'http://music.baidu.com/data/service/sum*',
- // 'http://music.baidu.com/data/music/songlink*',
- // 'http://music.baidu.com/data/music/songinfo*',
- // 'http://music.baidu.com/data/music/fmlink*',
- // 'http://music.baidu.com/song/*/download*',
- // 'http://fm.baidu.com/*',
- // 'http://www.kugou.com/*',
- // 'http://music.baidu.com/data/user/collect*',
-
- // 'http://d.dxy.cn/*',
- // 'http://ac.qq.com/*/v/cid/*',
- // 'http://v.pptv.com/show/*.html',
- // 'http://www.songtaste.com/*',
- // 'http://songtaste.com/*',
- // 'http://www.yyets.com/*',
- // 'http://mainv2.img.duomi.com/*',
- // 'http://imanhua.com/comic/*',
- // 'http://www.imanhua.com/comic/*',
- // 'http://imanhua.com/v2*',
- // 'http://www.imanhua.com/v2*',
-
- // for development purposes
- 'http://flask-test-iwauxcyxjb.cn-hangzhou.fcapp.run/*',
- 'https://flask-test-iwauxcyxjb.cn-hangzhou.fcapp.run/*'
-];
-
-
-// These URLs will not go through proxy servers (for our Chrome extension).
-unblock_youku.chrome_proxy_bypass_urls = [
- 'http://bangumi.bilibili.com/index/ding-count.json',
-];
-
-
-// These URLs are for other software, such as iOS/Android Apps and TV boxes.
-unblock_youku.pac_proxy_urls = unblock_youku.chrome_proxy_urls.concat(unblock_youku.header_urls, [
- 'http://a.play.api.3g.youku.com/common/v3/play?*',
- 'http://i.play.api.3g.youku.com/common/v3/play?*',
- 'http://i.play.api.3g.youku.com/common/v3/hasadv/play?*',
- 'http://api.3g.youku.com/layout*',
- 'http://api.3g.youku.com/v3/play/address*',
- 'http://api.3g.youku.com/openapi-wireless/videos/*/download*',
- 'http://api.3g.youku.com/videos/*/download*',
- 'http://api.3g.youku.com/common/v3/play*',
- 'http://tv.api.3g.youku.com/openapi-wireless/v3/play/address*',
- 'http://tv.api.3g.youku.com/common/v3/hasadv/play*',
- 'http://tv.api.3g.youku.com/common/v3/play*',
- 'http://play.api.3g.youku.com/common/v3/hasadv/play*',
- 'http://play.api.3g.youku.com/common/v3/play*',
- 'http://play.api.3g.youku.com/v3/play/address*',
- 'http://i-play.mobile.youku.com/*',
- 'http://play.api.3g.tudou.com/v*',
- 'http://tv.api.3g.tudou.com/tv/play?*',
- 'http://api.3g.tudou.com/*',
- 'http://api.tv.sohu.com/mobile_user/device/clientconf.json?*',
- 'http://access.tv.sohu.com/*',
- 'http://iface.iqiyi.com/api/searchIface?*',
- 'http://iface.iqiyi.com/api/ip2area?*',
- 'http://iface2.iqiyi.com/php/xyz/iface/*',
- 'http://iface2.iqiyi.com/php/xyz/entry/galaxy.php?*',
- 'http://iface2.iqiyi.com/php/xyz/entry/nebula.php?*',
- 'http://cache.m.iqiyi.com/jp/tmts/*',
- 'http://dynamic.app.m.letv.com/*/dynamic.php?*ctl=videofile*',
- 'http://dynamic.meizi.app.m.letv.com/*/dynamic.php?*ctl=videofile*',
- 'http://dynamic.search.app.m.letv.com/*/dynamic.php?*ctl=videofile*',
- 'http://dynamic.live.app.m.letv.com/*/dynamic.php?*act=canplay*',
- 'http://listso.m.areainfo.ppstream.com/ip/q.php*',
- 'http://epg.api.pptv.com/detail.api?*',
- 'http://play.api.pptv.com/boxplay.api?*',
- 'http://api.letv.com/getipgeo',
- 'http://m.letv.com/api/geturl?*',
- 'http://api.mob.app.letv.com/play*',
- 'http://static.api.sports.letv.com/*vod?*',
- 'http://api.itv.letv.com/iptv/api/new/video/play/get.json?*', //for letv TV boxes
- 'http://vdn.apps.cntv.cn/api/getLiveUrlCommonApi.do?pa://cctv_p2p_hdcctv5*',
- 'http://vdn.apps.cntv.cn/api/getLiveUrlCommonApi.do?pa://cctv_p2p_hdcctv6*',
- 'http://vdn.apps.cntv.cn/api/getLiveUrlCommonApi.do?pa://cctv_p2p_hdcctv8*',
- 'http://vdn.apps.cntv.cn/api/getLiveUrlCommonApi.do?pa://cctv_p2p_hdbtv6*',
- // 'http://vdn.live.cntv.cn/api2/live.do?channel=pa://cctv_p2p_hdcctv5*',
- // 'http://vdn.live.cntv.cn/api2/live.do?channel=pa://cctv_p2p_hdcctv6*',
- // 'http://vdn.live.cntv.cn/api2/live.do?channel=pa://cctv_p2p_hdcctv8*',
- // 'http://vdn.live.cntv.cn/api2/live.do?channel=pa://cctv_p2p_hdbtv6*',
- 'http://vdn.live.cntv.cn/*',
- 'http://app.bilibili.com/*',
- 'https://app.bilibili.com/*',
- 'http://bangumi.bilibili.com/api/*',
-
-
- // Music apps
- 'http://3g.music.qq.com/*',
- 'http://mqqplayer.3g.qq.com/*',
- 'http://proxy.music.qq.com/*',
- 'http://proxymc.qq.com/*',
- //Disable follow url because its hijackable.
- // 'http://*/base/fcgi-bin/getsession*', //for ios qq music v5.8, issue #536
- 'http://220.249.243.70/base/fcgi-bin/getsession*',
- 'http://117.185.116.152/base/fcgi-bin/getsession*',
- 'http://101.227.139.217/base/fcgi-bin/getsession*',
- 'http://59.37.96.220/base/fcgi-bin/getsession*',
- 'http://140.207.69.99/base/fcgi-bin/getsession*',
- 'http://103.7.31.186/base/fcgi-bin/getsession*',
- 'http://103.7.30.89/base/fcgi-bin/getsession*',
- 'http://182.254.34.151/base/fcgi-bin/getsession*', //temperary solutions for issue #536
- 'http://223.167.82.139/base/fcgi-bin/getsession*',
- 'http://101.227.169.200/base/fcgi-bin/getsession*',
- 'http://182.254.11.174/base/fcgi-bin/getsession*',
- 'http://183.192.192.139/base/fcgi-bin/getsession*',
- 'http://163.177.90.61/base/fcgi-bin/getsession*',
- 'http://14.18.245.250/base/fcgi-bin/getsession*',
- 'http://183.232.126.23/base/fcgi-bin/getsession*',
- 'http://183.232.119.198/base/fcgi-bin/getsession*',
- 'http://182.254.4.234/base/fcgi-bin/getsession*', //another fix for QQ music in #731
- 'http://203.205.151.23/base/fcgi-bin/getsession*',
- 'http://ip2.kugou.com/check/isCn/*',
- 'http://ip.kugou.com/check/iscn*',
- 'http://client.api.ttpod.com/global*',
- 'http://mobi.kuwo.cn/*',
- 'http://nmobi.kuwo.cn/*',
- 'http://mobilefeedback.kugou.com/*',
- 'http://tingapi.ting.baidu.com/v1/restserver/ting?*method=baidu.ting.song*',
- 'http://music.baidu.com/data/music/links?*',
- 'http://serviceinfo.sdk.duomi.com/api/serviceinfo/getserverlist*',
- 'http://music.163.com/api/copyright/restrict/?*',
- 'http://music.163.com/api/batch',
- 'http://www.xiami.com/web/spark*',
- 'http://www.xiami.com/web/*?*xiamitoken=*',
- 'http://spark.api.xiami.com/api?*method=AuthIp*',
- 'http://spark.api.xiami.com/api?*method=Start.init*',
- 'http://spark.api.xiami.com/api?*method=Songs.getTrackDetail*',
- 'http://spark.api.xiami.com/api?*method=Songs.detail*',
- // for PC Clients only
- 'http://iplocation.geo.qiyi.com/cityjson',
- 'http://sns.video.qq.com/tunnel/fcgi-bin/tunnel*',
- 'http://v5.pc.duomi.com/single-ajaxsingle-isban*',
-
- // the access control for https are per domain name
- 'https://openapi.youku.com/*', // see issue #118
- 'https://61.135.196.99/*', //n-openapi.youku.com
- 'https://220.181.185.150/*', //zw-openapi.youku.com
- 'https://111.13.127.46/*',//bj-m-openapi.youku.com
- 'https://211.151.50.10/*',//b-openapi.youku.com
- 'https://123.126.99.57/*',//openapi.youku.com
- 'https://123.126.99.39/*',//zw-n-openapi.youku.com
- 'https://220.181.154.137/*',//zw-t-openapi.youku.com
-
- // for MiBox iCNTV Authentication
- 'http://tms.is.ysten.com:8080/yst-tms/login.action?*',
- 'http://chrome.2345.com/dianhua/mobileApi/check.php',
- 'http://internal.check.duokanbox.com/check.json*',
- // for 3rd party's DNS for Apple TV (see pull request #78)
- 'http://180.153.225.136/*',
- 'http://118.244.244.124/*',
- 'http://210.129.145.150/*',
- 'http://182.16.230.98/*', // Updated on Jan. 3, for new DNS of apple tv.
-
- // for NeteaseMusic on iOS
- 'http://103.65.41.126/eapi/*',
- 'http://103.65.41.125/eapi/*'
-]);
-
-
-// These URLs will not go through proxy servers (for our PAC service).
-unblock_youku.pac_proxy_bypass_urls = unblock_youku.chrome_proxy_bypass_urls.concat([
- // 'http://*/ipad?file=/*' // Not useful anymore for iOS
-]);
-
-
-(function() {
- // Remove all path info from unblock_youku.pac_proxy_urls due to the latest iOS update
- // See https://bbs.uku.im/t/43
- "use strict";
-
- var new_list = [];
- var regex_to_extract_domain = /^https?:\/\/[^\/]*/i;
-
- for (var i = 0; i < unblock_youku.pac_proxy_urls.length; i++) {
- new_list.push(unblock_youku.pac_proxy_urls[i].match(regex_to_extract_domain)[0] + '/*');
- }
-
- unblock_youku.pac_proxy_urls = new_list;
-}());
-
-
-function urls2regexs(url_list) {
- "use strict";
-
- var regex_list = [];
-
- for (var i = 0; i < url_list.length; i++) {
- var re_str = url_list[i];
-
- // Escape all possibly problematic symbols
- // http://stackoverflow.com/a/6969486/1766096
- re_str = re_str.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
- re_str = re_str.replace(/\*/g, '.*');
-
- // make the first * matches only domain names or ip addresses
- // just as http://developer.chrome.com/extensions/match_patterns.html
- re_str = re_str.replace(/^http:\\\/\\\/\.\*/i, 'http:\\/\\/[^\/]*');
- re_str = re_str.replace(/^https:\\\/\\\/\.\*/i, 'https:\\/\\/[^\/]*');
-
- regex_list.push(new RegExp('^' + re_str + '$', 'i'));
- }
-
- // console.log(regex_list);
- return regex_list;
-}
-
-
-function produce_squid_regex_list(for_pac_server) {
- 'use strict';
-
- var squid_url_list = unblock_youku.chrome_proxy_urls;
- if (for_pac_server === true) {
- squid_url_list = unblock_youku.pac_proxy_urls;
- }
-
- var squid_regex_list = urls2regexs(squid_url_list);
- var i, single_str;
-
- var regex_to_extract_https_domain = /^\^https:\\\/\\\/([^:]+)\\\//i;
-
- var ret_list = [];
- for (i = 0; i < squid_regex_list.length; i++) {
- single_str = squid_regex_list[i].toString();
- single_str = single_str.substring(1, single_str.length - 2);
-
- if (single_str.match(regex_to_extract_https_domain)) {
- single_str = '^' + single_str.match(regex_to_extract_https_domain)[1] + ':443';
- }
-
- ret_list.push(single_str);
- }
-
- return ret_list;
-}
-
-
-// Also export as a node.js module
-var exports = exports || {};
-// Exported for the redirect server
-exports.regex_crx_urls = urls2regexs(unblock_youku.chrome_proxy_urls);
-exports.regex_crx_bypass_urls = urls2regexs(unblock_youku.chrome_proxy_bypass_urls);
-// Exported for generating the PAC file
-exports.pac_urls = unblock_youku.pac_proxy_urls;
-exports.pac_bypass_urls = unblock_youku.pac_proxy_bypass_urls;
-
-exports.produce_squid_regex_list = produce_squid_regex_list;
diff --git a/src/background.mjs b/src/background.mjs
new file mode 100644
index 00000000..cd3a463d
--- /dev/null
+++ b/src/background.mjs
@@ -0,0 +1,17 @@
+import * as Settings from './modules/settings.mjs';
+
+
+chrome.runtime.onInstalled.addListener(function(details) {
+ if (details.reason === 'install') {
+ chrome.tabs.create({
+ url: chrome.i18n.getMessage('donation_url'),
+ });
+ }
+});
+
+
+console.group('To intialize the extension...');
+Settings.loadCurrentSettings().then(() => {
+ console.log('Successfully initialized the chrome extension');
+ console.groupEnd();
+});
diff --git a/src/configs/servers.mjs b/src/configs/servers.mjs
new file mode 100644
index 00000000..fb00c5d0
--- /dev/null
+++ b/src/configs/servers.mjs
@@ -0,0 +1,4 @@
+export const DEFAULT_PROXY_PROTOCOL = 'HTTPS'; // Can be HTTP, HTTPS, SOCKS5
+export const DEFAULT_PROXY_ADDRESS = 'secure.uku.im:8443';
+export const BACKUP_PROXY_PROTOCOL = 'HTTPS';
+export const BACKUP_PROXY_ADDRESS = 'secure.uku.im:993';
diff --git a/src/configs/urls.mjs b/src/configs/urls.mjs
new file mode 100644
index 00000000..04994c58
--- /dev/null
+++ b/src/configs/urls.mjs
@@ -0,0 +1,329 @@
+/*
+ * Note that the HTTPS URLs must NOT have any path portions.
+ *
+ * For example:
+ * - Wrong: https://example.com/abc/*
+ * - Right: https://example.com/*
+ */
+
+
+// The HTTP headers of these URLs will be modified.
+export const HEADER_URLS = [
+ 'http://*.ssports.com/*',
+ 'http://ssports.com/*',
+ 'http://ssports.smgbb.cn/*',
+ 'https://pstream.api.mgtv.com/*',
+ 'http://kandian.com/player/getEpgInfo*',
+];
+
+
+// These URLs will not go through proxy servers.
+export const PROXY_BYPASS_URLS = [
+ 'http://bangumi.bilibili.com/index/ding-count.json',
+];
+
+
+// These URLs will be proxied.
+export const PROXY_URLS = [
+ 'https://dmd-fifajs-h5-ikuweb.youku.com/*',
+ 'https://dmd-fifa-h5-ikuweb.youku.com/*',
+ 'http://acs.youku.com/*',
+ 'https://acs.youku.com/*',
+ // 'http://v.youku.com/player/*',
+ 'http://pl-ali.youku.com/*',
+ 'http://list.youku.com/category/*',
+ 'http://api.youku.com/player/*',
+ 'http://play.youku.com/play/get.json*',
+ 'http://play-dxk.youku.com/play/get.json*',
+ 'http://play-ali.youku.com/play/get.json*',
+ 'http://list.youku.com/show/module?*',
+ 'http://list.youku.com/show/point?*',
+ 'http://list.youku.com/show/episode?*',
+ 'http://v.youku.com/page/playlist*',
+ 'http://ups.youku.com/ups/get.json*',
+ 'https://ups.youku.com/',
+ 'https://ac.qq.com/*',
+ 'http://player.aplus.pptv.com/*',
+ 'http://api.aixifan.com/plays/*',
+ 'http://www.acfun.cn/video/getVideo*',
+ 'https://www.acfun.cn/*',
+ // 'https://v.youku.com/*',
+ // 'http://v2.tudou.com/*',
+ 'http://video.tudou.com/v/*',
+ 'http://www.tudou.com/a/*',
+ 'http://www.tudou.com/v/*',
+ 'http://www.tudou.com/outplay/goto/*',
+ 'http://www.tudou.com/tvp/alist.action*',
+ 'http://s.plcloud.music.qq.com/fcgi-bin/p.fcg*',
+ 'http://i.y.qq.com/*/fcg-bin/*',
+ 'http://i.y.qq.com/*/fcgi-bin/*',
+ 'http://c.y.qq.com/*/fcg-bin/*',
+ 'http://c.y.qq.com/*/fcgi-bin/*',
+ 'https://c.y.qq.com/*',
+ 'http://api.unipay.qq.com/cgi-bin/get_pay_info.fcg?*',
+ 'https://api.unipay.qq.com/*',
+ 'http://hot.vrs.sohu.com/*',
+ 'https://hot.vrs.sohu.com/*',
+ 'http://live.tv.sohu.com/live/player*',
+ 'http://pad.tv.sohu.com/playinfo*',
+ 'http://my.tv.sohu.com/play/m3u8version.do*',
+ 'http://hot.vrs.letv.com/*',
+ 'http://api.le.com/mms/out/video/*',
+ 'http://player.pc.le.com/mms/out/video/*',
+ 'http://player-pc.le.com/mms/out/video/*',
+ // 'http://g3.letv.cn/*',
+ 'http://data.video.qiyi.com/*',
+ 'http://data.video.iqiyi.com/*',
+ 'https://data.video.qiyi.com/*',
+ 'https://data.video.iqiyi.com/*',
+ // 'http://nl.rcd.iqiyi.com/apis/urc/*',
+ 'http://cache.vip.qiyi.com/*',
+ 'http://cache.video.qiyi.com/*',
+ 'http://cache.vip.iqiyi.com/*',
+ 'http://cache.video.iqiyi.com/*',
+ 'https://cache.video.iqiyi.com/*',
+ 'http://iplocation.geo.qiyi.com/cityjson*',
+ 'http://iplocation.geo.iqiyi.com/cityjson*',
+ 'http://*.cupid.iqiyi.com/*',
+ 'http://v.api.hunantv.com/player/video*',
+ 'http://mobile.api.hunantv.com/v5/video/getSource*',
+ 'http://v.api.mgtv.com/player/video*',
+ 'https://v.api.mgtv.com/*',
+ 'http://pcweb.api.mgtv.com/player/video*',
+ 'https://pcweb.api.mgtv.com/*',
+ 'http://acc.music.qq.com/base/fcgi-bin/getsession*',
+ 'http://182.254.116.117/*',
+
+ 'http://api.appsdk.soku.com/*',
+
+ 'http://app.bilibili.com/bangumi/*',
+ 'http://bangumi.bilibili.com/*',
+
+ 'http://122.72.82.31/*',
+ 'http://211.151.158.155/*',
+
+ // 'http://tt.video.qq.com/get*',
+ // 'http://ice.video.qq.com/get*',
+ // 'http://tjsa.video.qq.com/get*',
+ // 'http://a10.video.qq.com/get*',
+ // 'http://xyy.video.qq.com/get*',
+ // 'http://vcq.video.qq.com/get*',
+ // 'http://vsh.video.qq.com/get*',
+ // 'http://vbj.video.qq.com/get*',
+ // 'http://bobo.video.qq.com/get*',
+ // 'http://flvs.video.qq.com/get*',
+ // 'http://bkvv.video.qq.com/get*',
+ // 'http://h5vv.video.qq.com/get*',
+ 'http://*.video.qq.com/get*',
+ 'http://*.video.qq.com/fcgi-bin/*',
+ 'https://*.video.qq.com/*',
+ 'http://info.zb.qq.com/?*',
+ 'https://info.zb.qq.com/*',
+ 'http://info.zb.video.qq.com/?*',
+ 'https://info.zb.video.qq.com/*',
+ 'http://qzs.qq.com/tencentvideo_v1/*',
+ 'https://qzs.qq.com/*',
+ 'http://vd.l.qq.com/*',
+ 'https://vd.l.qq.com/*',
+ 'http://vi.l.qq.com/*',
+ 'https://vi.l.qq.com/*',
+
+ 'http://dispatcher.video.sina.com.cn/*',
+ 'http://geo.js.kankan.com/*',
+ 'http://web-play.pptv.com/*',
+ 'http://v.pptv.com/show/*',
+ 'https://ppi.api.pptv.com/*',
+ 'http://web-play.pplive.cn/*',
+ 'http://tools.aplusapi.pptv.com/get_ppi?*',
+ 'http://live.pptv.com/api/subject_list?*',
+ // 'http://c1.pptv.com/*',
+ 'http://dyn.ugc.pps.tv/*',
+ 'http://v.pps.tv/ugc/ajax/aj_html5_url.php*',
+ 'http://inner.kandian.com/*',
+ 'http://ipservice.163.com/*',
+ 'http://so.open.163.com/open/info.htm*',
+ 'http://zb.s.qq.com/*',
+ 'https://zb.s.qq.com/*',
+ 'http://ip.kankan.com/*',
+ 'http://vxml.56.com/json/*',
+
+ 'http://music.sina.com.cn/yueku/intro/*',
+ // 'http://ting.baidu.com/data/music/songlink*',
+ // 'http://ting.baidu.com/data/music/songinfo*',
+ // 'http://ting.baidu.com/song/*/download*',
+ 'http://music.sina.com.cn/radio/port/webFeatureRadioLimitList.php*',
+ 'http://play.baidu.com/data/music/songlink*',
+
+ 'http://v.iask.com/v_play.php*',
+ 'http://v.iask.com/v_play_ipad.cx.php*',
+ 'http://tv.weibo.com/player/*',
+ 'http://wtv.v.iask.com/*.m3u8*',
+ 'http://wtv.v.iask.com/mcdn.php',
+ 'http://video.sina.com.cn/interface/l/u/getFocusStatus.php*',
+ 'http://wtv.v.iask.com/player/ovs1_idc_list.php*',
+
+ // 'http://kandian.com/player/getEpgInfo*', // !!!
+ // 'http://cdn.kandian.com/*',
+ 'http://www.yinyuetai.com/insite/*',
+ 'http://www.yinyuetai.com/main/get-*',
+ 'http://www.kugou.com/interface/geoip/*',
+ 'http://www.kuwo.cn/yy/PlayCheckIp?callback=checkIpCallback&_=*',
+ 'http://antiserver.kuwo.cn/anti.s?*',
+ 'http://ipcheck.kuwo.cn/ip_check.kuwo*',
+ 'http://*.dpool.sina.com.cn/iplookup*',
+ 'http://api.letv.com/streamblock*',
+ 'http://api.letv.com/mms/out/video/play*',
+ 'http://api.www.letv.com/mms/out/video/playJson?*',
+ 'http://*.letv.com/mms/out/video/play*',
+ 'http://api.letv.com/mms/out/common/geturl*',
+ 'http://api.letv.com/geturl*',
+ 'http://api.letv.com/api/geturl*',
+ 'http://st.live.letv.com/live/*',
+ 'http://live.gslb.letv.com/gslb?*',
+ 'http://live.g3proxy.lecloud.com/gslb?*',
+ 'http://api.live.letv.com/crossdomain.xml',
+ 'http://static.itv.letv.com/api*',
+ 'http://ip.apps.cntv.cn/js/player.do*',
+ 'http://vdn.apps.cntv.cn/api/get*',
+ 'http://vdn.live.cntv.cn/api2/*',
+ 'http://cctv1.vtime.cntv.cloudcdn.net/cache/*',
+ 'http://cctv5.vtime.cntv.cloudcdn.net/cache/*',
+ 'http://cctv5plus.vtime.cntv.cloudcdn.net/cache/*',
+ 'http://cctv13.vtime.cntv.cloudcdn.net/cache/*',
+ 'http://sports1pull.live.wscdns.com/live/aoyun2',
+ 'http://vip.sports.cntv.cn/check.do*',
+ 'http://vip.sports.cntv.cn/play.do*',
+ 'http://vip.sports.cntv.cn/servlets/encryptvideopath.do*',
+ 'http://211.151.157.15/*',
+
+ 'http://www.tudou.com/programs/view/*',
+ 'http://www.tudou.com/albumplay/*',
+ 'http://www.tudou.com/listplay/*',
+
+ 'http://www.youku.com/show_page/*',
+ 'http://v.youku.com/v_show/*',
+ 'http://www.soku.com/search_video/*',
+ 'http://search.api.3g.youku.com/*',
+ 'http://search.api.3g.tudou.com/*',
+ 'http://*.api.tv.itc.cn/*',
+ 'http://api.tv.sohu.com/*',
+ 'http://ac.qq.com/Comic*',
+ 'http://ac.qq.com/Jump*',
+ 'http://live.api.hunantv.com/pc/getSourceById*',
+ 'http://mobile.api.hunantv.com/*',
+ 'http://www.qie.tv/*',
+ 'http://www.bilibili.com/video/*',
+ 'https://www.bilibili.com/*',
+ 'http://api.bilibili.com/*',
+ 'https://api.bilibili.com/*',
+ 'http://interface.bilibili.com/*',
+ 'https://interface.bilibili.com/*',
+ 'http://bangumi.bilibili.com/api/*',
+ 'https://bangumi.bilibili.com/*',
+ // 'http://live-play.acgvideo.com/live/*',
+ 'http://m10.music.126.net/*', // for the testing of netease music
+
+ 'http://douban.fm/*',
+ 'https://douban.fm/*',
+ 'http://www.xiami.com/*',
+ 'http://lixian.xunlei.com/*',
+ 'http://lixian.vip.xunlei.com/*',
+ 'http://dynamic.cloud.vip.xunlei.com/*',
+ 'http://cloud.vip.xunlei.com/*',
+ // 'http://vod.lixian.xunlei.com/*',
+ 'http://www.iqiyi.com/dongman/',
+ 'https://www.iqiyi.com/',
+
+ // LETV https://github.com/Unblocker/Unblock-Youku/issues/590
+ 'http://36.110.222.105/*',
+ 'http://36.110.222.119/*',
+ 'http://36.110.222.146/*',
+ 'http://36.110.222.156/*',
+ 'http://123.125.89.6/*',
+ 'http://123.125.89.101/*',
+ 'http://123.125.89.102/*',
+ 'http://123.125.89.103/*',
+ 'http://123.125.89.157/*',
+ 'http://123.125.89.159/*',
+ 'http://123.126.32.134/*',
+ 'http://123.59.122.75/*',
+ 'http://123.59.122.76/*',
+ 'http://123.59.122.77/*',
+ 'http://123.59.122.104/*',
+ 'http://111.206.208.36/*',
+ 'http://111.206.208.37/*',
+ 'http://111.206.208.38/*',
+ 'http://111.206.208.61/*',
+ 'http://111.206.208.62/*',
+ 'http://111.206.208.163/*',
+ 'http://111.206.208.164/*',
+ 'http://111.206.208.166/*',
+ 'http://111.206.211.145/*',
+ 'http://111.206.211.146/*',
+ 'http://111.206.211.147/*',
+ 'http://111.206.211.148/*',
+ 'http://111.206.211.129/*',
+ 'http://111.206.211.130/*',
+ 'http://111.206.211.131/*',
+ 'http://220.181.153.113/*',
+ 'http://14.152.77.32/*',
+ 'http://14.152.77.26/*',
+ 'http://14.152.77.25/*',
+ 'http://14.152.77.22/*',
+ 'http://183.232.229.22/*',
+ 'http://183.232.229.21/*',
+ 'http://183.232.229.25/*',
+ 'http://183.232.229.32/*',
+ 'http://115.182.200.51/*',
+ 'http://115.182.200.50/*',
+ 'http://115.182.200.54/*',
+ 'http://115.182.200.53/*',
+ 'http://115.182.200.52/*',
+ 'http://115.182.63.51/*',
+ 'http://115.182.63.93/*',
+ 'http://*.letv.cn/vod/v2/*',
+ 'http://ark.letv.com/s*',
+ 'http://search.lekan.letv.com/*',
+
+ // 'http://live.video.sina.com.cn/room/*',
+ // 'http://edge.v.iask.com/*', // may be large files
+
+ 'http://pay.youku.com/buy/redirect.html*',
+ 'http://pay.tudou.com/buy/redirect.html*',
+ 'http://aid.video.qq.com/fcgi-bin/userip?*',
+ 'http://aidbak.video.qq.com/fcgi-bin/userip?*',
+ 'http://pay.video.qq.com/fcgi-bin/paylimit*',
+ 'http://paybak.video.qq.com/fcgi-bin/paylimit*',
+ 'https://*.l.qq.com/*',
+ 'http://chrome.2345.com/dianhua/index.php?m=call&f=check&*',
+
+ 'http://music.163.com/eapi/*',
+
+ // 'http://play.baidu.com/*',
+ // 'http://zhangmenshiting.baidu.com/*',
+ // 'http://music.baidu.com/box*',
+ // 'http://music.baidu.com/data/service/sum*',
+ // 'http://music.baidu.com/data/music/songlink*',
+ // 'http://music.baidu.com/data/music/songinfo*',
+ // 'http://music.baidu.com/data/music/fmlink*',
+ // 'http://music.baidu.com/song/*/download*',
+ // 'http://fm.baidu.com/*',
+ // 'http://www.kugou.com/*',
+ // 'http://music.baidu.com/data/user/collect*',
+
+ // 'http://d.dxy.cn/*',
+ // 'http://ac.qq.com/*/v/cid/*',
+ // 'http://v.pptv.com/show/*.html',
+ // 'http://www.songtaste.com/*',
+ // 'http://songtaste.com/*',
+ // 'http://www.yyets.com/*',
+ // 'http://mainv2.img.duomi.com/*',
+ // 'http://imanhua.com/comic/*',
+ // 'http://www.imanhua.com/comic/*',
+ // 'http://imanhua.com/v2*',
+ // 'http://www.imanhua.com/v2*',
+
+ // for development purposes
+ 'http://flask-test-iwauxcyxjb.cn-hangzhou.fcapp.run/*',
+ 'https://flask-test-iwauxcyxjb.cn-hangzhou.fcapp.run/*',
+];
diff --git a/chrome/content/music.163.js b/src/content_scripts/music.163.js
similarity index 100%
rename from chrome/content/music.163.js
rename to src/content_scripts/music.163.js
diff --git a/chrome/content/play.baidu.css b/src/content_scripts/play.baidu.css
similarity index 100%
rename from chrome/content/play.baidu.css
rename to src/content_scripts/play.baidu.css
diff --git a/chrome/content/play.baidu.js b/src/content_scripts/play.baidu.js
similarity index 100%
rename from chrome/content/play.baidu.js
rename to src/content_scripts/play.baidu.js
diff --git a/chrome/content/tudou.js b/src/content_scripts/tudou.js
similarity index 100%
rename from chrome/content/tudou.js
rename to src/content_scripts/tudou.js
diff --git a/chrome/content/unblockcn.js b/src/content_scripts/unblockcn.js
similarity index 100%
rename from chrome/content/unblockcn.js
rename to src/content_scripts/unblockcn.js
diff --git a/chrome/pages/css/popup.css b/src/css/popup.css
similarity index 50%
rename from chrome/pages/css/popup.css
rename to src/css/popup.css
index a6cec8eb..70293596 100644
--- a/chrome/pages/css/popup.css
+++ b/src/css/popup.css
@@ -1,21 +1,3 @@
-/*
- * Copyright (C) 2012 - 2014 Bo Zhu http://zhuzhu.org
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero General Public License as
- * published by the Free Software Foundation, either version 3 of the
- * License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see .
- */
-
-
body {
width: 32em;
display: block;
@@ -47,7 +29,6 @@ div#social-networks {
font-size: 110%;
}
-
/* mode setting section */
div#setting {
margin-top: 15px;
@@ -56,7 +37,7 @@ div#setting {
div#buttons {
margin: 15px auto;
clear: both;
- display: inline-block;
+ display: inline-block;
}
div#setting_description tr:last-child {
@@ -70,13 +51,7 @@ div.text_block {
}
/* extra info and links */
-div#rating {
- display: none;
-}
-
-
div#version {
margin-top: 15px;
text-align: center;
-}
-
+}
\ No newline at end of file
diff --git a/src/modules/_header.mjs b/src/modules/_header.mjs
new file mode 100644
index 00000000..f869e15c
--- /dev/null
+++ b/src/modules/_header.mjs
@@ -0,0 +1,57 @@
+/*
+ * Do NOT use this module directly. Use the functions in settings.js to change settings.
+ */
+
+
+function newRandomIp() {
+ 'use strict';
+ let ipAddr = '220.181.111.';
+ // ipAddr += Math.floor(Math.random() * 255) + '.';
+ ipAddr += Math.floor(Math.random() * 254 + 1); // 1 ~ 254
+ return ipAddr;
+}
+
+const RANDOM_IP = newRandomIp();
+// TODO: Store in local storage, and change everytime the chrome is restarted.
+
+
+function headerModifier(details) {
+ console.log('modify headers of ' + details.url);
+
+ details.requestHeaders.push({
+ name: 'X-Forwarded-For',
+ value: RANDOM_IP,
+ }, {
+ name: 'Client-IP',
+ value: RANDOM_IP,
+ });
+
+ return {requestHeaders: details.requestHeaders};
+}
+
+
+export function setHeaderModifier() {
+ if (!chrome.webRequest.onBeforeSendHeaders.hasListener(headerModifier)) {
+ // TODO: Fix this
+ // chrome.webRequest.onBeforeSendHeaders.addListener(
+ // headerModifier,
+ // {
+ // urls: unblock_youku.header_urls,
+ // },
+ // ['requestHeaders', 'blocking'],
+ // );
+ console.log('header_modifier is successfully set');
+ } else {
+ console.error('header modifer is already there! So didn\'t set it again.');
+ }
+}
+
+
+export function clearHeaderModifier() {
+ if (chrome.webRequest.onBeforeSendHeaders.hasListener(headerModifier)) {
+ chrome.webRequest.onBeforeSendHeaders.removeListener(headerModifier);
+ console.log('header modifier is removed');
+ } else {
+ console.error('header modifier is not there!');
+ }
+}
diff --git a/src/modules/_icon.mjs b/src/modules/_icon.mjs
new file mode 100644
index 00000000..872b109c
--- /dev/null
+++ b/src/modules/_icon.mjs
@@ -0,0 +1,75 @@
+/*
+ * Do NOT use this module directly. Use the functions in settings.js to change settings.
+ */
+
+
+import {Modes} from './modes.mjs';
+
+const ICON_RELATIVE_PATH = '../../icons/';
+
+// Around Spring festival
+function isSpring() {
+ const today = new Date();
+ const y = today.getFullYear();
+ const m = today.getMonth() + 1;
+
+ // Hard code some Spring festival dates
+ switch (y) {
+ case 2023: // Jan. 22, 2023
+ return m === 1;
+ case 2024: // Feb. 10, 2024
+ return m === 2;
+ case 2025: // Jan. 29, 2025
+ return m === 1;
+ case 2026: // Feb. 17, 2026
+ return m === 2;
+ case 2027: // Feb. 7, 2027
+ return m === 2;
+ }
+
+ return false;
+}
+
+// Around Christmas
+function isXmas() {
+ const today = new Date();
+ const m = today.getMonth() + 1;
+ const d = today.getDate();
+
+ return m === 12 && d >= 15;
+}
+
+
+export function setIcon(mode) {
+ if (mode === Modes.OFF) {
+ chrome.action.setIcon({path: ICON_RELATIVE_PATH + 'icon19gray.png'});
+ chrome.action.setBadgeText({text: 'OFF'});
+ chrome.action.setTitle({title: 'Unblock Youku has been turned off.'});
+ return;
+ }
+
+ if (isSpring()) {
+ chrome.action.setIcon({path: ICON_RELATIVE_PATH + 'icon19xmas.png'});
+ chrome.action.setTitle(
+ {title: 'Happy Spring Festival! (Unblock Youku ' +
+ chrome.runtime.getManifest().version + ')'});
+ return;
+ } else if (isXmas()) {
+ chrome.action.setIcon({path: ICON_RELATIVE_PATH + 'icon19xmas.png'});
+ chrome.action.setTitle(
+ {title: 'Merry Christmas! (Unblock Youku ' +
+ chrome.runtime.getManifest().version + ')'});
+ return;
+ }
+
+ chrome.action.setIcon({path: ICON_RELATIVE_PATH + 'icon19.png'});
+ chrome.action.setTitle({title: 'Unblock Youku ' + chrome.runtime.getManifest().version});
+}
+
+export function setIconText(text) {
+ chrome.action.setBadgeText({text: text});
+}
+
+export function clearIconText() {
+ chrome.action.setBadgeText({text: ''});
+}
diff --git a/src/modules/_proxy.mjs b/src/modules/_proxy.mjs
new file mode 100644
index 00000000..e7ef5a81
--- /dev/null
+++ b/src/modules/_proxy.mjs
@@ -0,0 +1,70 @@
+/*
+ * Do NOT use this module directly. Use the functions in settings.js to change settings.
+ */
+
+
+import {PROXY_BYPASS_URLS, PROXY_URLS} from '../configs/urls.mjs';
+import {
+ DEFAULT_PROXY_PROTOCOL, DEFAULT_PROXY_ADDRESS, BACKUP_PROXY_PROTOCOL, BACKUP_PROXY_ADDRESS}
+ from '../configs/servers.mjs';
+import {urls2pac} from './_url_utils.mjs';
+
+
+async function setPacScript(
+ defaultProxyProtocol, defaultProxyAddress, backupProxyProtocol, backupProxyAddress) {
+ console.log('To set up proxy...');
+
+ const pacScript = urls2pac(
+ PROXY_BYPASS_URLS,
+ PROXY_URLS,
+ defaultProxyAddress, defaultProxyProtocol,
+ backupProxyAddress, backupProxyProtocol);
+
+ console.group('Printing PAC script content:');
+ console.log(pacScript);
+ console.groupEnd();
+
+ await chrome.proxy.settings.set({
+ value: {
+ mode: 'pac_script',
+ pacScript: {
+ data: pacScript,
+ },
+ },
+ scope: 'regular',
+ });
+ console.log(
+ 'Successfully set the proxy server: ' + defaultProxyProtocol + ' ' + defaultProxyAddress);
+}
+
+
+export function setDefaultProxy() {
+ return setPacScript(
+ DEFAULT_PROXY_PROTOCOL, DEFAULT_PROXY_ADDRESS,
+ BACKUP_PROXY_PROTOCOL, BACKUP_PROXY_ADDRESS);
+}
+
+
+export function setCustomProxy(customProxyProtocol, customProxyAddress) {
+ // Use the given proxy server as both the default and backup servers.
+ return setPacScript(
+ customProxyProtocol, customProxyAddress,
+ customProxyProtocol, customProxyAddress);
+}
+
+
+export async function clearProxy() {
+ await chrome.proxy.settings.set({
+ value: {
+ mode: 'system',
+ },
+ scope: 'regular',
+ });
+ console.log('Successfully cleared the proxy (changed to system setting)');
+}
+
+
+chrome.proxy.onProxyError.addListener(function(details) {
+ console.error('Received errors from onProxyError:');
+ console.error(details);
+});
diff --git a/src/modules/_storage.mjs b/src/modules/_storage.mjs
new file mode 100644
index 00000000..9b8ff833
--- /dev/null
+++ b/src/modules/_storage.mjs
@@ -0,0 +1,25 @@
+/*
+ * Do NOT use this module directly. Use the functions in settings.js to change settings.
+ */
+
+
+export async function getItem(key) {
+ const items = await chrome.storage.sync.get(key);
+ if (typeof items !== 'undefined' && items.hasOwnProperty(key)) {
+ return items[key];
+ }
+ return undefined;
+}
+
+
+export function setItem(key, value) {
+ // Can't just use {key: value}; otherwise the string "key" will be used as the key instead
+ const obj = {};
+ obj[key] = value;
+ return chrome.storage.sync.set(obj);
+}
+
+
+export function removeItem(key) {
+ return chrome.storage.sync.remove(key);
+}
diff --git a/src/modules/_url_utils.mjs b/src/modules/_url_utils.mjs
new file mode 100644
index 00000000..5fd6ae81
--- /dev/null
+++ b/src/modules/_url_utils.mjs
@@ -0,0 +1,238 @@
+/*
+ * Do NOT use this module directly. Use the functions in settings.js to change settings.
+ */
+
+
+function _parse_url(url_str) {
+ let protocol = null;
+ if (url_str.startsWith('http://')) {
+ url_str = url_str.slice('http://'.length);
+ protocol = 'http';
+ } else if (url_str.startsWith('https://')) {
+ url_str = url_str.slice('https://'.length);
+ protocol = 'https';
+ } else {
+ console.error('URL does not start with http:// or https://');
+ return null;
+ }
+
+ let path_idx = url_str.indexOf('/');
+ if (path_idx < 0) {
+ path_idx = url_str.length;
+ url_str += '/';
+ }
+ const colon_idx = url_str.indexOf(':'); // the colon before the optional port number
+
+ let sep_idx = path_idx;
+ if (colon_idx >= 0 && colon_idx < path_idx) {
+ sep_idx = colon_idx;
+ }
+
+ return {
+ protocol: protocol,
+ // the parameter in FindProxyForURL only doesn't contain port numbers
+ hostname: url_str.slice(0, sep_idx),
+ portpath: url_str.slice(sep_idx),
+ };
+}
+// console.log(_parse_url('http://test.com'));
+// console.log(_parse_url('http://test.com:123'));
+// console.log(_parse_url('http://test.com/path'));
+// console.log(_parse_url('http://test.com:123/path'));
+
+
+function gen_url_map(protocol, white_ulist, proxy_ulist) {
+ 'use strict';
+ const url_map = {
+ white: {
+ any: [],
+ },
+ proxy: {
+ any: [],
+ },
+ };
+
+ function stringify(map_obj) {
+ const white_map = map_obj.white;
+ const proxy_map = map_obj.proxy;
+
+ let res_str = [
+ '{',
+ ' \'white\': {',
+ ].join('\n') + '\n';
+ res_str += stringify_patterns(white_map);
+
+ res_str += [
+ ' },',
+ ' \'proxy\': {',
+ ].join('\n') + '\n';
+ res_str += stringify_patterns(proxy_map);
+
+ res_str += [
+ ' }',
+ '}',
+ ].join('\n');
+
+ return res_str;
+ }
+
+ function stringify_patterns(hostname_map) {
+ let res_str = '';
+ let i; let patterns = null;
+
+ for (const hostname in hostname_map) {
+ if (hostname_map.hasOwnProperty(hostname)) {
+ res_str += ' \'' + hostname + '\': [';
+ patterns = hostname_map[hostname];
+
+ if (patterns.length === 0) {
+ res_str += '],\n';
+ } else {
+ res_str += '\n';
+ for (i = 0; i < patterns.length; i++) {
+ res_str += ' ' + patterns[i] + ',\n';
+ }
+ res_str = res_str.slice(0, -2) + '\n'; // remove the last ,
+ res_str += ' ],\n';
+ }
+ }
+ }
+ res_str = res_str.slice(0, -2) + '\n'; // remove the last ,
+
+ return res_str;
+ }
+
+ function add_patterns(map_obj, ulist) {
+ let i; let uobj; let hostname; let portpath;
+ let key; let val;
+ for (i = 0; i < ulist.length; i++) {
+ uobj = _parse_url(ulist[i]);
+ if (uobj === null) {
+ console.error('Invalid URL pattern: ' + ulist[i]);
+ continue;
+ }
+
+ if (uobj.protocol === protocol) {
+ hostname = uobj.hostname;
+ portpath = uobj.portpath;
+ if (hostname.indexOf('*') >= 0) {
+ if (hostname.slice(1).indexOf('*') >= 0) { // * is only allowed to be the first char
+ console.error('Invalid wildcard URL pattern: ' + ulist[i]);
+ continue;
+ } else {
+ key = 'any';
+ val = hostname + portpath; // host:port/path
+ }
+ } else {
+ if (!map_obj.hasOwnProperty(hostname)) {
+ map_obj[hostname] = [];
+ }
+ key = hostname;
+ val = portpath; // only :port/path
+ }
+
+ val = val.replace(/[\-\[\]\/\{\}\(\)\+\?\.\\\^\$\|]/g, '\\$&');
+ val = val.replace(/\*/g, '.*');
+ // if starts with *; should not be possible for :port or /path
+ val = val.replace(/^\.\*/i, '[^\/]*');
+ // val = new RegExp('^' + val + '$', 'i');
+ val = '/^' + val + '$/i';
+
+ if (val.slice(-5) === '.*$/i') {
+ val = val.slice(0, -5) + '/i';
+ }
+
+ map_obj[key].push(val);
+ } // if
+ } // for
+ }
+
+
+ add_patterns(url_map.white, white_ulist);
+ add_patterns(url_map.proxy, proxy_ulist);
+
+ // console.log(stringify(url_map));
+ return stringify(url_map);
+}
+
+
+export function urls2pac(url_whitelist, url_list,
+ proxy_server_1, proxy_protocol_1,
+ proxy_server_2, proxy_protocol_2) {
+ 'use strict';
+ const http_map_str = gen_url_map('http', url_whitelist, url_list);
+ const https_map_str = gen_url_map('https', url_whitelist, url_list);
+
+ if (typeof proxy_protocol_1 === 'undefined') {
+ proxy_protocol_1 = 'PROXY';
+ } else {
+ proxy_protocol_1 = proxy_protocol_1.replace(/:/g, '');
+ proxy_protocol_1 = proxy_protocol_1.replace(/\//g, '');
+ proxy_protocol_1 = proxy_protocol_1.toUpperCase();
+ if (proxy_protocol_1 === 'HTTP') {
+ proxy_protocol_1 = 'PROXY';
+ }
+ }
+
+ let _proxy_str = proxy_protocol_1 + ' ' + proxy_server_1 + '; ';
+
+ if (typeof proxy_server_2 !== 'undefined') {
+ if (typeof proxy_protocol_2 === 'undefined') {
+ proxy_protocol_2 = 'PROXY';
+ }
+ proxy_protocol_2 = proxy_protocol_2.replace(/:/g, '');
+ proxy_protocol_2 = proxy_protocol_2.replace(/\//g, '');
+ proxy_protocol_2 = proxy_protocol_2.toUpperCase();
+ if (proxy_protocol_2 === 'HTTP') {
+ proxy_protocol_2 = 'PROXY';
+ }
+
+ _proxy_str += proxy_protocol_2 + ' ' + proxy_server_2 + '; ';
+ }
+
+ _proxy_str += 'DIRECT;';
+
+ return [
+ 'var _http_map = ' + http_map_str + ';',
+ 'var _https_map = ' + https_map_str + ';',
+ 'var _proxy_str = \'' + _proxy_str + '\';',
+ '',
+ 'function _check_regex_list(regex_list, str) {',
+ ' if (str.slice(0, 4) === \':80/\')',
+ ' str = str.slice(3);',
+ ' for (var i = 0; i < regex_list.length; i++)',
+ ' if (regex_list[i].test(str))',
+ ' return true;',
+ ' return false;',
+ '}',
+ '',
+ 'function _check_patterns(patterns, hostname, full_url, prot_len) {',
+ ' if (patterns.hasOwnProperty(hostname))',
+ ' if (_check_regex_list(patterns[hostname],',
+ ' full_url.slice(prot_len + hostname.length)))', // check only :port/path
+ ' return true;',
+ // try our best to speed up the checking for non-proxied urls
+ ' if (_check_regex_list(patterns.any,',
+ ' full_url.slice(prot_len)))', // check hostname:port/path
+ ' return true;',
+ ' return false;',
+ '}',
+ '',
+ 'function _find_proxy(url_map, host, url, prot_len) {',
+ ' if (_check_patterns(url_map.white, host, url, prot_len))',
+ ' return \'DIRECT\';',
+ ' if (_check_patterns(url_map.proxy, host, url, prot_len))',
+ ' return _proxy_str;',
+ ' return \'DIRECT\';',
+ '}',
+ '',
+ 'function FindProxyForURL(url, host) {', // host doesn't contain port
+ ' var prot = url.slice(0, 6);',
+ ' if (prot === \'http:/\')',
+ ' return _find_proxy(_http_map, host, url, 7);', // 'http://'.length
+ ' else if (prot === \'https:\')',
+ ' return _find_proxy(_https_map, host, url, 8);', // 'https://'.length
+ ' return \'DIRECT\';',
+ '}',
+ ].join('\n') + '\n';
+}
diff --git a/src/modules/modes.mjs b/src/modules/modes.mjs
new file mode 100644
index 00000000..1c263b57
--- /dev/null
+++ b/src/modules/modes.mjs
@@ -0,0 +1,5 @@
+// Enum definitions for which mode is the extension currently in
+export const Modes = Object.freeze({
+ OFF: 'off',
+ NORMAL: 'normal',
+});
diff --git a/src/modules/settings.mjs b/src/modules/settings.mjs
new file mode 100644
index 00000000..a64e21cd
--- /dev/null
+++ b/src/modules/settings.mjs
@@ -0,0 +1,120 @@
+import {Modes} from './modes.mjs';
+import * as Storage from './_storage.mjs';
+import * as Proxy from './_proxy.mjs';
+import * as Icon from './_icon.mjs';
+
+
+const MODE_STORAGE_KEY = 'unblock_youku_mode';
+const CUSTOM_PROXY_STORAGE_KEY = 'custom_proxy_server';
+
+export async function getCurrentMode() {
+ const mode = await Storage.getItem(MODE_STORAGE_KEY);
+ switch (mode) {
+ case Modes.OFF:
+ // Fall through
+ case Modes.NORMAL:
+ return mode;
+
+ default:
+ console.warn('Got unknown operation mode: ' + mode + '. Resetting to normal mode.');
+ // Overwrite with default mode
+ await Storage.setItem(MODE_STORAGE_KEY, Modes.NORMAL);
+ return Modes.NORMAL;
+ }
+}
+
+export function getCustomProxy() {
+ return Storage.getItem(CUSTOM_PROXY_STORAGE_KEY);
+}
+
+
+// Clear all settings regardless of the current mode
+// Note: Make sure a Promise is returned so that the caller can wait for it
+function clearAllSettings() {
+ return Promise.all([
+ Proxy.clearProxy(),
+ // Header.clearHeaderModifier(),
+ ]);
+}
+
+// Apply the settings for the given mode
+// Note: Make sure a Promise is returned so that the caller can wait for it
+async function applyModeSettings(mode) {
+ if (mode === Modes.OFF) {
+ Icon.setIcon(Modes.OFF);
+ return; // Do nothing else
+ }
+
+ const customProxy = await getCustomProxy();
+ if (typeof customProxy === 'undefined' ||
+ typeof customProxy.protocol === 'undefined' ||
+ typeof customProxy.address === 'undefined') {
+ await Proxy.setDefaultProxy();
+ } else {
+ await Proxy.setCustomProxy(customProxy.protocol, customProxy.address);
+ }
+ // TODO: Set header modifier
+ Icon.setIcon(Modes.NORMAL);
+}
+
+// ================================================================
+// Function called by background.js:
+// * reloadCurrentSettings()
+// 1. Load the latest config from storage
+// 2. Apply all settings for the Chrome extension
+
+export async function loadCurrentSettings() {
+ const mode = await getCurrentMode();
+ await applyModeSettings(mode);
+ console.log('Successfully loaded all the settings');
+}
+
+
+// ================================================================
+// Function called by popup.js:
+// * setNewMode()
+// 1. Tear down the settings of the old mode
+// 2. Set up the mode settings for the new mode
+// 3. Save the new mode into storage
+
+export async function setNewMode(mode) {
+ await clearAllSettings();
+ await applyModeSettings(mode);
+ await Storage.setItem(MODE_STORAGE_KEY, mode);
+
+ // Set this one-time text when the user changes the mode
+ if (mode == Modes.OFF) {
+ Icon.setIconText('OFF');
+ } else {
+ Icon.clearIconText();
+ }
+ console.log('Successfully set the mode to ' + mode);
+}
+
+
+// ================================================================
+// Functions called by options.js
+// * setCustomProxy()
+// 1. Save the custom proxy server configs into storage
+// 2. Change mode to use proxy, and apply settings with the custom proxy server
+// * clearCustomProxy()
+// 1. Remove the custom proxy server configs from storage
+// 2. Change mode to use proxy, and apply settings with the default proxy server
+
+
+export async function setCustomProxy(customProxyProtocol, customProxyAddress) {
+ await Storage.setItem(CUSTOM_PROXY_STORAGE_KEY, {
+ protocol: customProxyProtocol,
+ address: customProxyAddress,
+ });
+ await setNewMode(Modes.NORMAL); // Change the mode to use the proxy right away
+ console.log(
+ 'Successfully set the custom proxy server to ' +
+ customProxyProtocol + ' ' + customProxyAddress);
+}
+
+export async function clearCustomProxy() {
+ await Storage.removeItem(CUSTOM_PROXY_STORAGE_KEY);
+ await setNewMode(Modes.NORMAL); // Change the mode to use the proxy right away
+ console.log('Successfully cleared the custom proxy server');
+}
diff --git a/chrome/pages/options.html b/src/options.html
similarity index 76%
rename from chrome/pages/options.html
rename to src/options.html
index d8b543f5..b6c2e26d 100644
--- a/chrome/pages/options.html
+++ b/src/options.html
@@ -1,20 +1,22 @@
+
Options for Unblock Youku
-
-
-
-
-
+
+
+
+
+