/* * proxy.js * * Vimperator plugin to easily switch between * preconfigured proxy settings * @author Tim Hammerquist * @version 0.3 * * Copyright 2008 Tim Hammerquist * Compatible with Vimperator 1.2pre or higher. Requires Firefox 3. */ /***************************************************************************** USAGE :proxyadd myhttp 192.168.0.2:3128 :proxyadd mytor 192.168.0.3:9050 -type=socks -version=4 :proxyadd myssh 127.0.0.1:1080 -type=socks -remote_dns=on :proxyinfo :set proxy=myssh :pxrm myhttp COMMANDS :proxyinfo :pxi Displays summary of the currently defined proxies. :proxyadd :pxadd Adds a new proxy configuratino to the list of available configurations. Syntax: :proxyadd[!] name host:port [options] Available options: -type/-t Type of proxy. E.g., http, socks, etc. -remote_dns/-r Indicate whether DNS lookups should be performed via the proxy. If not set, Firefox default (off) is used. See 'network.proxy.socks_remote_dns'. -version/-v Version of SOCKS protocol to use. If not set, Firefox default (5) is used. See 'network.proxy.socks_version'. Notes: Attempting to add a proxy with the same name as one already in the list using :proxyadd will fail. Use :proxyadd! to replace the currently defined proxy with the new configuration. If proxy type is not specified, http is used and all other options are ignored. -version and -remote_dns options only apply to SOCKS proxies. SOCKS proxies must be defined using the -type/-t option. It is not inferred from use of the -version/-remote_dns options. :proxydel :pxrm Remove a proxy from the list of available configurations. Syntax: :pxrm name BACKGROUND This plugin was written to replace the many proxy-switching plugins. It does not yet offer per-URL settings, but much of this functionality may be available using the Vimperator's LocationChange event. While currently functional as a plugin for the user's ~/.vimperator/plugin/ directory, this code was written to closely resemble Vimperator's own modules and should work with muttator with little or no modification. KNOWN ISSUES * Effects of switching between 2 different SOCKS proxies are not seen immediately. New settings will (eventually) take effect after 1 or 2 minutes. Tips/suggestions/patches welcome. * Configured proxies are saved in JSON format in the user's prefs.js file under the id 'extensions.vimperator.proxies' whenever any proxy is added/removed/changed. Be aware, however, that Mozilla's nsIJSON service is very picky about its quotes. It does not interpret single-quotes (') correctly. TODO * The current proxy configuration is displayed next to progress bar. I plan to make this configurable. Comments/suggestions on the best way are welcome. * Expand :proxyinfo to display socks-specific settings for proxies. For now, use :echo proxies.get('proxyname') *****************************************************************************/ const jsonService = Components.classes["@mozilla.org/dom/json;1"] .createInstance(Components.interfaces.nsIJSON); Proxy = function() //{{{ { if (arguments[0].constructor == Array) arguments = arguments[0]; var [name, type, host, port, flags] = arguments; if (flags) var ver = flags.version, rdns = flags.remote_dns; if (!(name && type && host && port)) return null; const supportedTypes = ['ftp','gopher','http','socks','ssl']; var data = {}; // this holds our internal data this.__defineGetter__('name', function() { return data.name; }); this.__defineSetter__('name', function(v){ }); this.__defineGetter__('type', function() { return data.type; }); this.__defineSetter__('type', function(v){ v = v.toLowerCase(); if (supportedTypes.some(function(t){return t == v})) data.type = v; }); this.__defineGetter__('host', function() { return data.host; }); this.__defineSetter__('host', function(v){ data.host = v; }); this.__defineGetter__('port', function() { return data.port; }); this.__defineSetter__('port', function(v){ data.port = parseInt(v); }); this.__defineGetter__('remote_dns', function() { return data.rdns; }); this.__defineSetter__('remote_dns', function(v){ data.rdns = (v ? true : false); }); this.__defineGetter__('version', function() { return data.ver; }); this.__defineSetter__('version', function(v){ data.ver = ({4:4,5:5}[parseInt(v)]); }); data.name = name; // this is set once this.type = type; this.host = host; this.port = port; // apply SOCKS-specific settings if (type == 'socks') { this.remote_dns = rdns; this.version = ver; } return this; }; //}}} Proxy.prototype = { //{{{ // so we can store our data export: function () { var fields = [this.name, this.type, this.host, this.port]; if (this.type == 'socks') { var flags = []; ['remote_dns','version'].forEach(function(attr){ if (this[attr] != undefined) flags.push([attr,this[attr]]); }); if (flags.length > 0) { var obj = {}; flags.forEach(function(f){ obj[f[0]] = f[1]; }); fields.push(obj); } } return fields; }, toString: function () { return this.name + ": " + this.host + ":" + this.port + " (" + this.type.toUpperCase() + ")"; }, }; //}}} Proxies = function () //{{{ { //////////////////////////////////////////////////////////////////////////////// ////////////////////// PRIVATE SECTION ///////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ PROXY_NONE = 0; PROXY_MANUAL = 1; PROXY_URL_DETECT = 2; PROXY_NET_DETECT = 4; const proxyStatusId = 'liberator-statusline-field-proxystatus' const proxyPrefId = 'extensions.vimperator.proxies'; const proxyPrefs = [ "network.proxy.ftp", "network.proxy.ftp_port", "network.proxy.gopher", "network.proxy.gopher_port", "network.proxy.http", "network.proxy.http_port", "network.proxy.socks", "network.proxy.socks_port", "network.proxy.socks_remote_dns", "network.proxy.socks_version", "network.proxy.ssl", "network.proxy.ssl_port", "network.proxy.type", ]; var _proxies; // private delegate object var proxyStatusContainer, proxyStatusText; // DOM objects for status function createProxyStatusWidget () { if (proxyStatusContainer && proxyStatusText) return; // create our text node proxyStatusContainer = document.createElement('span'); proxyStatusText = document.createTextNode(''); proxyStatusContainer.setAttribute('id',proxyStatusId); proxyStatusContainer.style.display = 'none'; proxyStatusContainer.appendChild(proxyStatusText); // insert text node into the statusline, next to progress bar var progressBar = document.getElementById( 'liberator-statusline-field-progress'); progressBar.parentNode.insertBefore( proxyStatusContainer, progressBar.nextSibling); } function setProxyStatusText (str) { if (!proxyStatusContainer) createProxyStatusWidget(); proxyStatusText.data = str; if (str && (str.length > 0)) showProxyStatus(); else hideProxyStatus(); } function showProxyStatus () { if (!proxyStatusContainer) createProxyStatusWidget(); proxyStatusContainer.style.display = ''; } function hideProxyStatus () { if (!proxyStatusContainer) createProxyStatusWidget(); proxyStatusContainer.style.display = 'none'; } function sortProxies() { _proxies.sort(function(a,b){return (a.name > b.name);}); } function loadProxies() { var prefValue = liberator.options.getPref(proxyPrefId); _proxies = []; if (prefValue.length == 0) return; try { jsonService.decode(prefValue).forEach(function(a){ _proxies.push(new Proxy(a)); }); } catch (err) { liberator.log("proxy loading failed: " + err, 3); return false; } sortProxies(); } function storeProxies() { const setP = liberator.options.setPref; sortProxies(); var data = _proxies.map(function(p){return p.export();}); var prefValue = jsonService.encode(data); liberator.options.setPref(proxyPrefId, prefValue); return true; } function getProxyByName (name) { for (var i=0; i < _proxies.length; i++) if (_proxies[i].name == name) return _proxies[i]; return null; } function clearProxyPrefs() { proxyPrefs.forEach(function(n){ try { execute("set! "+n+"&"); } catch (e) { liberator.log('ignored "'+e+'"',3); } }); } function activateProxy(name) { const np = "network.proxy."; const setP = liberator.options.setPref; var proxy = getProxyByName(name); clearProxyPrefs(); if (proxy) { setP(np + 'type', PROXY_MANUAL); setP(np + proxy.type, proxy.host); setP(np + proxy.type + "_port", proxy.port); if (proxy.type == 'socks') { setP(np + 'socks_remote_dns', proxy.remote_dns || false ); setP(np + 'socks_version', proxy.version || 5 ); } // update the ui setProxyStatusText("(" + proxy.name + ")"); liberator.echo("Now using proxy: " + proxy.name); } else { setProxyStatusText(""); liberator.echo("Proxy disabled."); } } createProxyStatusWidget(); loadProxies(); /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// OPTIONS ///////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ liberator.options.add(["proxy"], "Set the active proxy configuration", "string", "", { setter: function (name) { activateProxy(name); }, validator: function (value) { return (getProxyByName(value) || (value == "")); }, completer: function (filter) { return [["", "Direct Connection"]] .concat(_proxies.map(function(p){return [p.name, p];})); }, }); /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// COMMANDS //////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ liberator.commands.add(["proxyi[nfo]","pxi"], "Display proxy configuration", function (args, special) { liberator.proxies.list(special) }); liberator.commands.add(["proxya[dd]","pxa[dd]"], "Add a new proxy", function (args, special) { if (args.arguments.length < 2) { liberator.echoerr("Insufficient arguments; 2 required."); return; } var name, type, host, port, rdns, ver; name = args.arguments[0]; if (liberator.proxies.get(name) && !special) { liberator.echoerr("Proxy '"+name+"' already exists." + " Use ! to override."); return; } [host, port] = args.arguments[1].split(':'); type = args["-type"] || 'http'; if (type == 'socks') { rdns = args["-remote_dns"]; ver = args["-version"]; } var proxy = liberator.proxies.add( name, host, port, type, rdns, ver); if (proxy) liberator.echo("Added proxy: " + proxy); else liberator.echoerr("Exxx: Could not add proxy `" + name + "'", liberator.commandline.FORCE_SINGLELINE); }, { options: [[["-type","-t"], liberator.commands.OPTION_STRING], [["-remote_dns","-r"], liberator.commands.OPTION_BOOL], [["-version","-v"], liberator.commands.OPTION_INT]], }); liberator.commands.add(["proxyd[el]","pxrm"], "Remove a proxy from the list", function (args, special) { if (args.arguments.length < 1) { liberator.echoerr("Insufficient arguments; 1 required."); return; } var name = args.arguments[0]; if (!liberator.proxies.get(name)) { liberator.echoerr( "Can't delete nonexistent proxy '" + name + "'"); return; } liberator.echo(liberator.util.objectToString(args.arguments)); var proxy = liberator.proxies.destroy(name); if (proxy) liberator.echo("Removed proxy: " + proxy.name); else liberator.echoerr("Exxx: Could not remove proxy `" + name + "'", liberator.commandline.FORCE_SINGLELINE); }); /////////////////////////////////////////////////////////////////////////////}}} ////////////////////// PUBLIC SECTION ////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////{{{ return { get: function (name) { return getProxyByName(name); }, list: function () { liberator.commandline.echo(_proxies.length + " proxies defined:", null, liberator.commandline.FORCE_MULTILINE); _proxies.forEach(function(p){ var prefix = (p.name == liberator.options.proxy) ? "*" : " "; liberator.commandline.echo(prefix + p, null, liberator.commandline.FORCE_MULTILINE); }); }, add: function (n, h, p, t, rdns, ver) { if (!t) t = 'http'; var new_proxy = new Proxy(n, t, h, p, {remote_dns:rdns, version:ver}); // if a proxy of this name already exists, remove it _proxies = _proxies.filter(function(p){ return (p.name != n); }); _proxies.push(new_proxy); storeProxies(); return this.get(n); }, destroy: function (name) { var proxyCount = _proxies.length; var destruct; _proxies = _proxies.filter(function(p){ if (p.name != name) return true; destruct = p; return false; }); storeProxies(); return destruct; }, save: function () { return storeProxies(); }, load: function () { return loadProxies(); }, }; //}}} }; //}}} liberator.proxies = new Proxies(); liberator.log("Loading proxy plugin", 0); // vim: set fdm=marker sw=4 ts=4 sts=4 et: