[haXe] Automatic Proxy Detection

Nicolas Cannasse ncannasse at motion-twin.com
Mon Jan 8 16:50:31 CET 2007


Hi list,

Some people using HTTP proxies were unable to use some tools such as
haxeLib. Before adding Proxy support to haxe.Http, I would like to get
sure that proxy settings can be retrieved in most of the cases.

Here's a small class that retrieve the Proxy Settings from various
system-specific configurations places. It seems to work for the
following OS/Browsers :

- Windows 2K : Firefox 1.5 Settings and IE 6 Settings
- OSX 10.4 : System/Safari Settings Firefox Settings
- Linux : Firefox Settings

I would like a lot people to test the following :

a) compile using "haxe -main ProxyDetect -neko pdetect.n"
b) run using "neko pdetect.n"

You should get a trace with either "null" (no proxy detected) or a proxy
structure of the kind { host : 127.0.0.1, port : 8080, auth : null }.

If you get an exception or if the printed settings does not correspond
to your system configuration, try watching the sources of the class and
send me some feedback/patch so I can improve it.

Best,
Nicolas

-------------- next part --------------

typedef ProxySettings = {
	var host : String;
	var port : Int;
	var auth : {
		var user : String;
		var pass : String;
	};
}

class ProxyDetect {

	static function parseSettings( settings : String ) {
		var r = ~/^([^:]+):([^@]*)@([^:]+):([0-9]+)$/;
		if( r.match(settings) )
			return {
				auth : {
					user : r.matched(1),
					pass : r.matched(2),
				},
				host : r.matched(3),
				port : Std.parseInt(r.matched(4)),
			};
		var r = ~/^([^:]+):([0-9]+)$/;
		if( !r.match(settings) )
			throw "Invalid settings '"+settings+"'";
		return {
			host : r.matched(1),
			port : Std.parseInt(r.matched(2)),
			auth : null,
		};
	}

	static function detectFF( basedir : String ) {
		var files = try neko.FileSystem.readDirectory(basedir) catch( e : Dynamic ) throw "Invalid Firefox config directory "+basedir;
		var profile = null;
		for( f in files )
			if( f.substr(-8) == ".default" ) {
				profile = f;
				break;
			}
		if( profile == null )
			throw "Profile not found in "+basedir;
		var prefs = neko.io.File.getContent(basedir+"/"+profile+"/prefs.js");
		// enabled ?
		var r = ~/user_pref\("network\.proxy\.type", 1\);/;
		if( !r.match(prefs) )
			return null;
		// prefs
		var r = ~/user_pref\("network\.proxy\.http", "([^"]+)"\);/;
		if( !r.match(prefs) )
			return null;
		var host = r.matched(1);
		var r = ~/user_pref\("network\.proxy\.http_port", ([0-9]+)\);/;
		if( !r.match(prefs) )
			throw "No http_port in prefs.js";
		var port = r.matched(1);
		return parseSettings(host+":"+port);
	}

	static function detectIE() {
		var temp = neko.Sys.getEnv("TMP") + "/proxy.txt";
		if( neko.Sys.command('regedit /E "'+temp+'" "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"') != 0 )
			throw "Failed to call REGEDIT";
		var content = neko.io.File.getContent(temp);
		neko.FileSystem.deleteFile(temp);
		// turn 16-bit string into 8-bit one
		var b = new StringBuf();
		var p = 0;
		while( p < content.length ) {
			b.addChar(content.charCodeAt(p));
			p += 2;
		}
		content = b.toString();
		// enabled ?
		var renabled = ~/"ProxyEnable"=dword:0000000(0|1)/;
		if( !renabled.match(content) )
			throw "Could not find 'ProxyEnable'";
		if( renabled.matched(1) == "0" )
			return null;
		// value ?
		var rproxy = ~/"ProxyServer"="([^"]+)"/;
		if( !rproxy.match(content) )
			throw "Could not find 'ProxyServer'";
		return parseSettings(rproxy.matched(1));
	}

	static function parseOSXConfiguration(xml : Xml) : Dynamic {
		switch( xml.nodeName ) {
		case "dict":
			var o = Reflect.empty();
			var it = xml.elements();
			for( x in it ) {
				if( x.nodeName != "key" ) throw "Missing key";
				var v = x.firstChild().nodeValue;
				var r = parseOSXConfiguration(it.next());
				Reflect.setField(o,v,r);
			}
			return o;
		case "string":
			return xml.firstChild().nodeValue;
		case "integer":
			return Std.parseInt(xml.firstChild().nodeValue);
		case "array":
			var a = new Array();
			for( x in xml.elements() )
				a.push(parseOSXConfiguration(x));
			return a;
		default:
			throw "Invalid value type '"+xml.nodeName+"'";
		}
	}

	static function detectOSX() {
		var prefs = neko.io.File.getContent("/Library/Preferences/SystemConfiguration/preferences.plist");
		var xml = Xml.parse(prefs).firstElement().firstElement(); // plist/dict
		var data : Dynamic = parseOSXConfiguration(xml);
		for( nsname in Reflect.fields(data.NetworkServices) ) {
			var ns : Dynamic = Reflect.field(data.NetworkServices,nsname);
			if( ns.Proxies.HTTPEnable == 1 )
				return { host : ns.Proxies.HTTPProxy, port : ns.Proxies.HTTPPort, auth : null };
		}
		return null;
	}

	static function detectAll() : ProxySettings {
		switch( neko.Sys.systemName() ) {
		case "Windows":
			try {
				var ffdir = neko.Sys.getEnv("APPDATA")+"/Mozilla/Firefox/Profiles";
				var p = detectFF(ffdir);
				if( p == null )
					throw "No Firefox proxy";
				return p;
			} catch( e : Dynamic ) {
				return detectIE();
			}
		case "Mac":
			var p = detectOSX();
			if( p != null )
				return p;
			var ffdir = neko.Sys.getEnv("HOME")+"/Library/Application Support/Firefox/Profiles";
			return detectFF(ffdir);
		case "Linux":
			var ffdir = neko.Sys.getEnv("HOME")+"/.mozilla/firefox";
			return detectFF(ffdir);
		default:
			throw "This system is not supported";
		}
	}

	static var save : { r : ProxySettings } = null;

	static function detect() {
		if( save == null )
			save = { r : detectAll() };
		return save.r;
	}

	static function main() {
		trace(detect());
	}

}


More information about the Haxe mailing list