+/*
+ * proxy.js
+ *
+ * Vimperator plugin to easily switch between
+ * preconfigured proxy settings
+ * @author Tim Hammerquist <penryu@gmail.com>
+ * @version 0.3
+ *
+ * Copyright 2008 Tim Hammerquist <penryu@gmail.com>
+ * 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: