1
0
Fork 0
foxyproxy-firefox-extension/src/scripts/import-proxy-list.js

241 lines
8.9 KiB
JavaScript
Raw Normal View History

'use strict';
// ----------------- Internationalization ------------------
document.querySelectorAll('[data-i18n]').forEach(node => {
let [text, attr] = node.dataset.i18n.split('|');
text = chrome.i18n.getMessage(text);
attr ? node[attr] = text : node.appendChild(document.createTextNode(text));
});
// ----------------- /Internationalization -----------------
document.addEventListener('keyup', evt => {
if (evt.keyCode === 27) {
location.href = '/options.html';
}
});
// ----------------- Spinner -------------------------------
const spinner = document.querySelector('.spinner');
function hideSpinner() {
spinner.classList.remove('on');
setTimeout(() => { spinner.style.display = 'none'; }, 600);
}
function showSpinner() {
spinner.style.display = 'flex';
spinner.classList.add('on');
}
// ----------------- /spinner ------------------------------
document.addEventListener('DOMContentLoaded', () => {
hideSpinner();
});
// addEventListener for all buttons & handle together
document.querySelectorAll('button').forEach(item => item.addEventListener('click', process));
let proxiesAdded = 0; // Global to this module in case user does multiple bulk imports before closing import-bulk.html
function process(e) {
switch (this.id || this.dataset.i18n) {
case 'back': location.href = '/options.html'; break;
case 'import': imp0rt(); break;
}
}
function imp0rt() {
const {parsedList, skippedList} = parseList(document.getElementById('proxyList').value);
if (parsedList.length > 0) {
if (document.querySelector('#overwrite').checked) {
if (confirm(chrome.i18n.getMessage('confirmOverwrite'))) {
showSpinner();
chrome.storage.local.clear(() => chrome.storage.sync.clear(() => {
hideSpinner();
storeProxies(parsedList);
}));
}
else {
return;
}
}
else {
storeProxies(parsedList);
}
}
if (skippedList.length > 0) {
alert(`${chrome.i18n.getMessage('importsSkipped', [skippedList.length + "", skippedList.toString()])}`);
}
if (parsedList.length > 0) {
alert(`${chrome.i18n.getMessage('importSucceeded', [parsedList.length])}`);
}
location.href = '/options.html';
}
function parseList(rawList) {
const parsedList = [], skippedList = [], colors = ['#663300', '#284B63', '#C99656', '#7B758C', '#171E1D'];
if (!rawList) {
return {parsedList, skippedList};
}
rawList.split('\n').forEach((item) => {
if (!item) {
return; // continue to next
}
let p, patternIncludesAll = true, patternExcludesIntranet = true;
// Is this line simple or complete format?
let protocol = item.match(/.+:\/\//); // null for strings like 127.0.0.1:3128 (simple format)
if (protocol) {
// This line is uses 'complete' format
let url;
try {
// In Firefox 78.0.2, the built-in javascript URL class will not parse URLs with custom schemes/protocols
// like socks://127.0.0.1. However, Chrome 84.0.4147.89 and Node 14.5.0 both do. In order to be compatible
// with Firefox, let's replace the scheme/protocol with 'http'. We could also instead write our own parsing
// logic with a regular expression, but that does not seems necessary.
if (protocol[0] !== 'http://' && protocol[0] !== 'https://') {
item = 'http://' + item.substring(protocol[0].length);
url = new URL(item);
protocol = protocol[0].substring(0, protocol[0].length-2); //strip ending //
}
else {
url = new URL(item);
protocol = url.protocol;
}
}
catch (e) {
console.log(e);
// URL couldn't be parsed
skippedList.push(item);
return; // continue to next
}
const type = protocol === 'proxy:' || protocol === 'http:' ? PROXY_TYPE_HTTP :
protocol === 'ssl:' || protocol === 'https:' ? PROXY_TYPE_HTTPS :
protocol === 'socks:' || protocol === 'socks5:' ? PROXY_TYPE_SOCKS5 :
protocol === 'socks4:' ? PROXY_TYPE_SOCKS4 : -1;
if (type === -1) {
console.log("unknown protocol");
skippedList.push(item);
return; // continue to next
}
// If color not specified in the URL, then rotate among the ones in the colors array.
const color = url.searchParams.get('color') ?
('#' + url.searchParams.get('color')) : colors[parsedList.length % colors.length];
const title = url.searchParams.get('title');
const countryCode = url.searchParams.get('countryCode') || url.searchParams.get('cc');
const country = url.searchParams.get('country') || countryCode;
// If paramName url param is not specified or it's specified and not 'false', then paramValue should equal true.
// We assume true in case the param is absent, which may be counterintuitive, but this fcn is used for params that
// we want to assume true when absent.
function parseBooleanParam(url, paramName, aliasParamName) {
const paramValue = url.searchParams.get(paramName) || (aliasParamName && url.searchParams.get(aliasParamName));
return paramValue ? !(paramValue.toLowerCase() === 'false') : true;
}
const proxyDNS = parseBooleanParam(url, 'proxyDns');
const active = parseBooleanParam(url, 'enabled', 'active');
patternIncludesAll = parseBooleanParam(url, 'patternIncludesAll');
patternExcludesIntranet = parseBooleanParam(url, 'patternExcludesIntranet');
// the URL class sets port === '' if not specified on the URL or it's an invalid port e.g. contains alpha chars
let port = url.port;
if (port === '') {
// Default ports are 3128 for HTTP proxy, 443 for tls/ssl/https proxy, 1080 for socks4/5
port = type === PROXY_TYPE_HTTP ? 3128 : type === PROXY_TYPE_HTTPS ? 443 : 1080;
}
console.log(url);
// the URL class sets username and password === '' if not specified on the URL
p = {type, username: url.username, password: url.password, address: url.hostname, port, color, title, proxyDNS, active, countryCode, country};
}
else {
// simple
const splitItem = item.split(':');
// Split always returns an array no matter what
p = {address: splitItem[0], port: splitItem[1], username: splitItem[2], password: splitItem[3], color: colors[parsedList.length % colors.length]};
}
const proxy = makeProxy(p, patternIncludesAll, patternExcludesIntranet);
if (proxy) {
parsedList.push(proxy);
}
else {
skippedList.push(item);
}
}); //forEach
return {parsedList, skippedList};
}
function makeProxy({type = PROXY_TYPE_HTTP, username, password, address, port, color, title, proxyDNS, active = true, countryCode, country},
patternIncludesAll, patternExcludesIntranet) {
port = port*1; // convert to digit
if (!port || port < 1) { // is port NaN or less than 1
console.log("port is NaN or less than 1");
return null;
}
// strip bad chars from all input except username, password, type, proxyDNS, and active
// (those last 3 are forced to boolean types before we are called)
// If we do strip bad chars from usernams or password, auth could fail.
address = Utils.stripBadChars(address);
color = Utils.stripBadChars(color);
title = Utils.stripBadChars(title);
countryCode = Utils.stripBadChars(countryCode);
country = Utils.stripBadChars(country);
if (!address) {
console.log("no address");
return null;
}
const proxy = {type, address, port, color, active};
// Only set the properties needed. null and undefined props seem to be saved if set, so don't set them.
function setPropertyIfHasValue(prop, value, proxy) {
if (value || value === 0) {
proxy[prop] = value;
}
}
setPropertyIfHasValue('username', username, proxy);
setPropertyIfHasValue('password', password, proxy);
setPropertyIfHasValue('title', title, proxy);
setPropertyIfHasValue('cc', countryCode, proxy);
setPropertyIfHasValue('country', country, proxy);
if (type === PROXY_TYPE_SOCKS5) {
// Only set if socks5
proxy.proxyDNS = proxyDNS;
}
if (FOXYPROXY_BASIC) {
proxy.whitePatterns = proxy.blackPatterns = [];
}
else {
proxy.whitePatterns = patternIncludesAll ? [PATTERN_ALL_WHITE] : [];
proxy.blackPatterns = patternExcludesIntranet ? [...blacklistSet] : [];
}
return proxy;
}
function storeProxies(parsedList) {
const sync = localStorage.getItem('sync') === 'true';
const storageArea = !sync ? chrome.storage.local : chrome.storage.sync;
for (const idx in parsedList) {
const proxy = parsedList[idx];
console.log(proxy);
// Get the nextIndex given to us by options.js and add by the number of proxies we've added.
// This ensures this proxy setting is last in list of all proxy settings.
proxy.index = (localStorage.getItem('nextIndex')) + (++proxiesAdded);
storageArea.set({[Utils.getUniqueId()]: proxy}, () => {
console.log(`stored proxy`);
});
}
}