summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/PacIPv6.txt6
-rw-r--r--doc/changelog.txt56
-rw-r--r--doc/internet_explorer_info.txt4
-rw-r--r--doc/mac_osx.txt7
-rw-r--r--doc/misc_links.txt5
-rw-r--r--doc/pac_files_docu.html1182
-rw-r--r--misc/logo.pngbin0 -> 11923 bytes
-rw-r--r--misc/logo.xcfbin0 -> 26871 bytes
-rw-r--r--misc/splash.xcfbin0 -> 350958 bytes
-rw-r--r--misc/stylesheet.css50
-rw-r--r--pom.xml214
-rw-r--r--src/main/java/com/btr/proxy/search/ProxySearch.java258
-rw-r--r--src/main/java/com/btr/proxy/search/ProxySearchStrategy.java22
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java23
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java267
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java79
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java46
-rw-r--r--src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java71
-rw-r--r--src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java28
-rw-r--r--src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java213
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java69
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java353
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java60
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java198
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java132
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java325
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java171
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java68
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java88
-rw-r--r--src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java52
-rw-r--r--src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java130
-rw-r--r--src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java133
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java234
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java315
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java880
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java235
-rw-r--r--src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java107
-rw-r--r--src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java70
-rw-r--r--src/main/java/com/btr/proxy/selector/fixed/FixedProxySelector.java69
-rw-r--r--src/main/java/com/btr/proxy/selector/fixed/FixedSocksSelector.java27
-rw-r--r--src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java126
-rw-r--r--src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java115
-rw-r--r--src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java150
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/JavaxPacScriptParser.java147
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java184
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java656
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/PacScriptParser.java29
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/PacScriptSource.java31
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/ProxyEvaluationException.java51
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.java318
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java46
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java256
-rw-r--r--src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java270
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java78
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java93
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java29
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java83
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java84
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java74
-rw-r--r--src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java24
-rw-r--r--src/main/java/com/btr/proxy/test/ProxyTester.java176
-rw-r--r--src/main/java/com/btr/proxy/util/EmptyXMLResolver.java26
-rw-r--r--src/main/java/com/btr/proxy/util/Logger.java87
-rw-r--r--src/main/java/com/btr/proxy/util/PListParser.java544
-rw-r--r--src/main/java/com/btr/proxy/util/PlatformUtil.java114
-rw-r--r--src/main/java/com/btr/proxy/util/ProxyException.java50
-rw-r--r--src/main/java/com/btr/proxy/util/ProxyUtil.java84
-rw-r--r--src/main/java/com/btr/proxy/util/UriFilter.java21
-rw-r--r--src/main/resources/lib/gsettings-amd64.sobin0 -> 11979 bytes
-rw-r--r--src/main/resources/lib/gsettings-x86.sobin0 -> 11822 bytes
-rw-r--r--src/main/resources/lib/proxy_util_amd64.dllbin0 -> 40448 bytes
-rw-r--r--src/main/resources/lib/proxy_util_ia64.dllbin0 -> 91136 bytes
-rw-r--r--src/main/resources/lib/proxy_util_w32.dllbin0 -> 43520 bytes
-rw-r--r--src/test/java/com/btr/proxy/Examples.java47
-rw-r--r--src/test/java/com/btr/proxy/TestUtil.java61
-rw-r--r--src/test/java/com/btr/proxy/search/browser/FirefoxTest.java144
-rw-r--r--src/test/java/com/btr/proxy/search/browser/IeTest.java59
-rw-r--r--src/test/java/com/btr/proxy/search/desktop/DesktopProxySearchTest.java45
-rw-r--r--src/test/java/com/btr/proxy/search/desktop/win/DLLManagerTest.java78
-rw-r--r--src/test/java/com/btr/proxy/search/gnome/GnomeProxySearchTest.java117
-rw-r--r--src/test/java/com/btr/proxy/search/java/JavaProxySearchTest.java137
-rw-r--r--src/test/java/com/btr/proxy/search/kde/KdeProxySearchTest.java177
-rw-r--r--src/test/java/com/btr/proxy/selector/fixed/FixedProxyTest.java56
-rw-r--r--src/test/java/com/btr/proxy/selector/java/JavaProxySelectorTest.java28
-rw-r--r--src/test/java/com/btr/proxy/selector/misc/ProtocolDispatchTest.java87
-rw-r--r--src/test/java/com/btr/proxy/selector/misc/ProxyListFallbackSelectorTest.java94
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/JavaxPacScriptParserTest.java119
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/PacPerProtocolTest.java47
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/PacProxyDebugging.java103
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/PacProxySelectorTest.java133
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/PacScriptMethodsTest.java165
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/RhinoPacScriptParserTest.java114
-rw-r--r--src/test/java/com/btr/proxy/selector/pac/UrlPacScriptSourceTest.java34
-rw-r--r--src/test/java/com/btr/proxy/selector/whitelist/NoProxyTest.java108
-rw-r--r--src/test/java/com/btr/proxy/util/PListParserTest.java101
-rw-r--r--src/test/java/com/btr/proxy/util/ProxyUtilTest.java78
-rw-r--r--src/test/java/com/btr/proxy/util/UriFilterTest.java122
-rw-r--r--src/test/resources/data/ff3_manual/.mozilla/firefox/9f1uyzzu.default/prefs.js76
-rw-r--r--src/test/resources/data/ff3_none/.mozilla/firefox/9f1uyzzu.default/prefs.js77
-rw-r--r--src/test/resources/data/ff3_pac_script/.mozilla/firefox/9f1uyzzu.default/prefs.js76
-rw-r--r--src/test/resources/data/ff3_white_list/.mozilla/firefox/9f1uyzzu.default/prefs.js77
-rwxr-xr-xsrc/test/resources/data/gnome_manual/.gconf/system/http_proxy/%gconf.xml23
-rwxr-xr-xsrc/test/resources/data/gnome_manual/.gconf/system/proxy/%gconf.xml36
-rwxr-xr-xsrc/test/resources/data/gnome_none/.gconf/system/http_proxy/%gconf.xml21
-rwxr-xr-xsrc/test/resources/data/gnome_pac_script/.gconf/system/http_proxy/%gconf.xml26
-rwxr-xr-xsrc/test/resources/data/gnome_pac_script/.gconf/system/proxy/%gconf.xml39
-rwxr-xr-xsrc/test/resources/data/gnome_white_list/.gconf/system/http_proxy/%gconf.xml26
-rwxr-xr-xsrc/test/resources/data/gnome_white_list/.gconf/system/proxy/%gconf.xml36
-rw-r--r--src/test/resources/data/kde_env/.kde/share/config/kioslaverc17
-rw-r--r--src/test/resources/data/kde_manual/.kde/share/config/kioslaverc17
-rw-r--r--src/test/resources/data/kde_none/.kde/share/config/kioslaverc17
-rw-r--r--src/test/resources/data/kde_pac_script/.kde/share/config/kioslaverc17
-rw-r--r--src/test/resources/data/kde_white_list/.kde/share/config/kioslaverc17
-rw-r--r--src/test/resources/data/osx/osx_all.plist144
-rw-r--r--src/test/resources/data/osx/osx_manual.plist144
-rw-r--r--src/test/resources/data/osx/osx_pac.plist144
-rw-r--r--src/test/resources/data/pac/test1.pac4
-rw-r--r--src/test/resources/data/pac/test2.pac10
-rw-r--r--src/test/resources/data/pac/testDateRange.pac11
-rw-r--r--src/test/resources/data/pac/testLocalIP.pac4
-rw-r--r--src/test/resources/data/pac/testMultiProxy.pac4
-rw-r--r--src/test/resources/data/pac/testTimeRange.pac11
-rw-r--r--src/test/resources/data/pac/testWeekDay.pac10
-rw-r--r--src/test/resources/data/win/proxy_util_amd64.dll0
-rw-r--r--src/test/resources/data/win/proxy_util_ia64.dll0
-rw-r--r--src/test/resources/data/win/proxy_util_w32.dll0
-rw-r--r--src/test/resources/data/wpad/wpad.pac4
-rw-r--r--src_native/gnome/ProxySchemasGSettingsAccess.c117
-rw-r--r--src_native/gnome/ProxySchemasGSettingsAccess.h21
-rw-r--r--src_native/gnome/make2
-rwxr-xr-xsrc_native/win/proxy_util_w23/compile.bat1
-rw-r--r--src_native/win/proxy_util_w23/dllmain.cpp19
-rw-r--r--src_native/win/proxy_util_w23/proxy_util_w23.vcproj255
-rw-r--r--src_native/win/proxy_util_w23/proxy_util_w32.cpp205
-rw-r--r--src_native/win/proxy_util_w23/proxy_util_w32.h50
-rw-r--r--src_native/win/proxy_util_w23/stdafx.cpp2
-rw-r--r--src_native/win/proxy_util_w23/stdafx.h11
-rw-r--r--src_native/win/proxy_util_w23/targetver.h28
-rw-r--r--src_native/win/proxy_util_w32.sln20
-rw-r--r--src_native/win/proxy_util_w32.suobin0 -> 41984 bytes
140 files changed, 14266 insertions, 0 deletions
diff --git a/doc/PacIPv6.txt b/doc/PacIPv6.txt
new file mode 100644
index 0000000..3b3587e
--- /dev/null
+++ b/doc/PacIPv6.txt
@@ -0,0 +1,6 @@
+
+PAC IPV6 in Windows Vista and newer
+http://blogs.msdn.com/b/wndp/archive/2006/07/18/ipv6-wpad-for-winhttp-and-wininet.aspx
+
+And the extension specs at:
+http://blogs.msdn.com/b/wndp/archive/2006/07/13/ipv6-pac-extensions-v0-9.aspx \ No newline at end of file
diff --git a/doc/changelog.txt b/doc/changelog.txt
new file mode 100644
index 0000000..4bcff46
--- /dev/null
+++ b/doc/changelog.txt
@@ -0,0 +1,56 @@
+Change Log:
+This file contains the change log. The changes are marked with the following symbology.
+
++ for a new feature
+- for a removed feature
+* for a modified feature or a bugfix
+
+-----------------
+Current version
+* Fixed issue 29 : KDE settings file was not found on KDE4. This is solved now.
+* Fixed issue 25 : Disabled DTD and schema validation in settings parsers.
+
+20120920
+* Improved the fix for issue 26 PAC download recursion bug
+
+20120905
+* Fixed issue 26 : Implemented (hopefully) better fix for PAC download recursion bug
+* Fixed issue 27 + 22 : JavaProxySearch was used always even if no http.proxyHost property was set.
+
+20120727
+* Fixed issue 19 : Added check to return null if no KDE settings are available.
+* Fixed issue 20 : Fix in isInNet method. Resolve host names to IP addresses.
+* Fixed issue 21 : Improved handling of empty/invalid URIs
+
+20111102
+* Fixed issue 14 : Added code to cleanup temporary dll files that were not deleted on VM exit.
+* Fixed issue 18 : Added code to support the https.proxy system properties too.
+* Fixed issue 9 : IE PAC file urls are now patched to be in line with Firefox URLs.
++ Added support for automatic retry if PAC returns a list of fallback proxies.
++ Started to implement IPv6 support
+
+20110515
++ Added (experiemental) support for Max OSX. This is not very well tested yet but should work.
++ Fixed issue 15 : The Firefox proxy search lacked support for socks proxy. This is now available.
+* Fixed issue 12 : The PAC JavaScript method isInNet did not work for all patterns. This is fixed now.
+
+20110210
+* Fixed issue 11: Parsing was unreliable when a PAC script returned more than one proxy
+* Some code cleanup to fix some minor problems (platform detection issues and others)
++ Added a debug system property "com.btr.proxy.pac.overrideLocalIP" for the PAC parser
++ Added Support for the IE "Bypass Proxy Server for local addresses" feature
+
+20100914
++ PAC support on Java 1.6 will now use the javax.script framework and the internal javascript engine that is shipped with JRE6. This will allow you to support PAC without bundling the Rhino engine which will dramatically reduce the footprint of the library. Java 1.5 will still be supported but then you need to bundle it with the Rhino engine.
++ Added contributed dlls to support 64 windows version for the IE proxy detection.
+* Some fixes for the http client that is used to download PAC scripts from a webserver. We accept now all content-type send by the server and we support now different charsets for the scripts.
+* Fixed some unit tests that did not pass and did some other small fixes.
+
+20100724
+* Fixed Issue 5: PAC Parser was not working correctly in multithreaded environments.
+
+20100411
+* Fixed Issue 4: Improved parser used to parse proxy urls from environment varibales and other places
+
+Older
+Did not track changes for older releases \ No newline at end of file
diff --git a/doc/internet_explorer_info.txt b/doc/internet_explorer_info.txt
new file mode 100644
index 0000000..6b49928
--- /dev/null
+++ b/doc/internet_explorer_info.txt
@@ -0,0 +1,4 @@
+
+The following article gives a good description of IEs "Bypass Proxy Server for local addresses" feature and how it works.
+
+http://www.windowsreference.com/internet-explorer/bypass-proxy-server-for-local-addresses-in-internet-explorer-explained/ \ No newline at end of file
diff --git a/doc/mac_osx.txt b/doc/mac_osx.txt
new file mode 100644
index 0000000..df9c18e
--- /dev/null
+++ b/doc/mac_osx.txt
@@ -0,0 +1,7 @@
+Settings are stored in a XML file that can be found at
+
+/Library/Preferences/SystemConfiguration/preferences.plist
+
+The plist file format is specified under
+
+http://www.apple.com/DTDs/PropertyList-1.0.dtd
diff --git a/doc/misc_links.txt b/doc/misc_links.txt
new file mode 100644
index 0000000..b16885f
--- /dev/null
+++ b/doc/misc_links.txt
@@ -0,0 +1,5 @@
+
+Oracle iPlanet Web Proxy Server
+--------------------------------
+http://download.oracle.com/docs/cd/E21692_01/821-1882/adyrr/index.html
+
diff --git a/doc/pac_files_docu.html b/doc/pac_files_docu.html
new file mode 100644
index 0000000..9e08bf8
--- /dev/null
+++ b/doc/pac_files_docu.html
@@ -0,0 +1,1182 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<html><head>
+
+
+<!-- base href="http://wp.netscape.com.wstub.archive.org/eng/mozilla/2.0/relnotes/demo/proxy-live.html" -->
+
+ <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
+ <meta name="GENERATOR" content="Mozilla/4.5b2 [en] (Win95; U) [Netscape]">
+ <title>Proxy Client Autoconfig File Format</title>
+</head><body text="#000000" vlink="#ff0000" alink="#ff0000" bgcolor="#ffffff" link="#0000ff">
+
+<center><!-- BANNER:s3 --><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/maps/banners/documentation_s3.map"><img ismap="ismap" src="proxy-live-Dateien/documentation_s3.gif" alt="Documentation" usemap="#banner_nav" width="612" border="0" height="50"></a><map name="banner_nav"><area shape="RECT" coords="62,11,91,40" href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/"><area shape="RECT" coords="153,41,221,50" href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/"><area shape="RECT" coords="298,8,374,34" href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/support/index.html"><area shape="RECT" coords="381,15,586,43" href="http://web.archive.org/web/20060424005037/http://help.netscape.com/browse/index.html"><area shape="default" nohref=""></map><!-- BANNER:s3 --></center>
+
+<center>
+<h2>
+Navigator Proxy Auto-Config File Format</h2></center>
+
+<center><i>March 1996</i>
+<p><i>(There are several examples and tips in the end of this document)</i>
+</p><hr size="4"></center>
+
+<br>&nbsp;
+<p><br>
+<br>
+<br>
+<br>
+<br>
+</p><p>The proxy autoconfig file is written in JavaScript. The file must define
+the function:
+</p><pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</pre>
+which will be called by the Navigator in the following way for every URL
+that is retrieved by it:
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ret = FindProxyForURL(url, host);</pre>
+where:
+<dl compact="compact">
+<dt>
+<tt>url</tt></dt>
+
+<dd>
+the full URL being accessed.</dd>
+
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+the hostname extracted from the URL. This is only for convenience, it is
+the exact same string as between <tt>://</tt> and the first <tt>:</tt>
+or <tt>/</tt> after that. The port number is not included in this parameter.
+It can be extracted from the URL when necessary.</dd>
+
+<dt>
+<tt>ret</tt></dt>
+
+<dd>
+(the return value) a string describing the configuration. The format of
+this string is defined below.</dd>
+</dl>
+
+<hr size="4">
+<h2>
+Saving the Auto-Config File<br>
+Setting the MIME Type</h2>
+
+<ol>
+<li>
+You should save the JavaScript function to file with a
+<tt>.pac</tt> filename
+extension; for example:</li>
+
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; proxy.pac</pre>
+<b>Note 1:</b> You should save the JavaScript function <b>by itself</b>,
+not embed it in HTML.
+<p><b>Note 2:</b> The examples in the end of this document are
+<b>complete</b>,
+there is no additional syntax needed to save it into a file and use it
+(of course, the JavaScripts have to be edited to reflect your site's domain
+name and/or subnets).
+</p><li>
+Next, you should configure your server to map the
+<tt>.pac</tt> filename
+extension to the MIME type:</li>
+
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; application/x-ns-proxy-autoconfig</pre>
+If using a Netscape server, edit the <tt>mime.types</tt> file in the <tt>config</tt>
+directory. If using Apache, CERN or NCSA servers, use the <tt>AddType</tt>
+directive.
+<br>&nbsp;</ol>
+
+<hr size="4">
+<h2>
+Return Value Format</h2>
+The JavaScript function returns a single string.
+<p>If the string is null, no proxies should be used.
+</p><p>The string can contain any number of the following building blocks,
+separated by a semicolon:
+</p><dl>
+<dt>
+<tt>DIRECT</tt></dt>
+
+<dd>
+Connections should be made directly, without any proxies.</dd>
+
+<dt>
+<tt>PROXY <i>host:port</i></tt></dt>
+
+<dd>
+The specified proxy should be used.</dd>
+
+<dt>
+<tt>SOCKS <i>host:port</i></tt></dt>
+
+<dd>
+The specified SOCKS server should be used.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+If there are multiple semicolon-separated settings, the left-most setting
+will be used, until the Navigator fails to establish the connection to
+the proxy. In that case the next value will be used, etc.
+<p>The Navigator will automatically retry a previously unresponsive proxy
+after 30 minutes, then after 1 hour from the previous try (always adding
+an extra 30 minutes).
+</p><p>If all proxies are down, and there was no <tt>DIRECT</tt> option specified,
+the Navigator will ask if proxies should be temporarily ignored, and direct
+connections attempted. The Navigator will ask if proxies should be retried
+after 20 minutes has passed (then the next time 40 minutes from the previous
+question, always adding 20 minutes).
+</p><h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>PROXY w3proxy.netscape.com:8080; PROXY mozilla.netscape.com:8081</tt></dt>
+
+<dd>
+Primary proxy is <tt>w3proxy:8080</tt>; if that goes down start using <tt>mozilla:8081</tt>
+until the primary proxy comes up again.</dd>
+
+<dt>
+<tt>PROXY w3proxy.netscape.com:8080; PROXY mozilla.netscape.com:8081; DIRECT</tt></dt>
+
+<dd>
+Same as above, but if both proxies go down, automatically start making
+direct connections. (In the first example above, Netscape will ask user
+confirmation about making direct connections; in this third case, there
+is no user intervention.)</dd>
+
+<dt>
+<tt>PROXY w3proxy.netscape.com:8080; SOCKS socks:1080</tt></dt>
+
+<dd>
+Use SOCKS if the primary proxy goes down.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+
+<hr size="4">
+<h2>
+Predefined Functions and Environment for the JavaScript Function</h2>
+
+<ul>
+<li>
+Hostname based conditions:</li>
+
+<ul>
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#isPlainHostName">isPlainHostName()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#dnsDomainIs">dnsDomainIs()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#localHostOrDomainIs">localHostOrDomainIs()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#isResolvable">isResolvable()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#isInNet">isInNet()</a></tt></li>
+</ul>
+
+<li>
+Related utility functions:</li>
+
+<ul>
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#dnsResolve">dnsResolve()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#myIpAddress">myIpAddress()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#dnsDomainLevels">dnsDomainLevels()</a></tt></li>
+</ul>
+
+<li>
+URL/hostname based conditions:</li>
+
+<ul>
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#shExpMatch">shExpMatch()</a></tt></li>
+</ul>
+
+<li>
+Time based conditions:</li>
+
+<ul>
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#weekdayRange">weekdayRange()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#dateRange">dateRange()</a></tt></li>
+
+<li>
+<tt><a href="http://web.archive.org/web/20060424005037/http://wp.netscape.com/eng/mozilla/2.0/relnotes/demo/proxy-live.html#timeRange">timeRange()</a></tt></li>
+</ul>
+
+<li>
+There is one associative array already defined (because a JavaScript currently
+cannot define them on its own):</li>
+
+<ul>
+<li>
+<tt>ProxyConfig.bindings</tt></li>
+</ul>
+</ul>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="isPlainHostName"></a><tt>isPlainHostName(host)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+the hostname from the URL (excluding port number).</dd>
+</dl>
+True iff there is no domain name in the hostname (no dots).
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>isPlainHostName("www")</tt></dt>
+
+<dd>
+is true.</dd>
+
+<dt>
+<tt>isPlainHostName("www.netscape.com")</tt></dt>
+
+<dd>
+is false.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="dnsDomainIs"></a><tt>dnsDomainIs(host, domain)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+is the hostname from the URL.</dd>
+
+<dt>
+<tt>domain</tt></dt>
+
+<dd>
+is the domain name to test the hostname against.</dd>
+</dl>
+Returns true iff the domain of hostname matches.
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>dnsDomainIs("www.netscape.com", ".netscape.com")</tt></dt>
+
+<dd>
+is true.</dd>
+
+<dt>
+<tt>dnsDomainIs("www", ".netscape.com")</tt></dt>
+
+<dd>
+is false.</dd>
+
+<dt>
+<tt>dnsDomainIs("www.mcom.com", ".netscape.com")</tt></dt>
+
+<dd>
+is false.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="localHostOrDomainIs"></a><tt>localHostOrDomainIs(host, hostdom)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+the hostname from the URL.</dd>
+
+<dt>
+<tt>hostdom</tt></dt>
+
+<dd>
+fully qualified hostname to match against.</dd>
+</dl>
+Is true if the hostname matches exactly the specified hostname, or if there
+is no domain name part in the hostname, but the unqualified hostname matches.
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>localHostOrDomainIs("www.netscape.com", "www.netscape.com")</tt></dt>
+
+<dd>
+is true (exact match).</dd>
+
+<dt>
+<tt>localHostOrDomainIs("www", "www.netscape.com")</tt></dt>
+
+<dd>
+is true (hostname match, domain not specified).</dd>
+
+<dt>
+<tt>localHostOrDomainIs("www.mcom.com", "www.netscape.com")</tt></dt>
+
+<dd>
+is false (domain name mismatch).</dd>
+
+<dt>
+<tt>localHostOrDomainIs("home.netscape.com", "www.netscape.com")</tt></dt>
+
+<dd>
+is false (hostname mismatch).</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="isResolvable"></a><tt>isResolvable(host)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+is the hostname from the URL.</dd>
+</dl>
+Tries to resolve the hostname. Returns true if succeeds.
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>isResolvable("www.netscape.com")</tt></dt>
+
+<dd>
+is true (unless DNS fails to resolve it due to a firewall or some other
+reason).</dd>
+
+<dt>
+<tt>isResolvable("bogus.domain.foobar")</tt></dt>
+
+<dd>
+is false.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="isInNet"></a><tt>isInNet(host, pattern, mask)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+a DNS hostname, or IP address. If a hostname is passed, it will be resoved
+into an IP address by this function.</dd>
+
+<dt>
+<tt>pattern</tt></dt>
+
+<dd>
+an IP address pattern in the dot-separated format</dd>
+
+<dt>
+<tt>mask</tt></dt>
+
+<dd>
+mask for the IP address pattern informing which parts of the IP address
+should be matched against. 0 means ignore, 255 means match.</dd>
+</dl>
+True iff the IP address of the host matches the specified IP address pattern.
+<p>Pattern and mask specification is done the same way as for SOCKS configuration.
+</p><h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>isInNet(host, "198.95.249.79", "255.255.255.255")</tt></dt>
+
+<dd>
+is true iff the IP address of host matches exactly 198.95.249.79.</dd>
+
+<dt>
+<tt>isInNet(host, "198.95.0.0", "255.255.0.0")</tt></dt>
+
+<dd>
+is true iff the IP address of the host matches 198.95.*.*.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="dnsResolve"></a><tt>dnsResolve(host)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+hostname to resolve</dd>
+</dl>
+Resolves the given DNS hostname into an IP address, and returns it in the
+dot separated format as a string.
+<h4>
+Example:</h4>
+
+<dl>
+<dt>
+<tt>dnsResolve("home.netscape.com")</tt></dt>
+
+<dd>
+returns the string <tt>"198.95.249.79"</tt>.</dd>
+</dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="myIpAddress"></a><tt>myIpAddress()</tt></h3>
+Returns the IP address of the host that the Navigator is running on, as
+a string in the dot-separated integer format.
+<h4>
+Example:</h4>
+
+<dl>
+<dt>
+<tt>myIpAddress()</tt></dt>
+
+<dd>
+would return the string <tt>"198.95.249.79"</tt> if you were running the
+Navigator on that host.</dd>
+</dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="dnsDomainLevels"></a><tt>dnsDomainLevels(host)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>host</tt></dt>
+
+<dd>
+is the hostname from the URL.</dd>
+</dl>
+Returns the number (integer) of DNS domain levels (number of dots) in the
+hostname.
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>dnsDomainLevels("www")</tt></dt>
+
+<dd>
+returns 0.</dd>
+
+<dt>
+<tt>dnsDomainLevels("www.netscape.com")</tt></dt>
+
+<dd>
+returns 2.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="shExpMatch"></a><tt>shExpMatch(str, shexp)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>str</tt></dt>
+
+<dd>
+is any string to compare (e.g. the URL, or the hostname).</dd>
+
+<dt>
+<tt>shexp</tt></dt>
+
+<dd>
+is a shell expression to compare against.</dd>
+</dl>
+Returns true if the string matches the specified shell expression.
+<p><b>Actually, currently the patterns are <i>shell expressions</i>, not
+regular expressions.</b>
+</p><h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>shExpMatch("http://home.netscape.com/people/ari/index.html", "*/ari/*")</tt></dt>
+
+<dd>
+is true.</dd>
+
+<dt>
+<tt>shExpMatch("http://home.netscape.com/people/montulli/index.html", "*/ari/*")</tt></dt>
+
+<dd>
+is false.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="weekdayRange"></a><tt>weekdayRange(wd1, wd2, gmt)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>wd1</tt></dt>
+
+<dd>
+and</dd>
+
+<dt>
+<tt>wd2</tt></dt>
+
+<dd>
+are one of the weekday strings:</dd>
+
+<pre>&nbsp;&nbsp;&nbsp; SUN MON TUE WED THU FRI SAT</pre>
+
+<dt>
+<tt>gmt</tt></dt>
+
+<dd>
+is either the string: <tt>GMT</tt> or is left out.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+Only the first parameter is mandatory. Either the second, the third, or
+both may be left out.
+<p>If only one parameter is present, the function yeilds a true value on
+the weekday that the parameter represents. If the string
+<tt>"GMT"</tt>
+is specified as a second parameter, times are taken to be in GMT, otherwise
+in local timezone.
+</p><p>If both <tt>wd1</tt> and <tt>wd1</tt> are defined, the condition is
+true if the current weekday is in between those two weekdays. Bounds are
+inclusive. If the <tt>"GMT"</tt> parameter is specified, times are taken
+to be in GMT, otherwise the local timezone is used.
+</p><h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>weekdayRange("MON", "FRI")</tt></dt>
+
+<dd>
+true Monday trhough Friday (local timezone).</dd>
+
+<dt>
+<tt>weekdayRange("MON", "FRI", "GMT")</tt></dt>
+
+<dd>
+same as above, but GMT timezone.</dd>
+
+<dt>
+<tt>weekdayRange("SAT")</tt></dt>
+
+<dd>
+true on Saturdays local time.</dd>
+
+<dt>
+<tt>weekdayRange("SAT", "GMT")</tt></dt>
+
+<dd>
+true on Saturdays GMT time.</dd>
+
+<dt>
+<tt>weekdayRange("FRI", "MON")</tt></dt>
+
+<dd>
+true Friday through Monday (note, order does matter!).</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="dateRange"></a><tt>dateRange(day)<br>
+dateRange(day1, day2)<br>
+dateRange(mon)<br>
+dateRange(month1, month2)<br>
+dateRange(year)<br>
+dateRange(year1, year2)<br>
+dateRange(day1, month1, day2, month2)<br>
+dateRange(month1, year1, month2, year2)<br>
+dateRange(day1, month1, year1, day2, month2, year2)<br>
+dateRange(day1, month1, year1, day2, month2, year2, gmt)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>day</tt></dt>
+
+<dd>
+is the day of month between 1 and 31 (as an integer).</dd>
+
+<dt>
+<tt>month</tt></dt>
+
+<dd>
+is one of the month strings:</dd>
+
+<pre>&nbsp;&nbsp;&nbsp; JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC</pre>
+
+<dt>
+<tt>year</tt></dt>
+
+<dd>
+is the full year number, for example 1995 (but <b>not</b> 95). Integer.</dd>
+
+<dt>
+<tt>gmt</tt></dt>
+
+<dd>
+is either the string <tt>"GMT"</tt>, which makes time comparison occur
+in GMT timezone; if left unspecified, times are taken to be in the local
+timezone.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+</p><p>Even though the above examples don't show, the <tt>"GMT"</tt> parameter
+can be specified in any of the 9 different call profiles, always as the
+last parameter.
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+If only a single value is specified (from each category: day, month, year),
+the function returns a true value only on days that match that specification.
+If both values are specified, the result is true between those times, including
+bounds.
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>dateRange(1)</tt></dt>
+
+<dd>
+true on the first day of each month, local timezone.</dd>
+
+<dt>
+<tt>dateRange(1, "GMT")</tt></dt>
+
+<dd>
+true on the first day of each month, GMT timezone.</dd>
+
+<dt>
+<tt>dateRange(1, 15)</tt></dt>
+
+<dd>
+true on the first half of each month.</dd>
+
+<dt>
+<tt>dateRange(24, "DEC")</tt></dt>
+
+<dd>
+true on 24th of December each year.</dd>
+
+<dt>
+<tt>dateRange(24, "DEC", 1995)</tt></dt>
+
+<dd>
+true on 24th of December, 1995.</dd>
+
+<dt>
+<tt>dateRange("JAN", "MAR")</tt></dt>
+
+<dd>
+true on the first quarter of the year.</dd>
+
+<dt>
+<tt>dateRange(1, "JUN", 15, "AUG")</tt></dt>
+
+<dd>
+true from June 1st until August 15th, each year (including June 1st and
+August 15th).</dd>
+
+<dt>
+<tt>dateRange(1, "JUN", 15, 1995, "AUG", 1995)</tt></dt>
+
+<dd>
+true from June 1st, 1995, until August 15th, same year.</dd>
+
+<dt>
+<tt>dateRange("OCT", 1995, "MAR", 1996)</tt></dt>
+
+<dd>
+true from October 1995 until March 1996 (including the entire month of
+October 1995 and March 1996).</dd>
+
+<dt>
+<tt>dateRange(1995)</tt></dt>
+
+<dd>
+true during the entire year 1995.</dd>
+
+<dt>
+<tt>dateRange(1995, 1997)</tt></dt>
+
+<dd>
+true from beginning of year 1995 until the end of year 1997.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+<!-- ---------------------------------------------------------- -->
+<hr size="4">
+<h3>
+<a name="timeRange"></a><tt>timeRange(hour)<br>
+timeRange(hour1, hour2)<br>
+timeRange(hour1, min1, hour2, min2)<br>
+timeRange(hour1, min1, sec1, hour2, min2, sec2)<br>
+timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)</tt></h3>
+
+<dl compact="compact">
+<dt>
+<tt>hour</tt></dt>
+
+<dd>
+is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)</dd>
+
+<dt>
+<tt>min</tt></dt>
+
+<dd>
+minutes from 0 to 59.</dd>
+
+<dt>
+<tt>sec</tt></dt>
+
+<dd>
+seconds from 0 to 59.</dd>
+
+<dt>
+<tt>gmt</tt></dt>
+
+<dd>
+either the string <tt>"GMT"</tt> for GMT timezone, or not specified, for
+local timezone. Again, even though the above list doesn't show it, this
+parameter may be present in each of the different parameter profiles, always
+as the last parameter.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+True during (or between) the specified time(s).
+<h4>
+Examples:</h4>
+
+<dl>
+<dt>
+<tt>timerange(12)</tt></dt>
+
+<dd>
+true from noon to 1pm.</dd>
+
+<dt>
+<tt>timerange(12, 13)</tt></dt>
+
+<dd>
+same as above.</dd>
+
+<dt>
+<tt>timerange(12, "GMT")</tt></dt>
+
+<dd>
+true from noon to 1pm, in GMT timezone.</dd>
+
+<dt>
+<tt>timerange(9, 17)</tt></dt>
+
+<dd>
+true from 9am to 5pm.</dd>
+
+<dt>
+<tt>timerange(8, 30, 17, 00)</tt></dt>
+
+<dd>
+true from 8:30am to 5:00pm.</dd>
+
+<dt>
+<tt>timerange(0, 0, 0, 0, 0, 30)</tt></dt>
+
+<dd>
+true between midnight and 30 seconds past midnight.</dd>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></dl>
+
+<hr size="4">
+<h2>
+Example #1:
+<font size="+0">Use proxy for everything except local hosts</font></h2>
+This would work in Netscape's environment. All hosts which aren't fully
+qualified, or the ones that are in local domain, will be connected to directly.
+Everything else will go through
+<tt>w3proxy:8080</tt>. If the proxy goes
+down, connections become automatically direct.
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isPlainHostName(host) ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dnsDomainIs(host, ".netscape.com"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY w3proxy.netscape.com:8080; DIRECT";
+&nbsp;&nbsp;&nbsp; }</pre>
+<b>Note:</b> This is the simplest and most efficient autoconfig file for
+cases where there's only one proxy.
+<p>
+</p><hr size="4">
+<h2>
+Example #1b:
+<font size="+0">As above, but use proxy for local servers which
+are outside the firewall</font></h2>
+If there are hosts (such as the main Web server) that belong to the local
+domain but are outside the firewall, and are only reachable through the
+proxy server, those exceptions can be handled using the
+<tt>localHostOrDomainIs()</tt>
+function:
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((isPlainHostName(host) ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dnsDomainIs(host, ".netscape.com")) &amp;&amp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !localHostOrDomainIs(host, "www.netscape.com") &amp;&amp;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; !localHostOrDoaminIs(host, "merchant.netscape.com"))
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY w3proxy.netscape.com:8080; DIRECT";
+&nbsp;&nbsp;&nbsp; }</pre>
+The above will use the proxy for everything else except local hosts in
+the <tt>netscape.com</tt> domain, with the further exception that hosts
+<tt>www.netscape.com</tt>
+and
+<tt>merchant.netscape.com</tt> will go through the proxy.
+<p><b>Note</b> the order of the above exceptions for efficiency: localHostOrDomainIs()
+functions only get executed for URLs that are in local domain, not for
+every URL. Be careful to note the parentheses around the <i>or</i> expression
+before the <i>and</i> expression to achieve the abovementioned efficient
+behaviour.
+</p><p>
+</p><hr size="4">
+<h2>
+Example #2:
+<font size="+0">Use proxy only if cannot resolve host</font></h2>
+This example would work in an environment where internal DNS is set up
+so that it can only resolve internal host names, and the goal is to use
+a proxy only for hosts which aren't resolvable:
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isResolvable(host))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp; }</pre>
+The above requires consulting the DNS every time; it can be grouped smartly
+with other rules so that DNS is consulted only if other rules do not yield
+a result:
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isPlainHostName(host) ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dnsDomainIs(host, ".mydomain.com") ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isResolvable(host))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp; }</pre>
+
+<hr size="4">
+<h2>
+Example #3:
+<font size="+0">Subnet based decisions</font></h2>
+In this example all the hosts in a given subnet are connected to directly,
+others through the proxy.
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isInNet(host, "198.95.0.0", "255.255.0.0"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp; }</pre>
+Again, use of DNS in the above can be minimized by adding redundant rules
+in the beginning:
+<pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isPlainHostName(host) ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; dnsDomainIs(host, ".mydomain.com") ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; isInNet(host, "198.95.0.0", "255.255.0.0"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp; }</pre>
+
+<hr size="4">
+<h2>
+Example #4:
+<font size="+0">Load balancing/routing based on URL patterns</font></h2>
+This example is more sophisticated. There are four (4) proxy servers; one
+of them is a hot stand-by for all of the other ones, so if any of the remaining
+three goes down, the fourth one will take over.
+<p>Furthermore, the three remaining proxy servers share the load based
+on URL patterns, which makes their caching more effective (there is only
+one copy of any document on the three servers -- as opposed to one copy
+on each of them). The load is distributed like this:
+<table border="2" cellpadding="8">
+<tbody><tr>
+<th>Proxy</th>
+
+<th>Purpose</th>
+</tr>
+
+<tr>
+<td align="center">#1</td>
+
+<td align="center"><tt>.com</tt> domain</td>
+</tr>
+
+<tr>
+<td align="center">#2</td>
+
+<td align="center"><tt>.edu</tt> domain</td>
+</tr>
+
+<tr>
+<td align="center">#3</td>
+
+<td align="center">all other domains</td>
+</tr>
+
+<tr>
+<td align="center">#4</td>
+
+<td align="center">hot stand-by</td>
+</tr>
+</tbody></table>
+
+</p><p>All local accesses are desired to be direct. All proxy servers run on
+the port 8080 (they wouldn't need to). Note how strings can be concatenated
+by the <tt>+</tt> operator in JavaScript.
+</p><pre>&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (isPlainHostName(host) || dnsDomainIs(host, ".mydomain.com"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (shExpMatch(host, "*.com"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy1.mydomain.com:8080; " +
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "PROXY proxy4.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (shExpMatch(host, "*.edu"))
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy2.mydomain.com:8080; " +
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "PROXY proxy4.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY proxy3.mydomain.com:8080; " +
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "PROXY proxy4.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp; }</pre>
+
+<hr size="4">
+<h2>
+Example #5:
+<font size="+0">Setting a proxy for a specific protocol</font></h2>
+Most of the standard JavaScript functionality is available for use in the
+<tt>FindProxyForURL()</tt>
+function. As an example, to set different proxies based on the protocol,
+the <tt>substring()</tt> function can be used:
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; function FindProxyForURL(url, host)
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (url.substring(0, 5) == "http:") {
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY http-proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (url.substring(0, 4) == "ftp:") {
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY ftp-proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (url.substring(0, 7) == "gopher:") {
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY gopher-proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (url.substring(0, 6) == "https:" ||
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; url.substring(0, 6) == "snews:") {
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY security-proxy.mydomain.com:8080";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {
+
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "DIRECT";
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</pre>
+<b>Note:</b> The same can be accomplished using the
+<tt>shExpMatch()</tt>
+function described earlier; for example:
+<pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (shExpMatch(url, "http:*")) {
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "PROXY http-proxy.mydomain.com:8080;
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }
+&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ...</pre>
+
+<hr size="4">
+<h2>
+Tips</h2>
+
+<ul>
+<li>
+The autoconfig file can be output by a CGI script. This is useful e.g.
+when making the autoconfig file act differently based on the client IP
+address (the <tt>REMOTE_ADDR</tt> environment variable in CGI).</li>
+
+<li>
+Use of <tt>isInNet()</tt>, <tt>isResolvable()</tt> and
+<tt>dnsResolve()</tt>
+functions should be carefully considered, as they require DNS server to
+be consulted (whereas all other autoconfig related functions are mere string
+matching functions). If a proxy is used, the proxy will perform its own
+DNS lookup which would double the impact on the DNS server. Most of the
+time these functions are not necessary to achieve the desired result.</li>
+
+<br>&nbsp;
+<p>&nbsp;
+<br>&nbsp;
+<br>&nbsp;</p></ul>
+
+<hr size="4"><i>March 1996</i>
+<center>
+<p><!-- footer -->
+<table width="600" border="0" cellpadding="0" cellspacing="0">
+<tbody><tr>
+<td width="600" height="8"><hr size="1" noshade="noshade"></td></tr>
+<tr><td valign="top" align="left"><font size="-2" face="sans-serif, Arial, Helvetica"><a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/help.html" target="_top">Help</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/site_map.html" target="_top">Site&nbsp;Map</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/howtoget.html" target="_top">How&nbsp;to&nbsp;Get&nbsp;Netscape&nbsp;Products</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/ad.html" target="_top">Advertise&nbsp;With&nbsp;Us</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/addsite.html" target="_top">Add Site</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp;&nbsp;<a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/custom_browser.html" target="_top">Custom Browser Program</a></font></td></tr>
+<tr>
+<td colspan="1" width="600" height="8"></td>
+</tr>
+
+<tr>
+<td valign="top" align="left">
+<!-- Channels -->
+<font size="-2" face="sans-serif, Arial, Helvetica"><a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/autos.html" target="_top">Autos</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/business.html" target="_top">Business</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/computers_internet.html" target="_top">Computing&nbsp;&amp;&nbsp;Internet</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/entertainment.html" target="_top">Entertainment</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/kids_family.html" target="_top">Family</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/games.html" target="_top">Games</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/health.html" target="_top">Health</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/lifestyles.html" target="_top">Lifestyles</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/local.html" target="_top">Local</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/netscape.html" target="_top">Netscape</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/open_directory.html">Netscape&nbsp;Open&nbsp;Directory</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/news.html" target="_top">News</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/personalize_finance.html" target="_top">Personal&nbsp;Finance</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/real_estate.html" target="_top">Real Estate</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/education.html" target="_top">Research&nbsp;&amp;&nbsp;Learn</a>&nbsp;&nbsp;&nbsp;|&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/shopping.html" target="_top">Shopping</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/smallbiz.html" target="_top">Small Business</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/sports.html" target="_top">Sports</a>&nbsp;&nbsp;&nbsp;|&nbsp;&nbsp; <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/misc/nav_redir/channels/travel.html" target="_top">Travel</a></font></td></tr>
+</tbody></table>
+
+<table width="600" border="0" cellpadding="0" cellspacing="0">
+<tbody><tr><td colspan="1" width="600" height="8"></td></tr>
+<tr>
+<td colspan="5" valign="top" width="600" align="left">
+<font size="-2" face="sans-serif, Arial, Helvetica">
+© 1999 Netscape, All Rights Reserved. <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/legal_notices/index.html">Legal &amp; Privacy Notices</a><br>This site powered by <a href="http://web.archive.org/web/20060424005037/http://home.netscape.com/comprod/server_central/index.html" target="_top">Netscape SuiteSpot servers</a>.</font></td>
+</tr>
+</tbody></table>
+<!-- end footer -->
+</p></center>
+
+<br>&nbsp;
+<p><br>
+<br>
+<script language="Javascript">
+<!--
+
+// FILE ARCHIVED ON 20060424005037 AND RETRIEVED FROM THE
+// INTERNET ARCHIVE ON 20090507200437.
+// JAVASCRIPT APPENDED BY WAYBACK MACHINE, COPYRIGHT INTERNET ARCHIVE.
+// ALL OTHER CONTENT MAY ALSO BE PROTECTED BY COPYRIGHT (17 U.S.C.
+// SECTION 108(a)(3)).
+
+ var sWayBackCGI = "http://web.archive.org/web/20060424005037/";
+
+ function xResolveUrl(url) {
+ var image = new Image();
+ image.src = url;
+ return image.src;
+ }
+ function xLateUrl(aCollection, sProp) {
+ var i = 0;
+ for(i = 0; i < aCollection.length; i++) {
+ var url = aCollection[i][sProp]; if (typeof(url) == "string") {
+ if (url.indexOf("mailto:") == -1 &&
+ url.indexOf("javascript:") == -1
+ && url.length > 0) {
+ if(url.indexOf("http") != 0) {
+ url = xResolveUrl(url);
+ }
+ url = url.replace('.wstub.archive.org','');
+ aCollection[i][sProp] = sWayBackCGI + url;
+ }
+ }
+ }
+ }
+
+ xLateUrl(document.getElementsByTagName("IMG"),"src");
+ xLateUrl(document.getElementsByTagName("A"),"href");
+ xLateUrl(document.getElementsByTagName("AREA"),"href");
+ xLateUrl(document.getElementsByTagName("OBJECT"),"codebase");
+ xLateUrl(document.getElementsByTagName("OBJECT"),"data");
+ xLateUrl(document.getElementsByTagName("APPLET"),"codebase");
+ xLateUrl(document.getElementsByTagName("APPLET"),"archive");
+ xLateUrl(document.getElementsByTagName("EMBED"),"src");
+ xLateUrl(document.getElementsByTagName("BODY"),"background");
+ xLateUrl(document.getElementsByTagName("TD"),"background");
+ xLateUrl(document.getElementsByTagName("INPUT"),"src");
+ var forms = document.getElementsByTagName("FORM");
+ if (forms) {
+ var j = 0;
+ for (j = 0; j < forms.length; j++) {
+ f = forms[j];
+ if (typeof(f.action) == "string") {
+ if(typeof(f.method) == "string") {
+ if(typeof(f.method) != "post") {
+ f.action = sWayBackCGI + f.action;
+ }
+ }
+ }
+ }
+ }
+
+
+//-->
+</script>
+
+</p></body></html> \ No newline at end of file
diff --git a/misc/logo.png b/misc/logo.png
new file mode 100644
index 0000000..2578a28
--- /dev/null
+++ b/misc/logo.png
Binary files differ
diff --git a/misc/logo.xcf b/misc/logo.xcf
new file mode 100644
index 0000000..341b9ec
--- /dev/null
+++ b/misc/logo.xcf
Binary files differ
diff --git a/misc/splash.xcf b/misc/splash.xcf
new file mode 100644
index 0000000..be7f243
--- /dev/null
+++ b/misc/splash.xcf
Binary files differ
diff --git a/misc/stylesheet.css b/misc/stylesheet.css
new file mode 100644
index 0000000..51dd0b8
--- /dev/null
+++ b/misc/stylesheet.css
@@ -0,0 +1,50 @@
+/* Javadoc style sheet */
+
+/* Define colors, fonts and other style attributes here to override the defaults */
+
+/* Page background color */
+body { background-color: #FFFFFF; color:#000000 }
+
+/*
+ #B0DAFF Light blue text
+ #1279C6 Blue background
+ #04469D Darker blue background
+ #E86F00 Orange Text
+ #F1F1F1 Ligher gray
+*/
+
+/* Headings */
+h1 { font-size: 145%; color:#E86F00; }
+h2 { font-size: 145%; color:#E86F00; }
+
+
+/* Links */
+a:link{color:#1279C6; text-decoration:none; }
+a:visited {color:#1279C6; text-decoration:none; }
+/*a:focus {font-weight:bold; color:#04469D; text-decoration:underline; }*/
+a:hover {font-weight:bold; color:#1279C6; text-decoration:underline; }
+
+/* HR and table borders*/
+hr{border-style: solid; }
+table{border-style: solid; }
+th{border-style: none; }
+
+/* Table colors */
+.TableHeadingColor { background: #1279C6; color:#FFFFFF; }
+.TableSubHeadingColor { background: #1279C6; color:#FFFFFF; }
+.TableRowColor { background: #FFFFFF; color:#000000; }
+
+/* Font used in left-hand frame lists */
+.FrameTitleFont { font-size: 100%; font-family: Helvetica, Arial, sans-serif; color:#E86F00 }
+.FrameHeadingFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#E86F00 }
+.FrameItemFont { font-size: 90%; font-family: Helvetica, Arial, sans-serif; color:#E86F00 }
+
+/* Navigation bar fonts and colors */
+.NavBarCell1 { background-color:#1279C6; color:#000000}
+.NavBarCell1Rev { background-color:#04469D; color:#FFFFFF}
+.NavBarFont1 { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+.NavBarFont1Rev { font-family: Arial, Helvetica, sans-serif; color:#FFFFFF;}
+
+.NavBarCell2 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000}
+.NavBarCell3 { font-family: Arial, Helvetica, sans-serif; background-color:#FFFFFF; color:#000000}
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..051368e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,214 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>com.googlecode.vestige</groupId>
+ <artifactId>proxy_vole</artifactId>
+ <version>0.0.3-SNAPSHOT</version>
+ <packaging>jar</packaging>
+ <name>Proxy Vole</name>
+ <build>
+ <defaultGoal>install</defaultGoal>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.6</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <version>2.2-beta-5</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-clean-plugin</artifactId>
+ <version>2.4.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-ear-plugin</artifactId>
+ <version>2.4.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-ejb-plugin</artifactId>
+ <version>2.2.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-help-plugin</artifactId>
+ <version>2.1.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-install-plugin</artifactId>
+ <version>2.3.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.3.1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <version>2.7</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-plugin-plugin</artifactId>
+ <version>2.6</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-rar-plugin</artifactId>
+ <version>2.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.0</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-repository-plugin</artifactId>
+ <version>2.3</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <version>2.4.3</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-scm-plugin</artifactId>
+ <version>1.3</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-site-plugin</artifactId>
+ <version>2.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-source-plugin</artifactId>
+ <version>2.1.2</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.8</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-war-plugin</artifactId>
+ <version>2.1-beta-1</version>
+ </plugin>
+ <plugin>
+ <artifactId>maven-checkstyle-plugin</artifactId>
+ <version>2.6</version>
+ </plugin>
+ <plugin>
+ <groupId>org.riedelcastro</groupId>
+ <artifactId>gcupload-maven-plugin</artifactId>
+ <version>0.9</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>jaxb2-maven-plugin</artifactId>
+ <version>1.3</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.5</source>
+ <target>1.5</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ <extensions>
+ <extension>
+ <groupId>org.jvnet.wagon-svn</groupId>
+ <artifactId>wagon-svn</artifactId>
+ <version>1.9</version>
+ </extension>
+ </extensions>
+ </build>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <repositories>
+ <repository>
+ <id>googlecode</id>
+ <url>http://vestige.googlecode.com/svn/repo</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </repository>
+ </repositories>
+ <pluginRepositories>
+ <pluginRepository>
+ <id>googlecode</id>
+ <url>http://vestige.googlecode.com/svn/repo</url>
+ <releases>
+ <enabled>true</enabled>
+ </releases>
+ <snapshots>
+ <enabled>false</enabled>
+ </snapshots>
+ </pluginRepository>
+ </pluginRepositories>
+ <distributionManagement>
+ <repository>
+ <id>googlecode</id>
+ <url>svn:https://vestige.googlecode.com/svn/repo</url>
+ </repository>
+ <snapshotRepository>
+ <id>googlecode</id>
+ <url>svn:https://vestige.googlecode.com/svn/repo</url>
+ </snapshotRepository>
+ <!-- disable site because svn auto-props do not work -->
+ <!-- <site> -->
+ <!-- <id>googlecode</id> -->
+ <!-- <url>svn:https://vestige.googlecode.com/svn/site</url> -->
+ <!-- </site> -->
+ </distributionManagement>
+ <scm>
+ <connection>scm:svn:http://vestige.googlecode.com/svn/proxy_vole/trunk</connection>
+ <developerConnection>scm:svn:https://vestige.googlecode.com/svn/proxy_vole/trunk</developerConnection>
+ </scm>
+ <reporting>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-report-plugin</artifactId>
+ <version>2.5</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-project-info-reports-plugin</artifactId>
+ <version>2.2</version>
+ <reportSets>
+ <reportSet>
+ <reports>
+ <report>index</report>
+ </reports>
+ </reportSet>
+ </reportSets>
+ </plugin>
+ </plugins>
+ </reporting>
+ <dependencies>
+ <dependency>
+ <groupId>org.mozilla</groupId>
+ <artifactId>rhino</artifactId>
+ <version>1.7R4</version>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.8.2</version>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/src/main/java/com/btr/proxy/search/ProxySearch.java b/src/main/java/com/btr/proxy/search/ProxySearch.java
new file mode 100644
index 0000000..19db95f
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/ProxySearch.java
@@ -0,0 +1,258 @@
+package com.btr.proxy.search;
+
+import java.awt.GraphicsEnvironment;
+import java.net.ProxySelector;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.btr.proxy.search.browser.firefox.FirefoxProxySearchStrategy;
+import com.btr.proxy.search.browser.ie.IEProxySearchStrategy;
+import com.btr.proxy.search.desktop.DesktopProxySearchStrategy;
+import com.btr.proxy.search.desktop.gnome.GnomeProxySearchStrategy;
+import com.btr.proxy.search.desktop.kde.KdeProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.WinProxySearchStrategy;
+import com.btr.proxy.search.env.EnvProxySearchStrategy;
+import com.btr.proxy.search.java.JavaProxySearchStrategy;
+import com.btr.proxy.selector.misc.BufferedProxySelector;
+import com.btr.proxy.selector.misc.ProxyListFallbackSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.Logger.LogBackEnd;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Main class to setup and initialize the proxy detection system.<br/>
+ * This class can be used to select a proxy discovery strategy.<br/>
+ * Implements the "Builder" pattern.<br/>
+ * Use <code>addStrategy</code> to add one or more search strategies.<br/>
+ * If you are done call the <code>getProxySelector</code> method. <br/>
+ * Then the strategies are asked one after the other for a ProxySelector until
+ * an valid selector is found. <br/>
+ * <p>
+ * Invoke the static <code>getDefaultProxySearch</code> method to use a default search strategy.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxySearch implements ProxySearchStrategy {
+
+ private static final int DEFAULT_PAC_CACHE_SIZE = 20;
+
+ private static final long DEFAULT_PAC_CACHE_TTL = 1000*60*10; // 10 Minutes
+
+ private List<ProxySearchStrategy> strategies;
+ private int pacCacheSize;
+ private long pacCacheTTL;
+
+ /*****************************************************************************
+ * Types of proxy detection supported by the builder.
+ ****************************************************************************/
+
+ public enum Strategy {
+ /// Use the platform settings.
+ OS_DEFAULT,
+ /// Use the settings of the platforms default browser.
+ BROWSER,
+ /// Use Firefox settings
+ FIREFOX,
+ /// Use InternetExplorer settings
+ IE,
+ /// Use environment variables for proxy settings.
+ ENV_VAR,
+ /// Use windows default proxy settings.
+ WIN,
+ /// Use KDE desktop default proxy settings.
+ KDE,
+ /// Use KDE desktop default proxy settings.
+ GNOME,
+ /// Use Java Networking system properties
+ JAVA
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProxySearch() {
+ super();
+ this.strategies = new ArrayList<ProxySearchStrategy>();
+ this.pacCacheSize = DEFAULT_PAC_CACHE_SIZE;
+ this.pacCacheTTL = DEFAULT_PAC_CACHE_TTL;
+ }
+
+ /*************************************************************************
+ * Sets up a ProxySearch that uses a default search strategy suitable for
+ * every platform.
+ * @return a ProxySearch initialized with default settings.
+ ************************************************************************/
+
+ public static ProxySearch getDefaultProxySearch() {
+ ProxySearch s = new ProxySearch();
+
+ // Test if we are a server or a client.
+ boolean headless = GraphicsEnvironment.isHeadless();
+
+ if (headless) {
+ s.addStrategy(Strategy.JAVA);
+ s.addStrategy(Strategy.OS_DEFAULT);
+ s.addStrategy(Strategy.ENV_VAR);
+ } else {
+ s.addStrategy(Strategy.JAVA);
+ s.addStrategy(Strategy.BROWSER);
+ s.addStrategy(Strategy.OS_DEFAULT);
+ s.addStrategy(Strategy.ENV_VAR);
+ }
+ Logger.log(ProxySearch.class, LogLevel.TRACE, "Using default search priority: {0}", s);
+
+ return s;
+ }
+
+ /*************************************************************************
+ * Adds an search strategy to the list of proxy searches strategies.
+ * @param strategy the search strategy to add.
+ ************************************************************************/
+
+ public void addStrategy(Strategy strategy) {
+ switch (strategy) {
+ case OS_DEFAULT:
+ this.strategies.add(new DesktopProxySearchStrategy());
+ break;
+ case BROWSER:
+ this.strategies.add(getDefaultBrowserStrategy());
+ break;
+ case FIREFOX:
+ this.strategies.add(new FirefoxProxySearchStrategy());
+ break;
+ case IE:
+ this.strategies.add(new IEProxySearchStrategy());
+ break;
+ case ENV_VAR:
+ this.strategies.add(new EnvProxySearchStrategy());
+ break;
+ case WIN:
+ this.strategies.add(new WinProxySearchStrategy());
+ break;
+ case KDE:
+ this.strategies.add(new KdeProxySearchStrategy());
+ break;
+ case GNOME:
+ this.strategies.add(new GnomeProxySearchStrategy());
+ break;
+ case JAVA:
+ this.strategies.add(new JavaProxySearchStrategy());
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown strategy code!");
+ }
+ }
+
+ /*************************************************************************
+ * Sets the cache size of the PAC proxy selector cache.
+ * This defines the number of URLs that are cached together with the PAC
+ * script result. This improves performance because for URLs that are
+ * in the cache the script is not executed again.
+ * You have to set this before you add any strategies that may create a
+ * PAC script proxy selector.
+ * @param size of the cache. Set it to 0 to disable caching.
+ * @param ttl is the time to live of the cache entries as amount of milliseconds.
+ ************************************************************************/
+
+ public void setPacCacheSettings(int size, long ttl) {
+ this.pacCacheSize = size;
+ this.pacCacheTTL = ttl;
+ }
+
+ /*************************************************************************
+ * Gets the search strategy for the platforms default browser.
+ * @return a ProxySearchStrategy, null if no supported browser was found.
+ ************************************************************************/
+
+ private ProxySearchStrategy getDefaultBrowserStrategy() {
+ switch (PlatformUtil.getDefaultBrowser()) {
+ case IE:
+ return new IEProxySearchStrategy();
+ case FIREFOX:
+ return new FirefoxProxySearchStrategy();
+ }
+ return null;
+ }
+
+ /*************************************************************************
+ * Gets the proxy selector that will use the configured search order.
+ * @return a ProxySelector, null if none was found for the current
+ * builder configuration.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+ Logger.log(getClass(), LogLevel.TRACE, "Executing search strategies to find proxy selector");
+ for (ProxySearchStrategy strat : this.strategies) {
+ try {
+ ProxySelector selector = strat.getProxySelector();
+ if (selector != null) {
+ selector = installBufferingAndFallbackBehaviour(selector);
+ return selector;
+ }
+ } catch (ProxyException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Strategy {0} failed trying next one.", e);
+ // Ignore and try next strategy.
+ }
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * If it is PAC and we have caching enabled set it here.
+ * @param selector
+ * @return
+ ************************************************************************/
+
+ private ProxySelector installBufferingAndFallbackBehaviour(ProxySelector selector) {
+ if (selector instanceof PacProxySelector) {
+ if (this.pacCacheSize > 0) {
+ selector = new BufferedProxySelector(this.pacCacheSize, this.pacCacheTTL, selector);
+ }
+ selector = new ProxyListFallbackSelector(selector);
+ }
+ return selector;
+ }
+
+ /*************************************************************************
+ * toString
+ * @see java.lang.Object#toString()
+ ************************************************************************/
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder("Proxy search: ");
+ for (ProxySearchStrategy strat : this.strategies) {
+ sb.append(strat);
+ sb.append(" ");
+ }
+ return sb.toString();
+ }
+
+ /*************************************************************************
+ * For testing only. Will print the logging & proxy information to the console.
+ * @param args the command line arguments.
+ ************************************************************************/
+
+ public static void main(String[] args) {
+ ProxySearch ps = ProxySearch.getDefaultProxySearch();
+ Logger.setBackend(new LogBackEnd() {
+
+ public void log(Class<?> clazz, LogLevel loglevel, String msg,
+ Object... params) {
+ System.out.println(MessageFormat.format(msg, params));
+ }
+
+ public boolean isLogginEnabled(LogLevel logLevel) {
+ return true;
+ }
+ });
+ ps.getProxySelector();
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java
new file mode 100644
index 0000000..6f14c28
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/ProxySearchStrategy.java
@@ -0,0 +1,22 @@
+package com.btr.proxy.search;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Interface for a proxy search strategy.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public interface ProxySearchStrategy {
+
+ /*************************************************************************
+ * Gets the a ProxySelector found by applying the search strategy.
+ * @return a ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException;
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java
new file mode 100644
index 0000000..8517074
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProfileSource.java
@@ -0,0 +1,23 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+import java.io.IOException;
+
+/*****************************************************************************
+ * A profile source for Firefox profiles.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+interface FirefoxProfileSource {
+
+ /*************************************************************************
+ * Gets a profile folder found on the current system.
+ * If multiple profile folders are available the "default" profile is chosen.
+ * @return a profile folder.
+ * @throws IOException on error.
+ ************************************************************************/
+
+ public File getProfileFolder() throws IOException;
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java
new file mode 100644
index 0000000..30c3be1
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxProxySearchStrategy.java
@@ -0,0 +1,267 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.DesktopProxySearchStrategy;
+import com.btr.proxy.search.wpad.WpadProxySearchStrategy;
+import com.btr.proxy.selector.direct.NoProxySelector;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.fixed.FixedSocksSelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.PlatformUtil.Platform;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Loads the Firefox3 proxy settings from the users Firefox3 settings.
+ * This will load the file <i>prefs.js</i> that is located in the
+ * <p>
+ * <i>.mozilla/firefox/(profile)/</i> folder.
+ * </p>
+ *
+ * See <a href="https://developer.mozilla.org/En/Mozilla_Networking_Preferences">Mozilla_Networking_Preferences</a>
+ * for an explanation of the proxy settings.
+ * <p>
+ * The following settings are extracted from
+ * this file:
+ * </p>
+ * Some generic settings:<br/>
+ * <ul>
+ * <li><i>network.proxy.type</i> -> n/a = use system settings, 0 = direct, 1 = Fixed proxy settings, 2 = proxy script (PAC), 3 = also direct , 4 = auto detect (WPAD)</li>
+ * <li><i>network.proxy.share_proxy_settings</i> -> true = use same proxy for all protocols</li>
+ * <li><i>network.proxy.no_proxies_on</i> -> a comma separated ignore list. </li>
+ * <li><i>network.proxy.autoconfig_url</i> -> a URL to an proxy configuration script</li>
+ * </ul>
+ * Host names and ports per protocol are stored in the following settings:
+ * <ul>
+ * <li><i>network.proxy.http</i></li>
+ * <li><i>network.proxy.http_port</i></li>
+ * <li><i>network.proxy.ssl</i></li>
+ * <li><i>network.proxy.ssl_port</i></li>
+ * <li><i>network.proxy.ftp</i></li>
+ * <li><i>network.proxy.ftp_port</i></li>
+ * <li><i>network.proxy.gopher</i></li>
+ * <li><i>network.proxy.gopher_port</i></li>
+ * <li><i>network.proxy.socks</i></li>
+ * <li><i>network.proxy.socks_port</i></li>
+ * <li><i>network.proxy.socks_version</i> -> 4 or 5</li>
+ * </u>
+ * <p>
+ * Note that if there are more than one profile the first profile found will be used.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class FirefoxProxySearchStrategy implements ProxySearchStrategy {
+
+ private FirefoxProfileSource profileScanner;
+ private FirefoxSettingParser settingsParser;
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public FirefoxProxySearchStrategy() {
+ super();
+ if (PlatformUtil.getCurrentPlattform() == Platform.WIN) {
+ this.profileScanner = new WinFirefoxProfileSource();
+ } else {
+ this.profileScanner = new LinuxFirefoxProfileSource();
+ }
+ this.settingsParser = new FirefoxSettingParser();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the firefox
+ * proxy settings.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on file reading error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting Firefox settings.");
+
+ Properties settings = readSettings();
+
+ ProxySelector result = null;
+ int type = Integer.parseInt(settings.getProperty("network.proxy.type", "-1"));
+ switch (type) {
+ case -1: // Use system settings
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses system settings");
+ result = new DesktopProxySearchStrategy().getProxySelector();
+ break;
+ case 0: // Use no proxy
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 1: // Fixed settings
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses manual settings");
+ result = setupFixedProxySelector(settings);
+ break;
+ case 2: // PAC Script
+ String pacScriptUrl = settings.getProperty("network.proxy.autoconfig_url", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses script (PAC) {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ break;
+ case 3: // Backward compatibility to netscape.
+ Logger.log(getClass(), LogLevel.TRACE, "Netscape compability mode -> uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 4: // WPAD auto config
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses automatic detection (WPAD)");
+ result = new WpadProxySearchStrategy().getProxySelector();
+ break;
+ default:
+ break;
+ }
+
+ // Wrap in white list filter.
+ String noProxyList = settings.getProperty("network.proxy.no_proxies_on", null);
+ if (result != null && noProxyList != null && noProxyList.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox uses proxy bypass list for: {0}", noProxyList);
+ result = new ProxyBypassListSelector(noProxyList, result);
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Reads the settings file and stores all settings in a Properties map.
+ * @return the parsed settings.
+ * @throws ProxyException on read error.
+ ************************************************************************/
+
+ public Properties readSettings() throws ProxyException {
+ try {
+ Properties settings = this.settingsParser.parseSettings(this.profileScanner);
+ return settings;
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error parsing settings", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ installHttpProxy(ps, settings);
+ if (isProxyShared(settings)) {
+ installSharedProxy(ps);
+ } else {
+ installFtpProxy(ps, settings);
+ installSecureProxy(ps, settings);
+ installSocksProxy(ps, settings);
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installFtpProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ installSelectorForProtocol(ps, settings, "ftp");
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installHttpProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ installSelectorForProtocol(ps, settings, "http");
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @return
+ ************************************************************************/
+
+ private boolean isProxyShared(Properties settings) {
+ return Boolean.TRUE.toString().equals(settings.getProperty("network.proxy.share_proxy_settings", "false").toLowerCase());
+ }
+
+ /*************************************************************************
+ * @param ps
+ ************************************************************************/
+
+ private void installSharedProxy(ProtocolDispatchSelector ps) {
+ ProxySelector httpProxy = ps.getSelector("http");
+ if (httpProxy != null) {
+ ps.setFallbackSelector(httpProxy);
+ }
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSocksProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ String proxyHost = settings.getProperty("network.proxy.socks", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy.socks_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox socks proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("socks", new FixedSocksSelector(proxyHost, proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @param settings
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSecureProxy(ProtocolDispatchSelector ps,
+ Properties settings) throws NumberFormatException {
+ String proxyHost = settings.getProperty("network.proxy.ssl", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy.ssl_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox secure proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("https", new FixedProxySelector(proxyHost, proxyPort));
+ ps.setSelector("sftp", new FixedProxySelector(proxyHost, proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Installs a proxy selector for the given protocol when settings are
+ * available.
+ * @param ps a ProtocolDispatchSelector to configure.
+ * @param settings to read the config from.
+ * @param protocol to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSelectorForProtocol(ProtocolDispatchSelector ps,
+ Properties settings, String protocol) throws NumberFormatException {
+
+ String proxyHost = settings.getProperty("network.proxy."+protocol, null);
+ int proxyPort = Integer.parseInt(settings.getProperty("network.proxy."+protocol+"_port", "0"));
+ if (proxyHost != null && proxyPort != 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox "+protocol+" proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector(protocol, new FixedProxySelector(proxyHost, proxyPort));
+ }
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java
new file mode 100644
index 0000000..b793299
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/FirefoxSettingParser.java
@@ -0,0 +1,79 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+/*****************************************************************************
+ * Parser for the Firefox settings file.
+ * Will extract all relevant proxy settings form the configuration file.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class FirefoxSettingParser {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public FirefoxSettingParser() {
+ super();
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @param source of the Firefox profiles.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ public Properties parseSettings(FirefoxProfileSource source) throws IOException {
+ // Search settings folder
+ File profileFolder = source.getProfileFolder();
+
+ // Read settings from file
+ File settingsFile = new File(profileFolder, "prefs.js");
+
+ BufferedReader fin = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(settingsFile)));
+
+ Properties result = new Properties();
+ try {
+ String line = fin.readLine();
+ while (line != null) {
+ line = line.trim();
+ if (line.startsWith("user_pref(\"network.proxy")) {
+ line = line.substring(10, line.length()-2);
+ int index = line.indexOf(",");
+ String key = line.substring(0, index).trim();
+ if (key.startsWith("\"")) {
+ key = key.substring(1);
+ }
+ if (key.endsWith("\"")) {
+ key = key.substring(0, key.length()-1);
+ }
+ String value = line.substring(index+1).trim();
+ if (value.startsWith("\"")) {
+ value = value.substring(1);
+ }
+ if (value.endsWith("\"")) {
+ value = value.substring(0, value.length()-1);
+ }
+ result.put(key, value);
+ }
+ line = fin.readLine();
+ }
+ } finally {
+ fin.close();
+ }
+
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java
new file mode 100644
index 0000000..38b1553
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/LinuxFirefoxProfileSource.java
@@ -0,0 +1,46 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Searches for Firefox profile on an Linux / Unix base system.
+ * This will scan the <i>.mozilla</i> folder in the users home directory to find the
+ * profiles.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class LinuxFirefoxProfileSource implements FirefoxProfileSource {
+
+ /*************************************************************************
+ * Get profile folder for the Linux Firefox profile
+ ************************************************************************/
+
+ public File getProfileFolder() {
+ File userDir = new File(System.getProperty("user.home"));
+ File cfgDir = new File(userDir, ".mozilla"+File.separator+"firefox"+File.separator);
+ if (!cfgDir.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox settings folder not found!");
+ return null;
+ }
+ File[] profiles = cfgDir.listFiles();
+ if (profiles == null || profiles.length == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox settings folder not found!");
+ return null;
+ }
+ for (File p : profiles) {
+ if (p.getName().endsWith(".default")) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox settings folder is {0}", p);
+ return p;
+ }
+ }
+
+ // Fall back -> take the first one found.
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox settings folder is {0}", profiles[0]);
+ return profiles[0];
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java b/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java
new file mode 100644
index 0000000..9189585
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/firefox/WinFirefoxProfileSource.java
@@ -0,0 +1,71 @@
+package com.btr.proxy.search.browser.firefox;
+
+import java.io.File;
+import java.io.IOException;
+
+import com.btr.proxy.search.desktop.win.Win32ProxyUtils;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Finds the Firefox profile on Windows platforms.
+ * On Windows the profiles are located in the users appdata directory under:
+ * <p>
+ * <i>Mozilla\Firefox\Profiles\</i>
+ * </p>
+ * The location of the appdata folder is read from the windows registry.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+class WinFirefoxProfileSource implements FirefoxProfileSource {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WinFirefoxProfileSource() {
+ super();
+ }
+
+ /*************************************************************************
+ * Reads the current location of the app data folder from the registry.
+ * @return a path to the folder.
+ ************************************************************************/
+
+ private String getAppFolder() {
+ return new Win32ProxyUtils().readUserHomedir();
+ }
+
+ /*************************************************************************
+ * Get profile folder for the Windows Firefox profile
+ * @throws IOException on error.
+ ************************************************************************/
+
+ public File getProfileFolder() throws IOException {
+
+ File appDataDir = new File(getAppFolder());
+ File cfgDir = new File(appDataDir, "Mozilla"+File.separator+"Firefox"+File.separator+"Profiles");
+
+ if (!cfgDir.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox windows settings folder not found.");
+ return null;
+ }
+ File[] profiles = cfgDir.listFiles();
+ if (profiles == null || profiles.length == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Firefox windows settings folder not found.");
+ return null;
+ }
+ for (File p : profiles) {
+ if (p.getName().endsWith(".default")) {
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox windows settings folder is {0}.", p);
+ return p;
+ }
+ }
+
+ // Fall back -> take the first one found.
+ Logger.log(getClass(), LogLevel.TRACE, "Firefox windows settings folder is {0}.", profiles[0]);
+ return profiles[0];
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java b/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java
new file mode 100644
index 0000000..fb81d6f
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/ie/IELocalByPassFilter.java
@@ -0,0 +1,28 @@
+package com.btr.proxy.search.browser.ie;
+
+import java.net.URI;
+
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IELocalByPassFilter implements UriFilter {
+
+ /*************************************************************************
+ * accept
+ * @see com.btr.proxy.util.UriFilter#accept(java.net.URI)
+ ************************************************************************/
+
+ public boolean accept(URI uri) {
+ if (uri == null) {
+ return false;
+ }
+ String host = uri.getAuthority();
+ return host != null && !host.contains(".");
+ }
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java
new file mode 100644
index 0000000..2a9b078
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/browser/ie/IEProxySearchStrategy.java
@@ -0,0 +1,213 @@
+package com.btr.proxy.search.browser.ie;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.Win32IESettings;
+import com.btr.proxy.search.desktop.win.Win32ProxyUtils;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.selector.pac.UrlPacScriptSource;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Extracts the proxy settings for Microsoft Internet Explorer.
+ * The settings are read by invoking native Windows API methods.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IEProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * getProxySelector
+ * @see com.btr.proxy.search.ProxySearchStrategy#getProxySelector()
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting IE proxy settings");
+
+ Win32IESettings ieSettings = readSettings();
+
+ ProxySelector result = createPacSelector(ieSettings);
+ if (result == null) {
+ result = createFixedProxySelector(ieSettings);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * Loads the settings from the windows registry.
+ * @return WinIESettings containing all proxy settings.
+ ************************************************************************/
+
+ public Win32IESettings readSettings() {
+ Win32IESettings ieSettings = new Win32ProxyUtils().winHttpGetIEProxyConfigForCurrentUser();
+ return ieSettings;
+ }
+
+ /*************************************************************************
+ * Parses the settings and creates an PAC ProxySelector for it.
+ * @param ieSettings the IE settings to use.
+ * @return a PacProxySelector the selector or null.
+ ************************************************************************/
+
+ private PacProxySelector createPacSelector(Win32IESettings ieSettings) {
+ String pacUrl = null;
+
+ if (ieSettings.isAutoDetect()) {
+ Logger.log(getClass(), LogLevel.TRACE, "Autodetecting script URL.");
+ // This will take some time.
+ pacUrl = new Win32ProxyUtils().winHttpDetectAutoProxyConfigUrl(
+ Win32ProxyUtils.WINHTTP_AUTO_DETECT_TYPE_DHCP+
+ Win32ProxyUtils.WINHTTP_AUTO_DETECT_TYPE_DNS_A);
+ }
+ if (pacUrl == null) {
+ pacUrl = ieSettings.getAutoConfigUrl();
+ }
+ if (pacUrl != null && pacUrl.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "IE uses script: "+pacUrl);
+
+ // Fix for issue 9
+ // If the IE has a file URL and it only starts has 2 slashes,
+ // add a third so it can be properly converted to the URL class
+ if (pacUrl.startsWith("file://") && !pacUrl.startsWith("file:///")) {
+ pacUrl = "file:///" + pacUrl.substring(7);
+ }
+ return ProxyUtil.buildPacSelectorForUrl(pacUrl);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Parses the proxy settings into an ProxySelector.
+ * @param ieSettings the settings to use.
+ * @return a ProxySelector, null if no settings are set.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ private ProxySelector createFixedProxySelector(Win32IESettings ieSettings) throws ProxyException {
+ String proxyString = ieSettings.getProxy();
+ String bypassList = ieSettings.getProxyBypass();
+ if (proxyString == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE,
+ "IE uses manual settings: {0} with bypass list: {1}", proxyString, bypassList);
+
+ Properties p = parseProxyList(proxyString);
+
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ addSelectorForProtocol(p, "http", ps);
+ addSelectorForProtocol(p, "https", ps);
+ addSelectorForProtocol(p, "ftp", ps);
+ addSelectorForProtocol(p, "gopher", ps);
+ addSelectorForProtocol(p, "socks", ps);
+ addFallbackSelector(p, ps);
+
+ ProxySelector result = setByPassListOnSelector(bypassList, ps);
+ return result;
+ }
+
+ /*************************************************************************
+ * @param bypassList
+ * @param ps
+ * @return
+ ************************************************************************/
+
+ private ProxySelector setByPassListOnSelector(String bypassList,
+ ProtocolDispatchSelector ps) {
+ if (bypassList != null && bypassList.trim().length() > 0) {
+ ProxyBypassListSelector result;
+ if ("<local>".equals(bypassList.trim())) {
+ result = buildLocalBypassSelector(ps);
+ } else {
+ bypassList = bypassList.replace(';', ',');
+ result = new ProxyBypassListSelector(bypassList, ps);
+ }
+ return result;
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * @param ps
+ * @return
+ ************************************************************************/
+
+ private ProxyBypassListSelector buildLocalBypassSelector(
+ ProtocolDispatchSelector ps) {
+ List<UriFilter> localBypassFilter = new ArrayList<UriFilter>();
+ localBypassFilter.add(new IELocalByPassFilter());
+ return new ProxyBypassListSelector(localBypassFilter, ps);
+ }
+
+ /*************************************************************************
+ * Installs a fallback selector that is used whenever no protocol specific
+ * selector is defined.
+ * @param settings to take the proxy settings from.
+ * @param ps to install the created selector on.
+ ************************************************************************/
+
+ private void addFallbackSelector(Properties settings, ProtocolDispatchSelector ps) {
+ String proxy = settings.getProperty("default");
+ if (proxy != null) {
+ ps.setFallbackSelector(ProxyUtil.parseProxySettings(proxy));
+ }
+ }
+
+ /*************************************************************************
+ * Creates a selector for a given protocol. The proxy will be taken
+ * from the settings and installed on the dispatch selector.
+ * @param settings to take the proxy settings from.
+ * @param protocol to create a selector for.
+ * @param ps to install the created selector on.
+ ************************************************************************/
+
+ private void addSelectorForProtocol(Properties settings, String protocol, ProtocolDispatchSelector ps) {
+ String proxy = settings.getProperty(protocol);
+ if (proxy != null) {
+ FixedProxySelector protocolSelector = ProxyUtil.parseProxySettings(proxy);
+ ps.setSelector(protocol, protocolSelector);
+ }
+ }
+
+ /*************************************************************************
+ * Parses the proxy list and splits it by protocol.
+ * @param proxyString the proxy list string
+ * @return Properties with separated settings.
+ * @throws ProxyException on parse error.
+ ************************************************************************/
+
+ private Properties parseProxyList(String proxyString) throws ProxyException {
+ Properties p = new Properties();
+ if (proxyString.indexOf('=') == -1) {
+ p.setProperty("default", proxyString);
+ } else {
+ try {
+ proxyString = proxyString.replace(';', '\n');
+ p.load(new ByteArrayInputStream(proxyString.getBytes("ISO-8859-1")));
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR,
+ "Error reading IE settings as properties: {0}", e);
+
+ throw new ProxyException(e);
+ }
+ }
+ return p;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java
new file mode 100644
index 0000000..8a5a5d8
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/DesktopProxySearchStrategy.java
@@ -0,0 +1,69 @@
+package com.btr.proxy.search.desktop;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.desktop.gnome.GnomeProxySearchStrategy;
+import com.btr.proxy.search.desktop.kde.KdeProxySearchStrategy;
+import com.btr.proxy.search.desktop.osx.OsxProxySearchStrategy;
+import com.btr.proxy.search.desktop.win.WinProxySearchStrategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.PlatformUtil.Desktop;
+import com.btr.proxy.util.PlatformUtil.Platform;
+
+/*****************************************************************************
+ * This search provider will try to find out on which desktop platform we
+ * are running and then will initialize the default proxy search.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class DesktopProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Gets the default ProxySelector for the current platform.
+ * @return a ProxySelector, null if none is found.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ ProxySearchStrategy strategy = findDesktopSpecificStrategy();
+ return strategy == null? null : strategy.getProxySelector();
+ }
+
+ /*************************************************************************
+ * Determine the desktop and create a strategy for it.
+ * @return a desktop specific strategy, null if none was found.
+ ************************************************************************/
+
+ private ProxySearchStrategy findDesktopSpecificStrategy() {
+ Platform pf = PlatformUtil.getCurrentPlattform();
+ Desktop dt = PlatformUtil.getCurrentDesktop();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting system settings.");
+
+ ProxySearchStrategy strategy = null;
+
+ if (pf == Platform.WIN) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Windows.");
+ strategy = new WinProxySearchStrategy();
+ } else
+ if (dt == Desktop.KDE) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on KDE.");
+ strategy = new KdeProxySearchStrategy();
+ } else
+ if (dt == Desktop.GNOME) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Gnome.");
+ strategy = new GnomeProxySearchStrategy();
+ } else
+ if (dt == Desktop.MAC_OS) {
+ Logger.log(getClass(), LogLevel.TRACE, "We are running on Mac OSX.");
+ strategy = new OsxProxySearchStrategy();
+ }
+ return strategy;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java
new file mode 100644
index 0000000..240fb39
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/gnome/GnomeProxySearchStrategy.java
@@ -0,0 +1,353 @@
+package com.btr.proxy.search.desktop.gnome;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.selector.direct.NoProxySelector;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.EmptyXMLResolver;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Loads the Gnome proxy settings from the Gnome GConf settings.
+ * <p>
+ * The following settings are extracted from the configuration that is stored
+ * in <i>.gconf</i> folder found in the user's home directory:
+ * </p>
+ * <ul>
+ * <li><i>/system/http_proxy/use_http_proxy</i> -> bool used only by gnome-vfs </li>
+ * <li><i>/system/http_proxy/host</i> -> string "my-proxy.example.com" without "http://"</li>
+ * <li><i>/system/http_proxy/port</i> -> int</li>
+ * <li><i>/system/http_proxy/use_authentication</i> -> bool</li>
+ * <li><i>/system/http_proxy/authentication_user</i> -> string</li>
+ * <li><i>/system/http_proxy/authentication_password</i> -> string</li>
+ * <li><i>/system/http_proxy/ignore_hosts</i> -> list-of-string</li>
+ * <li><i>/system/proxy/mode</i> -> string THIS IS THE CANONICAL KEY; SEE BELOW</li>
+ * <li><i>/system/proxy/secure_host</i> -> string "proxy-for-https.example.com"</li>
+ * <li><i>/system/proxy/secure_port</i> -> int</li>
+ * <li><i>/system/proxy/ftp_host</i> -> string "proxy-for-ftp.example.com"</li>
+ * <li><i>/system/proxy/ftp_port</i> -> int</li>
+ * <li><i>/system/proxy/socks_host</i> -> string "proxy-for-socks.example.com"</li>
+ * <li><i>/system/proxy/socks_port</i> -> int</li>
+ * <li><i>/system/proxy/autoconfig_url</i> -> string "http://proxy-autoconfig.example.com"</li>
+ * </ul>
+ * <i>/system/proxy/mode</i> can be either:<br/>
+ * "none" -> No proxy is used<br/>
+ * "manual" -> The user's configuration values are used (/system/http_proxy/{host,port,etc.})<br/>
+ * "auto" -> The "/system/proxy/autoconfig_url" key is used <br/>
+ * <p>
+ * GNOME Proxy_configuration settings are explained
+ * <a href="http://en.opensuse.org/GNOME/Proxy_configuration">here</a> in detail
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class GnomeProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public GnomeProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the Gnome
+ * proxy settings.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on file reading error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting Gnome proxy settings");
+
+ Properties settings = readSettings();
+
+ String type = settings.getProperty("/system/proxy/mode");
+ ProxySelector result = null;
+ if (type == null) {
+ String useProxy = settings.getProperty("/system/http_proxy/use_http_proxy");
+ if (useProxy == null) {
+ return null;
+ }
+ type = Boolean.parseBoolean(useProxy)?"manual":"none";
+ }
+
+ if ("none".equals(type)) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses no proxy");
+ result = NoProxySelector.getInstance();
+ }
+ if ("manual".equals(type)) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses manual proxy settings");
+ result = setupFixedProxySelector(settings);
+ }
+ if ("auto".equals(type)) {
+ String pacScriptUrl = settings.getProperty("/system/proxy/autoconfig_url", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses autodetect script {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ }
+
+ // Wrap into white-list filter?
+ String noProxyList = settings.getProperty("/system/http_proxy/ignore_hosts", null);
+ if (result != null && noProxyList != null && noProxyList.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome uses proxy bypass list: {0}", noProxyList);
+ result = new ProxyBypassListSelector(noProxyList, result);
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Load the proxy settings from the gconf settings XML file.
+ * @return the loaded settings stored in a properties object.
+ * @throws ProxyException on processing error.
+ ************************************************************************/
+
+ public Properties readSettings() throws ProxyException {
+ Properties settings = new Properties();
+ try {
+ parseSettings("/system/proxy/", settings);
+ parseSettings("/system/http_proxy/", settings);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings file error.", e);
+ throw new ProxyException(e);
+ }
+ return settings;
+ }
+
+ /*************************************************************************
+ * Finds the Gnome GConf settings file.
+ * @param context the gconf context to parse.
+ * @return a file or null if does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile(String context) {
+ // Normally we should inspect /etc/gconf/<version>/path to find out where the actual file is.
+ // But for normal systems this is always stored in .gconf folder in the user's home directory.
+ File userDir = new File(System.getProperty("user.home"));
+
+ // Build directory path for context
+ StringBuilder path = new StringBuilder();
+ String[] parts = context.split("/");
+ for (String part : parts) {
+ path.append(part);
+ path.append(File.separator);
+ }
+
+ File settingsFile = new File(userDir, ".gconf"+File.separator+path.toString()+"%gconf.xml");
+ if (!settingsFile.exists()) {
+ Logger.log(getClass(), LogLevel.WARNING, "Gnome settings: {0} not found.", settingsFile);
+ return null;
+ }
+ return settingsFile;
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ if (!hasProxySettings(settings)) {
+ return null;
+ }
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ installHttpSelector(settings, ps);
+
+ if (useForAllProtocols(settings)) {
+ ps.setFallbackSelector(ps.getSelector("http"));
+ } else {
+ installSecureSelector(settings, ps);
+ installFtpSelector(settings, ps);
+ installSocksSelector(settings, ps);
+ }
+ return ps;
+ }
+
+ /*************************************************************************
+ * Check if the http proxy should also be used for all other protocols.
+ * @param settings to inspect.
+ * @return true if only one proxy is configured else false.
+ ************************************************************************/
+
+ private boolean useForAllProtocols(Properties settings) {
+ return Boolean.parseBoolean(
+ settings.getProperty("/system/http_proxy/use_same_proxy", "false"));
+ }
+
+ /*************************************************************************
+ * Checks if we have Proxy configuration settings in the properties.
+ * @param settings to inspect.
+ * @return true if we have found Proxy settings.
+ ************************************************************************/
+
+ private boolean hasProxySettings(Properties settings) {
+ String proxyHost = settings.getProperty("/system/http_proxy/host", null);
+ return proxyHost != null && proxyHost.length() > 0;
+ }
+
+ /*************************************************************************
+ * Install a http proxy from the given settings.
+ * @param settings to inspect
+ * @param ps the dispatch selector to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installHttpSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/http_proxy/host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/http_proxy/port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome http proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("http", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Install a socks proxy from the given settings.
+ * @param settings to inspect
+ * @param ps the dispatch selector to configure.
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installSocksSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/socks_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/socks_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome socks proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("socks", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void installFtpSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/ftp_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/ftp_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome ftp proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("ftp", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * @param settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+
+ private void installSecureSelector(Properties settings,
+ ProtocolDispatchSelector ps) throws NumberFormatException {
+ String proxyHost = settings.getProperty("/system/proxy/secure_host", null);
+ int proxyPort = Integer.parseInt(settings.getProperty("/system/proxy/secure_port", "0").trim());
+ if (proxyHost != null && proxyHost.length() > 0 && proxyPort > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Gnome secure proxy is {0}:{1}", proxyHost, proxyPort);
+ ps.setSelector("https", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ ps.setSelector("sftp", new FixedProxySelector(proxyHost.trim(), proxyPort));
+ }
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @param context the gconf context to parse.
+ * @param settings the settings object to fill.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ private Properties parseSettings(String context, Properties settings) throws IOException {
+
+ // Read settings from file
+ File settingsFile = findSettingsFile(context);
+ if (settingsFile == null) {
+ return settings;
+ }
+
+ try {
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ documentBuilder.setEntityResolver(new EmptyXMLResolver());
+ Document doc = documentBuilder.parse(settingsFile);
+ Element root = doc.getDocumentElement();
+ Node entry = root.getFirstChild();
+ while (entry != null) {
+ if ("entry".equals(entry.getNodeName()) && entry instanceof Element) {
+ String entryName = ((Element)entry).getAttribute("name");
+ settings.setProperty(context+entryName, getEntryValue((Element) entry));
+ }
+ entry = entry.getNextSibling();
+ }
+ } catch (SAXException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings parse error", e);
+ throw new IOException(e.getMessage());
+ } catch (ParserConfigurationException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Gnome settings parse error", e);
+ throw new IOException(e.getMessage());
+ }
+
+ return settings;
+ }
+
+ /*************************************************************************
+ * Parse an entry value from a given entry node.
+ * @param entry the XML node to inspect.
+ * @return the value, null if it has no value.
+ ************************************************************************/
+
+ private String getEntryValue(Element entry) {
+ String type = entry.getAttribute("type");
+
+ if ("int".equals(type) || "bool".equals(type)) {
+ return entry.getAttribute("value");
+ }
+ if ("string".equals(type)) {
+ NodeList list = entry.getElementsByTagName("stringvalue");
+ if (list.getLength() > 0) {
+ return list.item(0).getTextContent();
+ }
+ }
+ if ("list".equals(type)) {
+ StringBuilder result = new StringBuilder();
+ NodeList list = entry.getElementsByTagName("li");
+
+ // Build comma separated list of items
+ for (int i = 0; i < list.getLength(); i++) {
+ if (result.length() > 0) {
+ result.append(",");
+ }
+ result.append(getEntryValue((Element) list.item(i)));
+ }
+ return result.toString();
+ }
+ return null;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java b/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java
new file mode 100644
index 0000000..6e1b425
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/gnome/ProxySchemasGSettingsAccess.java
@@ -0,0 +1,60 @@
+package com.btr.proxy.search.desktop.gnome;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+public class ProxySchemasGSettingsAccess {
+
+ private static void closeStream(Closeable c) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ // Ignore cleanup errors
+ }
+ }
+
+ static void copy(InputStream source, OutputStream dest) throws IOException {
+ try {
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ while (read >= 0) {
+ dest.write(buffer, 0, read);
+ read = source.read(buffer);
+ }
+ dest.flush();
+ } finally {
+ closeStream(source);
+ closeStream(dest);
+ }
+ }
+
+ static {
+ String arch = System.getProperty("os.arch");
+ if (arch.equals("x86_64") || arch.equals("x64") || arch.equals("x86-64")) {
+ arch = "amd64";
+ }
+ if (arch.equals("i386") || arch.equals("i686")) {
+ arch = "x86";
+ }
+ String libName = "gsettings-" + arch + ".so";
+ File libFile;
+ try {
+ InputStream source = ProxySchemasGSettingsAccess.class.getResourceAsStream("/lib/" + libName);
+ libFile = File.createTempFile("gsettings", ".so");
+ libFile.deleteOnExit();
+ FileOutputStream destination = new FileOutputStream(libFile);
+ copy(source, destination);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ System.load(libFile.getAbsolutePath());
+ }
+
+ public static native Map<String, Map<String, Object>> getValueByKeyBySchema();
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java
new file mode 100644
index 0000000..e05d72e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/kde/KdeProxySearchStrategy.java
@@ -0,0 +1,198 @@
+package com.btr.proxy.search.desktop.kde;
+
+import java.io.IOException;
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.env.EnvProxySearchStrategy;
+import com.btr.proxy.search.wpad.WpadProxySearchStrategy;
+import com.btr.proxy.selector.direct.NoProxySelector;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.selector.pac.UrlPacScriptSource;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.selector.whitelist.UseProxyWhiteListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Loads the KDE4 proxy settings from the KDE <i>kioslaverc</i> file.
+ * This will load properties from the file
+ * <p>
+ * <i>.kde/share/config/kioslaverc</i>
+ * </P>
+ * starting from the current users home directory.
+ * <p>
+ * The following settings are extracted from the section "[Proxy Settings]":
+ * </p>
+ * <ul>
+ * <li><i>AuthMode</i> -> 0 = no auth., 1 = use login.</li>
+ * <li><i>ProxyType</i> -> 0 = direct 1 = use fixed settings, 2 = use PAC, 3 = automatic (WPAD) , 4 = Use environment variables?</li>
+ * <li><i>Proxy Config</i> Script -> URL to PAC file</li>
+ * <li><i>ftpProxy</i> -> Fixed ftp proxy address e.g. http://www.ftp-proxy.com:8080</li>
+ * <li><i>httpProxy</i> -> Fixed http proxy e.g http://www.http-proxy.com:8080</li>
+ * <li><i>httpsProxy</i> -> Fixed https proxy e.g http://www.https-proxy.com:8080</li>
+ * <li><i>NoProxyFor</i> -> Proxy white list</li>
+ * <li><i>ReversedException</i> -> false = use NoProxyFor, true = revert meaning of the NoProxyFor list</li>
+ * </ul>
+ *
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class KdeProxySearchStrategy implements ProxySearchStrategy {
+
+ private KdeSettingsParser settingsParser;
+
+ /*************************************************************************
+ * ProxySelector using the given parser.
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public KdeProxySearchStrategy() {
+ this(new KdeSettingsParser());
+ }
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public KdeProxySearchStrategy(KdeSettingsParser settingsParser) {
+ super();
+ this.settingsParser = settingsParser;
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the firefox
+ * proxy settings.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on file reading error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting Kde proxy settings");
+
+ Properties settings = readSettings();
+ if (settings == null) {
+ return null;
+ }
+
+ ProxySelector result = null;
+ int type = Integer.parseInt(settings.getProperty("ProxyType", "-1"));
+ switch (type) {
+ case 0: // Use no proxy
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses no proxy");
+ result = NoProxySelector.getInstance();
+ break;
+ case 1: // Fixed settings
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses manual proxy settings");
+ result = setupFixedProxySelector(settings);
+ break;
+ case 2: // PAC Script
+ String pacScriptUrl = settings.getProperty("Proxy Config Script", "");
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses autodetect script {0}", pacScriptUrl);
+ result = ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ break;
+ case 3: // WPAD
+ Logger.log(getClass(), LogLevel.TRACE, "Kde uses WPAD to detect the proxy");
+ result = new WpadProxySearchStrategy().getProxySelector();
+ break;
+ case 4: // Use environment variables
+ Logger.log(getClass(), LogLevel.TRACE, "Kde reads proxy from environment");
+ result = setupEnvVarSelector(settings);
+ break;
+ default:
+ break;
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Reads the settings and stores them in a properties map.
+ * @return the parsed settings.
+ * @throws ProxyException
+ ************************************************************************/
+
+ private Properties readSettings() throws ProxyException {
+ try {
+ return this.settingsParser.parseSettings();
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Can't parse settings.", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Builds an environment variable selector.
+ * @param settings the settings to read from.
+ * @return the ProxySelector using environment variables.
+ ************************************************************************/
+
+ private ProxySelector setupEnvVarSelector(Properties settings) {
+ ProxySelector result;
+ result = new EnvProxySearchStrategy(
+ settings.getProperty("httpProxy"),
+ settings.getProperty("httpsProxy"),
+ settings.getProperty("ftpProxy"),
+ settings.getProperty("NoProxyFor")
+ ).getProxySelector();
+ return result;
+ }
+
+ /*************************************************************************
+ * Parse the fixed proxy settings and build an ProxySelector for this a
+ * chained configuration.
+ * @param settings the proxy settings to evaluate.
+ ************************************************************************/
+
+ private ProxySelector setupFixedProxySelector(Properties settings) {
+ String proxyVar = settings.getProperty("httpProxy", null);
+ FixedProxySelector httpPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (httpPS == null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde http proxy is {0}", proxyVar);
+ return null;
+ }
+
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ ps.setSelector("http", httpPS);
+
+ proxyVar = settings.getProperty("httpsProxy", null);
+ FixedProxySelector httpsPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (httpsPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde https proxy is {0}", proxyVar);
+ ps.setSelector("https", httpsPS);
+ }
+
+ proxyVar = settings.getProperty("ftpProxy", null);
+ FixedProxySelector ftpPS = ProxyUtil.parseProxySettings(proxyVar);
+ if (ftpPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde ftp proxy is {0}", proxyVar);
+ ps.setSelector("ftp", ftpPS);
+ }
+
+ // Wrap in white list filter.
+ String noProxyList = settings.getProperty("NoProxyFor", null);
+ if (noProxyList != null && noProxyList.trim().length() > 0) {
+ boolean reverse = "true".equals(settings.getProperty("ReversedException", "false"));
+ if (reverse) {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde proxy blacklist is {0}", noProxyList);
+ return new UseProxyWhiteListSelector(noProxyList, ps);
+ } else {
+ Logger.log(getClass(), LogLevel.TRACE, "Kde proxy whitelist is {0}", noProxyList);
+ return new ProxyBypassListSelector(noProxyList, ps);
+ }
+ }
+
+ return ps;
+ }
+
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java b/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java
new file mode 100644
index 0000000..904528e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/kde/KdeSettingsParser.java
@@ -0,0 +1,132 @@
+package com.btr.proxy.search.desktop.kde;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.Properties;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Parser for the KDE settings file.
+ * The KDE proxy settings are stored in the file:
+ * <p>
+ * <i>.kde/share/config/kioslaverc</i>
+ * </p>
+ * in the users home directory.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class KdeSettingsParser {
+
+ private File settingsFile;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public KdeSettingsParser() {
+ this(null);
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public KdeSettingsParser(File settingsFile) {
+ super();
+ this.settingsFile = settingsFile;
+ }
+
+ /*************************************************************************
+ * Parse the settings file and extract all network.proxy.* settings from it.
+ * @return the parsed properties.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ public Properties parseSettings() throws IOException {
+ // Search for existing settings.
+ if (this.settingsFile == null) {
+ this.settingsFile = findSettingsFile();
+ }
+ if (this.settingsFile == null) {
+ return null;
+ }
+
+ // Read settings from file.
+ BufferedReader fin = new BufferedReader(
+ new InputStreamReader(
+ new FileInputStream(this.settingsFile)));
+
+ Properties result = new Properties();
+ try {
+ String line = fin.readLine();
+
+ // Find section start.
+ while (line != null && !"[Proxy Settings]".equals(line.trim())) {
+ line = fin.readLine();
+ }
+ if (line == null) {
+ return result;
+ }
+
+ // Read full section
+ line = "";
+ while (line != null && !line.trim().startsWith("[")) {
+ line = line.trim();
+ int index = line.indexOf('=');
+ if (index > 0) {
+ String key = line.substring(0, index).trim();
+ String value = line.substring(index+1).trim();
+ result.setProperty(key, value);
+ }
+
+ line = fin.readLine();
+ }
+ } finally {
+ fin.close();
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Finds all the KDE network settings file.
+ * @return a file or null if does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile() {
+ File userDir = new File(System.getProperty("user.home"));
+ if ("4".equals(System.getenv("KDE_SESSION_VERSION"))) {
+ this.settingsFile = findSettingsFile(
+ new File(userDir, ".kde4"+File.separator+"share"+File.separator+"config"+File.separator+"kioslaverc"));
+ }
+ if (this.settingsFile == null) {
+ return findSettingsFile(
+ new File(userDir, ".kde"+File.separator+"share"+File.separator+"config"+File.separator+"kioslaverc"));
+ } else {
+ return this.settingsFile;
+ }
+ }
+
+ /*************************************************************************
+ * Internal method to test if the settings file is at the given place.
+ * @param settingsFile the path to test.
+ * @return the file or null if it does not exist.
+ ************************************************************************/
+
+ private File findSettingsFile(File settingsFile) {
+ Logger.log(getClass(), LogLevel.TRACE, "Searching Kde settings in {0}", settingsFile);
+ if (!settingsFile.exists()) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Settings not found");
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "Settings found");
+ return settingsFile;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java
new file mode 100644
index 0000000..3d67ae7
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/osx/OsxProxySearchStrategy.java
@@ -0,0 +1,325 @@
+package com.btr.proxy.search.desktop.osx;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.NetworkInterface;
+import java.net.ProxySelector;
+import java.net.SocketException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.List;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.browser.ie.IELocalByPassFilter;
+import com.btr.proxy.search.wpad.WpadProxySearchStrategy;
+import com.btr.proxy.selector.direct.NoProxySelector;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.fixed.FixedSocksSelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.PListParser;
+import com.btr.proxy.util.PListParser.Dict;
+import com.btr.proxy.util.PListParser.XmlParseException;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Loads the OSX system proxy settings from the settings file.
+ * <p>
+ * All settings are stored in OSX in a special XML file format.
+ * These settings file are named plist files and contain nested dictionaries, arrays and values.
+ * </p><p>
+ * To parse this file we use a parser that is derived from a plist parser that
+ * comes with the xmlwise XML parser package:
+ * </p><p>
+ * http://code.google.com/p/xmlwise/
+ * </p><p>
+ * I modified that parser to work with the default Java XML parsing library.
+ * </p><p>
+ * The plist file is located on OSX at:
+ * </p><p>
+ * /Library/Preferences/SystemConfiguration/preferences.plist
+ * </p>
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2011
+ ****************************************************************************/
+
+public class OsxProxySearchStrategy implements ProxySearchStrategy {
+
+ public static final String OVERRIDE_SETTINGS_FILE = "com.btr.proxy.osx.settingsFile";
+ public static final String OVERRIDE_ACCEPTED_DEVICES = "com.btr.proxy.osx.acceptedDevices";
+
+ private static final String SETTINGS_FILE = "/Library/Preferences/SystemConfiguration/preferences.plist";
+
+ /*************************************************************************
+ * ProxySelector
+ * @see java.net.ProxySelector#ProxySelector()
+ ************************************************************************/
+
+ public OsxProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings and initializes a proxy selector for the OSX
+ * proxy settings.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on file reading error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Detecting OSX proxy settings");
+
+ try {
+ List<String> acceptedInterfaces = getNetworkInterfaces();
+
+ Dict settings = PListParser.load(getSettingsFile());
+ Object currentSet = settings.getAtPath("/CurrentSet");
+ if (currentSet == null) {
+ throw new ProxyException("CurrentSet not defined");
+ }
+
+ Dict networkSet = (Dict) settings.getAtPath(String.valueOf(currentSet));
+ List<?> serviceOrder = (List<?>) networkSet.getAtPath("/Network/Global/IPv4/ServiceOrder");
+ if (serviceOrder == null || serviceOrder.size() == 0) {
+ throw new ProxyException("ServiceOrder not defined");
+ }
+
+ // Look at the Services in priority order and pick the first one that was
+ // also accepted above
+ Dict proxySettings = null;
+ for (int i = 0; i < serviceOrder.size() && proxySettings == null; i++) {
+ Object candidateService = serviceOrder.get(i);
+ Object networkService = networkSet.getAtPath("/Network/Service/"+candidateService+"/__LINK__");
+ if (networkService == null ) {
+ throw new ProxyException("NetworkService not defined.");
+ }
+ Dict selectedServiceSettings = (Dict) settings.getAtPath(""+networkService);
+ String interfaceName = (String) selectedServiceSettings.getAtPath("/Interface/DeviceName");
+ if (acceptedInterfaces.contains(interfaceName)) {
+ Logger.log(getClass(), LogLevel.TRACE, "Looking up proxies for device " + interfaceName);
+ proxySettings = (Dict) selectedServiceSettings.getAtPath("/Proxies");
+ }
+ }
+ if (proxySettings == null) {
+ return NoProxySelector.getInstance();
+ }
+
+ return buildSelector(proxySettings);
+ } catch (XmlParseException e) {
+ throw new ProxyException(e);
+ } catch (IOException e) {
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Build a selector from the given settings.
+ * @param proxySettings to parse
+ * @return the configured selector
+ * @throws ProxyException on error
+ ************************************************************************/
+
+ private ProxySelector buildSelector(Dict proxySettings) throws ProxyException {
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ installSelectorForProtocol(proxySettings, ps, "HTTP");
+ installSelectorForProtocol(proxySettings, ps, "HTTPS");
+ installSelectorForProtocol(proxySettings, ps, "FTP");
+ installSelectorForProtocol(proxySettings, ps, "Gopher");
+ installSelectorForProtocol(proxySettings, ps, "RTSP");
+ installSocksProxy(proxySettings, ps);
+
+ ProxySelector result = ps;
+ result = installPacProxyIfAvailable(proxySettings, result);
+ result = autodetectProxyIfAvailable(proxySettings, result);
+
+ result = installExceptionList(proxySettings, result);
+ result = installSimpleHostFilter(proxySettings, result);
+ return result;
+ }
+
+ /*************************************************************************
+ * Create a list of Ethernet interfaces that are connected
+ * @return
+ * @throws SocketException
+ ************************************************************************/
+
+ private List<String> getNetworkInterfaces() throws SocketException {
+ String override = System.getProperty(OVERRIDE_ACCEPTED_DEVICES);
+ if (override != null && override.length() > 0) {
+ return Arrays.asList(override.split(";"));
+ }
+
+ List<String> acceptedInterfaces = new ArrayList<String>();
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()) {
+ NetworkInterface ni = interfaces.nextElement();
+ if (isInterfaceAllowed(ni)) {
+ acceptedInterfaces.add(ni.getName());
+ }
+ }
+ return acceptedInterfaces;
+ }
+
+ /*************************************************************************
+ * Check if a given network interface is interesting for us.
+ * @param ni the interface to check
+ * @return true if accepted else false.
+ * @throws SocketException on error.
+ ************************************************************************/
+
+ private boolean isInterfaceAllowed(NetworkInterface ni) throws SocketException {
+ return !ni.isLoopback() &&
+ !ni.isPointToPoint() && // Not sure if we should filter the point to point interfaces?
+ !ni.isVirtual() &&
+ ni.isUp();
+ }
+
+ /*************************************************************************
+ * @return
+ ************************************************************************/
+
+ private File getSettingsFile() {
+ File result = new File(SETTINGS_FILE);
+ String overrideFile = System.getProperty(OVERRIDE_SETTINGS_FILE);
+ if (overrideFile != null) {
+ return new File(overrideFile);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * @param proxySettings
+ * @param result
+ * @return
+ ************************************************************************/
+
+ private ProxySelector installSimpleHostFilter(
+ Dict proxySettings, ProxySelector result) {
+ if (isActive(proxySettings.get("ExcludeSimpleHostnames"))) {
+ List<UriFilter> localBypassFilter = new ArrayList<UriFilter>();
+ localBypassFilter.add(new IELocalByPassFilter());
+ result = new ProxyBypassListSelector(localBypassFilter, result);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * @param proxySettings
+ * @param result
+ * @return
+ ************************************************************************/
+
+ private ProxySelector installExceptionList(
+ Dict proxySettings, ProxySelector result) {
+ List<?> proxyExceptions = (List<?>) proxySettings.get("ExceptionsList");
+ if (proxyExceptions != null && proxyExceptions.size() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "OSX uses proxy bypass list: {0}", proxyExceptions);
+ String noProxyList = toCommaSeparatedString(proxyExceptions);
+ result = new ProxyBypassListSelector(noProxyList, result);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * Convert a list to a comma separated list.
+ * @param proxyExceptions list of elements.
+ * @return a comma separated string of the list's content.
+ ************************************************************************/
+
+ private String toCommaSeparatedString(List<?> proxyExceptions) {
+ StringBuilder result = new StringBuilder();
+ for (Object object : proxyExceptions) {
+ if (result.length() > 0) {
+ result.append(",");
+ }
+ result.append(object);
+ }
+ return result.toString();
+ }
+
+ /*************************************************************************
+ * @param proxySettings
+ * @param result
+ * @return
+ * @throws ProxyException
+ ************************************************************************/
+
+ private ProxySelector autodetectProxyIfAvailable(
+ Dict proxySettings, ProxySelector result)
+ throws ProxyException {
+ if (isActive(proxySettings.get("ProxyAutoDiscoveryEnable"))) {
+ ProxySelector wp = new WpadProxySearchStrategy().getProxySelector();
+ if (wp != null) {
+ result = wp;
+ }
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * @param proxySettings
+ * @param result
+ * @return
+ ************************************************************************/
+
+ private ProxySelector installPacProxyIfAvailable(Dict proxySettings,
+ ProxySelector result) {
+ if (isActive(proxySettings.get("ProxyAutoConfigEnable"))) {
+ String url = (String) proxySettings.get("ProxyAutoConfigURLString");
+ result = ProxyUtil.buildPacSelectorForUrl(url);
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * Build a socks proxy and set it for the socks protocol.
+ * @param proxySettings to read the config values from.
+ * @param ps the ProtocolDispatchSelector to install the new proxy on.
+ ************************************************************************/
+
+ private void installSocksProxy(Dict proxySettings,
+ ProtocolDispatchSelector ps) {
+ if (isActive(proxySettings.get("SOCKSEnable"))) {
+ String proxyHost = (String) proxySettings.get("SOCKSProxy");
+ int proxyPort = (Integer) proxySettings.get("SOCKSPort");
+ ps.setSelector("socks", new FixedSocksSelector(proxyHost, proxyPort));
+ Logger.log(getClass(), LogLevel.TRACE, "OSX socks proxy is {0}:{1}", proxyHost, proxyPort);
+ }
+ }
+
+ /*************************************************************************
+ * Installs a proxy selector for the given protocoll on the ProtocolDispatchSelector
+ * @param proxySettings to read the config for the procotol from.
+ * @param ps the ProtocolDispatchSelector to install the new selector on.
+ * @param protocol to use.
+ ************************************************************************/
+
+ private void installSelectorForProtocol(Dict proxySettings,
+ ProtocolDispatchSelector ps, String protocol) {
+ String prefix = protocol.trim();
+ if (isActive(proxySettings.get(prefix+"Enable"))) {
+ String proxyHost = (String) proxySettings.get(prefix+"Proxy");
+ int proxyPort = (Integer) proxySettings.get(prefix+"Port");
+ FixedProxySelector fp = new FixedProxySelector(proxyHost, proxyPort);
+ ps.setSelector(protocol.toLowerCase(), fp);
+ Logger.log(getClass(), LogLevel.TRACE, "OSX uses for {0} the proxy {1}:{2}", protocol, proxyHost, proxyPort);
+ }
+ }
+
+ /*************************************************************************
+ * Checks if the given value is set to "on".
+ * @param value the value to test.
+ * @return true if it is set else false.
+ ************************************************************************/
+
+ private boolean isActive(Object value) {
+ return Integer.valueOf(1).equals(value);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java b/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java
new file mode 100644
index 0000000..8d67984
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/DLLManager.java
@@ -0,0 +1,171 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * This class provides some helper methods to work with the dll
+ * extracting /loading for windows.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+final class DLLManager {
+
+ private static final class TempDLLFileFilter implements FileFilter {
+ public boolean accept(File pathname) {
+ String name = pathname.getName();
+ return pathname.isFile() &&
+ name.startsWith(TEMP_FILE_PREFIX) &&
+ name.endsWith(DLL_EXTENSION);
+ }
+ }
+
+ public static final String LIB_DIR_OVERRIDE = "proxy_vole_lib_dir";
+
+ static final String TEMP_FILE_PREFIX = "proxy_vole";
+ static final String DLL_EXTENSION = ".dll";
+ static String LIB_NAME_BASE = "proxy_util_";
+ static final String DEFAULT_LIB_FOLDER = "lib";
+
+ /*************************************************************************
+ * Find the location of the native code dll file.
+ * @return the File pointing to the dll.
+ * @throws IOException on IO error.
+ ************************************************************************/
+
+ static File findLibFile() throws IOException {
+ String libName = buildLibName();
+ File libFile = getOverrideLibFile(libName);
+ if (libFile == null || libFile.exists() == false) {
+ libFile = getDefaultLibFile(libName);
+ }
+ if (libFile == null || libFile.exists() == false) {
+ libFile = extractToTempFile(libName);
+ }
+ return libFile;
+ }
+
+ /*************************************************************************
+ * Delete old temp files that may be there because they were extracted
+ * from the jar but could not be deleted on VM shutdown because they
+ * are still locked by windows.
+ * This is here to prevent a lot of temp dll files on disk.
+ ************************************************************************/
+
+ static void cleanupTempFiles() {
+ try {
+ String tempFolder = System.getProperty("java.io.tmpdir");
+ if (tempFolder == null || tempFolder.trim().length() == 0) {
+ return;
+ }
+ File fldr = new File(tempFolder);
+ File[] oldFiles = fldr.listFiles(new TempDLLFileFilter());
+ if (oldFiles == null) {
+ return;
+ }
+ for (File tmp : oldFiles) {
+ tmp.delete();
+ }
+ } catch (Exception e) {
+ Logger.log(DLLManager.class, LogLevel.DEBUG, "Error cleaning up temporary dll files. ", e);
+ }
+ }
+
+ /*************************************************************************
+ * @param libName
+ * @return
+ ************************************************************************/
+
+ private static File getDefaultLibFile(String libName) {
+ return new File(DEFAULT_LIB_FOLDER, libName);
+ }
+
+ /*************************************************************************
+ * Gets the file name that was overriden via system property.
+ * @param libName
+ * @return the file, null if it is not existing.
+ ************************************************************************/
+
+ private static File getOverrideLibFile(String libName) {
+ String libDir = System.getProperty(LIB_DIR_OVERRIDE);
+ if (libDir == null || libDir.trim().length() == 0) {
+ return null;
+ }
+ return new File(libDir, libName);
+ }
+
+ /*************************************************************************
+ * @param libName
+ * @return
+ * @throws IOException
+ * @throws FileNotFoundException
+ ************************************************************************/
+
+ static File extractToTempFile(String libName) throws IOException {
+ InputStream source = Win32ProxyUtils.class.getResourceAsStream("/"+DEFAULT_LIB_FOLDER+"/"+libName);
+ File tempFile = File.createTempFile(TEMP_FILE_PREFIX, DLL_EXTENSION);
+ tempFile.deleteOnExit();
+ FileOutputStream destination = new FileOutputStream(tempFile);
+ copy(source, destination);
+ return tempFile;
+ }
+
+ /*************************************************************************
+ * @param c a closeable to cleanup ignoring all errors.
+ ************************************************************************/
+
+ private static void closeStream(Closeable c) {
+ try {
+ c.close();
+ } catch (IOException e) {
+ // Ignore cleanup errors
+ }
+ }
+
+ /*************************************************************************
+ * Copies the content from source to destination.
+ * @param source
+ * @param dest
+ * @throws IOException
+ ************************************************************************/
+
+ static void copy(InputStream source, OutputStream dest)
+ throws IOException {
+ try {
+ byte[] buffer = new byte[1024];
+ int read = 0;
+ while (read >= 0) {
+ dest.write(buffer, 0, read);
+ read = source.read(buffer);
+ }
+ dest.flush();
+ } finally {
+ closeStream(source);
+ closeStream(dest);
+ }
+ }
+
+ /*************************************************************************
+ * @return the name of the dll valid for the current architecture.
+ ************************************************************************/
+
+ private static String buildLibName() {
+ String arch = "w32";
+ if(!System.getProperty("os.arch").equals("x86") ) {
+ arch = System.getProperty("os.arch");
+ }
+ return LIB_NAME_BASE + arch + DLL_EXTENSION;
+ }
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java b/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java
new file mode 100644
index 0000000..ef91a22
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/Win32IESettings.java
@@ -0,0 +1,68 @@
+package com.btr.proxy.search.desktop.win;
+
+/*****************************************************************************
+ * Proxy settings container used for the native methods.
+ * Will contain the Internet Explorer proxy settings as reported by windows
+ * WinHTTP API.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Win32IESettings {
+
+ private boolean autoDetect;
+ private String autoConfigUrl;
+ private String proxy;
+ private String proxyBypass;
+
+ /*************************************************************************
+ * Constructor
+ * @param autoDetect flag is autodetect is active or not.
+ * @param autoConfigUrl the URL for a PAC script
+ * @param proxy the proxy server selected
+ * @param proxyBypass the proxy bypass address list.
+ ************************************************************************/
+
+ public Win32IESettings(boolean autoDetect, String autoConfigUrl, String proxy, String proxyBypass) {
+ super();
+ this.autoDetect = autoDetect;
+ this.autoConfigUrl = autoConfigUrl;
+ this.proxy = proxy;
+ this.proxyBypass = proxyBypass;
+ }
+
+ /*************************************************************************
+ * @return Returns the autoDetect.
+ ************************************************************************/
+
+ public boolean isAutoDetect() {
+ return this.autoDetect;
+ }
+
+ /*************************************************************************
+ * @return Returns the autoConfigUrl.
+ ************************************************************************/
+
+ public String getAutoConfigUrl() {
+ return this.autoConfigUrl;
+ }
+
+ /*************************************************************************
+ * @return Returns the proxy.
+ ************************************************************************/
+
+ public String getProxy() {
+ return this.proxy;
+ }
+
+ /*************************************************************************
+ * @return Returns the proxyBypass.
+ ************************************************************************/
+
+ public String getProxyBypass() {
+ return this.proxyBypass;
+ }
+
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java b/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java
new file mode 100644
index 0000000..fb1f0b3
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/Win32ProxyUtils.java
@@ -0,0 +1,88 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.io.File;
+import java.io.IOException;
+
+/*****************************************************************************
+ * Defines the native methods used for windows to extract some system information.
+ * <p>
+ * This class will need some native code from the library proxy_util_"arch".dll.
+ * To load this library we use a three step algorithm as following:
+ * </p><P>
+ * First check the System property "proxy_vole_lib_dir" if it is set and
+ * it points to a folder where the dll is found than the dll from this
+ * folder is loaded as e.g. <i>"proxy_vole_lib_dir"/proxy_util_w32.dll</i>
+ * </p><p>
+ * Second we try to load the dll from the subfolder <i>lib</i> if that one exists.<br>
+ * Finally if we are inside of a jar file we need to extract the dll file
+ * to a temp-file because windows can not load dlls from a jar
+ * directly. This is a hack but it may work.
+ * </p><p>
+ * Please note that the file is named Win32ProxyUtils but has now also support
+ * for x64 architecture.
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Win32ProxyUtils {
+
+ public static final int WINHTTP_AUTO_DETECT_TYPE_DHCP = 0x00000001;
+ public static final int WINHTTP_AUTO_DETECT_TYPE_DNS_A = 0x00000002;
+
+
+ // Code for loading the windows native dll
+ static {
+ try {
+ File libFile = DLLManager.findLibFile();
+ System.load(libFile.getAbsolutePath());
+ DLLManager.cleanupTempFiles();
+ } catch (IOException e) {
+ throw new RuntimeException("Error loading dll"+e.getMessage(), e);
+ }
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public Win32ProxyUtils() {
+ super();
+ }
+
+ /*************************************************************************
+ * WinHTTP method to detect an PAC URL.
+ * @param mode the mode to use.
+ * @return the PAC URL, null if none was found.
+ ************************************************************************/
+
+ public native String winHttpDetectAutoProxyConfigUrl(int mode);
+
+ /*************************************************************************
+ * Gets the default windows proxy settings.
+ * The returned string will have the following format.
+ * TYPE PROXY | BYPASSLIST
+ * <p>
+ * e.g. DIRECT myproxy.mycompany.com:8080 | *.mycompany.com, localhost
+ * </p>
+ * @return a string containing all info, null if not found.
+ ************************************************************************/
+ // TODO Not implemented correctly in DLL yet.
+ native String winHttpGetDefaultProxyConfiguration();
+
+ /*************************************************************************
+ * Extracts the Internet Explorer proxy settings from the Windows system.
+ * @return a data structure containing all details, null on fail.
+ ************************************************************************/
+
+ public native Win32IESettings winHttpGetIEProxyConfigForCurrentUser();
+
+ /*************************************************************************
+ * Extracts the Internet Explorer proxy settings from the Windows system.
+ * @return a data structure containing all details, null on fail.
+ ************************************************************************/
+
+ public native String readUserHomedir();
+
+
+}
+
diff --git a/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java
new file mode 100644
index 0000000..1f26505
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/desktop/win/WinProxySearchStrategy.java
@@ -0,0 +1,52 @@
+package com.btr.proxy.search.desktop.win;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.browser.ie.IEProxySearchStrategy;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Extracts the proxy settings from the windows registry.
+ * This will read the windows system proxy settings.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WinProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WinProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * getProxySelector
+ * @see com.btr.proxy.search.ProxySearchStrategy#getProxySelector()
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ // TODO Rossi 08.05.2009 Implement this by using Win API calls.
+ // new Win32ProxyUtils().winHttpGetDefaultProxyConfiguration()
+ // Current fallback is to use the IE settings. This is better
+ // because the registry settings are most of the time not set.
+ // Some Windows server installations may use it though.
+ return new IEProxySearchStrategy().getProxySelector();
+ }
+
+ /*************************************************************************
+ * Loads the settings.
+ * @return a WinIESettings object containing all proxy settings.
+ ************************************************************************/
+
+ public Win32IESettings readSettings() {
+ return new IEProxySearchStrategy().readSettings();
+ }
+
+
+
+
+}
diff --git a/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java
new file mode 100644
index 0000000..37bd0a1
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/env/EnvProxySearchStrategy.java
@@ -0,0 +1,130 @@
+package com.btr.proxy.search.env;
+
+import java.net.ProxySelector;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Reads some environment variables and extracts the proxy settings from them.
+ * These variables are mainly set on linux / unix environments.
+ * The following variables are read per default:
+ * <ul>
+ * <li><i>http_proxy</i> -> This will be used for http / https</li>
+ * <li><i>https_proxy</i> -> Will be used for https, if not set then http_proxy is used instead.</li>
+ * <li><i>ftp_proxy</i> -> Used for FTP.</li>
+ * <li><i>no_proxy</i> -> a no proxy white list.</li>
+ * </ul>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class EnvProxySearchStrategy implements ProxySearchStrategy {
+
+ private String httpEnv;
+ private String httpsEnv;
+ private String ftpEnv;
+ private String noProxyEnv;
+
+ private String httpProxy;
+ private String httpsProxy;
+ private String ftpProxy;
+ private String noProxy;
+
+ /*************************************************************************
+ * Constructor
+ * Will use the default environment variables.
+ ************************************************************************/
+
+ public EnvProxySearchStrategy() {
+ this("http_proxy", "https_proxy", "ftp_proxy", "no_proxy");
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param httpEnv name of environment variable
+ * @param httpsEnv name of environment variable
+ * @param ftpEnv name of environment variable
+ * @param noProxyEnv name of environment variable
+ ************************************************************************/
+
+ public EnvProxySearchStrategy(String httpEnv, String httpsEnv, String ftpEnv, String noProxyEnv) {
+ super();
+ this.httpEnv = httpEnv;
+ this.httpsEnv = httpsEnv;
+ this.ftpEnv = ftpEnv;
+ this.noProxyEnv = noProxyEnv;
+
+ loadProxySettings();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from the system environment variables.
+ ************************************************************************/
+
+ private void loadProxySettings() {
+ this.httpProxy = System.getenv(this.httpEnv);
+ this.httpsProxy = System.getenv(this.httpsEnv);
+ this.ftpProxy = System.getenv(this.ftpEnv);
+ this.noProxy = System.getenv(this.noProxyEnv);
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ Properties result = new Properties();
+ result.setProperty(this.httpEnv, this.httpProxy);
+ result.setProperty(this.httpsEnv, this.httpsProxy);
+ result.setProperty(this.ftpEnv, this.ftpProxy);
+ result.setProperty(this.noProxyEnv, this.noProxy);
+ return result;
+ }
+
+
+ /*************************************************************************
+ * Loads the proxy settings from environment variables.
+ * @return a configured ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+
+ Logger.log(getClass(), LogLevel.TRACE, "Inspecting environment variables.");
+
+ // Check if http_proxy var is set.
+ ProxySelector httpPS = ProxyUtil.parseProxySettings(this.httpProxy);
+ if (httpPS == null) {
+ return null;
+ }
+
+ Logger.log(getClass(), LogLevel.TRACE, "Http Proxy is {0}", this.httpProxy);
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+ ps.setSelector("http", httpPS);
+
+ ProxySelector httpsPS = ProxyUtil.parseProxySettings(this.httpsProxy);
+ Logger.log(getClass(), LogLevel.TRACE, "Https Proxy is {0}", httpsPS == null? this.httpsProxy: httpsPS);
+ ps.setSelector("https", httpsPS != null? httpsPS: httpPS);
+
+ ProxySelector ftpPS = ProxyUtil.parseProxySettings(this.ftpProxy);
+ if (ftpPS != null) {
+ Logger.log(getClass(), LogLevel.TRACE, "Ftp Proxy is {0}", this.ftpProxy);
+ ps.setSelector("ftp", ftpPS);
+ }
+
+ // Wrap with white list support
+ ProxySelector result = ps;
+ if (this.noProxy != null && this.noProxy.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Using proxy bypass list: {0}", this.noProxy);
+ result = new ProxyBypassListSelector(this.noProxy, ps);
+ }
+
+ return result;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java
new file mode 100644
index 0000000..531ef94
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/java/JavaProxySearchStrategy.java
@@ -0,0 +1,133 @@
+package com.btr.proxy.search.java;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.fixed.FixedSocksSelector;
+import com.btr.proxy.selector.misc.ProtocolDispatchSelector;
+import com.btr.proxy.selector.whitelist.ProxyBypassListSelector;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Reads some java system properties and extracts the proxy settings from them.
+ * The following variables are read:
+ * <ul>
+ * <li><i>http.proxyHost</i> (default: none)</li>
+ * <li><i>http.proxyPort</i> (default: 80 if http.proxyHost specified)</li>
+ * <li><i>http.nonProxyHosts</i> (default: none)</li>
+ * </ul>
+ * <ul>
+ * <li><i>https.proxyHost</i> (default: none)</li>
+ * <li><i>https.proxyPort</i> (default: 443 if https.proxyHost specified)</li>
+ * </ul>
+ * <ul>
+ * <li><i>ftp.proxyHost</i> (default: none)</li>
+ * <li><i>ftp.proxyPort</i> (default: 80 if ftp.proxyHost specified)</li>
+ * <li><i>ftp.nonProxyHosts</i> (default: none)</li>
+ * </ul>
+ * <ul>
+ * <li><i>socksProxyHost</i></li>
+ * <li><i>socksProxyPort</i> (default: 1080)</li>
+ * </ul>
+ * <p>
+ * This is based on information found here: <br/>
+ * http://download.oracle.com/javase/6/docs/technotes/guides/net/proxies.html
+ * </p>
+ * If the "http.proxyHost" property is not set then the no proxy selector is setup
+ * This property is used as marker to signal that the System settings should be used.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class JavaProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ * Will use the default environment variables.
+ ************************************************************************/
+
+ public JavaProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from environment variables.
+ * @return a configured ProxySelector, null if none is found.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() {
+ ProtocolDispatchSelector ps = new ProtocolDispatchSelector();
+
+ if (!proxyPropertyPresent()) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "Using settings from Java System Properties");
+
+
+ setupProxyForProtocol(ps, "http", 80);
+ setupProxyForProtocol(ps, "https", 443);
+ setupProxyForProtocol(ps, "ftp", 80);
+ setupProxyForProtocol(ps, "ftps", 80);
+ setupSocktProxy(ps);
+
+ return ps;
+ }
+
+ /*************************************************************************
+ * @return true if the http.proxyHost is available as system property.
+ ************************************************************************/
+
+ private boolean proxyPropertyPresent() {
+ return System.getProperty("http.proxyHost") != null
+ && System.getProperty("http.proxyHost").trim().length() > 0;
+ }
+
+ /*************************************************************************
+ * Parse SOCKS settings
+ * @param ps
+ * @throws NumberFormatException
+ ************************************************************************/
+
+
+ private void setupSocktProxy(ProtocolDispatchSelector ps) {
+ String host = System.getProperty("socksProxyHost");
+ String port = System.getProperty("socksProxyPort", "1080");
+ if (host != null && host.trim().length() > 0) {
+ Logger.log(getClass(), LogLevel.TRACE, "Socks proxy {0}:{1} found", host, port);
+ ps.setSelector("socks", new FixedSocksSelector(host, Integer.parseInt(port)));
+ }
+ }
+
+ /*************************************************************************
+ * Parse properties for the given protocol.
+ * @param ps
+ * @param protocol
+ * @throws NumberFormatException
+ ************************************************************************/
+
+ private void setupProxyForProtocol(ProtocolDispatchSelector ps, String protocol, int defaultPort) {
+ String host = System.getProperty(protocol+".proxyHost");
+ String port = System.getProperty(protocol+".proxyPort", ""+defaultPort);
+ String whiteList = System.getProperty(protocol+".nonProxyHosts", "").replace('|', ',');
+
+ if ("https".equalsIgnoreCase(protocol)) { // This is dirty but https has no own property for it.
+ whiteList = System.getProperty("http.nonProxyHosts", "").replace('|', ',');
+ }
+
+ if (host == null || host.trim().length() == 0) {
+ return;
+ }
+
+ Logger.log(getClass(), LogLevel.TRACE, protocol.toUpperCase()+" proxy {0}:{1} found using whitelist: {2}", host, port, whiteList);
+
+ ProxySelector protocolSelector = new FixedProxySelector(host, Integer.parseInt(port));
+ if (whiteList.trim().length() > 0) {
+ protocolSelector = new ProxyBypassListSelector(whiteList, protocolSelector);
+ }
+
+ ps.setSelector(protocol, protocolSelector);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
new file mode 100644
index 0000000..cf01fd3
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategy.java
@@ -0,0 +1,234 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.Socket;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.net.UnknownHostException;
+import java.util.List;
+import java.util.Properties;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented.
+ * All others are missing.
+ * </p><p>
+ * For more information about WPAD:
+ * <a href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol">Web_Proxy_Autodiscovery_Protocol</a>
+ * </p><p>
+ * Outdated RFC draft:
+ * <a href="http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt">draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategy implements ProxySearchStrategy {
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategy() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file.
+ * The location of the PAC file is determined automatically.
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}", pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.", e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL.
+ * Attention: this detection method is known to have some severe security issues.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /** Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ # Dynamic resolv.conf(5) file for glibc resolver(3) generated by resolvconf(8)
+ # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE OVERWRITTEN
+ nameserver 127.0.1.1
+ search ruf.uni-freiburg.de lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ String[] addresses = null;
+ // using the 4th line of the file.
+ while ((input = br.readLine()) != null) {
+ if (input.startsWith("search")) {
+ // the first one is "search" and afterwards addresses are following.
+ addresses = input.substring(6).split(" ");
+ break;
+ }
+ }
+
+ if (addresses == null) {
+ addresses = new String[]{""};
+ }
+
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ if (index != -1) {
+ address = address.substring(index);
+ } else {
+ address = "";
+ }
+
+ // Try to connect to URL
+ try {
+ address = ".uni-freiburg.de";
+ URL lookupURL = new URL("http://wpad"+ address +"/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}", lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL.openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ if (address.length() == 0) {
+ break;
+ }
+ index = address.indexOf('.', 1);
+ } while (true);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG, "Searching per DHCP not supported yet.");
+ // TODO Rossi 28.04.2009 Not implemented yet.
+ return null;
+ }
+
+ // Main method for testing.
+ public static void main( String[] args ) throws IOException {
+ WpadProxySearchStrategy wPSS = new WpadProxySearchStrategy();
+// System.setProperty("com.btr.proxy.pac.overrideLocalIP", "10.0.0.1");
+ try {
+ ProxySelector pS = wPSS.getProxySelector();
+ ProxySelector.setDefault(pS);
+ List<Proxy> proxyList = pS.select(new URI("http://www.google.de"));
+
+ if (proxyList.isEmpty()) {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "ProxyList is empty!");
+ } else {
+ Logger.log(WpadProxySearchStrategy.class, LogLevel.INFO, "proxyList contains: {0}", proxyList.toString());
+ }
+ } catch (ProxyException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ } catch (URISyntaxException e) {
+ // TODO bjoern 28.10.2014 Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ URL test = new URL("http://www.google.de");
+ URLConnection uc = test.openConnection();
+ BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream()));
+
+ String inputLine;
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ Socket socket = new Socket("www.google.de", 80);
+ BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
+ bw.write("GET / HTTP/1.1\r\nHost: www.google.de\r\nConnection: close\r\nAccept-Encoding: *\r\n\r\n");
+ bw.flush();
+
+ br = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ while ((inputLine = br.readLine()) != null) {
+ System.out.println(inputLine);
+ }
+
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
new file mode 100644
index 0000000..bb56cc0
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/WpadProxySearchStrategyWithDHPC.java
@@ -0,0 +1,315 @@
+package com.btr.proxy.search.wpad;
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.InetAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketException;
+import java.net.URL;
+import java.net.UnknownHostException;
+import java.util.Properties;
+import java.util.Random;
+import java.util.StringTokenizer;
+
+import com.btr.proxy.search.ProxySearchStrategy;
+import com.btr.proxy.search.wpad.dhcp.DHCPMessage;
+import com.btr.proxy.search.wpad.dhcp.DHCPOptions;
+import com.btr.proxy.search.wpad.dhcp.DHCPSocket;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * Uses automatic proxy script search (WPAD) to find an PAC file automatically.
+ * <p>
+ * Note: at the moment only the DNS name guessing schema is implemented. All
+ * others are missing.
+ * </p>
+ * <p>
+ * For more information about WPAD: <a
+ * href="http://en.wikipedia.org/wiki/Web_Proxy_Autodiscovery_Protocol"
+ * >Web_Proxy_Autodiscovery_Protocol</a>
+ * </p>
+ * <p>
+ * Outdated RFC draft: <a href=
+ * "http://www.web-cache.com/Writings/Internet-Drafts/draft-ietf-wrec-wpad-01.txt"
+ * >draft-ietf-wrec-wpad-01.txt</a>
+ * </p>
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class WpadProxySearchStrategyWithDHPC implements ProxySearchStrategy {
+ DHCPSocket bindSocket = null;
+ byte hwaddr[] = new byte[16];
+ InetAddress serverIP;
+ int portNum;
+ boolean gSentinel;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public WpadProxySearchStrategyWithDHPC() {
+ super();
+ }
+
+ /*************************************************************************
+ * Loads the proxy settings from a PAC file. The location of the PAC file is
+ * determined automatically.
+ *
+ * @return a configured ProxySelector, null if none is found.
+ * @throws ProxyException
+ * on error.
+ ************************************************************************/
+
+ public ProxySelector getProxySelector() throws ProxyException {
+ try {
+ Logger.log(getClass(), LogLevel.TRACE, "Using WPAD to find a proxy");
+
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Logger.log(getClass(), LogLevel.TRACE, "PAC script url found: {0}",
+ pacScriptUrl);
+ return ProxyUtil.buildPacSelectorForUrl(pacScriptUrl);
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Error during WPAD search.",
+ e);
+ throw new ProxyException(e);
+ }
+ }
+
+ /*************************************************************************
+ * Loads the settings and stores them in a properties map.
+ *
+ * @return the settings.
+ ************************************************************************/
+
+ public Properties readSettings() {
+ try {
+ String pacScriptUrl = detectScriptUrlPerDHCP();
+ if (pacScriptUrl == null) {
+ pacScriptUrl = detectScriptUrlPerDNS();
+ }
+ if (pacScriptUrl == null) {
+ return null;
+ }
+ Properties result = new Properties();
+ result.setProperty("url", pacScriptUrl);
+ return result;
+ } catch (IOException e) {
+ // Ignore and return empty properties.
+ return new Properties();
+ }
+ }
+
+ /*************************************************************************
+ * Uses DNS to find the script URL. Attention: this detection method is
+ * known to have some severe security issues.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDNS() throws IOException {
+ String result = null;
+ // String fqdn = InetAddress.getLocalHost().getCanonicalHostName();
+
+ Logger.log(getClass(), LogLevel.TRACE, "Searching per DNS guessing.");
+ // Logger.log(getClass(), LogLevel.INFO, "fqdn: ", fqdn);
+
+ /**
+ * Reading address from "/etc/resolv.conf"; file looks like:
+ *
+ * # Dynamic resolv.conf(5) file for glibc resolver(3) generated by
+ * resolvconf(8) # DO NOT EDIT THIS FILE BY HAND -- YOUR CHANGES WILL BE
+ * OVERWRITTEN nameserver 127.0.1.1 search ruf.uni-freiburg.de
+ * lp.ruf.uni-freiburg.de
+ */
+
+ FileReader fr = new FileReader("/etc/resolv.conf");
+ BufferedReader br = new BufferedReader(fr);
+
+ String input;
+ // using the 4th line of the file.
+ br.readLine();
+ br.readLine();
+ br.readLine();
+ input = br.readLine();
+
+ String[] addresses = input.split(" ");
+ // the first one is "search" and afterwards addresses are following.
+
+ for (int i = 0; i < addresses.length; ++i) {
+ String address = addresses[i];
+ int index = -1;
+ do {
+ address = address.substring(index + 1);
+
+ // if we are already on TLD level then escape
+ if (address.indexOf('.') == -1) {
+ break;
+ }
+
+ // Try to connect to URL
+ try {
+ URL lookupURL = new URL("http://wpad." + address
+ + "/wpad.dat");
+ Logger.log(getClass(), LogLevel.TRACE, "Trying url: {0}",
+ lookupURL);
+
+ HttpURLConnection con = (HttpURLConnection) lookupURL
+ .openConnection(Proxy.NO_PROXY);
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept",
+ "application/x-ns-proxy-autoconfig");
+ if (con.getResponseCode() == 200) {
+ result = lookupURL.toString();
+ return result;
+ }
+ con.disconnect();
+ } catch (UnknownHostException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
+ // Not a real error, try next address
+ }
+ index = address.indexOf('.');
+ } while (index != -1);
+ }
+
+ return null;
+ }
+
+ /*************************************************************************
+ * Uses DHCP to find the script URL.
+ *
+ * @return the URL, null if not found.
+ ************************************************************************/
+
+ private String detectScriptUrlPerDHCP() {
+ Logger.log(getClass(), LogLevel.DEBUG,
+ "Searching per DHCP not supported yet.");
+
+ // create socket.
+ try {
+ // bindSocket = new DHCPSocket(DHCPMessage.CLIENT_PORT);
+ // --> Not able to use ports under 1024 without being root.
+ this.bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ }
+
+
+
+ return null;
+ }
+
+ // Sends DHCPDISCOVER Message and returns server message
+ private DHCPMessage SendDiscover() {
+ DHCPSocket bindSocket = null;
+ try {
+ bindSocket = new DHCPSocket(1068);
+ } catch (SocketException e1) {
+ // TODO bjoern 29.10.2014 Auto-generated catch block
+ e1.printStackTrace();
+ return null;
+ }
+
+ Random ranXid = new Random();
+ DHCPMessage messageOut = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ DHCPMessage messageIn = new DHCPMessage(DHCPMessage.BROADCAST_ADDR,
+ DHCPMessage.SERVER_PORT);
+ try {
+ // Specify type of message: 1 = request - message, 2 = reply - message
+ messageOut.setOp((byte) 1);
+ // set Hardware type: 1 = Ethernet, 6 = IEEE 802 Networks, ...
+ messageOut.setHtype((byte) 1); // 1 = ethernet
+ // set hardware address length: 6 for MAC address
+ messageOut.setHlen((byte) 6);
+ messageOut.setHops((byte) 0);
+ // set session ID for later comparing with incoming message.
+ messageOut.setXid(ranXid.nextInt());
+ messageOut.setSecs((short) 0);
+ messageOut.setFlags((short) 0x8000);
+ // set client hardware address, in this case my own hardware address of eth0.
+ messageOut.setChaddr(ChaddrToByte("D8:D3:85:80:8F:C9"));
+
+ byte[] opt = new byte[1];
+ byte[] wpadOpt = new byte[1];
+ opt[0] = DHCPMessage.DHCPDISCOVER;
+
+ // change message type
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_MESSAGE_TYPE, opt);
+ wpadOpt[0] = DHCPMessage.OPTION_DHCP_WPAD;
+ messageOut.setOption(DHCPOptions.OPTION_DHCP_PARAMETER_REQUEST_LIST, wpadOpt);
+// messageOut.setOption(DHCPOptions.OPTION_DHCP_IP_ADRESS_REQUESTED,
+// offerMessageIn.getYiaddr());
+
+ bindSocket.send(messageOut); // send DHCPREQUEST
+ System.out.println("Sending DHCPDISCOVER ...");
+ boolean sentinal = true;
+ int counter = 0;
+ while (sentinal) {
+ if (counter == 2) { return null; }
+ if (bindSocket.receive(messageIn)) {
+ if (messageOut.getXid() == messageIn.getXid()) {
+ sentinal = false;
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ } else {
+ bindSocket.send(messageOut);
+ counter++;
+ }
+ }
+ } catch (SocketException e) {
+ System.err.println(e);
+ return null;
+ } catch (IOException e) {
+ System.err.println(e);
+ return null;
+ } finally {
+ try {
+ bindSocket.close();
+ } catch (Exception e) {
+ // do nothing.
+ }
+ } // end catch
+ return messageIn;
+ }
+
+ // Main method for testing.
+ public static void main(String[] args) throws UnsupportedEncodingException {
+ WpadProxySearchStrategyWithDHPC wPSSDHCP = new WpadProxySearchStrategyWithDHPC();
+ DHCPMessage serverAnswer = wPSSDHCP.SendDiscover();
+
+ Logger.log(WpadProxySearchStrategyWithDHPC.class, LogLevel.INFO,
+ "DHCP serverAnswer: {0}", new String(serverAnswer.getOption(DHCPMessage.OPTION_DHCP_WPAD), "UTF-8"));
+ }
+
+ private byte[] ChaddrToByte(String inChaddr) {
+ StringTokenizer token = new StringTokenizer(inChaddr, ":");
+ Integer tempInt = new Integer(0);
+ byte outHwaddr[] = new byte[16];
+ int temp;
+ int i = 0;
+ while (i < 6) {
+ temp = tempInt.parseInt(token.nextToken(), 16);
+ outHwaddr[i] = (byte) temp;
+ i++;
+ }
+ return outHwaddr;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
new file mode 100644
index 0000000..86323d5
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPMessage.java
@@ -0,0 +1,880 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a DHCP Message.
+ *
+ * @author Jason Goldschmidt, Nick Stone and Simon Frankenberger
+ */
+public class DHCPMessage {
+ // -----------------------------------------------------------
+ // Constants
+ // -----------------------------------------------------------
+ /**
+ * Operation for a request
+ */
+ public static final byte OP_REQUEST = 1;
+
+ /**
+ * Operation for a reply
+ */
+ public static final byte OP_REPLY = 2;
+
+ /**
+ * Message Code representing a DHCPDISCOVER message
+ */
+ public static final byte DHCPDISCOVER = 1;
+
+ /**
+ * Message Code representing a DHCPOFFER message
+ */
+ public static final byte DHCPOFFER = 2;
+
+ /**
+ * Message Code representing a DHCPREQUEST message
+ */
+ public static final byte DHCPREQUEST = 3;
+
+ /**
+ * Message Code representing a DHCPDECLINE message
+ */
+ public static final byte DHCPDECLINE = 4;
+
+ /**
+ * Message Code representing a DHCPACK message
+ */
+ public static final byte DHCPACK = 5;
+
+ /**
+ * Message Code representing a DHCPNAK message
+ */
+ public static final byte DHCPNAK = 6;
+
+ /**
+ * Message Code representing a DHCPRELEASE message
+ */
+ public static final byte DHCPRELEASE = 7;
+
+ /**
+ * Message Code representing a DHCPINFORM message
+ */
+ public static final byte DHCPINFORM = 8;
+
+ /**
+ * Default DHCP client port
+ */
+ public static final int CLIENT_PORT = 68; // client port (by default)
+
+ /**
+ * Default DHCP server port
+ */
+ public static final int SERVER_PORT = 67; // server port (by default)
+
+ /**
+ * Broadcast Adress to send packets to
+ */
+ public static InetAddress BROADCAST_ADDR = null;
+
+ public static final byte OPTION_DHCP_WPAD = (byte) 252;
+
+
+
+ // -----------------------------------------------------------
+ // Fields defining a dhcp message
+ // -----------------------------------------------------------
+ /**
+ * Operation Code.<br>
+ * <br>
+ * Can either be {@link #OP_REQUEST} or {@link #OP_REPLY}.
+ */
+ private byte op;
+
+ /**
+ * Networktype as defined by
+ * <a href="http://tools.ietf.org/html/rfc1340#page-54">RFC1340 page 54</a>.
+ */
+ private byte htype;
+
+ /**
+ * Hardware address length (e.g. '6' for ethernet).
+ */
+ private byte hlen;
+
+ /**
+ * Client sets to zero, optionally used by relay-agents
+ * when booting via a relay-agent.
+ */
+ private byte hops;
+
+ /**
+ * Transaction ID, a random number chosen by the
+ * client, used by the client and server to associate
+ * messages and responses between a client and a
+ * server.
+ */
+ private int xid;
+
+ /**
+ * Filled in by client, seconds elapsed since client
+ * started trying to boot.
+ */
+ private short secs;
+
+ /**
+ * Flags for this message.<br>
+ * The leftmost bit is defined as the BROADCAST (B) flag.
+ */
+ private short flags;
+
+ /**
+ * Client IP address; filled in by client in
+ * DHCPREQUEST if verifying previously allocated
+ * configuration parameters.
+ */
+ private byte ciaddr[] = new byte[4];
+
+ /**
+ * 'your' (client) IP address.
+ */
+ private byte yiaddr[] = new byte[4];
+
+ /**
+ * IP address of next server to use in bootstrap;
+ * returned in DHCPOFFER, DHCPACK and DHCPNAK by
+ * server.
+ */
+ private byte siaddr[] = new byte[4];
+
+ /**
+ * Relay agent IP address, used in booting via a
+ * relay-agent.
+ */
+ private byte giaddr[] = new byte[4];
+
+ /**
+ * Client hardware address.
+ */
+ private byte chaddr[] = new byte[16];
+
+ /**
+ * Optional server host name, null terminated string.
+ */
+ private byte sname[] = new byte[64];
+
+ /**
+ * Boot file name, null terminated string; "generic"
+ * name or null in DHCPDISCOVER, fully qualified
+ * directory-path name in DHCPOFFER.
+ */
+ private byte file[] = new byte[128];
+
+ /**
+ * Internal representation of the given DHCP options.
+ */
+ private DHCPOptions optionsList = null;
+
+ /**
+ * global port variable for this message
+ */
+ private int gPort;
+
+ /**
+ * The destination IP-Adress of this message
+ */
+ private InetAddress destination_IP;
+
+ static {
+ try {
+ BROADCAST_ADDR = InetAddress.getByName("255.255.255.255");
+ // broadcast address(by default)
+ }
+ catch (UnknownHostException e) {
+ // Broadcast address must always exist
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Constructors
+ // -----------------------------------------------------------
+
+ /**
+ * Creates empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * the local subnet, binds to the default server port.
+ */
+ public DHCPMessage() {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage
+ *
+ * @param inMessage The message to be copied
+ */
+ public DHCPMessage(DHCPMessage inMessage) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server and port.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Copy constructor
+ * creates DHCPMessage from inMessage and sets server name.
+ *
+ * @param inMessage The message to be copied
+ * @param inServername The host name
+ */
+ public DHCPMessage(DHCPMessage inMessage, InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+
+ this.op = inMessage.getOp();
+ this.htype = inMessage.getHtype();
+ this.hlen = inMessage.getHlen();
+ this.hops = inMessage.getHops();
+ this.xid = inMessage.getXid();
+ this.secs = inMessage.getSecs();
+ this.flags = inMessage.getFlags();
+ this.ciaddr = inMessage.getCiaddr();
+ this.yiaddr = inMessage.getYiaddr();
+ this.siaddr = inMessage.getSiaddr();
+ this.giaddr = inMessage.getGiaddr();
+ this.chaddr = inMessage.getChaddr();
+ this.sname = inMessage.getSname();
+ this.file = inMessage.getFile();
+ this.optionsList.internalize(inMessage.getOptions());
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to a specified port.
+ *
+ * @param inServername The host name
+ * @param inPort The port number
+ */
+ public DHCPMessage(InetAddress inServername, int inPort) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to a specified host name,
+ * and binds to the default port.
+ *
+ * @param inServername The host name
+ */
+ public DHCPMessage(InetAddress inServername) {
+ initialize();
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object, sets the host to the broadcast address,
+ * and binds to a specified port.
+ *
+ * @param inPort The port number
+ */
+ public DHCPMessage(int inPort) {
+ initialize();
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to default host name, the
+ * local subnet, and bind to the default server port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ */
+ public DHCPMessage(byte[] ibuf) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername, int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to broadcast address,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inPort The port number
+ */
+ public DHCPMessage(byte ibuf[], int inPort) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = BROADCAST_ADDR;
+ this.gPort = inPort;
+ }
+
+ /**
+ * Creates an empty DHCPMessage object,
+ * initializes the object with a specified byte array containing
+ * DHCP message information, sets the host to specified host name,
+ * and binds to the specified port.
+ *
+ * @param ibuf The byte array to initialize DHCPMessage object
+ * @param inServername The hostname
+ */
+ public DHCPMessage(byte[] ibuf, InetAddress inServername) {
+ initialize();
+ internalize(ibuf);
+
+ this.destination_IP = inServername;
+ this.gPort = SERVER_PORT;
+ }
+
+ /**
+ * Creates a new DHCPMessage object from the giben DataInputStream.
+ *
+ * @param inStream The stream to read from
+ */
+ public DHCPMessage(DataInputStream inStream) {
+ initialize();
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ }
+ }
+
+ // -----------------------------------------------------------
+ // Methods
+ // -----------------------------------------------------------
+ /**
+ * Initializes datamembers in the constructors
+ * every empty DHCPMessage object will by default contain these params.
+ * Initializes options array from linked list form.
+ */
+ private void initialize() {
+ this.optionsList = new DHCPOptions();
+ }
+
+ /**
+ * Converts a DHCPMessage object to a byte array.
+ *
+ * @return A byte array with information from DHCPMessage object,
+ * ready to send.
+ */
+ public synchronized byte[] externalize() {
+ ByteArrayOutputStream outBStream = new ByteArrayOutputStream();
+ DataOutputStream outStream = new DataOutputStream(outBStream);
+
+ try {
+ outStream.writeByte(this.op);
+ outStream.writeByte(this.htype);
+ outStream.writeByte(this.hlen);
+ outStream.writeByte(this.hops);
+ outStream.writeInt(this.xid);
+ outStream.writeShort(this.secs);
+ outStream.writeShort(this.flags);
+ outStream.write(this.ciaddr, 0, 4);
+ outStream.write(this.yiaddr, 0, 4);
+ outStream.write(this.siaddr, 0, 4);
+ outStream.write(this.giaddr, 0, 4);
+ outStream.write(this.chaddr, 0, 16);
+ outStream.write(this.sname, 0, 64);
+ outStream.write(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ options = this.optionsList.externalize();
+ outStream.write(options, 0, 312);
+ } catch (IOException e) {
+ System.err.println(e);
+ }
+
+ // extract the byte array from the Stream
+ byte data[] = outBStream.toByteArray();
+
+ return data;
+ }
+
+ /**
+ * Convert a specified byte array containing a DHCP message into a
+ * DHCPMessage object.
+ *
+ * @param ibuff Byte array to convert to a DHCPMessage object
+ * @return A DHCPMessage object with information from byte array.
+ */
+
+ public synchronized DHCPMessage internalize(byte[] ibuff) {
+ ByteArrayInputStream inBStream =
+ new ByteArrayInputStream(ibuff, 0, ibuff.length);
+ DataInputStream inStream = new DataInputStream(inBStream);
+
+ try {
+ this.op = inStream.readByte();
+ this.htype = inStream.readByte();
+ this.hlen = inStream.readByte();
+ this.hops = inStream.readByte();
+ this.xid = inStream.readInt();
+ this.secs = inStream.readShort();
+ this.flags = inStream.readShort();
+ inStream.readFully(this.ciaddr, 0, 4);
+ inStream.readFully(this.yiaddr, 0, 4);
+ inStream.readFully(this.siaddr, 0, 4);
+ inStream.readFully(this.giaddr, 0, 4);
+ inStream.readFully(this.chaddr, 0, 16);
+ inStream.readFully(this.sname, 0, 64);
+ inStream.readFully(this.file, 0, 128);
+
+ byte[] options = new byte[312];
+ inStream.readFully(options, 0, 312);
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ this.optionsList.internalize(options);
+ }
+ catch (IOException e) {
+ System.err.println(e);
+ } // end catch
+
+ return this;
+ }
+
+ /**
+ * Set message Op code / message type.
+ *
+ * @param inOp message Op code / message type
+ */
+ public void setOp(byte inOp) {
+ this.op = inOp;
+ }
+
+ /**
+ * Set hardware address type.
+ *
+ * @param inHtype hardware address type
+ */
+ public void setHtype(byte inHtype) {
+ this.htype = inHtype;
+ }
+
+ /**
+ * Set hardware address length.
+ *
+ * @param inHlen hardware address length
+ */
+ public void setHlen(byte inHlen) {
+ this.hlen = inHlen;
+ }
+
+ /**
+ * Set hops field.
+ *
+ * @param inHops hops field
+ */
+ public void setHops(byte inHops) {
+ this.hops = inHops;
+ }
+
+ /**
+ * Set transaction ID.
+ *
+ * @param inXid transactionID
+ */
+ public void setXid(int inXid) {
+ this.xid = inXid;
+ }
+
+ /**
+ * Set seconds elapsed since client began address acquisition or
+ * renewal process.
+ *
+ * @param inSecs Seconds elapsed since client began address acquisition
+ * or renewal process
+ */
+ public void setSecs(short inSecs) {
+ this.secs = inSecs;
+ }
+
+ /**
+ * Set flags field.
+ *
+ * @param inFlags flags field
+ */
+ public void setFlags(short inFlags) {
+ this.flags = inFlags;
+ }
+
+ /**
+ * Set client IP address.
+ *
+ * @param inCiaddr client IP address
+ */
+ public void setCiaddr(byte[] inCiaddr) {
+ this.ciaddr = inCiaddr;
+ }
+
+ /**
+ * Set 'your' (client) IP address.
+ *
+ * @param inYiaddr 'your' (client) IP address
+ */
+ public void setYiaddr(byte[] inYiaddr) {
+ this.yiaddr = inYiaddr;
+ }
+
+ /**
+ * Set address of next server to use in bootstrap.
+ *
+ * @param inSiaddr address of next server to use in bootstrap
+ */
+ public void setSiaddr(byte[] inSiaddr) {
+ this.siaddr = inSiaddr;
+ }
+
+ /**
+ * Set relay agent IP address.
+ *
+ * @param inGiaddr relay agent IP address
+ */
+ public void setGiaddr(byte[] inGiaddr) {
+ this.giaddr = inGiaddr;
+ }
+
+ /**
+ * Set client harware address.
+ *
+ * @param inChaddr client hardware address
+ */
+ public void setChaddr(byte[] inChaddr) {
+ this.chaddr = inChaddr;
+ }
+
+ /**
+ * Set optional server host name.
+ *
+ * @param inSname server host name
+ */
+ public void setSname(byte[] inSname) {
+ this.sname = inSname;
+ }
+
+ /**
+ * Set boot file name.
+ *
+ * @param inFile boot file name
+ */
+ public void setFile(byte[] inFile) {
+ this.file = inFile;
+ }
+
+ /**
+ * Set message destination port.
+ *
+ * @param inPortNum port on message destination host
+ */
+ public void setPort(int inPortNum) {
+ this.gPort = inPortNum;
+ }
+
+ /**
+ * Set message destination IP
+ * @param inHost string representation of message destination IP or
+ * hostname
+ */
+ public void setDestinationHost(String inHost) {
+ try {
+ this.destination_IP = InetAddress.getByName(inHost);
+ }
+ catch (Exception e) {
+ System.err.println(e);
+ }
+ }
+
+ /**
+ * @return message Op code / message type.
+ */
+ public byte getOp() {
+ return this.op;
+ }
+
+ /**
+ * @return hardware address type.
+ */
+ public byte getHtype() {
+ return this.htype;
+ }
+
+ /**
+ * @return hardware address length.
+ */
+ public byte getHlen() {
+ return this.hlen;
+ }
+
+ /**
+ * @return hops field.
+ */
+ public byte getHops() {
+ return this.hops;
+ }
+
+ /**
+ * @return transaction ID.
+ */
+ public int getXid() {
+ return this.xid;
+ }
+
+ /**
+ * @return seconds elapsed since client began address
+ * acquisition or renewal process.
+ */
+ public short getSecs() {
+ return this.secs;
+ }
+
+ /**
+ * @return flags field.
+ */
+ public short getFlags() {
+ return this.flags;
+ }
+
+ /**
+ * @return client IP address.
+ */
+ public byte[] getCiaddr() {
+ return this.ciaddr;
+ }
+
+ /**
+ * @return 'your' (client) IP address.
+ */
+ public byte[] getYiaddr() {
+ return this.yiaddr;
+ }
+
+ /**
+ * @return address of next server to use in bootstrap.
+ */
+ public byte[] getSiaddr() {
+ return this.siaddr;
+ }
+
+ /**
+ * @return relay agent IP address.
+ */
+ public byte[] getGiaddr() {
+ return this.giaddr;
+ }
+
+ /**
+ * @return client harware address.
+ */
+ public byte[] getChaddr() {
+ return this.chaddr;
+ }
+
+ /**
+ * @return optional server host name.
+ */
+ public byte[] getSname() {
+ return this.sname;
+ }
+
+ /**
+ * @return boot file name.
+ */
+ public byte[] getFile() {
+ return this.file;
+ }
+
+ /**
+ * @return a byte array containing options
+ */
+ public byte[] getOptions() {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.externalize();
+ }
+
+ /**
+ * @return An interger representation of the message
+ * destination port
+ */
+ public int getPort() {
+ return this.gPort;
+ }
+
+ /**
+ * Get message destination hostname
+ *
+ * @return A string representing the hostname of the
+ * message destination server
+ */
+ public String getDestinationAddress() {
+ return this.destination_IP.getHostAddress();
+ }
+
+ /**
+ * Sets DHCP options in DHCPMessage. If option already exists
+ * then remove old option and insert a new one.
+ *
+ * @param inOptNum option number
+ * @param inOptionData option data
+ */
+ public void setOption(int inOptNum, byte[] inOptionData) {
+ this.optionsList.setOption((byte) inOptNum, inOptionData);
+ }
+
+ /**
+ * Returns specified DHCP option that matches the input code. Null is
+ * returned if option is not set.
+ *
+ * @param inOptNum option number
+ *
+ * @return the option matching input code
+ */
+ public byte[] getOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ return this.optionsList.getOption((byte) inOptNum);
+ }
+
+ /**
+ * Removes the specified DHCP option that matches the input code.
+ *
+ * @param inOptNum option number
+ */
+ public void removeOption(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+ this.optionsList.removeOption((byte) inOptNum);
+ }
+
+ /**
+ * Report whether or not the input option is set.
+ *
+ * @param inOptNum option number
+ *
+ * @return is the given option set?
+ */
+ public boolean IsOptSet(int inOptNum) {
+ if (this.optionsList == null) {
+ initialize();
+ }
+
+ return this.optionsList.contains((byte) inOptNum);
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
new file mode 100644
index 0000000..1a07efb
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPOptions.java
@@ -0,0 +1,235 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ * This class represents a linked list of options for a DHCP message.
+ * Its purpose is to ease option handling such as add, remove or change.
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ */
+public class DHCPOptions {
+ public static final int OPTION_PAD = 0;
+ public static final int OPTION_NETMASK = 1;
+ public static final int OPTION_TIME_OFFSET = 2;
+ public static final int OPTION_ROUTERS = 3;
+ public static final int OPTION_TIME_SERVERS = 4;
+ public static final int OPTION_NAME_SERVERS = 5;
+ public static final int OPTION_DNS_SERVERS = 6;
+ public static final int OPTION_LOG_SERVERS = 7;
+ public static final int OPTION_COOKIE_SERVERS = 8;
+ public static final int OPTION_LPR_SERVERS = 9;
+ public static final int OPTION_IMPRESS_SERVERS = 10;
+ public static final int OPTION_RESSOURCE_LOCATION_SERVERS = 11;
+ public static final int OPTION_HOSTNAME = 12;
+ public static final int OPTION_BOOT_FILESIZE = 13;
+ public static final int OPTION_MERIT_DUMPFILE = 14;
+ public static final int OPTION_DOMAIN_NAME = 15;
+ public static final int OPTION_SWAP_SERVER = 16;
+ public static final int OPTION_ROOT_PATH = 17;
+ public static final int OPTION_EXTENSIONS_PATH = 18;
+ public static final int OPTION_END = 255;
+
+ public static final int OPTION_IP_HOST_FORWARDING_ENABLE = 19;
+ public static final int OPTION_IP_HOST_NON_LOCAL_SOURCE_ROUTING_ENABLE = 20;
+ public static final int OPTION_IP_HOST_POLICY_FILTERS = 21;
+ public static final int OPTION_IP_HOST_MAXIMUM_DATAGRAM_REASSEMBLY_SIZE = 22;
+ public static final int OPTION_IP_HOST_DEFAULT_TTL = 23;
+ public static final int OPTION_IP_HOST_MTU_AGEING_TIMEOUT = 24;
+ public static final int OPTION_IP_HOST_MTU_PLATEAU_TABLE = 25;
+
+ public static final int OPTION_IP_INTERFACE_MTU = 26;
+ public static final int OPTION_IP_INTERFACE_ALL_SUBNETS_LOCAL_ENABLE = 27;
+ public static final int OPTION_IP_INTERFACE_BROADCAST_ADDRESS = 28;
+ public static final int OPTION_IP_INTERFACE_PERFORM_MASK_DISCOVERY_ENABLE = 29;
+ public static final int OPTION_IP_INTERFACE_MASK_SUPPLIER_ENABLE = 30;
+ public static final int OPTION_IP_INTERFACE_PERFORM_ROUTER_DISCOVERY_ENABLE = 31;
+ public static final int OPTION_IP_INTERFACE_ROUTER_SOLICITATION_ADDRESS = 32;
+ public static final int OPTION_IP_INTERFACE_STATIC_ROUTES = 33;
+
+ public static final int OPTION_LINK_TRAILER_ENCAPSULATION_ENABLE = 34;
+ public static final int OPTION_LINK_ARP_CACHE_TIMEOUT = 35;
+ public static final int OPTION_LINK_ETHERNET_ENCAPSULATION_ENABLE = 36;
+
+ public static final int OPTION_TCP_DEFAULT_TTL = 37;
+ public static final int OPTION_TCP_KEEP_ALIVE_INTERVAL = 38;
+ public static final int OPTION_TCP_KEEP_ALIVE_GERBAGE_ENABLE = 39;
+
+ public static final int OPTION_NIS_DOMAIN = 40;
+ public static final int OPTION_NIS_SERVERS = 41;
+ public static final int OPTION_NTP_SERVERS = 42;
+
+ public static final int OPTION_SERVICE_VENDOR_SPECIFIC_INFORMATIONS = 43;
+ public static final int OPTION_SERVICE_NETBOIS_NAME_SERVERS = 44;
+ public static final int OPTION_SERVICE_NETBOIS_DATAGRAM_DISTRIBUTION_SERVERS = 45;
+ public static final int OPTION_SERVICE_NETBOIS_NODE_TYPE = 46;
+ public static final int OPTION_SERVICE_NETBOIS_SCOPE_TYPE = 47;
+ public static final int OPTION_SERVICE_X_FONT_SERVERS = 48;
+ public static final int OPTION_SERVICE_X_DISPLAY_MANAGERS = 49;
+
+ public static final int OPTION_DHCP_IP_ADRESS_REQUESTED = 50;
+ public static final int OPTION_DHCP_IP_LEASE_TIME = 51;
+ public static final int OPTION_DHCP_OVERLOAD = 52;
+ public static final int OPTION_DHCP_MESSAGE_TYPE = 53;
+ public static final int OPTION_DHCP_SERVER_IDENTIFIER = 54;
+ public static final int OPTION_DHCP_PARAMETER_REQUEST_LIST = 55;
+ public static final int OPTION_DHCP_MESSAGE = 56;
+ public static final int OPTION_DHCP_MAXIMUM_MESSAGE_SIZE = 57;
+ public static final int OPTION_DHCP_RENEWAL_TIME = 58;
+ public static final int OPTION_DHCP_REBIND_TIME = 59;
+ public static final int OPTION_DHCP_CLASS_IDENTIFIER = 60;
+ public static final int OPTION_DHCP_CLIENT_IDENTIFIER = 61;
+
+
+ /**
+ *This inner class represent an entry in the Option Table
+ */
+
+ class DHCPOptionsEntry {
+ protected byte code;
+ protected byte length;
+ protected byte content[];
+
+ public DHCPOptionsEntry(byte entryCode, byte entryLength,
+ byte entryContent[]) {
+ this.code = entryCode;
+ this.length = entryLength;
+ this.content = entryContent;
+ }
+
+ @Override
+ public String toString() {
+ return "Code: " + this.code + "\nContent: " + new String(this.content);
+ }
+ }
+
+ private Hashtable<Byte, DHCPOptionsEntry> optionsTable = null;
+
+ public DHCPOptions() {
+ this.optionsTable = new Hashtable<Byte, DHCPOptionsEntry>();
+ }
+
+ /**
+ * Removes option with specified bytecode
+ * @param entryCode The code of option to be removed
+ */
+
+ public void removeOption(byte entryCode) {
+ this.optionsTable.remove(new Byte(entryCode));
+ }
+
+ /**
+ * Returns true if option code is set in list; false otherwise
+ * @param entryCode The node's option code
+ * @return true if option is set, otherwise false
+ */
+ public boolean contains(byte entryCode) {
+ return this.optionsTable.containsKey(new Byte(entryCode));
+ }
+
+ /**
+ * Determines if list is empty
+ * @return true if there are no options set, otherwise false
+ */
+ public boolean isEmpty() {
+ return this.optionsTable.isEmpty();
+ }
+
+ /**
+ * Fetches value of option by its option code
+ * @param entryCode The node's option code
+ * @return byte array containing the value of option entryCode.
+ * null is returned if option is not set.
+ */
+ public byte[] getOption(byte entryCode) {
+ if (this.contains(entryCode)) {
+ DHCPOptionsEntry ent = this.optionsTable.get(new Byte(entryCode));
+ return ent.content;
+ }
+ else {
+ return null;
+ }
+ }
+
+ /**
+ * Changes an existing option to new value
+ * @param entryCode The node's option code
+ * @param value Content of node option
+ */
+ public void setOption(byte entryCode, byte value[]) {
+ DHCPOptionsEntry opt = new DHCPOptionsEntry(entryCode, (byte) value.length, value);
+ this.optionsTable.put(new Byte(entryCode), opt);
+ }
+
+ /**
+ * Returns the option value of a specified option code in a byte array
+ * @param length Length of option content
+ * @param position Location in array of option node
+ * @param options The byte array of options
+ * @return byte array containing the value for the option
+ */
+ private byte[] getArrayOption(int length, int position, byte options[]) {
+ byte value[] = new byte[length];
+ for (int i = 0; i < length; i++) {
+ value[i] = options[position + i];
+ }
+ return value;
+ }
+
+ /**
+ * Converts an options byte array to a linked list
+ * @param optionsArray The byte array representation of the options list
+ */
+ public void internalize(byte[] optionsArray) {
+
+ /* Assume options valid and correct */
+ int pos = 4; // ignore vendor magic cookie
+ byte code, length;
+ byte value[];
+
+ while (optionsArray[pos] != (byte) 255) { // until end option
+ code = optionsArray[pos++];
+ length = optionsArray[pos++];
+ value = getArrayOption(length, pos, optionsArray);
+ setOption(code, value);
+ pos += length; // increment position pointer
+ }
+ }
+
+ /**
+ * Converts a linked options list to a byte array
+ * @return array representation of optionsTable
+ */
+ // todo provide overflow return
+ public byte[] externalize() {
+ byte[] options = new byte[312];
+
+ options[0] = (byte) 99; // insert vendor magic cookie
+ options[1] = (byte) 130;
+ options[2] = (byte) 83;
+ options[3] = (byte) 99;
+
+ int position = 4;
+ Enumeration<DHCPOptionsEntry> e = this.optionsTable.elements();
+
+ while (e.hasMoreElements()) {
+ DHCPOptionsEntry entry = e.nextElement();
+ options[position++] = entry.code;
+ options[position++] = entry.length;
+ for (int i = 0; i < entry.length; ++i) {
+ options[position++] = entry.content[i];
+ }
+ }
+
+ options[position] = (byte) 255; // insert end option
+ return options;
+ }
+
+ /**
+ * Prints the options linked list: For testing only.
+ */
+ public void printList() {
+ System.out.println(this.optionsTable.toString());
+ }
+}
diff --git a/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
new file mode 100644
index 0000000..6dbe2bf
--- /dev/null
+++ b/src/main/java/com/btr/proxy/search/wpad/dhcp/DHCPSocket.java
@@ -0,0 +1,107 @@
+package com.btr.proxy.search.wpad.dhcp;
+
+import java.io.IOException;
+import java.net.DatagramPacket;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+import java.net.UnknownHostException;
+
+/**
+ * This class represents a Socket for sending DHCP Messages
+ *
+ * @author Jason Goldschmidt and Simon Frankenberger
+ *
+ * @see java.net.DatagramSocket
+ */
+
+public class DHCPSocket extends DatagramSocket {
+ /**
+ * Default socket timeout (1 second)
+ */
+ private int SOCKET_TIMEOUT = 5000;
+
+ /**
+ * Default MTU (Maximum Transmission Unit) for ethernet (in bytes)
+ */
+ private int mtu = 1500;
+
+ /**
+ * Constructor for creating DHCPSocket on a specific port on the local
+ * machine.
+ *
+ * @param inPort The port for the application to bind.
+ *
+ * @throws SocketException As thrown by the {@link Socket} constructor
+ */
+ public DHCPSocket(int inPort) throws SocketException {
+ super(inPort);
+ setSoTimeout(this.SOCKET_TIMEOUT);
+ }
+
+ /**
+ * Sets the Maximum Transfer Unit for the UDP DHCP Packets to be set.
+ *
+ * @param inSize Integer representing desired MTU
+ */
+ public void setMTU(int inSize) {
+ this.mtu = inSize;
+ }
+
+ /**
+ * Returns the set MTU for this socket
+ *
+ * @return The Maximum Transfer Unit set for this socket
+ */
+ public int getMTU() {
+ return this.mtu;
+ }
+
+ /**
+ * Sends a DHCPMessage object to a predifined host.
+ *
+ * @param inMessage Well-formed DHCPMessage to be sent to a server
+ *
+ * @throws IOException If the message could not be sent.
+ */
+ public synchronized void send(DHCPMessage inMessage) throws IOException {
+ byte data[] = new byte[this.mtu];
+ data = inMessage.externalize();
+ InetAddress dest = null;
+ try {
+ dest = InetAddress.getByName(inMessage.getDestinationAddress());
+ }
+ catch (UnknownHostException e) {
+ }
+
+ DatagramPacket outgoing = new DatagramPacket(data, data.length, dest,
+ inMessage.getPort());
+
+ send(outgoing); // send outgoing message
+ }
+
+ /**
+ * Receives a datagram packet containing a DHCP Message into
+ * a DHCPMessage object.
+ *
+ * @return <code>true</code> if message is received,
+ * <code>false</code> if timeout occurs.
+ * @param outMessage DHCPMessage object to receive new message into
+ */
+ public synchronized boolean receive(DHCPMessage outMessage) {
+ try {
+ DatagramPacket incoming = new DatagramPacket(new byte[this.mtu],
+ this.mtu);
+ //gSocket.
+ receive(incoming); // block on receive for SOCKET_TIMEOUT
+
+ outMessage.internalize(incoming.getData());
+ }
+ catch (java.io.IOException e) {
+ e.printStackTrace();
+ return false;
+ } // end catch
+ return true;
+ }
+}
diff --git a/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java b/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java
new file mode 100644
index 0000000..cf9e150
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/direct/NoProxySelector.java
@@ -0,0 +1,70 @@
+package com.btr.proxy.selector.direct;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+import com.btr.proxy.util.ProxyUtil;
+
+/*****************************************************************************
+ * This proxy selector will always return a "DIRECT" proxy.
+ * Implemented as singleton.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class NoProxySelector extends ProxySelector {
+
+ private static NoProxySelector instance;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ private NoProxySelector() {
+ super();
+ }
+
+ /*************************************************************************
+ * Gets the one and only instance of this selector.
+ * @return a DirectSelector.
+ ************************************************************************/
+
+ public static synchronized NoProxySelector getInstance() {
+ if (NoProxySelector.instance == null) {
+ NoProxySelector.instance = new NoProxySelector();
+ }
+ return instance;
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used.
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ return ProxyUtil.noProxyList();
+ }
+
+}
+
+
+
+
+/*
+ * $Log: $
+ */ \ No newline at end of file
diff --git a/src/main/java/com/btr/proxy/selector/fixed/FixedProxySelector.java b/src/main/java/com/btr/proxy/selector/fixed/FixedProxySelector.java
new file mode 100644
index 0000000..498ac52
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/fixed/FixedProxySelector.java
@@ -0,0 +1,69 @@
+package com.btr.proxy.selector.fixed;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/*****************************************************************************
+ * This proxy selector is configured with a fixed proxy. This proxy will be
+ * returned for all URIs passed to the select method.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class FixedProxySelector extends ProxySelector {
+
+ private final List<Proxy> proxyList;
+
+
+ /*************************************************************************
+ * Constructor
+ * @param proxy the proxy to use.
+ ************************************************************************/
+
+ public FixedProxySelector(Proxy proxy) {
+ super();
+
+ List<Proxy> list = new ArrayList<Proxy>(1);
+ list.add(proxy);
+ this.proxyList = Collections.unmodifiableList(list);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param proxyHost the host name or IP address of the proxy to use.
+ * @param proxyPort the port of the proxy.
+ ************************************************************************/
+
+ public FixedProxySelector(String proxyHost, int proxyPort) {
+ this(new Proxy(Proxy.Type.HTTP,
+ InetSocketAddress.createUnresolved(proxyHost, proxyPort)));
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ return this.proxyList;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/fixed/FixedSocksSelector.java b/src/main/java/com/btr/proxy/selector/fixed/FixedSocksSelector.java
new file mode 100644
index 0000000..d1f7ccc
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/fixed/FixedSocksSelector.java
@@ -0,0 +1,27 @@
+package com.btr.proxy.selector.fixed;
+
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+
+/*****************************************************************************
+ * This proxy selector is configured with a fixed proxy. This proxy will be
+ * returned for all URIs passed to the select method. This implementation
+ * can be used for SOCKS 4 and 5 proxy support.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class FixedSocksSelector extends FixedProxySelector {
+
+ /*************************************************************************
+ * Constructor
+ * @param proxyHost the host name or IP address of the proxy to use.
+ * @param proxyPort the port of the proxy.
+ ************************************************************************/
+
+ public FixedSocksSelector(String proxyHost, int proxyPort) {
+ super(new Proxy(Proxy.Type.SOCKS,
+ InetSocketAddress.createUnresolved(proxyHost, proxyPort)));
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java b/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java
new file mode 100644
index 0000000..f006314
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/misc/BufferedProxySelector.java
@@ -0,0 +1,126 @@
+package com.btr.proxy.selector.misc;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*****************************************************************************
+ * Implements a cache that can be used to warp it around an existing ProxySelector.
+ * You can specify a maximum cache size and a "time to live" for positive resolves.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class BufferedProxySelector extends ProxySelector {
+
+ private ProxySelector delegate;
+
+ private ConcurrentHashMap<String, CacheEntry> cache;
+ private int maxSize;
+ private long ttl;
+
+ private static class CacheEntry {
+ List<Proxy> result;
+ long expireAt;
+
+ public CacheEntry(List<Proxy> r, long expireAt) {
+ super();
+ this.result = new ArrayList<Proxy>(r.size());
+ this.result.addAll(r);
+ this.result = Collections.unmodifiableList(this.result);
+ this.expireAt = expireAt;
+ }
+
+ public boolean isExpired() {
+ return System.nanoTime() >= this.expireAt;
+ }
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param maxSize the max size for the cache.
+ * @param ttl the "time to live" for cache entries as amount in milliseconds.
+ * @param delegate the delegate to use.
+ ************************************************************************/
+
+ public BufferedProxySelector(int maxSize, long ttl, ProxySelector delegate) {
+ super();
+ this.cache = new ConcurrentHashMap<String, CacheEntry>();
+ this.maxSize = maxSize;
+ this.delegate = delegate;
+ this.ttl = ttl;
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ this.delegate.connectFailed(uri, sa, ioe);
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ //String cacheKey = uri.getHost(); // Caching per host may produce wrong results
+ String cacheKey = uri.toString();
+
+ CacheEntry entry = this.cache.get(cacheKey);
+ if (entry == null || entry.isExpired()) {
+ List<Proxy> result = this.delegate.select(uri);
+ entry = new CacheEntry(result, System.nanoTime()+this.ttl*1000*1000);
+
+ synchronized (this.cache) {
+ if (this.cache.size() >= this.maxSize) {
+ purgeCache();
+ }
+ this.cache.put(cacheKey, entry);
+ }
+ }
+
+ return entry.result;
+ }
+
+ /*************************************************************************
+ * Purge cache to get some free space for a new entry.
+ ************************************************************************/
+
+ private void purgeCache() {
+
+ // Remove all expired entries and find the oldest.
+ boolean removedOne = false;
+ Entry<String, CacheEntry> oldest = null;
+
+ Set<Entry<String, CacheEntry>> entries = this.cache.entrySet();
+ for (Iterator<Entry<String, CacheEntry>> it = entries.iterator(); it.hasNext();) {
+ Entry<String, CacheEntry> entry = it.next();
+ if (entry.getValue().isExpired()) {
+ it.remove();
+ removedOne = true;
+ } else
+ if (oldest == null || entry.getValue().expireAt < oldest.getValue().expireAt) {
+ oldest = entry;
+ }
+ }
+
+ // Remove oldest if no expired entries were found.
+ if (!removedOne && oldest != null) {
+ this.cache.remove(oldest.getKey());
+ }
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java b/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java
new file mode 100644
index 0000000..5d7f563
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/misc/ProtocolDispatchSelector.java
@@ -0,0 +1,115 @@
+package com.btr.proxy.selector.misc;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import com.btr.proxy.selector.direct.NoProxySelector;
+
+/*****************************************************************************
+ * This is a facade for a list of ProxySelecor objects. You can register
+ * different ProxySelectors per Protocol.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProtocolDispatchSelector extends ProxySelector {
+
+ private Map<String, ProxySelector> selectors;
+ private ProxySelector fallbackSelector;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProtocolDispatchSelector() {
+ super();
+ this.selectors = new ConcurrentHashMap<String, ProxySelector>();
+ this.fallbackSelector = NoProxySelector.getInstance();
+ }
+
+ /*************************************************************************
+ * Sets a selector responsible for the given protocol.
+ * @param protocol the name of the protocol.
+ * @param selector the selector to use.
+ ************************************************************************/
+
+ public void setSelector(String protocol, ProxySelector selector) {
+ if (protocol == null) {
+ throw new NullPointerException("Protocol must not be null.");
+ }
+ if (selector == null) {
+ throw new NullPointerException("Selector must not be null.");
+ }
+ this.selectors.put(protocol, selector);
+ }
+
+ /*************************************************************************
+ * Removes the selector installed for the given protocol.
+ * @param protocol the protocol name.
+ * @return the old selector that is removed.
+ ************************************************************************/
+
+ public ProxySelector removeSelector(String protocol) {
+ return this.selectors.remove(protocol);
+ }
+
+ /*************************************************************************
+ * Gets the selector installed for the given protocol.
+ * @param protocol the protocol name.
+ * @return the selector for that protocol, null if none is currently set.
+ ************************************************************************/
+
+ public ProxySelector getSelector(String protocol) {
+ return this.selectors.get(protocol);
+ }
+
+ /*************************************************************************
+ * Sets the fallback selector that is always called when no matching
+ * protocol selector was found..
+ * @param selector the selector to use.
+ ************************************************************************/
+
+ public void setFallbackSelector(ProxySelector selector) {
+ if (selector == null) {
+ throw new NullPointerException("Selector must not be null.");
+ }
+ this.fallbackSelector = selector;
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ ProxySelector selector = this.fallbackSelector;
+ String protocol = uri.getScheme();
+ if (protocol != null && this.selectors.get(protocol) != null) {
+ selector = this.selectors.get(protocol);
+ }
+ selector.connectFailed(uri, sa, ioe);
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ ProxySelector selector = this.fallbackSelector;
+ String protocol = uri.getScheme();
+ if (protocol != null && this.selectors.get(protocol) != null) {
+ selector = this.selectors.get(protocol);
+ }
+ return selector.select(uri);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java b/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java
new file mode 100644
index 0000000..41859ec
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/misc/ProxyListFallbackSelector.java
@@ -0,0 +1,150 @@
+package com.btr.proxy.selector.misc;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map.Entry;
+import java.util.concurrent.ConcurrentHashMap;
+
+/*****************************************************************************
+ * Implements a fallback selector to warp it around an existing ProxySelector.
+ * This will remove proxies from a list of proxies and implement an automatic
+ * retry mechanism.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2011
+ ****************************************************************************/
+
+public class ProxyListFallbackSelector extends ProxySelector {
+
+ // Retry a unresponsive proxy after 10 minutes per default.
+ private static final int DEFAULT_RETRY_DELAY = 1000*60*10;
+
+ private ProxySelector delegate;
+ private ConcurrentHashMap<SocketAddress, Long> failedDelayCache;
+ private long retryAfterMs;
+
+ /*************************************************************************
+ * Constructor
+ * @param delegate the delegate to use.
+ ************************************************************************/
+
+ public ProxyListFallbackSelector(ProxySelector delegate) {
+ this(DEFAULT_RETRY_DELAY, delegate);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param retryAfterMs the "retry delay" as amount of milliseconds.
+ * @param delegate the delegate to use.
+ ************************************************************************/
+
+ public ProxyListFallbackSelector(long retryAfterMs, ProxySelector delegate) {
+ super();
+ this.failedDelayCache = new ConcurrentHashMap<SocketAddress, Long>();
+ this.delegate = delegate;
+ this.retryAfterMs = retryAfterMs;
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ this.failedDelayCache.put(sa, System.currentTimeMillis());
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+ cleanupCache();
+ List<Proxy> proxyList = this.delegate.select(uri);
+ List<Proxy> result = filterUnresponsiveProxiesFromList(proxyList);
+ return result;
+ }
+
+ /*************************************************************************
+ * Cleanup the entries from the cache that are no longer unresponsive.
+ ************************************************************************/
+
+ private void cleanupCache() {
+ Iterator<Entry<SocketAddress, Long>> it
+ = this.failedDelayCache.entrySet().iterator();
+ while (it.hasNext()) {
+ Entry<SocketAddress, Long> e = it.next();
+ Long lastFailTime = e.getValue();
+ if (retryDelayHasPassedBy(lastFailTime)) {
+ it.remove();
+ }
+ }
+ }
+
+ /*************************************************************************
+ * @param proxyList
+ * @return
+ ************************************************************************/
+
+ private List<Proxy> filterUnresponsiveProxiesFromList(List<Proxy> proxyList) {
+ if (this.failedDelayCache.isEmpty()) {
+ return proxyList;
+ }
+ List<Proxy> result = new ArrayList<Proxy>(proxyList.size());
+ for (Proxy proxy : proxyList) {
+ if (isDirect(proxy) || isNotUnresponsive(proxy)) {
+ result.add(proxy);
+ }
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * @param proxy
+ * @return
+ ************************************************************************/
+
+ private boolean isDirect(Proxy proxy) {
+ return Proxy.NO_PROXY.equals(proxy);
+ }
+
+ /*************************************************************************
+ * @param proxy
+ * @return
+ ************************************************************************/
+
+ private boolean isNotUnresponsive(Proxy proxy) {
+ Long lastFailTime = this.failedDelayCache.get(proxy.address());
+ return retryDelayHasPassedBy(lastFailTime);
+ }
+
+ /*************************************************************************
+ * @param lastFailTime
+ * @return
+ ************************************************************************/
+
+ private boolean retryDelayHasPassedBy(Long lastFailTime) {
+ return lastFailTime == null
+ || lastFailTime + this.retryAfterMs < System.currentTimeMillis();
+ }
+
+ /*************************************************************************
+ * Only used for unit testing not part of the public API.
+ * @param retryAfterMs The retryAfterMs to set.
+ ************************************************************************/
+
+ final void setRetryAfterMs(long retryAfterMs) {
+ this.retryAfterMs = retryAfterMs;
+ }
+
+
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/JavaxPacScriptParser.java b/src/main/java/com/btr/proxy/selector/pac/JavaxPacScriptParser.java
new file mode 100644
index 0000000..5d293fa
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/JavaxPacScriptParser.java
@@ -0,0 +1,147 @@
+package com.btr.proxy.selector.pac;
+
+import java.lang.reflect.Method;
+
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * PAC parser using the Rhino JavaScript engine bundled with Java 1.6<br/>
+ * If you need PAC support with Java 1.5 then you should have a look at
+ * RhinoPacScriptParser.
+ *
+ * More information about PAC can be found there:<br/>
+ * <a href="http://en.wikipedia.org/wiki/Proxy_auto-config">Proxy_auto-config</a><br/>
+ * <a href="http://homepages.tesco.net/~J.deBoynePollard/FGA/web-browser-auto-proxy-configuration.html">web-browser-auto-proxy-configuration</a>
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+public class JavaxPacScriptParser implements PacScriptParser {
+ static final String SCRIPT_METHODS_OBJECT = "__pacutil";
+
+ private final PacScriptSource source;
+ private final ScriptEngine engine;
+
+ /*************************************************************************
+ * Constructor
+ *
+ * @param source
+ * the source for the PAC script.
+ * @throws ProxyEvaluationException
+ * on error.
+ ************************************************************************/
+ public JavaxPacScriptParser(PacScriptSource source)
+ throws ProxyEvaluationException {
+ this.source = source;
+ this.engine = setupEngine();
+ }
+
+ /*************************************************************************
+ * Initializes the JavaScript engine and adds aliases for the functions
+ * defined in ScriptMethods.
+ *
+ * @throws ProxyEvaluationException
+ * on error.
+ ************************************************************************/
+ private ScriptEngine setupEngine() throws ProxyEvaluationException {
+ ScriptEngineManager mng = new ScriptEngineManager();
+ ScriptEngine engine = mng.getEngineByMimeType("text/javascript");
+ engine.put(SCRIPT_METHODS_OBJECT, new PacScriptMethods());
+
+ Class<?> scriptMethodsClazz = ScriptMethods.class;
+ Method[] scriptMethods = scriptMethodsClazz.getMethods();
+
+ for (Method method : scriptMethods) {
+ String name = method.getName();
+ int args = method.getParameterTypes().length;
+ StringBuilder toEval = new StringBuilder(name).append(" = function(");
+ for (int i = 0; i < args; i++) {
+ if (i > 0) {
+ toEval.append(",");
+ }
+ toEval.append("arg").append(i);
+ }
+ toEval.append(") {return ");
+
+ String functionCall = buildFunctionCallCode(name, args);
+
+ // If return type is java.lang.String convert it to a JS string
+ if (String.class.isAssignableFrom(method.getReturnType())) {
+ functionCall = "String("+functionCall+")";
+ }
+ toEval.append(functionCall).append("; }");
+ try {
+ engine.eval(toEval.toString());
+ } catch (ScriptException e) {
+ Logger.log(getClass(), LogLevel.ERROR,
+ "JS evaluation error when creating alias for " + name + ".", e);
+ throw new ProxyEvaluationException(
+ "Error setting up script engine", e);
+ }
+ }
+
+ return engine;
+ }
+
+ /*************************************************************************
+ * Builds a JavaScript code snippet to call a function that we bind.
+ * @param functionName of the bound function
+ * @param args of the bound function
+ * @return the JS code to invoke the method.
+ ************************************************************************/
+
+ private String buildFunctionCallCode(String functionName, int args) {
+ StringBuilder functionCall = new StringBuilder();
+ functionCall.append(SCRIPT_METHODS_OBJECT)
+ .append(".").append(functionName).append("(");
+ for (int i = 0; i < args; i++) {
+ if (i > 0) {
+ functionCall.append(",");
+ }
+ functionCall.append("arg").append(i);
+ }
+ functionCall.append(")");
+ return functionCall.toString();
+ }
+
+ /***************************************************************************
+ * Gets the source of the PAC script used by this parser.
+ *
+ * @return a PacScriptSource.
+ **************************************************************************/
+ public PacScriptSource getScriptSource() {
+ return this.source;
+ }
+
+ /*************************************************************************
+ * Evaluates the given URL and host against the PAC script.
+ *
+ * @param url
+ * the URL to evaluate.
+ * @param host
+ * the host name part of the URL.
+ * @return the script result.
+ * @throws ProxyEvaluationException
+ * on execution error.
+ ************************************************************************/
+ public String evaluate(String url, String host)
+ throws ProxyEvaluationException {
+ try {
+ StringBuilder script = new StringBuilder(
+ this.source.getScriptContent());
+ String evalMethod = " ;FindProxyForURL (\"" + url + "\",\"" + host + "\")";
+ script.append(evalMethod);
+ Object result = this.engine.eval(script.toString());
+ return (String) result;
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "JS evaluation error.", e);
+ throw new ProxyEvaluationException(
+ "Error while executing PAC script: " + e.getMessage(), e);
+ }
+
+ }
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java b/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java
new file mode 100644
index 0000000..8e81b03
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/PacProxySelector.java
@@ -0,0 +1,184 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+import com.btr.proxy.util.ProxyUtil;
+
+
+/*****************************************************************************
+ * ProxySelector that will use a PAC script to find an proxy for a given URI.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+public class PacProxySelector extends ProxySelector {
+
+ private final boolean JAVAX_PARSER = ScriptAvailability.isJavaxScriptingAvailable();
+
+ // private static final String PAC_PROXY = "PROXY";
+ private static final String PAC_SOCKS = "SOCKS";
+ private static final String PAC_DIRECT = "DIRECT";
+
+ private PacScriptParser pacScriptParser;
+
+ private static volatile boolean enabled = true;
+
+ /*************************************************************************
+ * Constructor
+ * @param pacSource the source for the PAC file.
+ ************************************************************************/
+
+ public PacProxySelector(PacScriptSource pacSource) {
+ super();
+ selectEngine(pacSource);
+ }
+
+ /*************************************************************************
+ * Can be used to enable / disable the proxy selector.
+ * If disabled it will return DIRECT for all urls.
+ * @param enable the new status to set.
+ ************************************************************************/
+
+ public static void setEnabled(boolean enable) {
+ enabled = enable;
+ }
+
+ /*************************************************************************
+ * Checks if the selector is currently enabled.
+ * @return true if enabled else false.
+ ************************************************************************/
+
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+ /*************************************************************************
+ * Selects one of the available PAC parser engines.
+ * @param pacSource to use as input.
+ ************************************************************************/
+
+ private void selectEngine(PacScriptSource pacSource) {
+ try {
+ if (this.JAVAX_PARSER) {
+ Logger.log(getClass(), LogLevel.INFO,
+ "Using javax.script JavaScript engine.");
+ this.pacScriptParser = new JavaxPacScriptParser(pacSource);
+ } else {
+ Logger.log(getClass(), LogLevel.INFO,
+ "Using Rhino JavaScript engine.");
+ this.pacScriptParser = new RhinoPacScriptParser(pacSource);
+ }
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "PAC parser error.", e);
+ }
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used.
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+ @Override
+ public List<Proxy> select(URI uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI must not be null.");
+ }
+
+ // Fix for Java 1.6.16+ where we get a infinite loop because
+ // URL.connect(Proxy.NO_PROXY) does not work as expected.
+ if (!enabled) {
+ return ProxyUtil.noProxyList();
+ }
+
+ return findProxy(uri);
+ }
+
+ /*************************************************************************
+ * Evaluation of the given URL with the PAC-file.
+ *
+ * Two cases can be handled here:
+ * DIRECT Fetch the object directly from the content HTTP server denoted by its URL
+ * PROXY name:port Fetch the object via the proxy HTTP server at the given location (name and port)
+ *
+ * @param uri <code>URI</code> to be evaluated.
+ * @return <code>Proxy</code>-object list as result of the evaluation.
+ ************************************************************************/
+
+ private List<Proxy> findProxy(URI uri) {
+ try {
+ List<Proxy> proxies = new ArrayList<Proxy>();
+ String parseResult = this.pacScriptParser.evaluate(uri.toString(),
+ uri.getHost());
+ String[] proxyDefinitions = parseResult.split("[;]");
+ for (String proxyDef : proxyDefinitions) {
+ if (proxyDef.trim().length() > 0) {
+ Proxy proxy = buildProxyFromPacResult(proxyDef);
+ if (proxy.type() == Proxy.Type.SOCKS) {
+ if (!proxies.isEmpty() && proxies.get(0).type() == Proxy.Type.DIRECT) {
+ proxies.add(1, proxy);
+ } else
+ proxies.add(0, proxy);
+ } else {
+ proxies.add(proxy);
+ }
+ }
+ }
+ return proxies;
+ } catch (ProxyEvaluationException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "PAC resolving error.", e);
+ return ProxyUtil.noProxyList();
+ }
+ }
+
+ /*************************************************************************
+ * The proxy evaluator will return a proxy string. This method will
+ * take this string and build a matching <code>Proxy</code> for it.
+ * @param pacResult the result from the PAC parser.
+ * @return a Proxy
+ ************************************************************************/
+
+ private Proxy buildProxyFromPacResult(String pacResult) {
+ if (pacResult == null || pacResult.trim().length() < 6) {
+ return Proxy.NO_PROXY;
+ }
+ String proxyDef = pacResult.trim();
+ if (proxyDef.toUpperCase().startsWith(PAC_DIRECT)) {
+ return Proxy.NO_PROXY;
+ }
+
+ // Check proxy type.
+ Proxy.Type type = Proxy.Type.HTTP;
+ if (proxyDef.toUpperCase().startsWith(PAC_SOCKS)) {
+ type = Proxy.Type.SOCKS;
+ }
+
+ String host = proxyDef.substring(6);
+ Integer port = ProxyUtil.DEFAULT_PROXY_PORT;
+
+ // Split port from host
+ int indexOfPort = host.indexOf(':');
+ if (indexOfPort != -1) {
+ port = Integer.parseInt(host.substring(indexOfPort+1).trim());
+ host = host.substring(0, indexOfPort).trim();
+ }
+
+ SocketAddress adr = InetSocketAddress.createUnresolved(host, port);
+ return new Proxy(type, adr);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java b/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
new file mode 100644
index 0000000..101c267
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/PacScriptMethods.java
@@ -0,0 +1,656 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.IOException;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.UnknownHostException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/***************************************************************************
+ * Implementation of PAC JavaScript functions.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ***************************************************************************
+ */
+public class PacScriptMethods implements ScriptMethods {
+
+ public static final String OVERRIDE_LOCAL_IP = "com.btr.proxy.pac.overrideLocalIP";
+
+ private final static String GMT = "GMT";
+
+ private final static List<String> DAYS = Collections.unmodifiableList(
+ Arrays.asList("SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"));
+
+ private final static List<String> MONTH = Collections.unmodifiableList(
+ Arrays.asList("JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"));
+
+ private Calendar currentTime;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public PacScriptMethods() {
+ super();
+ }
+
+ /*************************************************************************
+ * isPlainHostName
+ * @see com.btr.proxy.selector.pac.ScriptMethods#isPlainHostName(java.lang.String)
+ ************************************************************************/
+
+ public boolean isPlainHostName(String host) {
+ return host.indexOf(".") < 0;
+ }
+
+ /*************************************************************************
+ * Tests if an URL is in a given domain.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @param domain
+ * is the domain name to test the host name against.
+ * @return true if the domain of host name matches.
+ ************************************************************************/
+
+ public boolean dnsDomainIs(String host, String domain) {
+ return host.endsWith(domain);
+ }
+
+ /*************************************************************************
+ * Is true if the host name matches exactly the specified host name, or if
+ * there is no domain name part in the host name, but the unqualified host
+ * name matches.
+ *
+ * @param host
+ * the host name from the URL.
+ * @param domain
+ * fully qualified host name with domain to match against.
+ * @return true if matches else false.
+ ************************************************************************/
+
+ public boolean localHostOrDomainIs(String host, String domain) {
+ return domain.startsWith(host);
+ }
+
+ /*************************************************************************
+ * Tries to resolve the host name. Returns true if succeeds.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @return true if resolvable else false.
+ ************************************************************************/
+
+ public boolean isResolvable(String host) {
+ try {
+ InetAddress.getByName(host).getHostAddress();
+ return true;
+ } catch (UnknownHostException ex) {
+ Logger.log(JavaxPacScriptParser.class, LogLevel.DEBUG,
+ "Hostname not resolveable {0}.", host);
+ }
+ return false;
+ }
+
+ /*************************************************************************
+ * Returns true if the IP address of the host matches the specified IP
+ * address pattern. Pattern and mask specification is done the same way as
+ * for SOCKS configuration.
+ *
+ * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP
+ * address of the host matches 198.95.*.*.
+ *
+ * @param host
+ * a DNS host name, or IP address. If a host name is passed, it
+ * will be resolved into an IP address by this function.
+ * @param pattern
+ * an IP address pattern in the dot-separated format.
+ * @param mask
+ * mask for the IP address pattern informing which parts of the
+ * IP address should be matched against. 0 means ignore, 255
+ * means match.
+ * @return true if it matches else false.
+ ************************************************************************/
+
+ public boolean isInNet(String host, String pattern, String mask) {
+ host = dnsResolve(host);
+ if (host == null || host.length() == 0) {
+ return false;
+ }
+ long lhost = parseIpAddressToLong(host);
+ long lpattern = parseIpAddressToLong(pattern);
+ long lmask = parseIpAddressToLong(mask);
+ return (lhost & lmask) == lpattern;
+ }
+
+ /*************************************************************************
+ * Convert a string representation of a IP to a long.
+ * @param address to convert.
+ * @return the address as long.
+ ************************************************************************/
+
+ private long parseIpAddressToLong(String address) {
+ long result = 0;
+ String[] parts = address.split("\\.");
+ long shift = 24;
+ for (String part : parts) {
+ long lpart = Long.parseLong(part);
+
+ result |= (lpart << shift);
+ shift -= 8;
+ }
+ return result;
+ }
+
+ /*************************************************************************
+ * Resolves the given DNS host name into an IP address, and returns it in
+ * the dot separated format as a string.
+ *
+ * @param host
+ * the host to resolve.
+ * @return the resolved IP, empty string if not resolvable.
+ ************************************************************************/
+
+ public String dnsResolve(String host) {
+ try {
+ return InetAddress.getByName(host).getHostAddress();
+ } catch (UnknownHostException e) {
+ Logger.log(JavaxPacScriptParser.class, LogLevel.DEBUG,
+ "DNS name not resolvable {0}.", host);
+ }
+ return "";
+ }
+
+ /*************************************************************************
+ * Returns the IP address of the host that the process is running on, as a
+ * string in the dot-separated integer format.
+ *
+ * @return an IP as string.
+ ************************************************************************/
+
+ public String myIpAddress() {
+ return getLocalAddressOfType(Inet4Address.class);
+ }
+
+ /*************************************************************************
+ * Get the current IP address of the computer.
+ * This will return the first address of the first network interface that is
+ * a "real" IP address of the given type.
+ * @param cl the type of address we are searching for.
+ * @return the address as string or "" if not found.
+ ************************************************************************/
+
+ private String getLocalAddressOfType(Class<? extends InetAddress> cl) {
+ try {
+ String overrideIP = System.getProperty(OVERRIDE_LOCAL_IP);
+ if (overrideIP != null && overrideIP.trim().length() > 0) {
+ return overrideIP.trim();
+ }
+ Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
+ while (interfaces.hasMoreElements()){
+ NetworkInterface current = interfaces.nextElement();
+ if (!current.isUp() || current.isLoopback() || current.isVirtual()) {
+ continue;
+ }
+ Enumeration<InetAddress> addresses = current.getInetAddresses();
+ while (addresses.hasMoreElements()){
+ InetAddress adr = addresses.nextElement();
+ if (cl.isInstance(adr)) {
+ Logger.log(JavaxPacScriptParser.class, LogLevel.TRACE,
+ "Local address resolved to {0}", adr);
+ return adr.getHostAddress();
+ }
+ }
+ }
+ return "";
+ } catch (IOException e) {
+ Logger.log(JavaxPacScriptParser.class, LogLevel.DEBUG,
+ "Local address not resolvable.");
+ return "";
+ }
+ }
+
+ /*************************************************************************
+ * Returns the number of DNS domain levels (number of dots) in the host
+ * name.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @return number of DNS domain levels.
+ ************************************************************************/
+
+ public int dnsDomainLevels(String host) {
+ int count = 0;
+ int startPos = 0;
+ while ((startPos = host.indexOf(".", startPos + 1)) > -1) {
+ count++;
+ }
+ return count;
+ }
+
+ /*************************************************************************
+ * Returns true if the string matches the specified shell expression.
+ * Actually, currently the patterns are shell expressions, not regular
+ * expressions.
+ *
+ * @param str
+ * is any string to compare (e.g. the URL, or the host name).
+ * @param shexp
+ * is a shell expression to compare against.
+ * @return true if the string matches, else false.
+ ************************************************************************/
+
+ public boolean shExpMatch(String str, String shexp) {
+ StringTokenizer tokenizer = new StringTokenizer(shexp, "*");
+ int startPos = 0;
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ // 07.05.2009 Incorrect? first token can be startsWith and last one
+ // can be endsWith
+ int temp = str.indexOf(token, startPos);
+ if (temp == -1) {
+ return false;
+ } else {
+ startPos = temp + token.length();
+ }
+ }
+ return true;
+ }
+
+ /*************************************************************************
+ * Only the first parameter is mandatory. Either the second, the third, or
+ * both may be left out. If only one parameter is present, the function
+ * yields a true value on the weekday that the parameter represents. If the
+ * string "GMT" is specified as a second parameter, times are taken to be in
+ * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the
+ * condition is true if the current weekday is in between those two
+ * weekdays. Bounds are inclusive. If the "GMT" parameter is specified,
+ * times are taken to be in GMT, otherwise the local time zone is used.
+ *
+ * @param wd1
+ * weekday 1 is one of SUN MON TUE WED THU FRI SAT
+ * @param wd2
+ * weekday 2 is one of SUN MON TUE WED THU FRI SAT
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if current day matches the criteria.
+ ************************************************************************/
+
+ public boolean weekdayRange(String wd1, String wd2, String gmt) {
+ boolean useGmt = GMT.equalsIgnoreCase(wd2) || GMT.equalsIgnoreCase(gmt);
+ Calendar cal = getCurrentTime(useGmt);
+
+ int currentDay = cal.get(Calendar.DAY_OF_WEEK) - 1;
+ int from = DAYS.indexOf(wd1 == null ? null : wd1.toUpperCase());
+ int to = DAYS.indexOf(wd2 == null ? null : wd2.toUpperCase());
+ if (to == -1) {
+ to = from;
+ }
+
+ if (to < from) {
+ return currentDay >= from || currentDay <= to;
+ } else {
+ return currentDay >= from && currentDay <= to;
+ }
+ }
+
+ /*************************************************************************
+ * Sets a calendar with the current time. If this is set all date and time
+ * based methods will use this calendar to determine the current time
+ * instead of the real time. This is only be used by unit tests and is not
+ * part of the public API.
+ *
+ * @param cal
+ * a Calendar to set.
+ ************************************************************************/
+
+ public void setCurrentTime(Calendar cal) {
+ this.currentTime = cal;
+ }
+
+ /*************************************************************************
+ * Gets a calendar set to the current time. This is used by the date and
+ * time based methods.
+ *
+ * @param useGmt
+ * flag to indicate if the calendar is to be created in GMT time
+ * or local time.
+ * @return a Calendar set to the current time.
+ ************************************************************************/
+
+ private Calendar getCurrentTime(boolean useGmt) {
+ if (this.currentTime != null) { // Only used for unit tests
+ return (Calendar) this.currentTime.clone();
+ }
+ return Calendar.getInstance(useGmt ? TimeZone.getTimeZone(GMT)
+ : TimeZone.getDefault());
+ }
+
+ /*************************************************************************
+ * Only the first parameter is mandatory. All other parameters can be left
+ * out therefore the meaning of the parameters changes. The method
+ * definition shows the version with the most possible parameters filled.
+ * The real meaning of the parameters is guessed from it's value. If "from"
+ * and "to" are specified then the bounds are inclusive. If the "GMT"
+ * parameter is specified, times are taken to be in GMT, otherwise the local
+ * time zone is used.
+ *
+ * @param day1
+ * is the day of month between 1 and 31 (as an integer).
+ * @param month1
+ * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year1
+ * is the full year number, for example 1995 (but not 95).
+ * Integer.
+ * @param day2
+ * is the day of month between 1 and 31 (as an integer).
+ * @param month2
+ * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year2
+ * is the full year number, for example 1995 (but not 95).
+ * Integer.
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if the current date matches the given range.
+ ************************************************************************/
+
+ public boolean dateRange(Object day1, Object month1, Object year1,
+ Object day2, Object month2, Object year2, Object gmt) {
+
+ // Guess the parameter meanings.
+ Map<String, Integer> params = new HashMap<String, Integer>();
+ parseDateParam(params, day1);
+ parseDateParam(params, month1);
+ parseDateParam(params, year1);
+ parseDateParam(params, day2);
+ parseDateParam(params, month2);
+ parseDateParam(params, year2);
+ parseDateParam(params, gmt);
+
+ // Get current date
+ boolean useGmt = params.get("gmt") != null;
+ Calendar cal = getCurrentTime(useGmt);
+ Date current = cal.getTime();
+
+ // Build the "from" date
+ if (params.get("day1") != null) {
+ cal.set(Calendar.DAY_OF_MONTH, params.get("day1"));
+ }
+ if (params.get("month1") != null) {
+ cal.set(Calendar.MONTH, params.get("month1"));
+ }
+ if (params.get("year1") != null) {
+ cal.set(Calendar.YEAR, params.get("year1"));
+ }
+ Date from = cal.getTime();
+
+ // Build the "to" date
+ Date to;
+ if (params.get("day2") != null) {
+ cal.set(Calendar.DAY_OF_MONTH, params.get("day2"));
+ }
+ if (params.get("month2") != null) {
+ cal.set(Calendar.MONTH, params.get("month2"));
+ }
+ if (params.get("year2") != null) {
+ cal.set(Calendar.YEAR, params.get("year2"));
+ }
+ to = cal.getTime();
+
+ // Need to increment to the next month?
+ if (to.before(from)) {
+ cal.add(Calendar.MONTH, +1);
+ to = cal.getTime();
+ }
+ // Need to increment to the next year?
+ if (to.before(from)) {
+ cal.add(Calendar.YEAR, +1);
+ cal.add(Calendar.MONTH, -1);
+ to = cal.getTime();
+ }
+
+ return current.compareTo(from) >= 0 && current.compareTo(to) <= 0;
+ }
+
+ /*************************************************************************
+ * Try to guess the type of the given parameter and put it into the params
+ * map.
+ *
+ * @param params
+ * a map to put the parsed parameters into.
+ * @param value
+ * to parse and specify the type for.
+ ************************************************************************/
+
+ private void parseDateParam(Map<String, Integer> params, Object value) {
+ if (value instanceof Number) {
+ int n = ((Number) value).intValue();
+ if (n <= 31) {
+ // Its a day
+ if (params.get("day1") == null) {
+ params.put("day1", n);
+ } else {
+ params.put("day2", n);
+ }
+ } else {
+ // Its a year
+ if (params.get("year1") == null) {
+ params.put("year1", n);
+ } else {
+ params.put("year2", n);
+ }
+ }
+ }
+
+ if (value instanceof String) {
+ int n = MONTH.indexOf(((String) value).toUpperCase());
+ if (n > -1) {
+ // Its a month
+ if (params.get("month1") == null) {
+ params.put("month1", n);
+ } else {
+ params.put("month2", n);
+ }
+ }
+ }
+
+ if (GMT.equalsIgnoreCase(String.valueOf(value))) {
+ params.put("gmt", 1);
+ }
+ }
+
+ /*************************************************************************
+ * Some parameters can be left out therefore the meaning of the parameters
+ * changes. The method definition shows the version with the most possible
+ * parameters filled. The real meaning of the parameters is guessed from
+ * it's value. If "from" and "to" are specified then the bounds are
+ * inclusive. If the "GMT" parameter is specified, times are taken to be in
+ * GMT, otherwise the local time zone is used.<br/>
+ *
+ * <pre>
+ * timeRange(hour)
+ * timeRange(hour1, hour2)
+ * timeRange(hour1, min1, hour2, min2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+ * </pre>
+ *
+ * @param hour1
+ * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min1
+ * minutes from 0 to 59.
+ * @param sec1
+ * seconds from 0 to 59.
+ * @param hour2
+ * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min2
+ * minutes from 0 to 59.
+ * @param sec2
+ * seconds from 0 to 59.
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if the current time matches the given range.
+ ************************************************************************/
+
+ public boolean timeRange(Object hour1, Object min1, Object sec1,
+ Object hour2, Object min2, Object sec2, Object gmt) {
+ boolean useGmt = GMT.equalsIgnoreCase(String.valueOf(min1))
+ || GMT.equalsIgnoreCase(String.valueOf(sec1))
+ || GMT.equalsIgnoreCase(String.valueOf(min2))
+ || GMT.equalsIgnoreCase(String.valueOf(gmt));
+
+ Calendar cal = getCurrentTime(useGmt);
+ cal.set(Calendar.MILLISECOND, 0);
+ Date current = cal.getTime();
+ Date from;
+ Date to;
+ if (sec2 instanceof Number) {
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue());
+ cal.set(Calendar.MINUTE, ((Number) min1).intValue());
+ cal.set(Calendar.SECOND, ((Number) sec1).intValue());
+ from = cal.getTime();
+
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour2).intValue());
+ cal.set(Calendar.MINUTE, ((Number) min2).intValue());
+ cal.set(Calendar.SECOND, ((Number) sec2).intValue());
+ to = cal.getTime();
+ } else if (hour2 instanceof Number) {
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue());
+ cal.set(Calendar.MINUTE, ((Number) min1).intValue());
+ cal.set(Calendar.SECOND, 0);
+ from = cal.getTime();
+
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) sec1).intValue());
+ cal.set(Calendar.MINUTE, ((Number) hour2).intValue());
+ cal.set(Calendar.SECOND, 59);
+ to = cal.getTime();
+ } else if (min1 instanceof Number) {
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue());
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ from = cal.getTime();
+
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) min1).intValue());
+ cal.set(Calendar.MINUTE, 59);
+ cal.set(Calendar.SECOND, 59);
+ to = cal.getTime();
+ } else {
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue());
+ cal.set(Calendar.MINUTE, 0);
+ cal.set(Calendar.SECOND, 0);
+ from = cal.getTime();
+
+ cal.set(Calendar.HOUR_OF_DAY, ((Number) hour1).intValue());
+ cal.set(Calendar.MINUTE, 59);
+ cal.set(Calendar.SECOND, 59);
+ to = cal.getTime();
+ }
+
+ if (to.before(from)) {
+ cal.setTime(to);
+ cal.add(Calendar.DATE, +1);
+ to = cal.getTime();
+ }
+
+ return current.compareTo(from) >= 0 && current.compareTo(to) <= 0;
+ }
+
+ // Microsoft PAC extensions for IPv6 support.
+
+ /*************************************************************************
+ * isResolvableEx
+ * @see com.btr.proxy.selector.pac.ScriptMethods#isResolvableEx(java.lang.String)
+ ************************************************************************/
+
+ public boolean isResolvableEx(String host) {
+ return isResolvable(host);
+ }
+
+ /*************************************************************************
+ * isInNetEx
+ * @see com.btr.proxy.selector.pac.ScriptMethods#isInNetEx(java.lang.String, java.lang.String)
+ ************************************************************************/
+
+ public boolean isInNetEx(String ipAddress, String ipPrefix) {
+ // TODO rossi 27.06.2011 Auto-generated method stub
+ return false;
+ }
+
+ /*************************************************************************
+ * dnsResolveEx
+ * @see com.btr.proxy.selector.pac.ScriptMethods#dnsResolveEx(java.lang.String)
+ ************************************************************************/
+
+ public String dnsResolveEx(String host) {
+ StringBuilder result = new StringBuilder();
+ try {
+ InetAddress[] list = InetAddress.getAllByName(host);
+ for (InetAddress inetAddress : list) {
+ result.append(inetAddress.getHostAddress());
+ result.append("; ");
+ }
+ } catch (UnknownHostException e) {
+ Logger.log(JavaxPacScriptParser.class, LogLevel.DEBUG,
+ "DNS name not resolvable {0}.", host);
+ }
+ return result.toString();
+ }
+
+ /*************************************************************************
+ * myIpAddressEx
+ * @see com.btr.proxy.selector.pac.ScriptMethods#myIpAddressEx()
+ ************************************************************************/
+
+ public String myIpAddressEx() {
+ return getLocalAddressOfType(Inet6Address.class);
+ }
+
+ /*************************************************************************
+ * sortIpAddressList
+ * @see com.btr.proxy.selector.pac.ScriptMethods#sortIpAddressList(java.lang.String)
+ ************************************************************************/
+
+ public String sortIpAddressList(String ipAddressList) {
+ if (ipAddressList == null || ipAddressList.trim().length() == 0) {
+ return "";
+ }
+ String[] ipAddressToken = ipAddressList.split(";");
+ List<InetAddress> parsedAddresses = new ArrayList<InetAddress>();
+ for (String ip : ipAddressToken) {
+ try {
+ parsedAddresses.add(InetAddress.getByName(ip));
+ } catch (UnknownHostException e) {
+ // TODO rossi 01.11.2011 Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ Collections.sort(parsedAddresses, null);
+ // TODO rossi 27.06.2011 Implement me.
+ return ipAddressList;
+ }
+
+ /*************************************************************************
+ * getClientVersion
+ * @see com.btr.proxy.selector.pac.ScriptMethods#getClientVersion()
+ ************************************************************************/
+
+ public String getClientVersion() {
+ return "1.0";
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/PacScriptParser.java b/src/main/java/com/btr/proxy/selector/pac/PacScriptParser.java
new file mode 100644
index 0000000..5e7fd73
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/PacScriptParser.java
@@ -0,0 +1,29 @@
+package com.btr.proxy.selector.pac;
+
+/***************************************************************************
+ * Common interface for PAC script parsers.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ***************************************************************************/
+public interface PacScriptParser {
+
+ /***************************************************************************
+ * Gets the source of the PAC script used by this parser.
+ *
+ * @return a PacScriptSource.
+ **************************************************************************/
+ public PacScriptSource getScriptSource();
+
+ /*************************************************************************
+ * Evaluates the given URL and host against the PAC script.
+ *
+ * @param url
+ * the URL to evaluate.
+ * @param host
+ * the host name part of the URL.
+ * @return the script result.
+ * @throws ProxyEvaluationException
+ * on execution error.
+ ************************************************************************/
+ public String evaluate(String url, String host) throws ProxyEvaluationException;
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/PacScriptSource.java b/src/main/java/com/btr/proxy/selector/pac/PacScriptSource.java
new file mode 100644
index 0000000..05e00b6
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/PacScriptSource.java
@@ -0,0 +1,31 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.IOException;
+
+/*****************************************************************************
+ * An source to fetch the PAC script from.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public interface PacScriptSource {
+
+ /*************************************************************************
+ * Gets the PAC script content as String.
+ * @return a script.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ public String getScriptContent() throws IOException;
+
+ /*************************************************************************
+ * Checks if the content of the script is valid and if it is possible
+ * to use this script source for a PAC selector.
+ * Note that this might trigger a download of the script content from
+ * a remote location.
+ * @return true if everything is fine, else false.
+ ************************************************************************/
+
+ public boolean isScriptValid();
+
+} \ No newline at end of file
diff --git a/src/main/java/com/btr/proxy/selector/pac/ProxyEvaluationException.java b/src/main/java/com/btr/proxy/selector/pac/ProxyEvaluationException.java
new file mode 100644
index 0000000..25fd977
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/ProxyEvaluationException.java
@@ -0,0 +1,51 @@
+package com.btr.proxy.selector.pac;
+
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Exception for PAC script errors.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyEvaluationException extends ProxyException {
+
+ private static final long serialVersionUID = 1L;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProxyEvaluationException() {
+ super();
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param message the error message.
+ * @param cause the causing exception for exception chaining.
+ ************************************************************************/
+
+ public ProxyEvaluationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param message the error message.
+ ************************************************************************/
+
+ public ProxyEvaluationException(String message) {
+ super(message);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param cause the causing exception for exception chaining.
+ ************************************************************************/
+
+ public ProxyEvaluationException(Throwable cause) {
+ super(cause);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.java b/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.java
new file mode 100644
index 0000000..f6ff6e2
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/RhinoPacScriptParser.java
@@ -0,0 +1,318 @@
+package com.btr.proxy.selector.pac;
+
+import java.util.Calendar;
+import org.mozilla.javascript.Context;
+import org.mozilla.javascript.ContextFactory;
+import org.mozilla.javascript.Scriptable;
+import org.mozilla.javascript.ScriptableObject;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * PAC parser using the Rhino JavaScript engine.<br/>
+ * Depends on js.jar of the <a href="http://www.mozilla.org/rhino/">Apache Rhino </a> project.
+ * <p>
+ * More information about PAC can be found there:<br/>
+ * <a href="http://en.wikipedia.org/wiki/Proxy_auto-config">Proxy_auto-config</a><br/>
+ * <a href="http://homepages.tesco.net/~J.deBoynePollard/FGA/web-browser-auto-proxy-configuration.html">web-browser-auto-proxy-configuration</a>
+ * </p>
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class RhinoPacScriptParser extends ScriptableObject implements PacScriptParser {
+
+ private static final long serialVersionUID = 1L;
+
+ // Define some PAC script functions. These functions are not part of ECMA.
+ private static final String[] JS_FUNCTION_NAMES = {
+ "shExpMatch", "dnsResolve", "isResolvable",
+ "isInNet", "dnsDomainIs", "isPlainHostName", "myIpAddress",
+ "dnsDomainLevels", "localHostOrDomainIs", "weekdayRange",
+ "dateRange", "timeRange"
+ };
+
+ private Scriptable scope;
+ private PacScriptSource source;
+ private static final PacScriptMethods SCRIPT_METHODS = new PacScriptMethods();
+
+ /*************************************************************************
+ * Constructor
+ * @param source the source for the PAC script.
+ * @throws ProxyEvaluationException on error.
+ ************************************************************************/
+
+ public RhinoPacScriptParser(PacScriptSource source) throws ProxyEvaluationException {
+ super();
+ this.source = source;
+
+ setupEngine();
+ }
+
+ /*************************************************************************
+ * Initializes the JavaScript engine.
+ * @throws ProxyEvaluationException on error.
+ ************************************************************************/
+
+ public void setupEngine() throws ProxyEvaluationException {
+
+ Context context = new ContextFactory().enterContext();
+ try {
+ defineFunctionProperties(JS_FUNCTION_NAMES, RhinoPacScriptParser.class, ScriptableObject.DONTENUM);
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "JS Engine setup error.", e);
+ throw new ProxyEvaluationException(e.getMessage(), e);
+ }
+
+ this.scope = context.initStandardObjects(this);
+ }
+
+ /***************************************************************************
+ * Gets the source of the PAC script used by this parser.
+ * @return a PacScriptSource.
+ **************************************************************************/
+
+ public PacScriptSource getScriptSource() {
+ return this.source;
+ }
+
+ /*************************************************************************
+ * Evaluates the given URL and host against the PAC script.
+ * @param url the URL to evaluate.
+ * @param host the host name part of the URL.
+ * @return the script result.
+ * @throws ProxyEvaluationException on execution error.
+ ************************************************************************/
+
+ public String evaluate(String url, String host) throws ProxyEvaluationException {
+ try {
+ // FindProxyForURL function signature
+ StringBuilder script = new StringBuilder(this.source.getScriptContent());
+ String evalMethod = " ;FindProxyForURL (\"" + url + "\",\"" + host + "\")";
+ script.append(evalMethod);
+
+ Context context = Context.enter();
+ try {
+ Object result = context.evaluateString(this.scope,
+ script.toString(), "userPacFile", 1, null);
+
+ return Context.toString(result);
+ } finally {
+ Context.exit();
+ }
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "JS evaluation error.", e);
+ throw new ProxyEvaluationException(
+ "Error while executing PAC script: " + e.getMessage(), e);
+ }
+ }
+
+ /*************************************************************************
+ * getClassName
+ * See also org.mozilla.javascript.ScriptableObject#getClassName()
+ ************************************************************************/
+ @Override
+ public String getClassName() {
+ return getClass().getSimpleName();
+ }
+
+
+
+// ***************************************************************************
+// Defining PAC script methods needed in JS
+// ***************************************************************************
+
+
+ /*************************************************************************
+ * Tests if the given name is a plain host name without a domain name.
+ * @param host the host name from the URL (excluding port number)
+ * @return true if there is no domain name in the host name (no dots).
+ ************************************************************************/
+
+ public static boolean isPlainHostName(String host) {
+ return SCRIPT_METHODS.isPlainHostName(host);
+ }
+
+ /*************************************************************************
+ * Tests if an URL is in a given domain.
+ * @param host is the host name from the URL.
+ * @param domain is the domain name to test the host name against.
+ * @return true if the domain of host name matches.
+ ************************************************************************/
+
+ public static boolean dnsDomainIs(String host, String domain) {
+ return SCRIPT_METHODS.dnsDomainIs(host, domain);
+ }
+
+ /*************************************************************************
+ * Is true if the host name matches exactly the specified host name,
+ * or if there is no domain name part in the host name, but the unqualified
+ * host name matches.
+ * @param host the host name from the URL.
+ * @param domain fully qualified host name with domain to match against.
+ * @return true if matches else false.
+ ************************************************************************/
+
+ public static boolean localHostOrDomainIs(String host, String domain) {
+ return SCRIPT_METHODS.localHostOrDomainIs(host, domain);
+ }
+
+ /*************************************************************************
+ * Tries to resolve the host name. Returns true if succeeds.
+ * @param host is the host name from the URL.
+ * @return true if resolvable else false.
+ ************************************************************************/
+
+ public static boolean isResolvable(String host) {
+ return SCRIPT_METHODS.isResolvable(host);
+ }
+
+ /*************************************************************************
+ * Returns true if the IP address of the host matches the specified IP
+ * address pattern. Pattern and mask specification is done the same way
+ * as for SOCKS configuration.
+ *
+ * Example:
+ * isInNet(host, "198.95.0.0", "255.255.0.0")
+ * is true if the IP address of the host matches 198.95.*.*.
+ *
+ * @param host a DNS host name, or IP address.
+ * If a host name is passed, it will be resolved into an IP address by this function.
+ * @param pattern an IP address pattern in the dot-separated format.
+ * @param mask mask for the IP address pattern informing which parts of
+ * the IP address should be matched against. 0 means ignore, 255 means match.
+ * @return true if it matches else false.
+ ************************************************************************/
+
+ public static boolean isInNet(String host, String pattern, String mask) {
+ return SCRIPT_METHODS.isInNet(host, pattern, mask);
+ }
+
+ /*************************************************************************
+ * Resolves the given DNS host name into an IP address, and returns it in
+ * the dot separated format as a string.
+ * @param host the host to resolve.
+ * @return the resolved IP, empty string if not resolvable.
+ ************************************************************************/
+
+ public static String dnsResolve(String host) {
+ return SCRIPT_METHODS.dnsResolve(host);
+ }
+
+ /*************************************************************************
+ * Returns the IP address of the host that the process is running on,
+ * as a string in the dot-separated integer format.
+ * @return an IP as string.
+ ************************************************************************/
+
+ public static String myIpAddress() {
+ return SCRIPT_METHODS.myIpAddress();
+ }
+
+ /*************************************************************************
+ * Returns the number of DNS domain levels (number of dots) in the host name.
+ * @param host is the host name from the URL.
+ * @return number of DNS domain levels.
+ ************************************************************************/
+
+ public static int dnsDomainLevels(String host) {
+ return SCRIPT_METHODS.dnsDomainLevels(host);
+ }
+
+ /*************************************************************************
+ * Returns true if the string matches the specified shell expression.
+ * Actually, currently the patterns are shell expressions, not regular expressions.
+ * @param str is any string to compare (e.g. the URL, or the host name).
+ * @param shexp is a shell expression to compare against.
+ * @return true if the string matches, else false.
+ ************************************************************************/
+
+ public static boolean shExpMatch(String str, String shexp) {
+ return SCRIPT_METHODS.shExpMatch(str, shexp);
+ }
+
+ /*************************************************************************
+ * Only the first parameter is mandatory.
+ * Either the second, the third, or both may be left out.
+ * If only one parameter is present, the function yields a true value on
+ * the weekday that the parameter represents. If the string "GMT" is
+ * specified as a second parameter, times are taken to be in GMT,
+ * otherwise in local time zone. If both wd1 and wd2 are defined, the
+ * condition is true if the current weekday is in between those two weekdays.
+ * Bounds are inclusive. If the "GMT" parameter is specified, times are
+ * taken to be in GMT, otherwise the local time zone is used.
+ * @param wd1 weekday 1 is one of SUN MON TUE WED THU FRI SAT
+ * @param wd2 weekday 2 is one of SUN MON TUE WED THU FRI SAT
+ * @param gmt "GMT" for gmt time format else "undefined"
+ * @return true if current day matches the criteria.
+ ************************************************************************/
+
+ public static boolean weekdayRange(String wd1, String wd2, String gmt) {
+ return SCRIPT_METHODS.weekdayRange(wd1, wd2, gmt);
+ }
+
+ /*************************************************************************
+ * Sets a calendar with the current time. If this is set all date and time
+ * based methods will use this calendar to determine the current time
+ * instead of the real time. This is only be used by unit tests and is not
+ * part of the public API.
+ * @param cal a Calendar to set.
+ ************************************************************************/
+
+ static void setCurrentTime(Calendar cal) {
+ SCRIPT_METHODS.setCurrentTime(cal);
+ }
+
+ /*************************************************************************
+ * Only the first parameter is mandatory.
+ * All other parameters can be left out therefore the meaning of the parameters
+ * changes. The method definition shows the version with the most possible
+ * parameters filled. The real meaning of the parameters is guessed from it's
+ * value. If "from" and "to" are specified then the bounds are inclusive.
+ * If the "GMT" parameter is specified, times are taken to be in GMT,
+ * otherwise the local time zone is used.
+ * @param day1 is the day of month between 1 and 31 (as an integer).
+ * @param month1 one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year1 is the full year number, for example 1995 (but not 95). Integer.
+ * @param day2 is the day of month between 1 and 31 (as an integer).
+ * @param month2 one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year2 is the full year number, for example 1995 (but not 95). Integer.
+ * @param gmt "GMT" for gmt time format else "undefined"
+ * @return true if the current date matches the given range.
+ ************************************************************************/
+
+ public static boolean dateRange(Object day1, Object month1, Object year1, Object day2, Object month2, Object year2, Object gmt) {
+ return SCRIPT_METHODS.dateRange(day1, month1, year1, day2, month2, year2, gmt);
+ }
+
+ /*************************************************************************
+ * Some parameters can be left out therefore the meaning of the parameters
+ * changes. The method definition shows the version with the most possible
+ * parameters filled. The real meaning of the parameters is guessed from it's
+ * value. If "from" and "to" are specified then the bounds are inclusive.
+ * If the "GMT" parameter is specified, times are taken to be in GMT,
+ * otherwise the local time zone is used.<br/>
+ *
+ * <pre>
+ * timeRange(hour)
+ * timeRange(hour1, hour2)
+ * timeRange(hour1, min1, hour2, min2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+ * </pre>
+ *
+ * @param hour1 is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min1 minutes from 0 to 59.
+ * @param sec1 seconds from 0 to 59.
+ * @param hour2 is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min2 minutes from 0 to 59.
+ * @param sec2 seconds from 0 to 59.
+ * @param gmt "GMT" for gmt time format else "undefined"
+ * @return true if the current time matches the given range.
+ ************************************************************************/
+
+ public static boolean timeRange(Object hour1, Object min1, Object sec1, Object hour2, Object min2, Object sec2, Object gmt) {
+ return SCRIPT_METHODS.timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java b/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java
new file mode 100644
index 0000000..d7f6e04
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/ScriptAvailability.java
@@ -0,0 +1,46 @@
+package com.btr.proxy.selector.pac;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/****************************************************************************
+ * Utility to check availablility of javax.script
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ***************************************************************************/
+abstract class ScriptAvailability {
+
+ /*************************************************************************
+ * Checks whether javax.script is available or not.
+ * Completely done per Reflection to allow compilation under Java 1.5
+ * @return true if javax.script is available; false otherwise
+ ************************************************************************/
+ public static boolean isJavaxScriptingAvailable() {
+ Object engine = null;
+ try {
+ Class<?> managerClass = Class.forName("javax.script.ScriptEngineManager");
+ Method m = managerClass.getMethod("getEngineByMimeType", String.class);
+ engine = m.invoke(managerClass.newInstance(), "text/javascript");
+ } catch (ClassNotFoundException e) {
+ // javax.script not available
+ } catch (NoSuchMethodException e) {
+ // javax.script not available
+ } catch (IllegalAccessException e) {
+ // javax.script not available
+ } catch (InvocationTargetException e) {
+ // javax.script not available
+ } catch (InstantiationException e) {
+ // javax.script not available
+ }
+
+ return engine != null;
+ }
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ ScriptAvailability() {
+ super();
+ }
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java b/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java
new file mode 100644
index 0000000..9f559c5
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/ScriptMethods.java
@@ -0,0 +1,256 @@
+package com.btr.proxy.selector.pac;
+
+/***************************************************************************
+ * Defines the public interface for PAC scripts.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ***************************************************************************/
+public interface ScriptMethods {
+
+ public boolean isPlainHostName(String host);
+
+ /*************************************************************************
+ * Tests if an URL is in a given domain.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @param domain
+ * is the domain name to test the host name against.
+ * @return true if the domain of host name matches.
+ ************************************************************************/
+
+ public boolean dnsDomainIs(String host, String domain);
+
+ /*************************************************************************
+ * Is true if the host name matches exactly the specified host name, or if
+ * there is no domain name part in the host name, but the unqualified host
+ * name matches.
+ *
+ * @param host
+ * the host name from the URL.
+ * @param domain
+ * fully qualified host name with domain to match against.
+ * @return true if matches else false.
+ ************************************************************************/
+
+ public boolean localHostOrDomainIs(String host, String domain);
+
+ /*************************************************************************
+ * Tries to resolve the host name. Returns true if succeeds.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @return true if resolvable else false.
+ ************************************************************************/
+
+ public boolean isResolvable(String host);
+
+ /*************************************************************************
+ * Tries to resolve the host name. Returns true if succeeds to resolve
+ * the host to an IPv4 or IPv6 address.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @return true if resolvable else false.
+ ************************************************************************/
+
+ public boolean isResolvableEx(String host);
+
+ /*************************************************************************
+ * Returns true if the IP address of the host matches the specified IP
+ * address pattern. Pattern and mask specification is done the same way as
+ * for SOCKS configuration.
+ *
+ * Example: isInNet(host, "198.95.0.0", "255.255.0.0") is true if the IP
+ * address of the host matches 198.95.*.*.
+ *
+ * @param host
+ * a DNS host name, or IP address. If a host name is passed, it
+ * will be resolved into an IP address by this function.
+ * @param pattern
+ * an IP address pattern in the dot-separated format.
+ * @param mask
+ * mask for the IP address pattern informing which parts of the
+ * IP address should be matched against. 0 means ignore, 255
+ * means match.
+ * @return true if it matches else false.
+ ************************************************************************/
+
+ public boolean isInNet(String host, String pattern, String mask);
+
+ /*************************************************************************
+ * Extension of the isInNet method to support IPv6.
+ * @param ipAddress an IP4 or IP6 address
+ * @param ipPrefix A string containing colon delimited IP prefix
+ * with top n bits specified in the bit field
+ * (i.e. 3ffe:8311:ffff::/48 or 123.112.0.0/16).
+ * @return true if the host is in the given subnet, else false.
+ ************************************************************************/
+
+ public boolean isInNetEx(String ipAddress, String ipPrefix);
+
+ /*************************************************************************
+ * Resolves the given DNS host name into an IP address, and returns it in
+ * the dot separated format as a string.
+ *
+ * @param host
+ * the host to resolve.
+ * @return the resolved IP, empty string if not resolvable.
+ ************************************************************************/
+
+ public String dnsResolve(String host);
+
+ /*************************************************************************
+ * @param host the host to resolve
+ * @return a semicolon separated list of IP6 and IP4 addresses the host
+ * name resolves to, empty string if not resolvable.
+ ************************************************************************/
+
+ public String dnsResolveEx(String host);
+
+ /*************************************************************************
+ * Returns the IP address of the host that the process is running on, as a
+ * string in the dot-separated integer format.
+ *
+ * @return an IP as string.
+ ************************************************************************/
+
+ public String myIpAddress();
+
+ /*************************************************************************
+ * Returns a list of IP4 and IP6 addresses of the host that the process
+ * is running on. The list is separated with semicolons.
+ * @return the list, empty string if not available.
+ ************************************************************************/
+
+ public String myIpAddressEx();
+
+ /*************************************************************************
+ * Returns the number of DNS domain levels (number of dots) in the host
+ * name.
+ *
+ * @param host
+ * is the host name from the URL.
+ * @return number of DNS domain levels.
+ ************************************************************************/
+
+ public int dnsDomainLevels(String host);
+
+ /*************************************************************************
+ * Returns true if the string matches the specified shell expression.
+ * Actually, currently the patterns are shell expressions, not regular
+ * expressions.
+ *
+ * @param str
+ * is any string to compare (e.g. the URL, or the host name).
+ * @param shexp
+ * is a shell expression to compare against.
+ * @return true if the string matches, else false.
+ ************************************************************************/
+
+ public boolean shExpMatch(String str, String shexp);
+
+ /*************************************************************************
+ * Only the first parameter is mandatory. Either the second, the third, or
+ * both may be left out. If only one parameter is present, the function
+ * yields a true value on the weekday that the parameter represents. If the
+ * string "GMT" is specified as a second parameter, times are taken to be in
+ * GMT, otherwise in local time zone. If both wd1 and wd2 are defined, the
+ * condition is true if the current weekday is in between those two
+ * weekdays. Bounds are inclusive. If the "GMT" parameter is specified,
+ * times are taken to be in GMT, otherwise the local time zone is used.
+ *
+ * @param wd1
+ * weekday 1 is one of SUN MON TUE WED THU FRI SAT
+ * @param wd2
+ * weekday 2 is one of SUN MON TUE WED THU FRI SAT
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if current day matches the criteria.
+ ************************************************************************/
+
+ public boolean weekdayRange(String wd1, String wd2, String gmt);
+
+ /*************************************************************************
+ * Only the first parameter is mandatory. All other parameters can be left
+ * out therefore the meaning of the parameters changes. The method
+ * definition shows the version with the most possible parameters filled.
+ * The real meaning of the parameters is guessed from it's value. If "from"
+ * and "to" are specified then the bounds are inclusive. If the "GMT"
+ * parameter is specified, times are taken to be in GMT, otherwise the local
+ * time zone is used.
+ *
+ * @param day1
+ * is the day of month between 1 and 31 (as an integer).
+ * @param month1
+ * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year1
+ * is the full year number, for example 1995 (but not 95).
+ * Integer.
+ * @param day2
+ * is the day of month between 1 and 31 (as an integer).
+ * @param month2
+ * one of JAN FEB MAR APR MAY JUN JUL AUG SEP OCT NOV DEC
+ * @param year2
+ * is the full year number, for example 1995 (but not 95).
+ * Integer.
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if the current date matches the given range.
+ ************************************************************************/
+
+ public boolean dateRange(Object day1, Object month1, Object year1, Object day2, Object month2, Object year2,
+ Object gmt);
+
+ /*************************************************************************
+ * Some parameters can be left out therefore the meaning of the parameters
+ * changes. The method definition shows the version with the most possible
+ * parameters filled. The real meaning of the parameters is guessed from
+ * it's value. If "from" and "to" are specified then the bounds are
+ * inclusive. If the "GMT" parameter is specified, times are taken to be in
+ * GMT, otherwise the local time zone is used.<br/>
+ *
+ * <pre>
+ * timeRange(hour)
+ * timeRange(hour1, hour2)
+ * timeRange(hour1, min1, hour2, min2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2)
+ * timeRange(hour1, min1, sec1, hour2, min2, sec2, gmt)
+ * </pre>
+ *
+ * @param hour1
+ * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min1
+ * minutes from 0 to 59.
+ * @param sec1
+ * seconds from 0 to 59.
+ * @param hour2
+ * is the hour from 0 to 23. (0 is midnight, 23 is 11 pm.)
+ * @param min2
+ * minutes from 0 to 59.
+ * @param sec2
+ * seconds from 0 to 59.
+ * @param gmt
+ * "GMT" for gmt time format else "undefined"
+ * @return true if the current time matches the given range.
+ ************************************************************************/
+
+ public boolean timeRange(Object hour1, Object min1, Object sec1, Object hour2, Object min2, Object sec2, Object gmt);
+
+ /*************************************************************************
+ * Sorts a list of IP4 and IP6 addresses. Separated by semicolon.
+ * Dual addresses first, then IPv6 and last IPv4.
+ * @param ipAddressList the address list.
+ * @return the sorted list, empty string if sort is not possible
+ ************************************************************************/
+
+ public String sortIpAddressList(String ipAddressList);
+
+ /*************************************************************************
+ * Gets the version of the PAC extension that is available.
+ * @return the extension version, currently 1.0
+ ************************************************************************/
+
+ public String getClientVersion();
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
new file mode 100644
index 0000000..6ac8faa
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/pac/UrlPacScriptSource.java
@@ -0,0 +1,270 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Script source that will load the content of a PAC file from an webserver.
+ * The script content is cached once it was downloaded.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class UrlPacScriptSource implements PacScriptSource {
+
+ private static final int DEFAULT_CONNECT_TIMEOUT = 15 * 1000; // seconds
+ private static final int DEFAULT_READ_TIMEOUT = 20 * 1000; // seconds
+ public static final String OVERRIDE_CONNECT_TIMEOUT = "com.btr.proxy.url.connectTimeout";
+ public static final String OVERRIDE_READ_TIMEOUT = "com.btr.proxy.url.readTimeout";
+
+ private final String scriptUrl;
+ private String scriptContent;
+ private long expireAtMillis;
+
+ /*************************************************************************
+ * Constructor
+ * @param url the URL to download the script from.
+ ************************************************************************/
+
+ public UrlPacScriptSource(String url) {
+ super();
+ this.expireAtMillis = 0;
+ this.scriptUrl = url;
+ }
+
+ /*************************************************************************
+ * getScriptContent
+ * @see com.btr.proxy.selector.pac.PacScriptSource#getScriptContent()
+ ************************************************************************/
+
+ public synchronized String getScriptContent() throws IOException {
+ if (this.scriptContent == null ||
+ (this.expireAtMillis > 0
+ && this.expireAtMillis > System.currentTimeMillis())) {
+ try {
+ if (this.scriptUrl.startsWith("file:/") || this.scriptUrl.indexOf(":/") == -1) {
+ this.scriptContent = readPacFileContent(this.scriptUrl);
+ } else {
+ this.scriptContent = downloadPacContent(this.scriptUrl);
+ }
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.ERROR, "Loading script failed from: {0} with error {1}", this.scriptUrl, e);
+ this.scriptContent = "";
+ throw e;
+ }
+ }
+ return this.scriptContent;
+ }
+
+ /*************************************************************************
+ * Reads a PAC script from a local file.
+ * @param scriptUrl
+ * @return the content of the script file.
+ * @throws IOException
+ * @throws URISyntaxException
+ ************************************************************************/
+
+ private String readPacFileContent(String scriptUrl) throws IOException {
+ try {
+ File file = null;
+ if (scriptUrl.indexOf(":/") == -1) {
+ file = new File(scriptUrl);
+ } else {
+ file = new File(new URL(scriptUrl).toURI());
+ }
+ BufferedReader r = new BufferedReader(new FileReader(file));
+ StringBuilder result = new StringBuilder();
+ try {
+ String line;
+ while ((line = r.readLine()) != null) {
+ result.append(line).append("\n");
+ }
+ } finally {
+ r.close();
+ }
+ return result.toString();
+ } catch (Exception e) {
+ Logger.log(getClass(), LogLevel.ERROR, "File reading error.", e);
+ throw new IOException(e.getMessage());
+ }
+ }
+
+ /*************************************************************************
+ * Downloads the script from a webserver.
+ * @param url the URL to the script file.
+ * @return the script content.
+ * @throws IOException on read error.
+ ************************************************************************/
+
+ private String downloadPacContent(String url) throws IOException {
+ if (url == null) {
+ throw new IOException("Invalid PAC script URL: null");
+ }
+
+ setPacProxySelectorEnabled(false);
+
+ HttpURLConnection con = null;
+ try {
+ con = setupHTTPConnection(url);
+ if (con.getResponseCode() != 200) {
+ throw new IOException("Server returned: "+con.getResponseCode()+" "+con.getResponseMessage());
+ }
+ // Read expire date.
+ this.expireAtMillis = con.getExpiration();
+
+ BufferedReader r = getReader(con);
+ String result = readAllContent(r);
+ r.close();
+ return result;
+ } finally {
+ setPacProxySelectorEnabled(true);
+ if (con != null) {
+ con.disconnect();
+ }
+ }
+ }
+
+ /*************************************************************************
+ * Enables/disables the PAC proxy selector while we download to prevent recursion.
+ * See issue: 26 in the change tracker.
+ ************************************************************************/
+
+ private void setPacProxySelectorEnabled(boolean enable) {
+ PacProxySelector.setEnabled(enable);
+ }
+
+ /*************************************************************************
+ * Reads the whole content available into a String.
+ * @param r to read from.
+ * @return the complete PAC file content.
+ * @throws IOException
+ ************************************************************************/
+
+ private String readAllContent(BufferedReader r) throws IOException {
+ StringBuilder result = new StringBuilder();
+ String line;
+ while ((line = r.readLine()) != null) {
+ result.append(line).append("\n");
+ }
+ return result.toString();
+ }
+
+ /*************************************************************************
+ * Build a BufferedReader around the open HTTP connection.
+ * @param con to read from
+ * @return the BufferedReader.
+ * @throws UnsupportedEncodingException
+ * @throws IOException
+ ************************************************************************/
+
+ private BufferedReader getReader(HttpURLConnection con)
+ throws UnsupportedEncodingException, IOException {
+ String charsetName = parseCharsetFromHeader(con.getContentType());
+ BufferedReader r = new BufferedReader(new InputStreamReader(con.getInputStream(), charsetName));
+ return r;
+ }
+
+ /*************************************************************************
+ * Configure the connection to download from.
+ * @param url to get the pac file content from
+ * @return a HTTPUrlConnecion to this url.
+ * @throws IOException
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private HttpURLConnection setupHTTPConnection(String url)
+ throws IOException, MalformedURLException {
+ HttpURLConnection con = (HttpURLConnection) new URL(url).openConnection(Proxy.NO_PROXY);
+ con.setConnectTimeout(getTimeOut(OVERRIDE_CONNECT_TIMEOUT, DEFAULT_CONNECT_TIMEOUT));
+ con.setReadTimeout(getTimeOut(OVERRIDE_READ_TIMEOUT, DEFAULT_READ_TIMEOUT));
+ con.setInstanceFollowRedirects(true);
+ con.setRequestProperty("accept", "application/x-ns-proxy-autoconfig, */*;q=0.8");
+ return con;
+ }
+
+ /*************************************************************************
+ * Gets the timeout value from a property or uses the given default value if
+ * the property cannot be parsed.
+ * @param overrideProperty the property to define the timeout value in milliseconds
+ * @param defaultValue the default timeout value in milliseconds.
+ * @return the value to use.
+ ************************************************************************/
+
+ protected int getTimeOut(String overrideProperty, int defaultValue) {
+ int timeout = defaultValue;
+ String prop = System.getProperty(overrideProperty);
+ if (prop != null && prop.trim().length() > 0) {
+ try {
+ timeout = Integer.parseInt(prop.trim());
+ } catch (NumberFormatException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "Invalid override property : {0}={1}", overrideProperty, prop);
+ // In this case use the default value.
+ }
+ }
+ return timeout;
+ }
+
+ /*************************************************************************
+ * Response Content-Type could be something like this:
+ * application/x-ns-proxy-autoconfig; charset=UTF-8
+ * @param contentType header field.
+ * @return the extracted charset if set else a default charset.
+ ************************************************************************/
+
+ String parseCharsetFromHeader(String contentType) {
+ String result = "ISO-8859-1";
+ if (contentType != null) {
+ String[] paramList = contentType.split(";");
+ for (String param : paramList) {
+ if (param.toLowerCase().trim().startsWith("charset") && param.indexOf("=") != -1) {
+ result = param.substring(param.indexOf("=")+1).trim();
+ }
+ }
+ }
+ return result;
+ }
+
+ /***************************************************************************
+ * @see java.lang.Object#toString()
+ **************************************************************************/
+ @Override
+ public String toString() {
+ return this.scriptUrl;
+ }
+
+ /*************************************************************************
+ * isScriptValid
+ * @see com.btr.proxy.selector.pac.PacScriptSource#isScriptValid()
+ ************************************************************************/
+
+ public boolean isScriptValid() {
+ try {
+ String script = getScriptContent();
+ if (script == null || script.trim().length() == 0) {
+ Logger.log(getClass(), LogLevel.DEBUG, "PAC script is empty. Skipping script!");
+ return false;
+ }
+ if (script.indexOf("FindProxyForURL") == -1) {
+ Logger.log(getClass(), LogLevel.DEBUG, "PAC script entry point FindProxyForURL not found. Skipping script!");
+ return false;
+ }
+ return true;
+ } catch (IOException e) {
+ Logger.log(getClass(), LogLevel.DEBUG, "File reading error: {0}", e);
+ return false;
+ }
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java
new file mode 100644
index 0000000..0f949e1
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/DefaultWhiteListParser.java
@@ -0,0 +1,78 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.btr.proxy.selector.whitelist.HostnameFilter.Mode;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Default implementation for an white list parser. This will support the most
+ * common forms of filters found in white lists.
+ * The white list is a comma (or space) separated list of domain names or IP addresses.
+ * The following section shows some examples.
+ *
+ * .mynet.com - Filters all host names ending with .mynet.com
+ * *.mynet.com - Filters all host names ending with .mynet.com
+ * www.mynet.* - Filters all host names starting with www.mynet.
+ * 123.12.32.1 - Filters the IP 123.12.32.1
+ * 123.12.32.1/255 - Filters the IP range
+ * http://www.mynet.com - Filters only HTTP protocol not FTP and no HTTPS
+ *
+ * Example of a list:
+ *
+ * .mynet.com, *.my-other-net.org, 123.55.23.222, 123.55.23.0/24
+ *
+ * Some info about this topic can be found here:
+ * http://kb.mozillazine.org/No_proxy_for
+ * http://technet.microsoft.com/en-us/library/dd361953.aspx
+ *
+ * Note that this implementation does not cover all variations of all browsers
+ * but should cover the most used formats.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class DefaultWhiteListParser implements WhiteListParser {
+
+ /*************************************************************************
+ * parseWhiteList
+ * @see com.btr.proxy.selector.whitelist.WhiteListParser#parseWhiteList(java.lang.String)
+ ************************************************************************/
+
+ public List<UriFilter> parseWhiteList(String whiteList) {
+ List<UriFilter> result = new ArrayList<UriFilter>();
+
+ String[] token = whiteList.split("[, ]+");
+ for (int i = 0; i < token.length; i++) {
+ String tkn = token[i].trim();
+ if (isIP4SubnetFilter(tkn)) {
+ result.add(new IpRangeFilter(tkn));
+ continue;
+ } else
+ if (tkn.endsWith("*")) {
+ tkn = tkn.substring(0, tkn.length()-1);
+ result.add(new HostnameFilter(Mode.BEGINS_WITH, tkn));
+ continue;
+ } else
+ if (tkn.trim().startsWith("*")) {
+ tkn = tkn.substring(1);
+ result.add(new HostnameFilter(Mode.ENDS_WITH, tkn));
+ } else {
+ result.add(new HostnameFilter(Mode.ENDS_WITH, tkn));
+ }
+ }
+
+ return result;
+ }
+
+ /*************************************************************************
+ * @param token
+ * @return
+ ************************************************************************/
+
+ private boolean isIP4SubnetFilter(String token) {
+ return IPv4WithSubnetChecker.isValid(token);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java b/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java
new file mode 100644
index 0000000..176eaeb
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/HostnameFilter.java
@@ -0,0 +1,93 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.net.URI;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Tests if a host name of a given URI matches some criteria.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class HostnameFilter implements UriFilter {
+
+ private static final String PROTOCOL_ENDING = "://";
+
+ public enum Mode {BEGINS_WITH, ENDS_WITH, REGEX}
+
+ private String matchTo;
+ private String protocolFilter;
+ private Mode mode;
+
+ /*************************************************************************
+ * Constructor
+ * @param mode the filter mode.
+ * @param matchTo the match criteria.
+ ************************************************************************/
+
+ public HostnameFilter(Mode mode, String matchTo) {
+ super();
+ this.mode = mode;
+ this.matchTo = matchTo.toLowerCase();
+
+ extractProtocolFilter();
+ }
+
+ /*************************************************************************
+ * Extracts the protocol if one is given to initialize the protocol matcher.
+ ************************************************************************/
+
+ private void extractProtocolFilter() {
+ int protocolIndex = this.matchTo.indexOf(PROTOCOL_ENDING);
+ if (protocolIndex != -1) {
+ this.protocolFilter = this.matchTo.substring(0, protocolIndex);
+ this.matchTo = this.matchTo.substring(protocolIndex+PROTOCOL_ENDING.length());
+ }
+ }
+
+ /*************************************************************************
+ * accept
+ * @see com.btr.proxy.util.UriFilter#accept(java.net.URI)
+ ************************************************************************/
+
+ public boolean accept(URI uri) {
+ if (uri == null || uri.getAuthority() == null) {
+ return false;
+ }
+
+ if (!isProtocolMatching(uri)) {
+ return false;
+ }
+
+ String host = uri.getAuthority();
+
+ // Strip away port.
+ int index = host.indexOf(':');
+ if (index != -1) {
+ host = host.substring(0, index);
+ }
+
+ switch (this.mode) {
+ case BEGINS_WITH :
+ return host.toLowerCase().startsWith(this.matchTo);
+ case ENDS_WITH :
+ return host.toLowerCase().endsWith(this.matchTo);
+ case REGEX :
+ return host.toLowerCase().matches(this.matchTo);
+ }
+ return false;
+ }
+
+ /*************************************************************************
+ * Applies the protocol filter if available to see if we have a match.
+ * @param uri to test for a correct protocol.
+ * @return true if passed else false.
+ ************************************************************************/
+
+ private boolean isProtocolMatching(URI uri) {
+ return this.protocolFilter == null
+ || uri.getScheme() == null
+ || uri.getScheme().equalsIgnoreCase(this.protocolFilter);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java b/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java
new file mode 100644
index 0000000..b8e713e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/IPv4WithSubnetChecker.java
@@ -0,0 +1,29 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.util.regex.Pattern;
+
+/*****************************************************************************
+ * Checks if the given string is a IP4 range subnet definition
+ * of the format 192.168.0/24
+ * Based on a contribution by Jan Engler
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IPv4WithSubnetChecker {
+
+ private static Pattern IP_SUB_PATTERN = Pattern.compile(
+ "^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\."
+ + "([01]?\\d\\d?|2[0-4]\\d|25[0-5])/(\\d|([12]\\d|3[0-2]))$");
+
+ /*************************************************************************
+ * Tests if a given string is of in the correct format for an IP4 subnet mask.
+ * @param possibleIPAddress to test for valid format.
+ * @return true if valid else false.
+ ************************************************************************/
+
+ public static boolean isValid(String possibleIPAddress) {
+ return IP_SUB_PATTERN.matcher(possibleIPAddress).matches();
+ }
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java b/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java
new file mode 100644
index 0000000..293f520
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/IpRangeFilter.java
@@ -0,0 +1,83 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.net.InetAddress;
+import java.net.URI;
+import java.net.UnknownHostException;
+
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Filters an URI by inspecting it's IP address is in a given range.
+ * The range as must be defined in CIDR notation.
+ * e.g. 192.0.2.1/24,
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IpRangeFilter implements UriFilter {
+
+ private byte[] matchTo;
+ int numOfBits;
+
+ /*************************************************************************
+ * Constructor
+ * @param matchTo the match subnet in CIDR notation.
+ ************************************************************************/
+
+ public IpRangeFilter(String matchTo) {
+ super();
+
+ String[] parts = matchTo.split("/");
+ if (parts.length != 2) {
+ throw new IllegalArgumentException("IP range is not valid:"+matchTo);
+ }
+
+ try {
+ InetAddress address = InetAddress.getByName(parts[0].trim());
+ this.matchTo = address.getAddress();
+ } catch (UnknownHostException e) {
+ throw new IllegalArgumentException("IP range is not valid:"+matchTo);
+ }
+
+ this.numOfBits = Integer.parseInt(parts[1].trim());
+ }
+
+ /*************************************************************************
+ * accept
+ * @see com.btr.proxy.util.UriFilter#accept(java.net.URI)
+ ************************************************************************/
+
+ public boolean accept(URI uri) {
+ if (uri == null || uri.getHost() == null) {
+ return false;
+ }
+ try {
+ InetAddress address = InetAddress.getByName(uri.getHost());
+ byte[] addr = address.getAddress();
+
+ // Comparing IP6 against IP4?
+ if (addr.length != this.matchTo.length) {
+ return false;
+ }
+
+ int bit = 0;
+ for (int nibble = 0; nibble < addr.length; nibble++) {
+ for (int nibblePos = 7; nibblePos >= 0; nibblePos--) {
+ int mask = 1 << nibblePos;
+ if ((this.matchTo[nibble] & mask) != (addr[nibble] & mask)) {
+ return false;
+ }
+ bit++;
+ if (bit >= this.numOfBits) {
+ return true;
+ }
+ }
+ }
+
+ } catch (UnknownHostException e) {
+ // In this case we can not get the IP do not match.
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java b/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java
new file mode 100644
index 0000000..81e2a7d
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/ProxyBypassListSelector.java
@@ -0,0 +1,84 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Special purpose ProxySelector used as Facade on top of a normal ProxySelector.
+ * A wrapper that will first check the URI against a white list and if it matches
+ * it will return DIRECT else it will pass the URI to an delegate for inspection.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyBypassListSelector extends ProxySelector {
+
+ private ProxySelector delegate;
+ private List<UriFilter> whiteListFilter;
+
+
+ /*************************************************************************
+ * Constructor
+ * @param whiteListFilter a list of filters for whitelist URLs.
+ * @param proxySelector the proxy selector to use.
+ ************************************************************************/
+
+ public ProxyBypassListSelector(List<UriFilter> whiteListFilter, ProxySelector proxySelector) {
+ super();
+ if (whiteListFilter == null) {
+ throw new NullPointerException("Whitelist must not be null.");
+ }
+ if (proxySelector == null) {
+ throw new NullPointerException("ProxySelector must not be null.");
+ }
+
+ this.delegate = proxySelector;
+ this.whiteListFilter = whiteListFilter;
+ }
+
+
+ /*************************************************************************
+ * Constructor
+ * @param whiteList a list of filters for whitelist URLs as comma/space separated string.
+ * @param proxySelector the proxy selector to use.
+ ************************************************************************/
+
+ public ProxyBypassListSelector(String whiteList, ProxySelector proxySelector) {
+ this(new DefaultWhiteListParser().parseWhiteList(whiteList), proxySelector);
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ this.delegate.connectFailed(uri, sa, ioe);
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+
+ // If in white list, use DIRECT connection.
+ for (UriFilter filter : this.whiteListFilter) {
+ if (filter.accept(uri)) {
+ return ProxyUtil.noProxyList();
+ }
+ }
+
+ return this.delegate.select(uri);
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java b/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java
new file mode 100644
index 0000000..1aface9
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/UseProxyWhiteListSelector.java
@@ -0,0 +1,74 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+import com.btr.proxy.util.ProxyUtil;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Special purpose ProxySelector used as Facade on top of a normal ProxySelector.
+ * A wrapper that will first check the URI against a white list and if it matches
+ * it will use a proxy as provided by the delegate ProxySelector else it will
+ * return DIRECT.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class UseProxyWhiteListSelector extends ProxySelector {
+
+ private ProxySelector delegate;
+ private List<UriFilter> whiteListFilter;
+
+ /*************************************************************************
+ * Constructor
+ * @param proxySelector the proxy selector to use.
+ ************************************************************************/
+
+ public UseProxyWhiteListSelector(String whiteList, ProxySelector proxySelector) {
+ super();
+ if (whiteList == null) {
+ throw new NullPointerException("Whitelist must not be null.");
+ }
+ if (proxySelector == null) {
+ throw new NullPointerException("ProxySelector must not be null.");
+ }
+
+ this.delegate = proxySelector;
+
+ WhiteListParser parser = new DefaultWhiteListParser();
+ this.whiteListFilter = parser.parseWhiteList(whiteList);
+ }
+
+ /*************************************************************************
+ * connectFailed
+ * @see java.net.ProxySelector#connectFailed(java.net.URI, java.net.SocketAddress, java.io.IOException)
+ ************************************************************************/
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ this.delegate.connectFailed(uri, sa, ioe);
+ }
+
+ /*************************************************************************
+ * select
+ * @see java.net.ProxySelector#select(java.net.URI)
+ ************************************************************************/
+
+ @Override
+ public List<Proxy> select(URI uri) {
+
+ // If in white list, use proxy selector.
+ for (UriFilter filter : this.whiteListFilter) {
+ if (filter.accept(uri)) {
+ return this.delegate.select(uri);
+ }
+ }
+
+ return ProxyUtil.noProxyList();
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java b/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java
new file mode 100644
index 0000000..7cd5fd5
--- /dev/null
+++ b/src/main/java/com/btr/proxy/selector/whitelist/WhiteListParser.java
@@ -0,0 +1,24 @@
+package com.btr.proxy.selector.whitelist;
+
+import java.util.List;
+
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Interface for an white list parser. This will take an white list string and
+ * parse it into a list of UriFilter rules.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public interface WhiteListParser {
+
+ /*************************************************************************
+ * Parses a list of host name and IP filters into UriFilter objects.
+ * @param whiteList the string to parse.
+ * @return a list of UriFilters
+ ************************************************************************/
+
+ public List<UriFilter> parseWhiteList(String whiteList);
+
+}
diff --git a/src/main/java/com/btr/proxy/test/ProxyTester.java b/src/main/java/com/btr/proxy/test/ProxyTester.java
new file mode 100644
index 0000000..c01c7bb
--- /dev/null
+++ b/src/main/java/com/btr/proxy/test/ProxyTester.java
@@ -0,0 +1,176 @@
+package com.btr.proxy.test;
+
+import java.awt.BorderLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URL;
+import java.text.MessageFormat;
+import java.util.List;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextArea;
+import javax.swing.JTextField;
+import javax.swing.SwingUtilities;
+import javax.swing.UIManager;
+import com.btr.proxy.search.ProxySearch;
+import com.btr.proxy.search.ProxySearch.Strategy;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Small test application that allows you to select a proxy search strategy
+ * and then validate URLs against it.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyTester extends JFrame {
+
+ private static final long serialVersionUID = 1L;
+
+ private JComboBox modes;
+ private JButton testButton;
+ private JTextField urlField;
+
+ private JTextArea logArea;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProxyTester() {
+ super();
+ init();
+ }
+
+ /*************************************************************************
+ * Initializes the GUI.
+ ************************************************************************/
+
+ private void init() {
+ setTitle("Proxy Vole Tester");
+ setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
+
+ JPanel p = new JPanel();
+
+ p.add(new JLabel("Mode:"));
+
+ this.modes = new JComboBox(ProxySearch.Strategy.values());
+ p.add(this.modes);
+
+ p.add(new JLabel("URL:"));
+ this.urlField = new JTextField(30);
+ this.urlField.setText("http://code.google.com/p/proxy-vole/");
+ p.add(this.urlField);
+
+ this.testButton = new JButton("Test");
+ this.testButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent arg0) {
+ testUrl();
+ }
+ });
+ p.add(this.testButton);
+
+ this.logArea = new JTextArea(5, 50);
+ JPanel contenPane = new JPanel(new BorderLayout());
+ contenPane.add(p, BorderLayout.NORTH);
+ contenPane.add(new JScrollPane(this.logArea), BorderLayout.CENTER);
+ setContentPane(contenPane);
+
+ pack();
+ setLocationRelativeTo(null);
+ installLogger();
+ }
+
+ /*************************************************************************
+ * Install the framework logger.
+ ************************************************************************/
+
+ private void installLogger() {
+ Logger.setBackend(new Logger.LogBackEnd() {
+ public void log(Class<?> clazz, LogLevel loglevel, String msg, Object... params) {
+ ProxyTester.this.logArea.append(loglevel+"\t"+MessageFormat.format(msg, params)+"\n");
+ }
+ public boolean isLogginEnabled(LogLevel logLevel) {
+ return true;
+ }
+ });
+ }
+
+ /*************************************************************************
+ * Test the given URL with the given Proxy Search.
+ ************************************************************************/
+
+ protected void testUrl() {
+ try {
+ if (this.urlField.getText().trim().length() == 0) {
+ JOptionPane.showMessageDialog(this, "Please enter an URL first.");
+ return;
+ }
+
+ this.logArea.setText("");
+
+ Strategy pss = (Strategy) this.modes.getSelectedItem();
+ ProxySearch ps = new ProxySearch();
+ ps.addStrategy(pss);
+ ProxySelector psel = ps.getProxySelector();
+ if (psel == null) {
+ JOptionPane.showMessageDialog(this, "No proxy settings available for this mode.");
+ return;
+ }
+ ProxySelector.setDefault(psel);
+
+ URL url = new URL(this.urlField.getText().trim());
+ List<Proxy> result = psel.select(url.toURI());
+ if (result == null || result.size() == 0) {
+ JOptionPane.showMessageDialog(this, "No proxy found for this url.");
+ return;
+ }
+
+ JOptionPane.showMessageDialog(this,
+ "Proxy Settings found using "+pss+" strategy.\n" +
+ "Proxy used for URL is: "+result.get(0));
+
+ } catch (Exception e) {
+ JOptionPane.showMessageDialog(this, "Error:"+e.getMessage(), "Error checking URL.", JOptionPane.ERROR_MESSAGE);
+ }
+
+ }
+
+ /*************************************************************************
+ * Main entry point for the application.
+ * @param args command line arguments.
+ ************************************************************************/
+
+ public static void main(String[] args) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ setLookAndFeel();
+
+ ProxyTester mainFrame = new ProxyTester();
+ mainFrame.setVisible(true);
+ }
+
+ });
+ }
+
+ /*************************************************************************
+ * Change the L&F to the system default.
+ ************************************************************************/
+
+ private static void setLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (Exception e) {
+ // Use default
+ }
+ }
+}
+
diff --git a/src/main/java/com/btr/proxy/util/EmptyXMLResolver.java b/src/main/java/com/btr/proxy/util/EmptyXMLResolver.java
new file mode 100644
index 0000000..9330556
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/EmptyXMLResolver.java
@@ -0,0 +1,26 @@
+package com.btr.proxy.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/*****************************************************************************
+ * This resolver is used to prevent network lookups of DTD or XML schemas.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class EmptyXMLResolver implements EntityResolver {
+
+ /*************************************************************************
+ * Overwritten to return an empty entity.
+ * @see org.xml.sax.EntityResolver#resolveEntity(java.lang.String, java.lang.String)
+ ************************************************************************/
+
+ public InputSource resolveEntity( String arg0, String arg1 ) throws SAXException, IOException {
+ return new InputSource( new ByteArrayInputStream("".getBytes()));
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/util/Logger.java b/src/main/java/com/btr/proxy/util/Logger.java
new file mode 100644
index 0000000..f434699
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/Logger.java
@@ -0,0 +1,87 @@
+package com.btr.proxy.util;
+
+import java.text.MessageFormat;
+
+/*****************************************************************************
+ * Simple logging support for the framework.
+ * You need to add an logging listener that needs to send the logging events
+ * to an backend.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Logger {
+
+ public enum LogLevel {ERROR, WARNING, INFO, TRACE, DEBUG}
+
+ /*****************************************************************************
+ * Interface for an logging backend that can be attached to the logger.
+ ****************************************************************************/
+
+ public interface LogBackEnd {
+
+ /*************************************************************************
+ * Invoked for every logging event.
+ * @param clazz the class that sends the log message.
+ * @param loglevel the logging level.
+ * @param msg the message format string.
+ * @param params the message parameters for the format string.
+ ************************************************************************/
+
+ public void log(Class<?> clazz, LogLevel loglevel, String msg, Object ...params);
+
+ /*************************************************************************
+ * Can be used to test if a given logging level is enabled.
+ * @param logLevel the loglevel to test.
+ * @return true if enabled, else false.
+ ************************************************************************/
+
+ public boolean isLogginEnabled(LogLevel logLevel);
+ }
+
+ private static LogBackEnd backend;
+
+ /*************************************************************************
+ * Gets the currently attached logging backend.
+ * @return Returns the backend.
+ ************************************************************************/
+
+ public static LogBackEnd getBackend() {
+ return backend;
+ }
+
+ /*************************************************************************
+ * Attaches a new logging backend replacing the existing one.
+ * @param backend The backend to set.
+ ************************************************************************/
+
+ public static void setBackend(LogBackEnd backend) {
+ Logger.backend = backend;
+ }
+
+ /*************************************************************************
+ * Logs a message.
+ * @param clazz the class that sends the log message.
+ * @param loglevel the logging level.
+ * @param msg the message format string.
+ * @param params the message parameters for the format string.
+ ************************************************************************/
+
+ public static void log(Class<?> clazz, LogLevel loglevel, String msg, Object ...params) {
+ System.out.println(MessageFormat.format(msg, params));
+ }
+
+ /*************************************************************************
+ * Can be used to test if a given logging level is enabled.
+ * @param logLevel the loglevel to test.
+ * @return true if enabled, else false.
+ ************************************************************************/
+
+ public static boolean isLogginEnabled(LogLevel logLevel) {
+ if (backend != null) {
+ return backend.isLogginEnabled(logLevel);
+ }
+ return false;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/util/PListParser.java b/src/main/java/com/btr/proxy/util/PListParser.java
new file mode 100644
index 0000000..b678900
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/PListParser.java
@@ -0,0 +1,544 @@
+package com.btr.proxy.util;
+
+import java.io.Closeable;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TimeZone;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Plist xml handling (serialization and deserialization)
+ * <p>
+ * <em>The xml plist dtd can be found at http://www.apple.com/DTDs/PropertyList-1.0.dtd</em>
+ * <p>
+ * The plist spec handles 8 types of objects: booleans, real, integers, dates, binary data,
+ * strings, arrays (lists) and dictionaries (maps).
+ * <p>
+ * The java Plist lib handles converting xml plists to a nested {@code Map<String, Object>}
+ * that can be trivially read from java. It also provides a simple way to convert a nested
+ * {@code Map<String, Object>} into an xml plist representation.
+ * <p>
+ * The following mapping will be done when converting from plist to <tt>Map</tt>:
+ * <pre>
+ * true/false -> Boolean
+ * real -> Double
+ * integer -> Integer/Long (depends on size, values exceeding an int will be rendered as longs)
+ * data -> byte[]
+ * string -> String
+ * array -> List
+ * dict -> Map
+ * </pre>
+ * <p>
+ * When converting from Map -> plist the conversion is as follows:
+ * <pre>
+ * Boolean -> true/false
+ * Float/Double -> real
+ * Byte/Short/Integer/Long -> integer
+ * byte[] -> data
+ * List -> array
+ * Map -> dict
+ * </pre>
+ *
+ * @author Christoffer Lerno / Modified by Bernd Rosstauscher
+ */
+public final class PListParser
+{
+ /*****************************************************************************
+ * Exception is used for XML parse problems.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+ public static class XmlParseException extends Exception {
+
+ /** Comment for <code>serialVersionUID</code>*/
+ private static final long serialVersionUID = 1L;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public XmlParseException() {
+ super();
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param msg the error message
+ ************************************************************************/
+
+ public XmlParseException(String msg) {
+ super(msg);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param msg error message
+ * @param e the cause.
+ ************************************************************************/
+
+ public XmlParseException(String msg, Exception e) {
+ super(msg, e);
+ }
+
+ }
+
+ /*****************************************************************************
+ * Small helper class representing a tree node.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+ public static class Dict implements Iterable<Map.Entry<String, Object>> {
+ private Map<String, Object> children;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public Dict() {
+ super();
+ this.children = new HashMap<String, Object>();
+ }
+
+ /*************************************************************************
+ * @param key of the child node.
+ * @return the child node, null if not existing.
+ ************************************************************************/
+
+ public Object get(String key) {
+ return this.children.get(key);
+ }
+
+ /*************************************************************************
+ * iterator
+ * @see java.lang.Iterable#iterator()
+ ************************************************************************/
+
+ public Iterator<Entry<String, Object>> iterator() {
+ return this.children.entrySet().iterator();
+ }
+
+ /*************************************************************************
+ * @return the size of this dictionary.
+ ************************************************************************/
+
+ public int size() {
+ return this.children.size();
+ }
+
+ /*************************************************************************
+ * Dumps a dictionary with all sub-nodes to the console.
+ ************************************************************************/
+
+ public void dump() {
+ System.out.println("PList");
+ dumpInternal(this, 1);
+ }
+
+ /*************************************************************************
+ * @param plist
+ * @param indent
+ ************************************************************************/
+
+ private static void dumpInternal(Dict plist, int indent) {
+ for (Map.Entry<String, Object> child : plist) {
+ if (child.getValue() instanceof Dict) {
+ for (int j = 0; j < indent; j++) {
+ System.out.print(" ");
+ }
+ System.out.println(child.getKey());
+ dumpInternal((Dict) child.getValue(), indent+1);
+ } else {
+ for (int j = 0; j < indent; j++) {
+ System.out.print(" ");
+ }
+ System.out.println(child.getKey()+" = "+child.getValue());
+ }
+ }
+
+ }
+
+ /*************************************************************************
+ * Get a node at a given path.
+ * @param path a / separated path into the plist hirarchy.
+ * @return the object located at the given path, null if it does not exist.
+ ************************************************************************/
+
+ public Object getAtPath(String path) {
+ Dict currentNode = this;
+
+ String[] pathSegments = path.trim().split("/");
+ for (int i = 0; i < pathSegments.length; i++) {
+ String segment = pathSegments[i].trim();
+ if (segment.length() == 0) {
+ continue;
+ }
+ Object o = currentNode.get(segment);
+ if (i >= pathSegments.length-1) {
+ return o;
+ }
+ if (o == null || !(o instanceof Dict)){
+ break;
+ }
+ currentNode = (Dict) o;
+ }
+ return null;
+ }
+
+ }
+
+ /**
+ * Singleton instance.
+ */
+ private final static PListParser PLIST = new PListParser();
+
+ /**
+ * All element types possible for a plist.
+ */
+ private static enum ElementType
+ {
+ INTEGER,
+ STRING,
+ REAL,
+ DATA,
+ DATE,
+ DICT,
+ ARRAY,
+ TRUE,
+ FALSE,
+ }
+
+ private static final String BASE64_STRING
+ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+ private static final char[] BASE64_CHARS = BASE64_STRING.toCharArray();
+ private final DateFormat m_dateFormat;
+ private final Map<Class<?>, ElementType> m_simpleTypes;
+
+ /**
+ * Utility method to close a closeable.
+ *
+ * @param closeable or null.
+ */
+ static void silentlyClose(Closeable closeable)
+ {
+ try
+ {
+ if (closeable != null) {
+ closeable.close();
+ }
+ }
+ catch (IOException e)
+ {
+ // Ignore
+ }
+ }
+
+ /*************************************************************************
+ * @param input
+ * @return
+ * @throws XmlParseException
+ ************************************************************************/
+
+ private static Dict parse(InputSource input)
+ throws XmlParseException {
+ try {
+ DocumentBuilder documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
+ documentBuilder.setEntityResolver(new EmptyXMLResolver());
+ Document doc = documentBuilder.parse(input);
+ Element element = doc.getDocumentElement();
+ return PLIST.parse(element);
+ } catch (ParserConfigurationException e) {
+ throw new XmlParseException("Error reading input", e);
+ } catch (SAXException e) {
+ throw new XmlParseException("Error reading input", e);
+ } catch (IOException e) {
+ throw new XmlParseException("Error reading input", e);
+ }
+ }
+
+ /**
+ * Create a nested {@code map<String, Object>} from a plist xml file using the default mapping.
+ *
+ * @param file the File containing the the plist xml.
+ * @return the resulting map as read from the plist data.
+ * @throws XmlParseException if the plist could not be properly parsed.
+ * @throws IOException if there was an issue reading the plist file.
+ */
+ public static Dict load(File file) throws XmlParseException, IOException
+ {
+ FileInputStream byteStream = new FileInputStream(file);
+ try {
+ InputSource input = new InputSource(byteStream);
+ return parse(input);
+ } finally {
+ silentlyClose(byteStream);
+ }
+ }
+
+ /**
+ * Create a plist handler.
+ */
+ PListParser()
+ {
+ this.m_dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
+ this.m_dateFormat.setTimeZone(TimeZone.getTimeZone("Z"));
+ this.m_simpleTypes = new HashMap<Class<?>, ElementType>();
+ this.m_simpleTypes.put(Integer.class, ElementType.INTEGER);
+ this.m_simpleTypes.put(Byte.class, ElementType.INTEGER);
+ this.m_simpleTypes.put(Short.class, ElementType.INTEGER);
+ this.m_simpleTypes.put(Short.class, ElementType.INTEGER);
+ this.m_simpleTypes.put(Long.class, ElementType.INTEGER);
+ this.m_simpleTypes.put(String.class, ElementType.STRING);
+ this.m_simpleTypes.put(Float.class, ElementType.REAL);
+ this.m_simpleTypes.put(Double.class, ElementType.REAL);
+ this.m_simpleTypes.put(byte[].class, ElementType.DATA);
+ this.m_simpleTypes.put(Boolean.class, ElementType.TRUE);
+ this.m_simpleTypes.put(Date.class, ElementType.DATE);
+ }
+
+ /**
+ * Parses a plist top element into a map dictionary containing all the data
+ * in the plist.
+ *
+ * @param element the top plist element.
+ * @return the resulting data tree structure.
+ * @throws XmlParseException if there was any error parsing the xml.
+ */
+ Dict parse(Element element) throws XmlParseException
+ {
+ if (!"plist".equalsIgnoreCase(element.getNodeName())) {
+ throw new XmlParseException("Expected plist top element, was: " + element.getNodeName());
+ }
+
+ Node n = element.getFirstChild();
+ while (n != null && !n.getNodeName().equals("dict")) {
+ n = n.getNextSibling();
+ }
+
+ Dict result = (Dict) parseElement(n);
+ return result;
+ }
+
+ /**
+ * Parses a (non-top) xml element.
+ *
+ * @param element the element to parse.
+ * @return the resulting object.
+ * @throws XmlParseException if there was some error in the xml.
+ */
+ private Object parseElement(Node element) throws XmlParseException
+ {
+ try
+ {
+ return parseElementRaw(element);
+ }
+ catch (Exception e)
+ {
+ throw new XmlParseException("Failed to parse: " + element.getNodeName(), e);
+ }
+ }
+
+
+ /**
+ * Parses a (non-top) xml element.
+ *
+ * @param element the element to parse.
+ * @return the resulting object.
+ * @throws ParseException if there was some error parsing the xml.
+ */
+ private Object parseElementRaw(Node element) throws ParseException
+ {
+ ElementType type = ElementType.valueOf(element.getNodeName().toUpperCase());
+ switch (type)
+ {
+ case INTEGER:
+ return parseInt(getValue(element));
+ case REAL:
+ return Double.valueOf(getValue(element));
+ case STRING:
+ return getValue(element);
+ case DATE:
+ return this.m_dateFormat.parse(getValue(element));
+ case DATA:
+ return base64decode(getValue(element));
+ case ARRAY:
+ return parseArray(element.getChildNodes());
+ case TRUE:
+ return Boolean.TRUE;
+ case FALSE:
+ return Boolean.FALSE;
+ case DICT:
+ return parseDict(element.getChildNodes());
+ default:
+ throw new RuntimeException("Unexpected type: " + element.getNodeName());
+ }
+ }
+
+ /*************************************************************************
+ * @param n
+ * @return
+ ************************************************************************/
+
+ private String getValue(Node n) {
+ StringBuilder sb = new StringBuilder();
+ Node c = n.getFirstChild();
+ while (c != null) {
+ if (c.getNodeType() == Node.TEXT_NODE) {
+ sb.append(c.getNodeValue());
+ }
+ c = c.getNextSibling();
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Parses a string into a Long or Integer depending on size.
+ *
+ * @param value the value as a string.
+ * @return the long value of this string is the value doesn't fit in an integer,
+ * otherwise the int value of the string.
+ */
+ private Number parseInt(String value)
+ {
+ Long l = Long.valueOf(value);
+ if (l.intValue() == l) {
+ return l.intValue();
+ }
+ return l;
+ }
+
+ /**
+ * Parse a list of xml elements as a plist dict.
+ *
+ * @param elements the elements to parse.
+ * @return the dict deserialized as a map.
+ * @throws ParseException if there are any problems deserializing the map.
+ */
+ private Dict parseDict(NodeList elements) throws ParseException
+ {
+ Dict dict = new Dict();
+ for (int i = 0; i < elements.getLength(); i++) {
+ Node key = elements.item(i);
+ if (key.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ if (!"key".equals(key.getNodeName())) {
+ throw new ParseException("Expected key but was " + key.getNodeName(), -1);
+ }
+ i++;
+ Node value = elements.item(i);
+ while (value.getNodeType() != Node.ELEMENT_NODE) {
+ i++;
+ value = elements.item(i);
+ }
+ Object o = parseElementRaw(value);
+ String dictName = getValue(key);
+ dict.children.put(dictName, o);
+ }
+ return dict;
+ }
+
+ /**
+ * Parse a list of xml elements as a plist array.
+ *
+ * @param elements the elements to parse.
+ * @return the array deserialized as a list.
+ * @throws ParseException if there are any problems deserializing the list.
+ */
+ private List<Object> parseArray(NodeList elements) throws ParseException
+ {
+ ArrayList<Object> list = new ArrayList<Object>();
+ for (int i = 0; i < elements.getLength(); i++) {
+ Node o = elements.item(i);
+ if (o.getNodeType() != Node.ELEMENT_NODE) {
+ continue;
+ }
+ list.add(parseElementRaw(o));
+ }
+ return list;
+ }
+
+ /**
+ * Encode an array of bytes to a string using base64 encoding.
+ *
+ * @param bytes the bytes to convert.
+ * @return the base64 representation of the bytes.
+ */
+ static String base64encode(byte[] bytes)
+ {
+ StringBuilder builder = new StringBuilder(((bytes.length + 2)/ 3) * 4);
+ for (int i = 0; i < bytes.length; i += 3)
+ {
+ byte b0 = bytes[i];
+ byte b1 = i < bytes.length - 1 ? bytes[i + 1] : 0;
+ byte b2 = i < bytes.length - 2 ? bytes[i + 2] : 0;
+ builder.append(BASE64_CHARS[(b0 & 0xFF) >> 2]);
+ builder.append(BASE64_CHARS[((b0 & 0x03) << 4) | ((b1 & 0xF0) >> 4)]);
+ builder.append(i < bytes.length - 1 ? BASE64_CHARS[((b1 & 0x0F) << 2) | ((b2 & 0xC0) >> 6)] : "=");
+ builder.append(i < bytes.length - 2 ? BASE64_CHARS[b2 & 0x3F] : "=");
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Converts a string to a byte array assuming the string uses base64-encoding.
+ *
+ * @param base64 the string to convert.
+ * @return the resulting byte array.
+ */
+ static byte[] base64decode(String base64)
+ {
+ base64 = base64.trim();
+ int endTrim = base64.endsWith("==") ? 2 : base64.endsWith("=") ? 1 : 0;
+ int length = (base64.length() / 4) * 3 - endTrim;
+ base64 = base64.replace('=', 'A');
+ byte[] result = new byte[length];
+ int stringLength = base64.length();
+ int index = 0;
+ for (int i = 0; i < stringLength; i += 4)
+ {
+ int i0 = BASE64_STRING.indexOf(base64.charAt(i));
+ int i1 = BASE64_STRING.indexOf(base64.charAt(i + 1));
+ int i2 = BASE64_STRING.indexOf(base64.charAt(i + 2));
+ int i3 = BASE64_STRING.indexOf(base64.charAt(i + 3));
+ byte b0 = (byte) ((i0 << 2) | (i1 >> 4));
+ byte b1 = (byte) ((i1 << 4) | (i2 >> 2));
+ byte b2 = (byte) ((i2 << 6) | i3);
+ result[index++] = b0;
+ if (index < length)
+ {
+ result[index++] = b1;
+ if (index < length)
+ {
+ result[index++] = b2;
+ }
+ }
+ }
+ return result;
+ }
+
+
+
+
+}
+
diff --git a/src/main/java/com/btr/proxy/util/PlatformUtil.java b/src/main/java/com/btr/proxy/util/PlatformUtil.java
new file mode 100644
index 0000000..39de5d2
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/PlatformUtil.java
@@ -0,0 +1,114 @@
+package com.btr.proxy.util;
+
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Defines some helper methods to find the correct platform.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class PlatformUtil {
+
+ public enum Platform {WIN, LINUX, MAC_OS, SOLARIS, OTHER}
+ public enum Desktop {WIN, KDE, GNOME, MAC_OS, OTHER}
+ public enum Browser {IE, FIREFOX}
+
+ /*************************************************************************
+ * Gets the platform we are currently running on.
+ * @return a platform code.
+ ************************************************************************/
+
+ public static Platform getCurrentPlattform() {
+ String osName = System.getProperty("os.name");
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detecting platform. Name is: {0}", osName);
+
+ if (osName.toLowerCase().contains("windows")) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Windows platform: {0}", osName);
+ return Platform.WIN;
+ }
+ if (osName.toLowerCase().contains("linux")) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Linux platform: {0}", osName);
+ return Platform.LINUX;
+ }
+ if (osName.startsWith("Mac OS")) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Mac OS platform: {0}", osName);
+ return Platform.MAC_OS;
+ }
+ if (osName.startsWith("SunOS")) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Solaris platform: {0}", osName);
+ return Platform.SOLARIS;
+ }
+
+ return Platform.OTHER;
+ }
+
+ /*************************************************************************
+ * Gets the ID for the platform default browser.
+ * @return a browser ID, null if no supported browser was detected.
+ ************************************************************************/
+
+ public static Browser getDefaultBrowser() {
+ // Use better logic to detect default browser?
+ if (getCurrentPlattform() == Platform.WIN) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Browser is InternetExplorer");
+ return Browser.IE;
+ } else {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Browser Firefox. Fallback?");
+ return Browser.FIREFOX;
+ }
+ }
+
+ /*************************************************************************
+ * Gets the desktop that we are running on.
+ * @return the desktop identifier.
+ ************************************************************************/
+
+ public static Desktop getCurrentDesktop() {
+ Platform platf = getCurrentPlattform();
+
+ if (platf == Platform.WIN) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Windows desktop");
+ return Desktop.WIN;
+ }
+ if (platf == Platform.MAC_OS) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Mac OS desktop");
+ return Desktop.MAC_OS;
+ }
+
+ if (platf == Platform.LINUX
+ || platf == Platform.SOLARIS
+ || platf == Platform.OTHER) {
+
+ if (isKDE()) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected KDE desktop");
+ return Desktop.KDE;
+ }
+ if (isGnome()) {
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Gnome desktop");
+ return Desktop.GNOME;
+ }
+ }
+ Logger.log(PlatformUtil.class, LogLevel.TRACE, "Detected Unknown desktop");
+ return Desktop.OTHER;
+ }
+
+ /*************************************************************************
+ * Checks if we are currently running under Gnome desktop.
+ * @return true if it is a Gnome else false.
+ ************************************************************************/
+
+ private static boolean isGnome() {
+ return System.getenv("GNOME_DESKTOP_SESSION_ID") != null;
+ }
+
+ /*************************************************************************
+ * Checks if we are currently running under KDE desktop.
+ * @return true if it is a KDE else false.
+ ************************************************************************/
+
+ private static boolean isKDE() {
+ return System.getenv("KDE_SESSION_VERSION") != null;
+ }
+
+}
diff --git a/src/main/java/com/btr/proxy/util/ProxyException.java b/src/main/java/com/btr/proxy/util/ProxyException.java
new file mode 100644
index 0000000..a3c1424
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/ProxyException.java
@@ -0,0 +1,50 @@
+package com.btr.proxy.util;
+
+/*****************************************************************************
+ * Indicates an exception in the proxy framework.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyException extends Exception {
+
+ private static final long serialVersionUID = 1L;
+
+ /*************************************************************************
+ * Constructor
+ ************************************************************************/
+
+ public ProxyException() {
+ super();
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param message the error message
+ * @param cause the causing exception for chaining exceptions.
+ ************************************************************************/
+
+ public ProxyException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param message the error message
+ ************************************************************************/
+
+ public ProxyException(String message) {
+ super(message);
+ }
+
+ /*************************************************************************
+ * Constructor
+ * @param cause the causing exception for chaining exceptions.
+ ************************************************************************/
+
+ public ProxyException(Throwable cause) {
+ super(cause);
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/util/ProxyUtil.java b/src/main/java/com/btr/proxy/util/ProxyUtil.java
new file mode 100644
index 0000000..aad9293
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/ProxyUtil.java
@@ -0,0 +1,84 @@
+package com.btr.proxy.util;
+
+import java.net.Proxy;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+import com.btr.proxy.selector.pac.PacProxySelector;
+import com.btr.proxy.selector.pac.PacScriptSource;
+import com.btr.proxy.selector.pac.UrlPacScriptSource;
+
+/*****************************************************************************
+ * Small helper class for some common utility methods.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyUtil {
+
+ public static final int DEFAULT_PROXY_PORT = 80;
+
+ private static List<Proxy> noProxyList;
+ private static Pattern pattern = Pattern.compile("\\w*?:?/*([^:/]+):?(\\d*)/?");
+
+ /*************************************************************************
+ * Parse host and port out of a proxy variable.
+ * @param proxyVar the proxy string. example: http://192.168.10.9:8080/
+ * @return a FixedProxySelector using this settings, null on parse error.
+ ************************************************************************/
+
+ public static FixedProxySelector parseProxySettings(String proxyVar) {
+ if (proxyVar == null || proxyVar.trim().length() == 0) {
+ return null;
+ }
+ Matcher matcher = pattern.matcher(proxyVar);
+ if (matcher.matches()) {
+ String host = matcher.group(1);
+ int port;
+ if (!"".equals(matcher.group(2))) {
+ port = Integer.parseInt(matcher.group(2));
+ } else {
+ port = DEFAULT_PROXY_PORT;
+ }
+ return new FixedProxySelector(host.trim(), port);
+ } else {
+ return null;
+ }
+ }
+
+ /*************************************************************************
+ * Gets an unmodifiable proxy list that will have as it's only entry an DIRECT proxy.
+ * @return a list with a DIRECT proxy in it.
+ ************************************************************************/
+
+ public static synchronized List<Proxy> noProxyList() {
+ if (noProxyList == null) {
+ ArrayList<Proxy> list = new ArrayList<Proxy>(1);
+ list.add(Proxy.NO_PROXY);
+ noProxyList = Collections.unmodifiableList(list);
+ }
+ return noProxyList;
+ }
+
+ /*************************************************************************
+ * Build a PAC proxy selector for the given URL.
+ * @param url to fetch the PAC script from.
+ * @return a PacProxySelector or null if it is not possible to build a working
+ * selector.
+ ************************************************************************/
+
+ public static PacProxySelector buildPacSelectorForUrl(String url) {
+ PacProxySelector result = null;
+ PacScriptSource pacSource = new UrlPacScriptSource(url);
+ if (pacSource.isScriptValid()) {
+ result = new PacProxySelector(pacSource);
+ }
+ return result;
+ }
+
+
+}
diff --git a/src/main/java/com/btr/proxy/util/UriFilter.java b/src/main/java/com/btr/proxy/util/UriFilter.java
new file mode 100644
index 0000000..8145e0e
--- /dev/null
+++ b/src/main/java/com/btr/proxy/util/UriFilter.java
@@ -0,0 +1,21 @@
+package com.btr.proxy.util;
+
+import java.net.URI;
+
+/*****************************************************************************
+ * Interface for an URI filter.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public interface UriFilter {
+
+ /*************************************************************************
+ * Tests an URI against a given matching criteria.
+ * @param uri the URI to test.
+ * @return true if it matches the criteria else false.
+ ************************************************************************/
+
+ public abstract boolean accept(URI uri);
+
+}
diff --git a/src/main/resources/lib/gsettings-amd64.so b/src/main/resources/lib/gsettings-amd64.so
new file mode 100644
index 0000000..24d7b7a
--- /dev/null
+++ b/src/main/resources/lib/gsettings-amd64.so
Binary files differ
diff --git a/src/main/resources/lib/gsettings-x86.so b/src/main/resources/lib/gsettings-x86.so
new file mode 100644
index 0000000..dd7f936
--- /dev/null
+++ b/src/main/resources/lib/gsettings-x86.so
Binary files differ
diff --git a/src/main/resources/lib/proxy_util_amd64.dll b/src/main/resources/lib/proxy_util_amd64.dll
new file mode 100644
index 0000000..7871fe2
--- /dev/null
+++ b/src/main/resources/lib/proxy_util_amd64.dll
Binary files differ
diff --git a/src/main/resources/lib/proxy_util_ia64.dll b/src/main/resources/lib/proxy_util_ia64.dll
new file mode 100644
index 0000000..a08a647
--- /dev/null
+++ b/src/main/resources/lib/proxy_util_ia64.dll
Binary files differ
diff --git a/src/main/resources/lib/proxy_util_w32.dll b/src/main/resources/lib/proxy_util_w32.dll
new file mode 100644
index 0000000..65d547c
--- /dev/null
+++ b/src/main/resources/lib/proxy_util_w32.dll
Binary files differ
diff --git a/src/test/java/com/btr/proxy/Examples.java b/src/test/java/com/btr/proxy/Examples.java
new file mode 100644
index 0000000..e8b52f9
--- /dev/null
+++ b/src/test/java/com/btr/proxy/Examples.java
@@ -0,0 +1,47 @@
+package com.btr.proxy;
+
+import java.net.ProxySelector;
+
+import com.btr.proxy.search.ProxySearch;
+import com.btr.proxy.search.ProxySearch.Strategy;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.PlatformUtil.Platform;
+
+/*****************************************************************************
+ * Some examples on how to use the API
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class Examples {
+
+ public void example1() {
+ ProxySearch proxySearch = ProxySearch.getDefaultProxySearch();
+ ProxySelector myProxySelector = proxySearch.getProxySelector();
+
+ ProxySelector.setDefault(myProxySelector);
+ }
+
+ public void example2() {
+ ProxySearch proxySearch = new ProxySearch();
+
+ if (PlatformUtil.getCurrentPlattform() == Platform.WIN) {
+ proxySearch.addStrategy(Strategy.IE);
+ proxySearch.addStrategy(Strategy.FIREFOX);
+ proxySearch.addStrategy(Strategy.JAVA);
+ } else
+ if (PlatformUtil.getCurrentPlattform() == Platform.LINUX) {
+ proxySearch.addStrategy(Strategy.GNOME);
+ proxySearch.addStrategy(Strategy.KDE);
+ proxySearch.addStrategy(Strategy.FIREFOX);
+ } else {
+ proxySearch.addStrategy(Strategy.OS_DEFAULT);
+ }
+
+ ProxySelector myProxySelector = proxySearch.getProxySelector();
+
+ ProxySelector.setDefault(myProxySelector);
+ }
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/TestUtil.java b/src/test/java/com/btr/proxy/TestUtil.java
new file mode 100644
index 0000000..24fe8e9
--- /dev/null
+++ b/src/test/java/com/btr/proxy/TestUtil.java
@@ -0,0 +1,61 @@
+package com.btr.proxy;
+
+import java.io.File;
+import java.net.InetSocketAddress;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.Proxy.Type;
+
+/*****************************************************************************
+ * This class defines some constants and helper methods for the unit tests.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class TestUtil {
+
+ public static final String TEST_DATA_FOLDER = "data";
+
+ public static final Proxy HTTP_TEST_PROXY = new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("http_proxy.unit-test.invalid", 8090));
+ public static final Proxy HTTPS_TEST_PROXY = new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("https_proxy.unit-test.invalid", 8091));
+ public static final Proxy FTP_TEST_PROXY = new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("ftp_proxy.unit-test.invalid", 8092));
+ public static final Proxy SOCKS_TEST_PROXY = new Proxy(Type.SOCKS, InetSocketAddress.createUnresolved("socks_proxy.unit-test.invalid", 8095));
+
+ public static final URI NO_PROXY_TEST_URI;
+ public static final URI HTTP_TEST_URI;
+ public static final URI HTTPS_TEST_URI;
+ public static final URI FTP_TEST_URI;
+ public static final URI SOCKS_TEST_URI;
+ public static final URI LOCAL_TEST_URI;
+ public static final URI SOCKET_TEST_URI;
+
+ // Setup some testing constants.
+ static {
+ try {
+ NO_PROXY_TEST_URI = new URI("http://no_proxy.unit-test.invalid/");
+ HTTP_TEST_URI = new URI("http://host1.unit-test.invalid/");
+ HTTPS_TEST_URI = new URI("https://host1.unit-test.invalid/");
+ FTP_TEST_URI = new URI("ftp://host1.unit-test.invalid/");
+ SOCKS_TEST_URI = new URI("socks://host1.unit-test.invalid/");
+ LOCAL_TEST_URI = new URI("http://myhost");
+ SOCKET_TEST_URI = new URI("socket://host1.unit-test.invalid/");
+ } catch (URISyntaxException e) {
+ throw new RuntimeException("URI error"+e.getMessage());
+ }
+ }
+
+ /*************************************************************************
+ * Switch the current user home directory to the to the given test folder.
+ * @param folder the name of the test folder.
+ ************************************************************************/
+
+ public static final void setTestDataFolder(String folder) {
+ File testTargetDir = new File(TestUtil.class.getResource("/").getFile());
+ System.setProperty("user.home",
+ testTargetDir+
+ File.separator+TestUtil.TEST_DATA_FOLDER+File.separator+folder);
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/browser/FirefoxTest.java b/src/test/java/com/btr/proxy/search/browser/FirefoxTest.java
new file mode 100644
index 0000000..2a45140
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/browser/FirefoxTest.java
@@ -0,0 +1,144 @@
+package com.btr.proxy.search.browser;
+
+import static org.junit.Assert.*;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.search.browser.firefox.FirefoxProxySearchStrategy;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Unit tests for the firefox search.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+@Ignore
+public class FirefoxTest {
+
+ /*************************************************************************
+ * Setup environment for tests.
+ ************************************************************************/
+ @BeforeClass
+ public static void setup() {
+ // Fake the OS for this tests.
+ System.setProperty("os.name", "Linux");
+ }
+
+ /*************************************************************************
+ * Test method.
+ * @throws ProxyException on error.
+ ************************************************************************/
+ @Test
+ public void testNone() throws ProxyException {
+ TestUtil.setTestDataFolder("ff3_none");
+
+ FirefoxProxySearchStrategy ff = new FirefoxProxySearchStrategy();
+ ProxySelector ps = ff.getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(Proxy.NO_PROXY , result.get(0));
+
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_manual");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttps() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_manual");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualFtp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_manual");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualSocks() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_manual");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.SOCKS_TEST_URI);
+ assertEquals(TestUtil.SOCKS_TEST_PROXY, result.get(0));
+ }
+
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testPac() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_pac_script");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testWhiteList() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("ff3_white_list");
+
+ ProxySelector ps = new FirefoxProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.NO_PROXY_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/browser/IeTest.java b/src/test/java/com/btr/proxy/search/browser/IeTest.java
new file mode 100644
index 0000000..3c7e72c
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/browser/IeTest.java
@@ -0,0 +1,59 @@
+package com.btr.proxy.search.browser;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.assertFalse;
+
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.search.browser.ie.IELocalByPassFilter;
+import com.btr.proxy.search.browser.ie.IEProxySearchStrategy;
+import com.btr.proxy.util.PlatformUtil;
+import com.btr.proxy.util.ProxyException;
+import com.btr.proxy.util.PlatformUtil.Platform;
+import com.btr.proxy.util.UriFilter;
+
+/*****************************************************************************
+ * Unit tests for the InternetExplorer search.
+ * Only limited testing as this only runs on windwos and needs a
+ * installed IE and IE proxy settings written to the registry.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class IeTest {
+
+ /*************************************************************************
+ * Test method.
+ * @throws ProxyException on proxy detection error.
+ ************************************************************************/
+ @Test
+ public void testInvoke() throws ProxyException {
+ if (Platform.WIN.equals(PlatformUtil.getCurrentPlattform())) {
+ IEProxySearchStrategy st = new IEProxySearchStrategy();
+
+ // Try at least to invoke it and test if the dll does not crash
+ st.getProxySelector();
+ }
+ }
+
+ /*************************************************************************
+ * Test method.
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException if url syntax is wrong.
+ * @throws MalformedURLException on wrong url format.
+ ************************************************************************/
+ @Test
+ public void testLocalByPassFilter() throws ProxyException, MalformedURLException, URISyntaxException {
+ UriFilter filter = new IELocalByPassFilter();
+ assertTrue(filter.accept(TestUtil.LOCAL_TEST_URI));
+ assertFalse(filter.accept(TestUtil.HTTP_TEST_URI));
+ assertFalse(filter.accept(new URL("http://123.45.55.6").toURI()));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/desktop/DesktopProxySearchTest.java b/src/test/java/com/btr/proxy/search/desktop/DesktopProxySearchTest.java
new file mode 100644
index 0000000..0f8e6be
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/desktop/DesktopProxySearchTest.java
@@ -0,0 +1,45 @@
+package com.btr.proxy.search.desktop;
+
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+import com.btr.proxy.search.ProxySearch;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Unit tests for the desktop facade search strategy.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class DesktopProxySearchTest {
+
+ /*************************************************************************
+ * Test method.
+ * @throws ProxyException on error.
+ ************************************************************************/
+ @Test
+ public void testDesktopStrategsIsWorking() throws ProxyException {
+ new DesktopProxySearchStrategy().getProxySelector();
+ }
+
+ /*************************************************************************
+ * Test method.
+ * @throws URISyntaxException on error parsing the URI.
+ * @throws ProxyException on selector error.
+ ************************************************************************/
+ @Test
+ public void emptyURIShouldNotRaiseNPE() throws URISyntaxException, ProxyException {
+ ProxySearch proxySearch = ProxySearch.getDefaultProxySearch();
+ ProxySelector myProxySelector = proxySearch.getProxySelector();
+ if (myProxySelector != null) {
+ myProxySelector.select(new URI(""));
+ }
+ }
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/desktop/win/DLLManagerTest.java b/src/test/java/com/btr/proxy/search/desktop/win/DLLManagerTest.java
new file mode 100644
index 0000000..0711a8c
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/desktop/win/DLLManagerTest.java
@@ -0,0 +1,78 @@
+package com.btr.proxy.search.desktop.win;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.AfterClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+/*****************************************************************************
+ * Unit tests for DLL loading code.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+@Ignore
+public class DLLManagerTest {
+
+ /*************************************************************************
+ * Reset system property at the end.
+ ************************************************************************/
+ @AfterClass
+ public static void teardown() {
+ System.setProperty(DLLManager.LIB_DIR_OVERRIDE, "");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws IOException on error
+ ************************************************************************/
+ @Test
+ public void testFindLibFileOverride() throws IOException {
+ String path = TestUtil.class.getResource("/").getFile() + File.separator+"data"+File.separator+"win";
+ System.setProperty(DLLManager.LIB_DIR_OVERRIDE, path);
+ File actual = DLLManager.findLibFile();
+ assertTrue(actual.getAbsolutePath().contains(path));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws IOException on error
+ ************************************************************************/
+ @Test
+ public void testFindLibFileDefault() throws IOException {
+ System.setProperty(DLLManager.LIB_DIR_OVERRIDE, "");
+ File actual = DLLManager.findLibFile();
+ assertTrue(actual.getAbsolutePath().contains("lib"+File.separator));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws IOException on error
+ ************************************************************************/
+ @Test
+ public void testCleanupTempFiles() throws IOException {
+ File f1 = File.createTempFile(DLLManager.TEMP_FILE_PREFIX+"_ABC", DLLManager.DLL_EXTENSION);
+ assertTrue(f1.exists());
+ DLLManager.cleanupTempFiles();
+ assertFalse(f1.exists());
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws IOException on error
+ ************************************************************************/
+// @Test
+// public void testFileCopy() throws IOException {
+// URL originalFile = DLLManagerTest.class.getResource("/lib/proxy_util_w32.dll");
+// File tempFile = File.createTempFile(DLLManager.TEMP_FILE_PREFIX, DLLManager.TEMP_FILE_PREFIX);
+// DLLManager.copy(originalFile.openStream(), new FileOutputStream(tempFile));
+// assertTrue(tempFile.exists() && tempFile.length() == originalFile.length());
+// tempFile.delete();
+// }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/gnome/GnomeProxySearchTest.java b/src/test/java/com/btr/proxy/search/gnome/GnomeProxySearchTest.java
new file mode 100644
index 0000000..875e2d5
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/gnome/GnomeProxySearchTest.java
@@ -0,0 +1,117 @@
+package com.btr.proxy.search.gnome;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.search.desktop.gnome.GnomeProxySearchStrategy;
+import com.btr.proxy.util.ProxyException;
+
+
+/*****************************************************************************
+ * Unit tests for the Gnome settings search strategy.
+ * For every test the "user.home" system property is switched to the test/data
+ * folder where we provide some Gnome config files prepared for the test cases.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+@Ignore
+public class GnomeProxySearchTest {
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ ************************************************************************/
+ @Test
+ public void testNone() throws ProxyException {
+ TestUtil.setTestDataFolder("gnome_none");
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("gnome_manual");
+
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttps() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("gnome_manual");
+
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualFtp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("gnome_manual");
+
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testPac() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("gnome_pac_script");
+
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testWhiteList() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("gnome_white_list");
+
+ ProxySelector ps = new GnomeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.NO_PROXY_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+
+}
diff --git a/src/test/java/com/btr/proxy/search/java/JavaProxySearchTest.java b/src/test/java/com/btr/proxy/search/java/JavaProxySearchTest.java
new file mode 100644
index 0000000..cfc473a
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/java/JavaProxySearchTest.java
@@ -0,0 +1,137 @@
+package com.btr.proxy.search.java;
+
+import static org.junit.Assert.*;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+
+/*****************************************************************************
+ * Unit tests for the Java proxy search strategy.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class JavaProxySearchTest {
+
+ private ProxySelector selector;
+
+ /*************************************************************************
+ * Setup before the tests.
+ ************************************************************************/
+ @BeforeClass
+ public static void setupClass() {
+ System.setProperty("http.proxyHost", "http_proxy.unit-test.invalid");
+ System.setProperty("http.proxyPort", "8090");
+ System.setProperty("http.nonProxyHosts", "no_proxy.unit-test.invalid");
+ System.setProperty("https.proxyHost", "https_proxy.unit-test.invalid");
+ System.setProperty("https.proxyPort", "8091");
+ System.setProperty("ftp.proxyHost", "ftp_proxy.unit-test.invalid");
+ System.setProperty("ftp.nonProxyHosts", "no_proxy.unit-test.invalid");
+ System.setProperty("ftp.proxyPort", "8092");
+ System.setProperty("socksProxyHost", "socks_proxy.unit-test.invalid");
+ System.setProperty("socksProxyPort", "8095");
+ }
+
+ /*************************************************************************
+ * Setup before the tests.
+ ************************************************************************/
+ @AfterClass
+ public static void teardownClass() {
+ System.clearProperty("http.proxyHost");
+ System.clearProperty("http.proxyPort");
+ System.clearProperty("http.nonProxyHosts");
+ System.clearProperty("https.proxyHost");
+ System.clearProperty("https.proxyPort");
+ System.clearProperty("ftp.proxyHost");
+ System.clearProperty("ftp.nonProxyHosts");
+ System.clearProperty("ftp.proxyPort");
+ System.clearProperty("socksProxyHost");
+ System.clearProperty("socksProxyPort");
+ }
+
+ /*************************************************************************
+ * Setup before every single test
+ ************************************************************************/
+ @Before
+ public void setup() {
+ this.selector = new JavaProxySearchStrategy().getProxySelector();
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testHTTP() {
+ List<Proxy> result = this.selector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on wrong URI.
+ ************************************************************************/
+ @Test
+ public void testHTTPnoProxy() throws URISyntaxException {
+ List<Proxy> result = this.selector.select(new URI("http://no_proxy.unit-test.invalid"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testHTTPS() {
+ List<Proxy> result = this.selector.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on wrong URI.
+ ************************************************************************/
+ @Test
+ public void testHTTPSnoProxy() throws URISyntaxException {
+ List<Proxy> result = this.selector.select(new URI("https://no_proxy.unit-test.invalid"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFTP() {
+ List<Proxy> result = this.selector.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on wrong URI.
+ ************************************************************************/
+ @Test
+ public void testFTPnoProxy() throws URISyntaxException {
+ List<Proxy> result = this.selector.select(new URI("ftp://no_proxy.unit-test.invalid"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testSOCKS() {
+ List<Proxy> result = this.selector.select(TestUtil.SOCKS_TEST_URI);
+ assertEquals(TestUtil.SOCKS_TEST_PROXY, result.get(0));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/search/kde/KdeProxySearchTest.java b/src/test/java/com/btr/proxy/search/kde/KdeProxySearchTest.java
new file mode 100644
index 0000000..d600aa2
--- /dev/null
+++ b/src/test/java/com/btr/proxy/search/kde/KdeProxySearchTest.java
@@ -0,0 +1,177 @@
+package com.btr.proxy.search.kde;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.search.desktop.kde.KdeProxySearchStrategy;
+import com.btr.proxy.util.ProxyException;
+
+
+/*****************************************************************************
+ * Unit tests for the KDE settings search strategy.
+ * For every test the "user.home" system property is switched to the test/data
+ * folder where we provide some KDE config files prepared for the test cases.
+ *
+ * If the env tests fail you need to set the followingenvironment variables:
+ * <p>
+ * HTTP_PROXY = http://http_proxy.unit-test.invalid:8090 <br/>
+ * HTTPS_PROXY = http://https_proxy.unit-test.invalid:8091 <br/>
+ * FTP_PROXY = http://ftp_proxy.unit-test.invalid:8092 <br/>
+ * </p>
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+@Ignore
+public class KdeProxySearchTest {
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ ************************************************************************/
+ @Test
+ public void testNone() throws ProxyException {
+ TestUtil.setTestDataFolder("kde_none");
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("kde_manual");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualHttps() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("kde_manual");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testManualFtp() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("kde_manual");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testPac() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("kde_pac_script");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ @Ignore
+ public void testEnvHttp() throws ProxyException, URISyntaxException {
+ // There is no good was to initialize environment variables in the running process.
+ //System.getenv().put("HTTP_PROXY", "http://http_proxy.unit-test.invalid:8090"); // Does not work
+ TestUtil.setTestDataFolder("kde_env");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ @Ignore
+ public void testEnvHttps() throws ProxyException, URISyntaxException {
+ // There is no good was to initialize environment variables in the running process.
+ // System.getenv().put("HTTPS_PROXY", "http://http_proxy.unit-test.invalid:8090"); // Does not work
+ TestUtil.setTestDataFolder("kde_env");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ @Ignore
+ public void testEnvFtp() throws ProxyException, URISyntaxException {
+ // there is no good was to initialize environment variables in the running process.
+ //System.getenv().put("FTP_PROXY", "http://http_proxy.unit-test.invalid:8090"); // Does not work
+ TestUtil.setTestDataFolder("kde_env");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testWhiteList() throws ProxyException, URISyntaxException {
+ TestUtil.setTestDataFolder("kde_white_list");
+
+ ProxySelector ps = new KdeProxySearchStrategy().getProxySelector();
+
+ List<Proxy> result = ps.select(TestUtil.NO_PROXY_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+}
diff --git a/src/test/java/com/btr/proxy/selector/fixed/FixedProxyTest.java b/src/test/java/com/btr/proxy/selector/fixed/FixedProxyTest.java
new file mode 100644
index 0000000..c4a7d08
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/fixed/FixedProxyTest.java
@@ -0,0 +1,56 @@
+package com.btr.proxy.selector.fixed;
+
+import static org.junit.Assert.*;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+
+/*****************************************************************************
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class FixedProxyTest {
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFixedProxy() {
+ ProxySelector ps = new FixedProxySelector("http_proxy.unit-test.invalid", 8090);
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFixedProxy2() {
+ ProxySelector ps = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFixedProxy3() {
+ ProxySelector ps = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/java/JavaProxySelectorTest.java b/src/test/java/com/btr/proxy/selector/java/JavaProxySelectorTest.java
new file mode 100644
index 0000000..5701e25
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/java/JavaProxySelectorTest.java
@@ -0,0 +1,28 @@
+package com.btr.proxy.selector.java;
+
+import static org.junit.Assert.*;
+
+import java.net.ProxySelector;
+
+import org.junit.Test;
+
+import com.btr.proxy.search.java.JavaProxySearchStrategy;
+
+/*****************************************************************************
+ * Some unit tests for the Java Proxy search strategy.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class JavaProxySelectorTest {
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void withoutSystemPropertyShouldReturnNull() {
+ ProxySelector ps = new JavaProxySearchStrategy().getProxySelector();
+ assertNull(ps);
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/misc/ProtocolDispatchTest.java b/src/test/java/com/btr/proxy/selector/misc/ProtocolDispatchTest.java
new file mode 100644
index 0000000..da27327
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/misc/ProtocolDispatchTest.java
@@ -0,0 +1,87 @@
+package com.btr.proxy.selector.misc;
+
+import static org.junit.Assert.*;
+
+import java.net.Proxy;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+
+/*****************************************************************************
+ * Unit Tests for the ProtocolDispatchSelector
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProtocolDispatchTest {
+
+ private static ProtocolDispatchSelector ps;
+
+ @BeforeClass
+ public static void setup() {
+ ps = new ProtocolDispatchSelector();
+ ps.setSelector("http", new FixedProxySelector(TestUtil.HTTP_TEST_PROXY));
+ ps.setSelector("https", new FixedProxySelector(TestUtil.HTTPS_TEST_PROXY));
+ ps.setSelector("ftp", new FixedProxySelector(TestUtil.FTP_TEST_PROXY));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDispatchHttp() {
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDispatchHttps() {
+ List<Proxy> result = ps.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDispatchFtp() {
+ List<Proxy> result = ps.select(TestUtil.FTP_TEST_URI);
+ assertEquals(TestUtil.FTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testRemove() {
+ ProtocolDispatchSelector px = new ProtocolDispatchSelector();
+ FixedProxySelector selector = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ px.setSelector("http", selector);
+ assertEquals(selector, px.getSelector("http"));
+ px.removeSelector("http");
+ assertNull(px.getSelector("http"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFallback() {
+ ProtocolDispatchSelector px = new ProtocolDispatchSelector();
+ FixedProxySelector selector = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ px.setFallbackSelector(selector);
+
+ List<Proxy> proxies = px.select(TestUtil.HTTP_TEST_URI);
+
+ assertEquals(TestUtil.HTTP_TEST_PROXY, proxies.get(0));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/misc/ProxyListFallbackSelectorTest.java b/src/test/java/com/btr/proxy/selector/misc/ProxyListFallbackSelectorTest.java
new file mode 100644
index 0000000..12c172c
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/misc/ProxyListFallbackSelectorTest.java
@@ -0,0 +1,94 @@
+package com.btr.proxy.selector.misc;
+
+import static org.junit.Assert.*;
+
+import java.io.IOException;
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+/*****************************************************************************
+ * Unit Tests for the ProxyListFallbackSelector
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2011
+ ****************************************************************************/
+
+public class ProxyListFallbackSelectorTest {
+
+ private ProxyListFallbackSelector selector;
+
+ /*************************************************************************
+ * Setup before tests.
+ ************************************************************************/
+ @Before
+ public void setup() {
+ this.selector = new ProxyListFallbackSelector(
+ new ProxySelector() {
+ @Override
+ public List<Proxy> select(URI uri) {
+ return Arrays.asList(TestUtil.HTTP_TEST_PROXY, TestUtil.HTTPS_TEST_PROXY, Proxy.NO_PROXY);
+ }
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used on the delegate
+ }
+ });
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testList() {
+ List<Proxy> result = this.selector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(1));
+ assertEquals(Proxy.NO_PROXY, result.get(2));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testFailedProxy() {
+ this.selector.connectFailed(
+ TestUtil.HTTP_TEST_URI,
+ TestUtil.HTTP_TEST_PROXY.address(),
+ new IOException("TEST"));
+
+ List<Proxy> result = this.selector.select(TestUtil.HTTP_TEST_URI);
+
+ assertEquals(TestUtil.HTTPS_TEST_PROXY, result.get(0));
+ assertEquals(Proxy.NO_PROXY, result.get(1));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws InterruptedException if the test wait period was interrupted
+ ************************************************************************/
+ @Test
+ public void testFailedProxyRetry() throws InterruptedException {
+ this.selector.setRetryAfterMs(100);
+ this.selector.connectFailed(
+ TestUtil.HTTP_TEST_URI,
+ TestUtil.HTTP_TEST_PROXY.address(),
+ new IOException("TEST"));
+
+ List<Proxy> result = this.selector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(2, result.size());
+
+ Thread.sleep(200);
+ result = this.selector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(3, result.size());
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/JavaxPacScriptParserTest.java b/src/test/java/com/btr/proxy/selector/pac/JavaxPacScriptParserTest.java
new file mode 100644
index 0000000..ea963f0
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/JavaxPacScriptParserTest.java
@@ -0,0 +1,119 @@
+package com.btr.proxy.selector.pac;
+
+import java.net.MalformedURLException;
+import java.util.Calendar;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Tests for the javax.script PAC script parser.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class JavaxPacScriptParserTest {
+
+ /*************************************************************************
+ * Set calendar for date and time base tests.
+ * Current date for all tests is: 15. December 1994 12:00.00
+ * its a Thursday
+ ************************************************************************/
+ @BeforeClass
+ public static void setup() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, 1994);
+ cal.set(Calendar.MONTH, Calendar.DECEMBER);
+ cal.set(Calendar.DAY_OF_MONTH, 15);
+ cal.set(Calendar.HOUR_OF_DAY, 12);
+ cal.set(Calendar.MINUTE, 00);
+ cal.set(Calendar.SECOND, 00);
+ cal.set(Calendar.MILLISECOND, 00);
+
+ // TODO Rossi 26.08.2010 need to fake time
+ // JavaxPacScriptParser.setCurrentTime(cal);
+ }
+
+ /*************************************************************************
+ * Cleanup after the tests.
+ ************************************************************************/
+ @AfterClass
+ public static void teadDown() {
+ //JavaxPacScriptParser.setCurrentTime(null);
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptExecution() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new JavaxPacScriptParser(new UrlPacScriptSource(toUrl("test1.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testCommentsInScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new JavaxPacScriptParser(new UrlPacScriptSource(toUrl("test2.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ @Ignore //Test deactivated because it will not run in Java 1.5 and time based test are unstable
+ public void testScriptWeekDayScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new JavaxPacScriptParser(new UrlPacScriptSource(toUrl("testWeekDay.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ @Ignore //Test deactivated because it will not run in Java 1.5 and time based test are unstable
+ public void testDateRangeScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new JavaxPacScriptParser(new UrlPacScriptSource(toUrl("testDateRange.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ @Ignore //Test deactivated because it will not run in Java 1.5 and time based test are unstable
+ public void testTimeRangeScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new JavaxPacScriptParser(new UrlPacScriptSource(toUrl("testTimeRange.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Helper method to build the url to the given test file
+ * @param testFile the name of the test file.
+ * @return the URL.
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private String toUrl(String testFile) throws MalformedURLException {
+ return JavaxPacScriptParserTest.class.getResource("/" + TestUtil.TEST_DATA_FOLDER + "/pac/" + testFile).toString();
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/PacPerProtocolTest.java b/src/test/java/com/btr/proxy/selector/pac/PacPerProtocolTest.java
new file mode 100644
index 0000000..c0b9c67
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/PacPerProtocolTest.java
@@ -0,0 +1,47 @@
+package com.btr.proxy.selector.pac;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+
+public class PacPerProtocolTest {
+
+ /*************************************************************************
+ * Test the PAC selector for a given protocol.
+ * @throws IOException of read error.
+ * @throws URISyntaxException on uri syntax error.
+ ************************************************************************/
+ @Test
+ public void testPacForSocket() throws IOException, URISyntaxException {
+
+ new URI("socket://host1.unit-test.invalid/");
+
+ List<Proxy> result = new PacProxySelector(
+ new UrlPacScriptSource(toUrl("test1.pac"))).select(TestUtil.SOCKET_TEST_URI);
+
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Helper method to build the url to the given test file
+ * @param testFile the name of the test file.
+ * @return the URL.
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private String toUrl(String testFile) throws MalformedURLException {
+ return PacPerProtocolTest.class.getResource("/" + TestUtil.TEST_DATA_FOLDER + "/pac/" + testFile).toString();
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/PacProxyDebugging.java b/src/test/java/com/btr/proxy/selector/pac/PacProxyDebugging.java
new file mode 100644
index 0000000..695bc3a
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/PacProxyDebugging.java
@@ -0,0 +1,103 @@
+package com.btr.proxy.selector.pac;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.LineNumberReader;
+import java.net.ProxySelector;
+import java.net.URL;
+import java.net.URLConnection;
+import java.text.MessageFormat;
+
+import com.btr.proxy.search.ProxySearch;
+import com.btr.proxy.util.Logger;
+import com.btr.proxy.util.Logger.LogLevel;
+
+/*****************************************************************************
+ * Test program submitted to test the issue 27 with PAC proxy selector that is
+ * while downloading the PAC file invoking itself. This has lead to a endless
+ * loop. The issue is now solved but I keep this test program for future PAC
+ * testing.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class PacProxyDebugging {
+
+ private static final String TEST_URL = "http://www.asetune.com";
+
+ /*************************************************************************
+ * Setup a console logger.
+ ************************************************************************/
+
+ private void installLogger() {
+ Logger.setBackend(new Logger.LogBackEnd() {
+ public void log(Class<?> clazz, LogLevel loglevel, String msg, Object... params) {
+ System.out.println(loglevel + "\t"+ MessageFormat.format(msg, params));
+ }
+
+ public boolean isLogginEnabled(LogLevel logLevel) {
+ return true;
+ }
+ });
+ }
+
+ /*************************************************************************
+ * Main entry point for the test application.
+ * @param args the command line arguments.
+ ************************************************************************/
+
+ public static void main(String[] args) {
+ // System.setProperty("http.proxyHost", "10.65.12.21");
+ // System.setProperty("http.proxyPort", "8080");
+ // System.setProperty("java.net.useSystemProxies", "true");
+
+ PacProxyDebugging pt = new PacProxyDebugging();
+ pt.installLogger();
+
+ ProxySearch proxySearch = ProxySearch.getDefaultProxySearch();
+
+ // ProxySearch proxySearch = new ProxySearch();
+ // proxySearch.addStrategy(Strategy.JAVA);
+ // proxySearch.addStrategy(Strategy.BROWSER);
+ // proxySearch.addStrategy(Strategy.OS_DEFAULT);
+ // proxySearch.addStrategy(Strategy.ENV_VAR);
+
+ ProxySelector myProxySelector = proxySearch.getProxySelector();
+
+ ProxySelector.setDefault(myProxySelector);
+ System.out.println("Using proxy selector: " + myProxySelector);
+
+ // String webAddress = "http://www.google.com";
+ String webAddress = TEST_URL;
+ try {
+ URL url = new URL(webAddress);
+ // List<Proxy> result = myProxySelector.select(url.toURI());
+ // if (result == null || result.size() == 0)
+ // {
+ // System.out.println("No proxy found for this url.");
+ // return;
+ // }
+ // System.out.println("Proxy Settings found using 'xxx' strategy.\n"
+ // +
+ // "Proxy used for URL is: "+result.get(0));
+
+ System.out.println("Now open a connection to the url: "+ webAddress);
+ System.out.println("==============================================");
+
+ // open the connection and prepare it to POST
+ URLConnection conn = url.openConnection();
+ conn.setConnectTimeout(10 * 1000);
+
+ // Return the response
+ InputStream in = conn.getInputStream();
+ LineNumberReader lr = new LineNumberReader(new InputStreamReader(in));
+ String line;
+ while ((line = lr.readLine()) != null) {
+ System.out.println("response line " + lr.getLineNumber() + ": " + line);
+ }
+ System.out.println("---- END -------------------------------------");
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+}
diff --git a/src/test/java/com/btr/proxy/selector/pac/PacProxySelectorTest.java b/src/test/java/com/btr/proxy/selector/pac/PacProxySelectorTest.java
new file mode 100644
index 0000000..a08f9c1
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/PacProxySelectorTest.java
@@ -0,0 +1,133 @@
+package com.btr.proxy.selector.pac;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.Proxy;
+import java.net.Proxy.Type;
+import java.net.ProxySelector;
+import java.net.SocketAddress;
+import java.net.URI;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.util.ProxyException;
+
+
+/*****************************************************************************
+ * Tests for the Pac script parser and proxy selector.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class PacProxySelectorTest {
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptExecution() throws ProxyException, MalformedURLException {
+ List<Proxy> result = new PacProxySelector(
+ new UrlPacScriptSource(toUrl("test1.pac"))).select(TestUtil.HTTP_TEST_URI);
+
+ assertEquals(TestUtil.HTTP_TEST_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptExecution2() throws ProxyException, MalformedURLException {
+ PacProxySelector pacProxySelector = new PacProxySelector(
+ new UrlPacScriptSource(toUrl("test2.pac")));
+ List<Proxy> result = pacProxySelector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+
+ result = pacProxySelector.select(TestUtil.HTTPS_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test download fix to prevent infinite loop.
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void pacDownloadFromURLShouldNotUseProxy() throws ProxyException, MalformedURLException {
+ ProxySelector oldOne = ProxySelector.getDefault();
+ try {
+ ProxySelector.setDefault(new ProxySelector() {
+ @Override
+ public List<Proxy> select(URI uri) {
+ throw new IllegalStateException("Should not download via proxy");
+ }
+
+ @Override
+ public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
+ // Not used
+ }
+ });
+
+ PacProxySelector pacProxySelector = new PacProxySelector(
+ new UrlPacScriptSource("http://www.test.invalid/wpad.pac"));
+ pacProxySelector.select(TestUtil.HTTPS_TEST_URI);
+ } finally {
+ ProxySelector.setDefault(oldOne);
+ }
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptMuliProxy() throws ProxyException, MalformedURLException {
+ PacProxySelector pacProxySelector = new PacProxySelector(
+ new UrlPacScriptSource(toUrl("testMultiProxy.pac")));
+ List<Proxy> result = pacProxySelector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(2, result.size());
+ assertEquals(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("my-proxy.com", 80)), result.get(0));
+ assertEquals(new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("my-proxy2.com", 8080)), result.get(1));
+ }
+
+ /*************************************************************************
+ * Test method for the override local IP feature.
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testLocalIPOverride() throws ProxyException, MalformedURLException {
+ System.setProperty(PacScriptMethods.OVERRIDE_LOCAL_IP, "123.123.123.123");
+ try {
+ PacProxySelector pacProxySelector = new PacProxySelector(
+ new UrlPacScriptSource(toUrl("testLocalIP.pac")));
+ List<Proxy> result = pacProxySelector.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(result.get(0), new Proxy(Type.HTTP, InetSocketAddress.createUnresolved("123.123.123.123", 8080)));
+ } finally {
+ System.setProperty(PacScriptMethods.OVERRIDE_LOCAL_IP, "");
+ }
+
+ }
+
+ /*************************************************************************
+ * Helper method to build the url to the given test file
+ * @param testFile the name of the test file.
+ * @return the URL.
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private String toUrl(String testFile) throws MalformedURLException {
+ return PacProxySelectorTest.class.getResource("/" + TestUtil.TEST_DATA_FOLDER + "/pac/" + testFile).toString();
+ }
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/PacScriptMethodsTest.java b/src/test/java/com/btr/proxy/selector/pac/PacScriptMethodsTest.java
new file mode 100644
index 0000000..0d69a1e
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/PacScriptMethodsTest.java
@@ -0,0 +1,165 @@
+package com.btr.proxy.selector.pac;
+
+import static org.junit.Assert.assertEquals;
+
+import java.net.Inet4Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.Calendar;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+
+/*****************************************************************************
+ * Tests for the global PAC script methods that are used as context inside of
+ * the scripts.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class PacScriptMethodsTest {
+
+ /*************************************************************************
+ * Get a methods implementation with a calendar for date and time base tests
+ * set to a hardcoded data.
+ * Current date for all tests is: 15. December 1994 12:00.00
+ * its a Thursday
+ ************************************************************************/
+
+ private PacScriptMethods buildParser() {
+ PacScriptMethods result = new PacScriptMethods();
+
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, 1994);
+ cal.set(Calendar.MONTH, Calendar.DECEMBER);
+ cal.set(Calendar.DAY_OF_MONTH, 15);
+ cal.set(Calendar.HOUR_OF_DAY, 12);
+ cal.set(Calendar.MINUTE, 00);
+ cal.set(Calendar.SECOND, 00);
+ cal.set(Calendar.MILLISECOND, 00);
+ result.setCurrentTime(cal);
+
+ return result;
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDnsDomainIs() {
+ assertEquals(true, buildParser().dnsDomainIs("host1.unit-test.invalid", "unit-test.invalid"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDnsDomainLevels() {
+ assertEquals(2, buildParser().dnsDomainLevels(TestUtil.HTTP_TEST_URI.toString()));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws UnknownHostException on resolve error.
+ ************************************************************************/
+ @Test
+ public void testDnsResolve() throws UnknownHostException {
+ InetAddress adr = Inet4Address.getLocalHost();
+ assertEquals(adr.getHostAddress(), buildParser().dnsResolve(adr.getHostName()));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testIsInNet() {
+ assertEquals(true, buildParser().isInNet("192.168.0.122", "192.168.0.0", "255.255.255.0"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testIsInNet2() {
+ assertEquals(true, buildParser().isInNet("10.13.75.47", "10.13.72.0", "255.255.252.0"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testIsPlainHostName() {
+ assertEquals(false, buildParser().isPlainHostName("host1.unit-test.invalid"));
+ assertEquals(true, buildParser().isPlainHostName("host1"));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws UnknownHostException on resolve error.
+ ************************************************************************/
+ @Test
+ public void testIsResolveable() throws UnknownHostException {
+ InetAddress adr = Inet4Address.getLocalHost();
+ assertEquals(true, buildParser().isResolvable(adr.getHostName()));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testLocalHostOrDomainIs() {
+ assertEquals(true, buildParser().localHostOrDomainIs("host1.unit-test.invalid", "host1.unit-test.invalid"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testShExpMatch() {
+ assertEquals(true, buildParser().shExpMatch("host1.unit-test.invalid", "host1.unit-test.*"));
+ assertEquals(true, buildParser().shExpMatch("host1.unit-test.invalid", "*.unit-test.invalid"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testWeekdayRange() {
+ assertEquals(true, buildParser().weekdayRange("MON", "SUN", "GMT"));
+ assertEquals(true, buildParser().weekdayRange("SUN", "SAT", null));
+ assertEquals(false, buildParser().weekdayRange("MON", "WED", null));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testDateRange() {
+ assertEquals(true, buildParser().dateRange(15, "undefined", "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange(15, "DEC", "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange(15, "DEC", 1994, "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange(15, 17, "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange("OCT", "JAN", "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange(1994, 1994, "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().dateRange(1, "DEC", 1994, 1, "JAN", 1995, "GTM"));
+
+ assertEquals(false, buildParser().dateRange(16, "DEC", 1994, 1, "JAN", 1995, "GTM"));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testTimeRange() {
+ assertEquals(true, buildParser().timeRange(12, "undefined", "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().timeRange(11, 13, "undefined", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().timeRange(11, 13, "gmt", "undefined", "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().timeRange(11, 30, 13, 30, "undefined", "undefined", "undefined"));
+ assertEquals(true, buildParser().timeRange(11, 30, 15, 13, 30, 15, "undefined"));
+ assertEquals(true, buildParser().timeRange(11, 30, 15, 13, 30, 15, "GMT"));
+
+ assertEquals(false, buildParser().timeRange(12, 50, 00, 9, 30, 00, "GMT"));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/RhinoPacScriptParserTest.java b/src/test/java/com/btr/proxy/selector/pac/RhinoPacScriptParserTest.java
new file mode 100644
index 0000000..f7d245a
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/RhinoPacScriptParserTest.java
@@ -0,0 +1,114 @@
+package com.btr.proxy.selector.pac;
+
+import java.net.MalformedURLException;
+import java.util.Calendar;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.util.ProxyException;
+
+/*****************************************************************************
+ * Tests for the Rhino PAC script parser.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class RhinoPacScriptParserTest {
+
+ /*************************************************************************
+ * Set calendar for date and time base tests.
+ * Current date for all tests is: 15. December 1994 12:00.00
+ * its a Thursday
+ ************************************************************************/
+ @BeforeClass
+ public static void setup() {
+ Calendar cal = Calendar.getInstance();
+ cal.set(Calendar.YEAR, 1994);
+ cal.set(Calendar.MONTH, Calendar.DECEMBER);
+ cal.set(Calendar.DAY_OF_MONTH, 15);
+ cal.set(Calendar.HOUR_OF_DAY, 12);
+ cal.set(Calendar.MINUTE, 00);
+ cal.set(Calendar.SECOND, 00);
+ cal.set(Calendar.MILLISECOND, 00);
+
+ RhinoPacScriptParser.setCurrentTime(cal);
+ }
+
+ /*************************************************************************
+ * Cleanup after the tests.
+ ************************************************************************/
+ @AfterClass
+ public static void teadDown() {
+ RhinoPacScriptParser.setCurrentTime(null);
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptExecution() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new RhinoPacScriptParser(new UrlPacScriptSource(toUrl("test1.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testCommentsInScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new RhinoPacScriptParser(new UrlPacScriptSource(toUrl("test2.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testScriptWeekDayScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new RhinoPacScriptParser(new UrlPacScriptSource(toUrl("testWeekDay.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testDateRangeScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new RhinoPacScriptParser(new UrlPacScriptSource(toUrl("testDateRange.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws ProxyException on proxy detection error.
+ * @throws MalformedURLException on URL erros
+ ************************************************************************/
+ @Test
+ public void testTimeRangeScript() throws ProxyException, MalformedURLException {
+ PacScriptParser p = new RhinoPacScriptParser(new UrlPacScriptSource(toUrl("testTimeRange.pac")));
+ p.evaluate(TestUtil.HTTP_TEST_URI.toString(), "host1.unit-test.invalid");
+ }
+
+ /*************************************************************************
+ * Helper method to build the url to the given test file
+ * @param testFile the name of the test file.
+ * @return the URL.
+ * @throws MalformedURLException
+ ************************************************************************/
+
+ private String toUrl(String testFile) throws MalformedURLException {
+ return RhinoPacScriptParserTest.class.getResource("/" + TestUtil.TEST_DATA_FOLDER + "/pac/" + testFile).toString();
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/pac/UrlPacScriptSourceTest.java b/src/test/java/com/btr/proxy/selector/pac/UrlPacScriptSourceTest.java
new file mode 100644
index 0000000..61c8cd0
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/pac/UrlPacScriptSourceTest.java
@@ -0,0 +1,34 @@
+package com.btr.proxy.selector.pac;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Test;
+
+/*****************************************************************************
+ * Tests for the UrlPacScriptSource.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class UrlPacScriptSourceTest {
+
+ /*************************************************************************
+ * Unit Test
+ ************************************************************************/
+ @Test
+ public void testHttpCharsetParser() {
+ UrlPacScriptSource scriptSource = new UrlPacScriptSource("");
+ String charset = scriptSource.parseCharsetFromHeader("application/x-ns-proxy-autoconfig; charset=UTF-8");
+ assertEquals("UTF-8", charset);
+ }
+
+ /*************************************************************************
+ * Unit Test
+ ************************************************************************/
+ @Test
+ public void testHttpCharsetParserDefault() {
+ UrlPacScriptSource scriptSource = new UrlPacScriptSource("");
+ String charset = scriptSource.parseCharsetFromHeader("application/octet-stream;");
+ assertEquals("ISO-8859-1", charset);
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/selector/whitelist/NoProxyTest.java b/src/test/java/com/btr/proxy/selector/whitelist/NoProxyTest.java
new file mode 100644
index 0000000..9d187ba
--- /dev/null
+++ b/src/test/java/com/btr/proxy/selector/whitelist/NoProxyTest.java
@@ -0,0 +1,108 @@
+package com.btr.proxy.selector.whitelist;
+
+import static org.junit.Assert.*;
+
+import java.net.Proxy;
+import java.net.ProxySelector;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+
+/*****************************************************************************
+ * Some unit tests for the white list selector.
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class NoProxyTest {
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testWhiteList() {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("no_prox.*", delegate);
+
+ assertEquals(delegate.select(TestUtil.HTTP_TEST_URI).get(0), ps.select(TestUtil.HTTP_TEST_URI).get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testWhiteList2() {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("*.unit-test.invalid", delegate);
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testWhiteList3() throws URISyntaxException {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("*.unit-test.invalid, localhost, 127.0.0.1", delegate);
+
+ List<Proxy> result = ps.select(new URI("http://localhost:65/getDocument"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+
+ result = ps.select(new URI("http://127.0.0.1:65/getDocument"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testWhiteList4() {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("*.unit-test.invalid, ", delegate);
+
+ List<Proxy> result = ps.select(TestUtil.HTTP_TEST_URI);
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testWhiteList5() throws URISyntaxException {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("*.unit-test.invalid localhost 127.0.0.1", delegate);
+
+ List<Proxy> result = ps.select(new URI("http://localhost:65/getDocument"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+
+ result = ps.select(new URI("http://127.0.0.1:65/getDocument"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testIpRange() throws URISyntaxException {
+ ProxySelector delegate = new FixedProxySelector(TestUtil.HTTP_TEST_PROXY);
+ ProxyBypassListSelector ps = new ProxyBypassListSelector("192.168.0.0/24", delegate);
+
+ List<Proxy> result = ps.select(new URI("http://192.168.0.100:81/test.data"));
+ assertEquals(Proxy.NO_PROXY, result.get(0));
+
+ result = ps.select(new URI("http://192.168.1.100:81/test.data"));
+ assertEquals(delegate.select(TestUtil.HTTP_TEST_URI).get(0), result.get(0));
+ }
+
+}
+
diff --git a/src/test/java/com/btr/proxy/util/PListParserTest.java b/src/test/java/com/btr/proxy/util/PListParserTest.java
new file mode 100644
index 0000000..98ea57b
--- /dev/null
+++ b/src/test/java/com/btr/proxy/util/PListParserTest.java
@@ -0,0 +1,101 @@
+package com.btr.proxy.util;
+
+import static org.junit.Assert.*;
+
+import java.io.File;
+import java.io.IOException;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.util.PListParser.Dict;
+import com.btr.proxy.util.PListParser.XmlParseException;
+
+/*****************************************************************************
+ *
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class PListParserTest {
+
+ private static final String TEST_SETTINGS = TestUtil.class.getResource("/").getFile() + "data"+File.separator+"osx"+File.separator+"osx_all.plist";
+
+ private static Dict pList;
+
+ /*************************************************************************
+ * Setup the dictionary from the test data file.
+ ************************************************************************/
+ @BeforeClass
+ public static void setupClass() throws XmlParseException,
+ IOException {
+ pList = PListParser.load(new File(TEST_SETTINGS));
+ }
+
+ /**
+ * Test method for {@link com.btr.proxy.util.PListParser#load(java.io.File)}.
+ */
+ @Test
+ public void testLoadFile() {
+ pList.dump();
+ assertTrue(pList.size() > 0);
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+
+ @Test
+ public void testStructure() {
+ String currentSet = (String) pList.get("CurrentSet");
+ assertNotNull(currentSet);
+ Object networkServices = pList.get("NetworkServices");
+ assertTrue(networkServices instanceof Dict);
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+
+ @Test
+ public void testNavigate() {
+ Object result = pList.getAtPath("NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB/AppleTalk");
+ assertNotNull(result);
+ assertTrue(result instanceof Dict);
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+
+ @Test
+ public void testNavigate2() {
+ Object result = pList.getAtPath("/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB/AppleTalk");
+ assertNotNull(result);
+ assertTrue(result instanceof Dict);
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+
+ @Test
+ public void testNavigate3() {
+ Object result = pList.getAtPath("/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB/AppleTalk/");
+ assertNotNull(result);
+ assertTrue(result instanceof Dict);
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+
+ @Test
+ public void testNavigate4() {
+ Object result = pList.getAtPath("/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB/AppleTalkXXX/");
+ assertNull(result);
+ }
+
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/util/ProxyUtilTest.java b/src/test/java/com/btr/proxy/util/ProxyUtilTest.java
new file mode 100644
index 0000000..d8e10c5
--- /dev/null
+++ b/src/test/java/com/btr/proxy/util/ProxyUtilTest.java
@@ -0,0 +1,78 @@
+package com.btr.proxy.util;
+
+import static junit.framework.Assert.*;
+
+import java.net.Proxy;
+import java.util.List;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.selector.fixed.FixedProxySelector;
+
+
+/*****************************************************************************
+ * Unit tests for proxy util methods
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class ProxyUtilTest {
+
+ /*************************************************************************
+ * Test parsing method.
+ ************************************************************************/
+
+ @Test
+ public void testParseProxySettings() {
+ FixedProxySelector rs = ProxyUtil.parseProxySettings("http://http_proxy.unit-test.invalid/");
+ List<Proxy> psList = rs.select(TestUtil.HTTP_TEST_URI);
+ assertEquals("HTTP @ http_proxy.unit-test.invalid:80", psList.get(0).toString());
+ }
+
+ /*************************************************************************
+ * Test parsing method.
+ ************************************************************************/
+
+ @Test
+ public void testParseProxySettings2() {
+ FixedProxySelector rs = ProxyUtil.parseProxySettings("http://http_proxy.unit-test.invalid:8080/");
+ List<Proxy> psList = rs.select(TestUtil.HTTP_TEST_URI);
+ assertEquals("HTTP @ http_proxy.unit-test.invalid:8080", psList.get(0).toString());
+ }
+
+ /*************************************************************************
+ * Test parsing method.
+ ************************************************************************/
+
+ @Test
+ public void testParseProxySettings3() {
+ FixedProxySelector rs = ProxyUtil.parseProxySettings("http_proxy.unit-test.invalid");
+ List<Proxy> psList = rs.select(TestUtil.HTTP_TEST_URI);
+ assertEquals("HTTP @ http_proxy.unit-test.invalid:80", psList.get(0).toString());
+ }
+
+ /*************************************************************************
+ * Test parsing method.
+ ************************************************************************/
+
+ @Test
+ public void testParseProxySettings4() {
+ FixedProxySelector rs = ProxyUtil.parseProxySettings("http_proxy.unit-test.invalid:8080");
+ List<Proxy> psList = rs.select(TestUtil.HTTP_TEST_URI);
+ assertEquals("HTTP @ http_proxy.unit-test.invalid:8080", psList.get(0).toString());
+ }
+
+ /*************************************************************************
+ * Test parsing method.
+ ************************************************************************/
+
+ @Test
+ public void testParseProxySettings5() {
+ FixedProxySelector rs = ProxyUtil.parseProxySettings("192.123.123.1:8080");
+ List<Proxy> psList = rs.select(TestUtil.HTTP_TEST_URI);
+ assertEquals("HTTP @ 192.123.123.1:8080", psList.get(0).toString());
+ }
+
+
+}
+
diff --git a/src/test/java/com/btr/proxy/util/UriFilterTest.java b/src/test/java/com/btr/proxy/util/UriFilterTest.java
new file mode 100644
index 0000000..f1750ad
--- /dev/null
+++ b/src/test/java/com/btr/proxy/util/UriFilterTest.java
@@ -0,0 +1,122 @@
+package com.btr.proxy.util;
+
+import static org.junit.Assert.*;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import org.junit.Test;
+
+import com.btr.proxy.TestUtil;
+import com.btr.proxy.selector.whitelist.HostnameFilter;
+import com.btr.proxy.selector.whitelist.IpRangeFilter;
+import com.btr.proxy.selector.whitelist.HostnameFilter.Mode;
+
+
+/*****************************************************************************
+ * Some unit tests for the UriFilter class.
+ * @author Bernd Rosstauscher (proxyvole@rosstauscher.de) Copyright 2009
+ ****************************************************************************/
+
+public class UriFilterTest {
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testBeginsWithFilter1() {
+ UriFilter filter = new HostnameFilter(Mode.BEGINS_WITH, "no_proxy");
+
+ assertTrue(filter.accept(TestUtil.NO_PROXY_TEST_URI));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testBeginsWithFilter2() {
+ UriFilter filter = new HostnameFilter(Mode.BEGINS_WITH, "no_proxy");
+
+ assertFalse(filter.accept(TestUtil.HTTP_TEST_URI));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testBeginsWithFilter3() throws URISyntaxException {
+ UriFilter filter = new HostnameFilter(Mode.BEGINS_WITH, "192.168.0");
+
+ assertTrue(filter.accept(new URI("http://192.168.0.100:81/test.data")));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testBeginsWithFilter4() throws URISyntaxException {
+ UriFilter filter = new HostnameFilter(Mode.BEGINS_WITH, "192.168.0");
+
+ assertFalse(filter.accept(new URI("http://192.168.1.100:81/test.data")));
+ }
+
+ /*************************************************************************
+ * Test method
+ ************************************************************************/
+ @Test
+ public void testBeginsWithFilter() {
+ UriFilter filter = new HostnameFilter(Mode.BEGINS_WITH, "no_proxy");
+
+ assertTrue(filter.accept(TestUtil.NO_PROXY_TEST_URI));
+ assertFalse(filter.accept(TestUtil.HTTP_TEST_URI));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testEndsWithFilter() throws URISyntaxException {
+ UriFilter filter = new HostnameFilter(Mode.ENDS_WITH, ".unit-test.invalid");
+
+ assertTrue(filter.accept(TestUtil.NO_PROXY_TEST_URI));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testEndsWithFilter2() throws URISyntaxException {
+ UriFilter filter = new HostnameFilter(Mode.ENDS_WITH, ".unit-test.invalid");
+
+ assertFalse(filter.accept(new URI("http://test.no-host.invalid:81/test.data")));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testEndsWithFilter3() throws URISyntaxException {
+ UriFilter filter = new HostnameFilter(Mode.ENDS_WITH, ".100");
+
+ assertTrue(filter.accept(new URI("http://192.168.1.100:81/test.data")));
+ }
+
+ /*************************************************************************
+ * Test method
+ * @throws URISyntaxException on invalid URL syntax.
+ ************************************************************************/
+ @Test
+ public void testIpRangeFilter() throws URISyntaxException {
+ UriFilter filter = new IpRangeFilter("192.168.0.0/24");
+
+ assertTrue(filter.accept(new URI("http://192.168.0.100:81/test.data")));
+ assertFalse(filter.accept(new URI("http://192.168.1.100:81/test.data")));
+ }
+
+}
+
diff --git a/src/test/resources/data/ff3_manual/.mozilla/firefox/9f1uyzzu.default/prefs.js b/src/test/resources/data/ff3_manual/.mozilla/firefox/9f1uyzzu.default/prefs.js
new file mode 100644
index 0000000..568d96e
--- /dev/null
+++ b/src/test/resources/data/ff3_manual/.mozilla/firefox/9f1uyzzu.default/prefs.js
@@ -0,0 +1,76 @@
+# Mozilla User Preferences
+
+/* Do not edit this file.
+ *
+ * If you make changes to this file while the application is running,
+ * the changes will be overwritten when the application exits.
+ *
+ * To make a manual change to preferences, you can visit the URL about:config
+ * For more information, see http://www.mozilla.org/unix/customizing.html#prefs
+ */
+
+user_pref("accessibility.typeaheadfind.flashBar", 0);
+user_pref("app.update.auto", false);
+user_pref("app.update.lastUpdateTime.addon-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.blocklist-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.microsummary-generator-update-timer", 1242676783);
+user_pref("app.update.lastUpdateTime.search-engine-update-timer", 1243156646);
+user_pref("browser.download.dir", "/home/rossi/Downloads");
+user_pref("browser.download.folderList", 2);
+user_pref("browser.download.lastDir", "/home/rossi/Dokumente");
+user_pref("browser.download.save_converter_index", 0);
+user_pref("browser.feeds.showFirstRunUI", false);
+user_pref("browser.history_expire_days.mirror", 180);
+user_pref("browser.history_expire_days_min", 3);
+user_pref("browser.migration.version", 1);
+user_pref("browser.places.importBookmarksHTML", false);
+user_pref("browser.places.importDefaults", false);
+user_pref("browser.places.leftPaneFolderId", -1);
+user_pref("browser.places.migratePostDataAnnotations", false);
+user_pref("browser.places.smartBookmarksVersion", 1);
+user_pref("browser.places.updateRecentTagsUri", false);
+user_pref("browser.preferences.advanced.selectedTabIndex", 1);
+user_pref("browser.rights.3.shown", true);
+user_pref("browser.startup.homepage_override.mstone", "rv:1.9.0.10");
+user_pref("browser.startup.page", 0);
+user_pref("capability.policy.maonoscript.javascript.enabled", "allAccess");
+user_pref("capability.policy.maonoscript.sites", "addons.mozilla.org cineplex.de flashgot.net google.com googlesyndication.com hotmail.com informaction.com live.com maone.net msn.com noscript.net passport.com passport.net passportimages.com yahoo.com yimg.com about: about:blank about:certerror about:config about:credits about:neterror about:plugins about:privatebrowsing about:sessionrestore chrome: file://cineplex.de file://flashgot.net file://google.com file://googlesyndication.com file://hotmail.com file://informaction.com file://live.com file://maone.net file://msn.com file://noscript.net file://passport.com file://passport.net file://passportimages.com file://yahoo.com file://yimg.com http://cineplex.de http://flashgot.net http://google.com http://googlesyndication.com http://hotmail.com http://informaction.com http://live.com http://maone.net http://msn.com http://noscript.net http://passport.com http://passport.net http://passportimages.com http://yahoo.com http://yimg.com https://cineplex.de https://flashgot.net https://google.com https://googlesyndication.com https://hotmail.com https://informaction.com https://live.com https://maone.net https://msn.com https://noscript.net https://passport.com https://passport.net https://passportimages.com https://yahoo.com https://yimg.com resource:");
+user_pref("distribution.canonical.bookmarksProcessed", true);
+user_pref("dom.disable_window_move_resize", true);
+user_pref("dom.event.contextmenu.enabled", false);
+user_pref("extensions.adblockplus.currentVersion", "1.0.2");
+user_pref("extensions.enabledItems", "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}:1.0.2,langpack-de@firefox-3.0.ubuntu.com:3.0.7,langpack-en-GB@firefox-3.0.ubuntu.com:3.0.7,{73a6fe31-595d-460b-a920-fcc0f8843232}:1.9.2.8,langpack-de@xulrunner-1.9.ubuntu.com:1.9.0.8,langpack-en-GB@xulrunner-1.9.ubuntu.com:1.9.0.8,{972ce4c6-7e08-4474-a285-3208198ce6fd}:3.0.10");
+user_pref("extensions.lastAppVersion", "3.0.10");
+user_pref("extensions.update.notifyUser", false);
+user_pref("intl.charsetmenu.browser.cache", "us-ascii, ISO-8859-15, ISO-8859-1, UTF-8, windows-1252");
+user_pref("javascript.enabled", true);
+user_pref("network.cookie.lifetimePolicy", 2);
+user_pref("network.cookie.prefsMigrated", true);
+user_pref("network.proxy.autoconfig_url", "http://www.xxx.de/");
+user_pref("network.proxy.ftp", "ftp_proxy.unit-test.invalid");
+user_pref("network.proxy.ftp_port", 8092);
+user_pref("network.proxy.gopher", "gopher_proxy.unit-test.invalid");
+user_pref("network.proxy.gopher_port", 8093);
+user_pref("network.proxy.http", "http_proxy.unit-test.invalid");
+user_pref("network.proxy.http_port", 8090);
+user_pref("network.proxy.socks", "socks_proxy.unit-test.invalid");
+user_pref("network.proxy.socks_port", 8095);
+user_pref("network.proxy.socks_version", 4);
+user_pref("network.proxy.ssl", "https_proxy.unit-test.invalid");
+user_pref("network.proxy.ssl_port", 8091);
+user_pref("network.proxy.type", 1);
+user_pref("noscript.badInstall", false);
+user_pref("noscript.global", true);
+user_pref("noscript.gtemp", "");
+user_pref("noscript.notify", false);
+user_pref("noscript.options.tabSelectedIndexes", "5,4,1");
+user_pref("noscript.policynames", "");
+user_pref("noscript.temp", "");
+user_pref("noscript.version", "1.9.2.8");
+user_pref("pref.advanced.javascript.disable_button.advanced", false);
+user_pref("pref.downloads.disable_button.edit_actions", false);
+user_pref("privacy.item.offlineApps", true);
+user_pref("signon.rememberSignons", false);
+user_pref("spellchecker.dictionary", "de_AT");
+user_pref("urlclassifier.keyupdatetime.https://sb-ssl.google.com/safebrowsing/newkey", 1243725238);
diff --git a/src/test/resources/data/ff3_none/.mozilla/firefox/9f1uyzzu.default/prefs.js b/src/test/resources/data/ff3_none/.mozilla/firefox/9f1uyzzu.default/prefs.js
new file mode 100644
index 0000000..81a560c
--- /dev/null
+++ b/src/test/resources/data/ff3_none/.mozilla/firefox/9f1uyzzu.default/prefs.js
@@ -0,0 +1,77 @@
+# Mozilla User Preferences
+
+/* Do not edit this file.
+ *
+ * If you make changes to this file while the application is running,
+ * the changes will be overwritten when the application exits.
+ *
+ * To make a manual change to preferences, you can visit the URL about:config
+ * For more information, see http://www.mozilla.org/unix/customizing.html#prefs
+ */
+
+user_pref("accessibility.typeaheadfind.flashBar", 0);
+user_pref("app.update.auto", false);
+user_pref("app.update.lastUpdateTime.addon-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.blocklist-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.microsummary-generator-update-timer", 1242676783);
+user_pref("app.update.lastUpdateTime.search-engine-update-timer", 1243156646);
+user_pref("browser.download.dir", "/home/rossi/Downloads");
+user_pref("browser.download.folderList", 2);
+user_pref("browser.download.lastDir", "/home/rossi/Dokumente");
+user_pref("browser.download.save_converter_index", 0);
+user_pref("browser.feeds.showFirstRunUI", false);
+user_pref("browser.history_expire_days.mirror", 180);
+user_pref("browser.history_expire_days_min", 3);
+user_pref("browser.migration.version", 1);
+user_pref("browser.places.importBookmarksHTML", false);
+user_pref("browser.places.importDefaults", false);
+user_pref("browser.places.leftPaneFolderId", -1);
+user_pref("browser.places.migratePostDataAnnotations", false);
+user_pref("browser.places.smartBookmarksVersion", 1);
+user_pref("browser.places.updateRecentTagsUri", false);
+user_pref("browser.preferences.advanced.selectedTabIndex", 1);
+user_pref("browser.rights.3.shown", true);
+user_pref("browser.startup.homepage_override.mstone", "rv:1.9.0.10");
+user_pref("browser.startup.page", 0);
+user_pref("capability.policy.maonoscript.javascript.enabled", "allAccess");
+user_pref("capability.policy.maonoscript.sites", "addons.mozilla.org cineplex.de flashgot.net google.com googlesyndication.com hotmail.com informaction.com live.com maone.net msn.com noscript.net passport.com passport.net passportimages.com yahoo.com yimg.com about: about:blank about:certerror about:config about:credits about:neterror about:plugins about:privatebrowsing about:sessionrestore chrome: file://cineplex.de file://flashgot.net file://google.com file://googlesyndication.com file://hotmail.com file://informaction.com file://live.com file://maone.net file://msn.com file://noscript.net file://passport.com file://passport.net file://passportimages.com file://yahoo.com file://yimg.com http://cineplex.de http://flashgot.net http://google.com http://googlesyndication.com http://hotmail.com http://informaction.com http://live.com http://maone.net http://msn.com http://noscript.net http://passport.com http://passport.net http://passportimages.com http://yahoo.com http://yimg.com https://cineplex.de https://flashgot.net https://google.com https://googlesyndication.com https://hotmail.com https://informaction.com https://live.com https://maone.net https://msn.com https://noscript.net https://passport.com https://passport.net https://passportimages.com https://yahoo.com https://yimg.com resource:");
+user_pref("distribution.canonical.bookmarksProcessed", true);
+user_pref("dom.disable_window_move_resize", true);
+user_pref("dom.event.contextmenu.enabled", false);
+user_pref("extensions.adblockplus.currentVersion", "1.0.2");
+user_pref("extensions.enabledItems", "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}:1.0.2,langpack-de@firefox-3.0.ubuntu.com:3.0.7,langpack-en-GB@firefox-3.0.ubuntu.com:3.0.7,{73a6fe31-595d-460b-a920-fcc0f8843232}:1.9.2.8,langpack-de@xulrunner-1.9.ubuntu.com:1.9.0.8,langpack-en-GB@xulrunner-1.9.ubuntu.com:1.9.0.8,{972ce4c6-7e08-4474-a285-3208198ce6fd}:3.0.10");
+user_pref("extensions.lastAppVersion", "3.0.10");
+user_pref("extensions.update.notifyUser", false);
+user_pref("intl.charsetmenu.browser.cache", "us-ascii, ISO-8859-15, ISO-8859-1, UTF-8, windows-1252");
+user_pref("javascript.enabled", true);
+user_pref("network.cookie.lifetimePolicy", 2);
+user_pref("network.cookie.prefsMigrated", true);
+user_pref("network.proxy.autoconfig_url", "http://www.xxx.de/");
+user_pref("network.proxy.ftp", "Test2");
+user_pref("network.proxy.ftp_port", 222);
+user_pref("network.proxy.gopher", "Test3");
+user_pref("network.proxy.gopher_port", 333);
+user_pref("network.proxy.http", "TEST");
+user_pref("network.proxy.http_port", 999);
+user_pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1, test4454,");
+user_pref("network.proxy.socks", "Test4");
+user_pref("network.proxy.socks_port", 444);
+user_pref("network.proxy.socks_version", 4);
+user_pref("network.proxy.ssl", "Test1");
+user_pref("network.proxy.ssl_port", 111);
+user_pref("network.proxy.type", 0);
+user_pref("noscript.badInstall", false);
+user_pref("noscript.global", true);
+user_pref("noscript.gtemp", "");
+user_pref("noscript.notify", false);
+user_pref("noscript.options.tabSelectedIndexes", "5,4,1");
+user_pref("noscript.policynames", "");
+user_pref("noscript.temp", "");
+user_pref("noscript.version", "1.9.2.8");
+user_pref("pref.advanced.javascript.disable_button.advanced", false);
+user_pref("pref.downloads.disable_button.edit_actions", false);
+user_pref("privacy.item.offlineApps", true);
+user_pref("signon.rememberSignons", false);
+user_pref("spellchecker.dictionary", "de_AT");
+user_pref("urlclassifier.keyupdatetime.https://sb-ssl.google.com/safebrowsing/newkey", 1243725238);
diff --git a/src/test/resources/data/ff3_pac_script/.mozilla/firefox/9f1uyzzu.default/prefs.js b/src/test/resources/data/ff3_pac_script/.mozilla/firefox/9f1uyzzu.default/prefs.js
new file mode 100644
index 0000000..906bdb5
--- /dev/null
+++ b/src/test/resources/data/ff3_pac_script/.mozilla/firefox/9f1uyzzu.default/prefs.js
@@ -0,0 +1,76 @@
+# Mozilla User Preferences
+
+/* Do not edit this file.
+ *
+ * If you make changes to this file while the application is running,
+ * the changes will be overwritten when the application exits.
+ *
+ * To make a manual change to preferences, you can visit the URL about:config
+ * For more information, see http://www.mozilla.org/unix/customizing.html#prefs
+ */
+
+user_pref("accessibility.typeaheadfind.flashBar", 0);
+user_pref("app.update.auto", false);
+user_pref("app.update.lastUpdateTime.addon-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.blocklist-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.microsummary-generator-update-timer", 1242676783);
+user_pref("app.update.lastUpdateTime.search-engine-update-timer", 1243156646);
+user_pref("browser.download.dir", "/home/rossi/Downloads");
+user_pref("browser.download.folderList", 2);
+user_pref("browser.download.lastDir", "/home/rossi/Dokumente");
+user_pref("browser.download.save_converter_index", 0);
+user_pref("browser.feeds.showFirstRunUI", false);
+user_pref("browser.history_expire_days.mirror", 180);
+user_pref("browser.history_expire_days_min", 3);
+user_pref("browser.migration.version", 1);
+user_pref("browser.places.importBookmarksHTML", false);
+user_pref("browser.places.importDefaults", false);
+user_pref("browser.places.leftPaneFolderId", -1);
+user_pref("browser.places.migratePostDataAnnotations", false);
+user_pref("browser.places.smartBookmarksVersion", 1);
+user_pref("browser.places.updateRecentTagsUri", false);
+user_pref("browser.preferences.advanced.selectedTabIndex", 1);
+user_pref("browser.rights.3.shown", true);
+user_pref("browser.startup.homepage_override.mstone", "rv:1.9.0.10");
+user_pref("browser.startup.page", 0);
+user_pref("capability.policy.maonoscript.javascript.enabled", "allAccess");
+user_pref("capability.policy.maonoscript.sites", "addons.mozilla.org cineplex.de flashgot.net google.com googlesyndication.com hotmail.com informaction.com live.com maone.net msn.com noscript.net passport.com passport.net passportimages.com yahoo.com yimg.com about: about:blank about:certerror about:config about:credits about:neterror about:plugins about:privatebrowsing about:sessionrestore chrome: file://cineplex.de file://flashgot.net file://google.com file://googlesyndication.com file://hotmail.com file://informaction.com file://live.com file://maone.net file://msn.com file://noscript.net file://passport.com file://passport.net file://passportimages.com file://yahoo.com file://yimg.com http://cineplex.de http://flashgot.net http://google.com http://googlesyndication.com http://hotmail.com http://informaction.com http://live.com http://maone.net http://msn.com http://noscript.net http://passport.com http://passport.net http://passportimages.com http://yahoo.com http://yimg.com https://cineplex.de https://flashgot.net https://google.com https://googlesyndication.com https://hotmail.com https://informaction.com https://live.com https://maone.net https://msn.com https://noscript.net https://passport.com https://passport.net https://passportimages.com https://yahoo.com https://yimg.com resource:");
+user_pref("distribution.canonical.bookmarksProcessed", true);
+user_pref("dom.disable_window_move_resize", true);
+user_pref("dom.event.contextmenu.enabled", false);
+user_pref("extensions.adblockplus.currentVersion", "1.0.2");
+user_pref("extensions.enabledItems", "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}:1.0.2,langpack-de@firefox-3.0.ubuntu.com:3.0.7,langpack-en-GB@firefox-3.0.ubuntu.com:3.0.7,{73a6fe31-595d-460b-a920-fcc0f8843232}:1.9.2.8,langpack-de@xulrunner-1.9.ubuntu.com:1.9.0.8,langpack-en-GB@xulrunner-1.9.ubuntu.com:1.9.0.8,{972ce4c6-7e08-4474-a285-3208198ce6fd}:3.0.10");
+user_pref("extensions.lastAppVersion", "3.0.10");
+user_pref("extensions.update.notifyUser", false);
+user_pref("intl.charsetmenu.browser.cache", "us-ascii, ISO-8859-15, ISO-8859-1, UTF-8, windows-1252");
+user_pref("javascript.enabled", true);
+user_pref("network.cookie.lifetimePolicy", 2);
+user_pref("network.cookie.prefsMigrated", true);
+user_pref("network.proxy.autoconfig_url", "test/data/pac/test1.pac");
+user_pref("network.proxy.ftp", "ftp_proxy.unit-test.invalid");
+user_pref("network.proxy.ftp_port", 8092);
+user_pref("network.proxy.gopher", "gopher_proxy.unit-test.invalid");
+user_pref("network.proxy.gopher_port", 8093);
+user_pref("network.proxy.http", "http_proxy.unit-test.invalid");
+user_pref("network.proxy.http_port", 8090);
+user_pref("network.proxy.socks", "socks_proxy.unit-test.invalid");
+user_pref("network.proxy.socks_port", 8095);
+user_pref("network.proxy.socks_version", 4);
+user_pref("network.proxy.ssl", "https_proxy.unit-test.invalid");
+user_pref("network.proxy.ssl_port", 8091);
+user_pref("network.proxy.type", 2);
+user_pref("noscript.badInstall", false);
+user_pref("noscript.global", true);
+user_pref("noscript.gtemp", "");
+user_pref("noscript.notify", false);
+user_pref("noscript.options.tabSelectedIndexes", "5,4,1");
+user_pref("noscript.policynames", "");
+user_pref("noscript.temp", "");
+user_pref("noscript.version", "1.9.2.8");
+user_pref("pref.advanced.javascript.disable_button.advanced", false);
+user_pref("pref.downloads.disable_button.edit_actions", false);
+user_pref("privacy.item.offlineApps", true);
+user_pref("signon.rememberSignons", false);
+user_pref("spellchecker.dictionary", "de_AT");
+user_pref("urlclassifier.keyupdatetime.https://sb-ssl.google.com/safebrowsing/newkey", 1243725238);
diff --git a/src/test/resources/data/ff3_white_list/.mozilla/firefox/9f1uyzzu.default/prefs.js b/src/test/resources/data/ff3_white_list/.mozilla/firefox/9f1uyzzu.default/prefs.js
new file mode 100644
index 0000000..7da855d
--- /dev/null
+++ b/src/test/resources/data/ff3_white_list/.mozilla/firefox/9f1uyzzu.default/prefs.js
@@ -0,0 +1,77 @@
+# Mozilla User Preferences
+
+/* Do not edit this file.
+ *
+ * If you make changes to this file while the application is running,
+ * the changes will be overwritten when the application exits.
+ *
+ * To make a manual change to preferences, you can visit the URL about:config
+ * For more information, see http://www.mozilla.org/unix/customizing.html#prefs
+ */
+
+user_pref("accessibility.typeaheadfind.flashBar", 0);
+user_pref("app.update.auto", false);
+user_pref("app.update.lastUpdateTime.addon-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.blocklist-background-update-timer", 1243108090);
+user_pref("app.update.lastUpdateTime.microsummary-generator-update-timer", 1242676783);
+user_pref("app.update.lastUpdateTime.search-engine-update-timer", 1243156646);
+user_pref("browser.download.dir", "/home/rossi/Downloads");
+user_pref("browser.download.folderList", 2);
+user_pref("browser.download.lastDir", "/home/rossi/Dokumente");
+user_pref("browser.download.save_converter_index", 0);
+user_pref("browser.feeds.showFirstRunUI", false);
+user_pref("browser.history_expire_days.mirror", 180);
+user_pref("browser.history_expire_days_min", 3);
+user_pref("browser.migration.version", 1);
+user_pref("browser.places.importBookmarksHTML", false);
+user_pref("browser.places.importDefaults", false);
+user_pref("browser.places.leftPaneFolderId", -1);
+user_pref("browser.places.migratePostDataAnnotations", false);
+user_pref("browser.places.smartBookmarksVersion", 1);
+user_pref("browser.places.updateRecentTagsUri", false);
+user_pref("browser.preferences.advanced.selectedTabIndex", 1);
+user_pref("browser.rights.3.shown", true);
+user_pref("browser.startup.homepage_override.mstone", "rv:1.9.0.10");
+user_pref("browser.startup.page", 0);
+user_pref("capability.policy.maonoscript.javascript.enabled", "allAccess");
+user_pref("capability.policy.maonoscript.sites", "addons.mozilla.org cineplex.de flashgot.net google.com googlesyndication.com hotmail.com informaction.com live.com maone.net msn.com noscript.net passport.com passport.net passportimages.com yahoo.com yimg.com about: about:blank about:certerror about:config about:credits about:neterror about:plugins about:privatebrowsing about:sessionrestore chrome: file://cineplex.de file://flashgot.net file://google.com file://googlesyndication.com file://hotmail.com file://informaction.com file://live.com file://maone.net file://msn.com file://noscript.net file://passport.com file://passport.net file://passportimages.com file://yahoo.com file://yimg.com http://cineplex.de http://flashgot.net http://google.com http://googlesyndication.com http://hotmail.com http://informaction.com http://live.com http://maone.net http://msn.com http://noscript.net http://passport.com http://passport.net http://passportimages.com http://yahoo.com http://yimg.com https://cineplex.de https://flashgot.net https://google.com https://googlesyndication.com https://hotmail.com https://informaction.com https://live.com https://maone.net https://msn.com https://noscript.net https://passport.com https://passport.net https://passportimages.com https://yahoo.com https://yimg.com resource:");
+user_pref("distribution.canonical.bookmarksProcessed", true);
+user_pref("dom.disable_window_move_resize", true);
+user_pref("dom.event.contextmenu.enabled", false);
+user_pref("extensions.adblockplus.currentVersion", "1.0.2");
+user_pref("extensions.enabledItems", "{d10d0bf8-f5b5-c8b4-a8b2-2b9879e08c5d}:1.0.2,langpack-de@firefox-3.0.ubuntu.com:3.0.7,langpack-en-GB@firefox-3.0.ubuntu.com:3.0.7,{73a6fe31-595d-460b-a920-fcc0f8843232}:1.9.2.8,langpack-de@xulrunner-1.9.ubuntu.com:1.9.0.8,langpack-en-GB@xulrunner-1.9.ubuntu.com:1.9.0.8,{972ce4c6-7e08-4474-a285-3208198ce6fd}:3.0.10");
+user_pref("extensions.lastAppVersion", "3.0.10");
+user_pref("extensions.update.notifyUser", false);
+user_pref("intl.charsetmenu.browser.cache", "us-ascii, ISO-8859-15, ISO-8859-1, UTF-8, windows-1252");
+user_pref("javascript.enabled", true);
+user_pref("network.cookie.lifetimePolicy", 2);
+user_pref("network.cookie.prefsMigrated", true);
+user_pref("network.proxy.autoconfig_url", "file://~/wpad/wpad.txt");
+user_pref("network.proxy.ftp", "ftp_proxy.unit-test.invalid");
+user_pref("network.proxy.ftp_port", 8092);
+user_pref("network.proxy.gopher", "gopher_proxy.unit-test.invalid");
+user_pref("network.proxy.gopher_port", 8093);
+user_pref("network.proxy.http", "http_proxy.unit-test.invalid");
+user_pref("network.proxy.http_port", 8090);
+user_pref("network.proxy.no_proxies_on", "localhost, 127.0.0.1, no_proxy.unit-test.invalid");
+user_pref("network.proxy.socks", "socks_proxy.unit-test.invalid");
+user_pref("network.proxy.socks_port", 8095);
+user_pref("network.proxy.socks_version", 4);
+user_pref("network.proxy.ssl", "https_proxy.unit-test.invalid");
+user_pref("network.proxy.ssl_port", 8091);
+user_pref("network.proxy.type", 1);
+user_pref("noscript.badInstall", false);
+user_pref("noscript.global", true);
+user_pref("noscript.gtemp", "");
+user_pref("noscript.notify", false);
+user_pref("noscript.options.tabSelectedIndexes", "5,4,1");
+user_pref("noscript.policynames", "");
+user_pref("noscript.temp", "");
+user_pref("noscript.version", "1.9.2.8");
+user_pref("pref.advanced.javascript.disable_button.advanced", false);
+user_pref("pref.downloads.disable_button.edit_actions", false);
+user_pref("privacy.item.offlineApps", true);
+user_pref("signon.rememberSignons", false);
+user_pref("spellchecker.dictionary", "de_AT");
+user_pref("urlclassifier.keyupdatetime.https://sb-ssl.google.com/safebrowsing/newkey", 1243725238);
diff --git a/src/test/resources/data/gnome_manual/.gconf/system/http_proxy/%gconf.xml b/src/test/resources/data/gnome_manual/.gconf/system/http_proxy/%gconf.xml
new file mode 100755
index 0000000..c3881c5
--- /dev/null
+++ b/src/test/resources/data/gnome_manual/.gconf/system/http_proxy/%gconf.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="port" mtime="1243184973" type="int" value="8090">
+ </entry>
+ <entry name="use_same_proxy" mtime="1243184977" type="bool" value="false">
+ </entry>
+ <entry name="ignore_hosts" mtime="1242841133" type="list" ltype="string">
+ <li type="string">
+ <stringvalue>localhost</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>127.0.0.0/8</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>*.local</stringvalue>
+ </li>
+ </entry>
+ <entry name="host" mtime="1243184967" type="string">
+ <stringvalue>http_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="use_http_proxy" mtime="1243184951" type="bool" value="true">
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_manual/.gconf/system/proxy/%gconf.xml b/src/test/resources/data/gnome_manual/.gconf/system/proxy/%gconf.xml
new file mode 100755
index 0000000..8818703
--- /dev/null
+++ b/src/test/resources/data/gnome_manual/.gconf/system/proxy/%gconf.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="old_socks_port" mtime="1243184977" type="int" value="8889">
+ </entry>
+ <entry name="old_socks_host" mtime="1243184977" type="string">
+ <stringvalue>sodddddd</stringvalue>
+ </entry>
+ <entry name="old_ftp_port" mtime="1243184977" type="int" value="8099">
+ </entry>
+ <entry name="old_ftp_host" mtime="1243184977" type="string">
+ <stringvalue>ftpproxy</stringvalue>
+ </entry>
+ <entry name="old_secure_port" mtime="1243184977" type="int" value="8090">
+ </entry>
+ <entry name="old_secure_host" mtime="1243184977" type="string">
+ <stringvalue>sslproxy</stringvalue>
+ </entry>
+ <entry name="socks_port" mtime="1243185041" type="int" value="8093">
+ </entry>
+ <entry name="socks_host" mtime="1243185029" type="string">
+ <stringvalue>socks_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="ftp_port" mtime="1243185020" type="int" value="8092">
+ </entry>
+ <entry name="ftp_host" mtime="1243185014" type="string">
+ <stringvalue>ftp_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="secure_port" mtime="1243185005" type="int" value="8091">
+ </entry>
+ <entry name="secure_host" mtime="1243184991" type="string">
+ <stringvalue>https_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="mode" mtime="1243184951" type="string">
+ <stringvalue>manual</stringvalue>
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_none/.gconf/system/http_proxy/%gconf.xml b/src/test/resources/data/gnome_none/.gconf/system/http_proxy/%gconf.xml
new file mode 100755
index 0000000..fadaef0
--- /dev/null
+++ b/src/test/resources/data/gnome_none/.gconf/system/http_proxy/%gconf.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="use_same_proxy" mtime="1242844189" type="bool" value="true">
+ </entry>
+ <entry name="ignore_hosts" mtime="1242841133" type="list" ltype="string">
+ <li type="string">
+ <stringvalue>localhost</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>127.0.0.0/8</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>*.local</stringvalue>
+ </li>
+ </entry>
+ <entry name="host" mtime="1242840330" type="string">
+ <stringvalue>test.proxy.invalid</stringvalue>
+ </entry>
+ <entry name="use_http_proxy" mtime="1242848237" type="bool" value="false">
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_pac_script/.gconf/system/http_proxy/%gconf.xml b/src/test/resources/data/gnome_pac_script/.gconf/system/http_proxy/%gconf.xml
new file mode 100755
index 0000000..527b0bd
--- /dev/null
+++ b/src/test/resources/data/gnome_pac_script/.gconf/system/http_proxy/%gconf.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="port" mtime="1243184973" type="int" value="8090">
+ </entry>
+ <entry name="use_same_proxy" mtime="1243184977" type="bool" value="false">
+ </entry>
+ <entry name="ignore_hosts" mtime="1243185198" type="list" ltype="string">
+ <li type="string">
+ <stringvalue>localhost</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>127.0.0.0/8</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>*.local</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>no_proxy.unit-test.invalid</stringvalue>
+ </li>
+ </entry>
+ <entry name="host" mtime="1243184967" type="string">
+ <stringvalue>http_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="use_http_proxy" mtime="1243185310" type="bool" value="true">
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_pac_script/.gconf/system/proxy/%gconf.xml b/src/test/resources/data/gnome_pac_script/.gconf/system/proxy/%gconf.xml
new file mode 100755
index 0000000..a5dde13
--- /dev/null
+++ b/src/test/resources/data/gnome_pac_script/.gconf/system/proxy/%gconf.xml
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="autoconfig_url" mtime="1243185338" type="string">
+ <stringvalue>test/data/pac/test1.pac</stringvalue>
+ </entry>
+ <entry name="old_socks_port" mtime="1243184977" type="int" value="8889">
+ </entry>
+ <entry name="old_socks_host" mtime="1243184977" type="string">
+ <stringvalue>sodddddd</stringvalue>
+ </entry>
+ <entry name="old_ftp_port" mtime="1243184977" type="int" value="8099">
+ </entry>
+ <entry name="old_ftp_host" mtime="1243184977" type="string">
+ <stringvalue>ftpproxy</stringvalue>
+ </entry>
+ <entry name="old_secure_port" mtime="1243184977" type="int" value="8090">
+ </entry>
+ <entry name="old_secure_host" mtime="1243184977" type="string">
+ <stringvalue>sslproxy</stringvalue>
+ </entry>
+ <entry name="socks_port" mtime="1243185041" type="int" value="8093">
+ </entry>
+ <entry name="socks_host" mtime="1243185029" type="string">
+ <stringvalue>socks_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="ftp_port" mtime="1243185020" type="int" value="8092">
+ </entry>
+ <entry name="ftp_host" mtime="1243185014" type="string">
+ <stringvalue>ftp_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="secure_port" mtime="1243185005" type="int" value="8091">
+ </entry>
+ <entry name="secure_host" mtime="1243184991" type="string">
+ <stringvalue>https_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="mode" mtime="1243185310" type="string">
+ <stringvalue>auto</stringvalue>
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_white_list/.gconf/system/http_proxy/%gconf.xml b/src/test/resources/data/gnome_white_list/.gconf/system/http_proxy/%gconf.xml
new file mode 100755
index 0000000..dd2d5fd
--- /dev/null
+++ b/src/test/resources/data/gnome_white_list/.gconf/system/http_proxy/%gconf.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="port" mtime="1243184973" type="int" value="8090">
+ </entry>
+ <entry name="use_same_proxy" mtime="1243184977" type="bool" value="false">
+ </entry>
+ <entry name="ignore_hosts" mtime="1243185198" type="list" ltype="string">
+ <li type="string">
+ <stringvalue>localhost</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>127.0.0.0/8</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>*.local</stringvalue>
+ </li>
+ <li type="string">
+ <stringvalue>no_proxy.unit-test.invalid</stringvalue>
+ </li>
+ </entry>
+ <entry name="host" mtime="1243184967" type="string">
+ <stringvalue>http_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="use_http_proxy" mtime="1243185171" type="bool" value="true">
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/gnome_white_list/.gconf/system/proxy/%gconf.xml b/src/test/resources/data/gnome_white_list/.gconf/system/proxy/%gconf.xml
new file mode 100755
index 0000000..8818703
--- /dev/null
+++ b/src/test/resources/data/gnome_white_list/.gconf/system/proxy/%gconf.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0"?>
+<gconf>
+ <entry name="old_socks_port" mtime="1243184977" type="int" value="8889">
+ </entry>
+ <entry name="old_socks_host" mtime="1243184977" type="string">
+ <stringvalue>sodddddd</stringvalue>
+ </entry>
+ <entry name="old_ftp_port" mtime="1243184977" type="int" value="8099">
+ </entry>
+ <entry name="old_ftp_host" mtime="1243184977" type="string">
+ <stringvalue>ftpproxy</stringvalue>
+ </entry>
+ <entry name="old_secure_port" mtime="1243184977" type="int" value="8090">
+ </entry>
+ <entry name="old_secure_host" mtime="1243184977" type="string">
+ <stringvalue>sslproxy</stringvalue>
+ </entry>
+ <entry name="socks_port" mtime="1243185041" type="int" value="8093">
+ </entry>
+ <entry name="socks_host" mtime="1243185029" type="string">
+ <stringvalue>socks_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="ftp_port" mtime="1243185020" type="int" value="8092">
+ </entry>
+ <entry name="ftp_host" mtime="1243185014" type="string">
+ <stringvalue>ftp_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="secure_port" mtime="1243185005" type="int" value="8091">
+ </entry>
+ <entry name="secure_host" mtime="1243184991" type="string">
+ <stringvalue>https_proxy.unit-test.invalid</stringvalue>
+ </entry>
+ <entry name="mode" mtime="1243184951" type="string">
+ <stringvalue>manual</stringvalue>
+ </entry>
+</gconf>
diff --git a/src/test/resources/data/kde_env/.kde/share/config/kioslaverc b/src/test/resources/data/kde_env/.kde/share/config/kioslaverc
new file mode 100644
index 0000000..03e3f0f
--- /dev/null
+++ b/src/test/resources/data/kde_env/.kde/share/config/kioslaverc
@@ -0,0 +1,17 @@
+PersistentProxyConnection=false
+
+[$Version]
+update_info=kioslave.upd:kde2.2/r1,kioslave.upd:kde2.2/r2,kioslave.upd:kde2.2/r3
+
+[Notification Messages]
+WarnOnLeaveSSLMode=false
+
+[Proxy Settings]
+AuthMode=0
+NoProxyFor=
+Proxy Config Script=
+ProxyType=4
+ReversedException=false
+ftpProxy=FTP_PROXY
+httpProxy=HTTP_PROXY
+httpsProxy=HTTPS_PROXY
diff --git a/src/test/resources/data/kde_manual/.kde/share/config/kioslaverc b/src/test/resources/data/kde_manual/.kde/share/config/kioslaverc
new file mode 100644
index 0000000..0d4f115
--- /dev/null
+++ b/src/test/resources/data/kde_manual/.kde/share/config/kioslaverc
@@ -0,0 +1,17 @@
+PersistentProxyConnection=false
+
+[$Version]
+update_info=kioslave.upd:kde2.2/r1,kioslave.upd:kde2.2/r2,kioslave.upd:kde2.2/r3
+
+[Notification Messages]
+WarnOnLeaveSSLMode=false
+
+[Proxy Settings]
+AuthMode=0
+NoProxyFor=
+Proxy Config Script=
+ProxyType=1
+ReversedException=false
+ftpProxy=ftp://ftp_proxy.unit-test.invalid:8092
+httpProxy=http://http_proxy.unit-test.invalid:8090
+httpsProxy=https://https_proxy.unit-test.invalid:8091
diff --git a/src/test/resources/data/kde_none/.kde/share/config/kioslaverc b/src/test/resources/data/kde_none/.kde/share/config/kioslaverc
new file mode 100644
index 0000000..413575f
--- /dev/null
+++ b/src/test/resources/data/kde_none/.kde/share/config/kioslaverc
@@ -0,0 +1,17 @@
+PersistentProxyConnection=false
+
+[$Version]
+update_info=kioslave.upd:kde2.2/r1,kioslave.upd:kde2.2/r2,kioslave.upd:kde2.2/r3
+
+[Notification Messages]
+WarnOnLeaveSSLMode=false
+
+[Proxy Settings]
+AuthMode=0
+NoProxyFor=
+Proxy Config Script=
+ProxyType=0
+ReversedException=false
+ftpProxy=
+httpProxy=
+httpsProxy=
diff --git a/src/test/resources/data/kde_pac_script/.kde/share/config/kioslaverc b/src/test/resources/data/kde_pac_script/.kde/share/config/kioslaverc
new file mode 100644
index 0000000..f3fa699
--- /dev/null
+++ b/src/test/resources/data/kde_pac_script/.kde/share/config/kioslaverc
@@ -0,0 +1,17 @@
+PersistentProxyConnection=false
+
+[$Version]
+update_info=kioslave.upd:kde2.2/r1,kioslave.upd:kde2.2/r2,kioslave.upd:kde2.2/r3
+
+[Notification Messages]
+WarnOnLeaveSSLMode=false
+
+[Proxy Settings]
+AuthMode=0
+NoProxyFor=
+Proxy Config Script=test/data/pac/test1.pac
+ProxyType=2
+ReversedException=false
+ftpProxy=
+httpProxy=
+httpsProxy=
diff --git a/src/test/resources/data/kde_white_list/.kde/share/config/kioslaverc b/src/test/resources/data/kde_white_list/.kde/share/config/kioslaverc
new file mode 100644
index 0000000..80d0286
--- /dev/null
+++ b/src/test/resources/data/kde_white_list/.kde/share/config/kioslaverc
@@ -0,0 +1,17 @@
+PersistentProxyConnection=false
+
+[$Version]
+update_info=kioslave.upd:kde2.2/r1,kioslave.upd:kde2.2/r2,kioslave.upd:kde2.2/r3
+
+[Notification Messages]
+WarnOnLeaveSSLMode=false
+
+[Proxy Settings]
+AuthMode=0
+NoProxyFor=no_proxy.unit-test.invalid,.unit-test.invalid
+Proxy Config Script=
+ProxyType=1
+ReversedException=false
+ftpProxy=ftp://ftp_proxy.unit-test.invalid:8092
+httpProxy=http://http_proxy.unit-test.invalid:8090
+httpsProxy=https://https_proxy.unit-test.invalid:8091
diff --git a/src/test/resources/data/osx/osx_all.plist b/src/test/resources/data/osx/osx_all.plist
new file mode 100644
index 0000000..62dcab7
--- /dev/null
+++ b/src/test/resources/data/osx/osx_all.plist
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CurrentSet</key>
+ <string>/Sets/8458B09B-93EA-44C2-AD05-0B4E4A4D9651</string>
+ <key>NetworkServices</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>AppleTalk</key>
+ <dict/>
+ <key>DNS</key>
+ <dict/>
+ <key>IPv4</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>DHCP</string>
+ </dict>
+ <key>IPv6</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>Automatic</string>
+ </dict>
+ <key>Interface</key>
+ <dict>
+ <key>DeviceName</key>
+ <string>en0</string>
+ <key>Hardware</key>
+ <string>Ethernet</string>
+ <key>Type</key>
+ <string>Ethernet</string>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ <key>Proxies</key>
+ <dict>
+ <key>ExceptionsList</key>
+ <array>
+ <string>*.local</string>
+ <string>169.254/16</string>
+ </array>
+ <key>ExcludeSimpleHostnames</key>
+ <integer>1</integer>
+ <key>FTPEnable</key>
+ <integer>1</integer>
+ <key>FTPPassive</key>
+ <integer>1</integer>
+ <key>FTPPort</key>
+ <integer>8092</integer>
+ <key>FTPProxy</key>
+ <string>ftp_proxy.unit-test.invalid</string>
+ <key>GopherEnable</key>
+ <integer>1</integer>
+ <key>GopherPort</key>
+ <integer>8090</integer>
+ <key>GopherProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPEnable</key>
+ <integer>1</integer>
+ <key>HTTPPort</key>
+ <integer>8090</integer>
+ <key>HTTPProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPSEnable</key>
+ <integer>1</integer>
+ <key>HTTPSPort</key>
+ <integer>8091</integer>
+ <key>HTTPSProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>ProxyAutoConfigEnable</key>
+ <integer>1</integer>
+ <key>ProxyAutoConfigURLString</key>
+ <string>http://http_proxy.unit-test.invalid/wpad.pac</string>
+ <key>ProxyAutoDiscoveryEnable</key>
+ <integer>1</integer>
+ <key>RTSPEnable</key>
+ <integer>1</integer>
+ <key>RTSPPort</key>
+ <integer>8094</integer>
+ <key>RTSPProxy</key>
+ <string>rtsp_proxy.unit-test.invalid</string>
+ <key>SOCKSEnable</key>
+ <integer>1</integer>
+ <key>SOCKSPort</key>
+ <integer>8095</integer>
+ <key>SOCKSProxy</key>
+ <string>socks_proxy.unit-test.invalid</string>
+ </dict>
+ <key>SMB</key>
+ <dict/>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ </dict>
+ <key>Sets</key>
+ <dict>
+ <key>8458B09B-93EA-44C2-AD05-0B4E4A4D9651</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>Global</key>
+ <dict>
+ <key>IPv4</key>
+ <dict>
+ <key>ServiceOrder</key>
+ <array>
+ <string>299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </array>
+ </dict>
+ </dict>
+ <key>Service</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>__LINK__</key>
+ <string>/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>UserDefinedName</key>
+ <string>Automatic</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>HostNames</key>
+ <dict>
+ <key>LocalHostName</key>
+ <string>rossis-Mac-mini</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>ComputerName</key>
+ <string>rossi’s Mac mini</string>
+ <key>ComputerNameEncoding</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/src/test/resources/data/osx/osx_manual.plist b/src/test/resources/data/osx/osx_manual.plist
new file mode 100644
index 0000000..9c7b0b3
--- /dev/null
+++ b/src/test/resources/data/osx/osx_manual.plist
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CurrentSet</key>
+ <string>/Sets/8458B09B-93EA-44C2-AD05-0B4E4A4D9651</string>
+ <key>NetworkServices</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>AppleTalk</key>
+ <dict/>
+ <key>DNS</key>
+ <dict/>
+ <key>IPv4</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>DHCP</string>
+ </dict>
+ <key>IPv6</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>Automatic</string>
+ </dict>
+ <key>Interface</key>
+ <dict>
+ <key>DeviceName</key>
+ <string>en0</string>
+ <key>Hardware</key>
+ <string>Ethernet</string>
+ <key>Type</key>
+ <string>Ethernet</string>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ <key>Proxies</key>
+ <dict>
+ <key>ExceptionsList</key>
+ <array>
+ <string>*.local</string>
+ <string>no_proxy.unit-test.invalid</string>
+ </array>
+ <key>ExcludeSimpleHostnames</key>
+ <integer>1</integer>
+ <key>FTPEnable</key>
+ <integer>1</integer>
+ <key>FTPPassive</key>
+ <integer>1</integer>
+ <key>FTPPort</key>
+ <integer>8092</integer>
+ <key>FTPProxy</key>
+ <string>ftp_proxy.unit-test.invalid</string>
+ <key>GopherEnable</key>
+ <integer>1</integer>
+ <key>GopherPort</key>
+ <integer>8090</integer>
+ <key>GopherProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPEnable</key>
+ <integer>1</integer>
+ <key>HTTPPort</key>
+ <integer>8090</integer>
+ <key>HTTPProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPSEnable</key>
+ <integer>1</integer>
+ <key>HTTPSPort</key>
+ <integer>8091</integer>
+ <key>HTTPSProxy</key>
+ <string>https_proxy.unit-test.invalid</string>
+ <key>ProxyAutoConfigEnable</key>
+ <integer>0</integer>
+ <key>ProxyAutoConfigURLString</key>
+ <string>http://http_proxy.unit-test.invalid/wpad.pac</string>
+ <key>ProxyAutoDiscoveryEnable</key>
+ <integer>0</integer>
+ <key>RTSPEnable</key>
+ <integer>1</integer>
+ <key>RTSPPort</key>
+ <integer>8094</integer>
+ <key>RTSPProxy</key>
+ <string>rtsp_proxy.unit-test.invalid</string>
+ <key>SOCKSEnable</key>
+ <integer>1</integer>
+ <key>SOCKSPort</key>
+ <integer>8095</integer>
+ <key>SOCKSProxy</key>
+ <string>socks_proxy.unit-test.invalid</string>
+ </dict>
+ <key>SMB</key>
+ <dict/>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ </dict>
+ <key>Sets</key>
+ <dict>
+ <key>8458B09B-93EA-44C2-AD05-0B4E4A4D9651</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>Global</key>
+ <dict>
+ <key>IPv4</key>
+ <dict>
+ <key>ServiceOrder</key>
+ <array>
+ <string>299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </array>
+ </dict>
+ </dict>
+ <key>Service</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>__LINK__</key>
+ <string>/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>UserDefinedName</key>
+ <string>Automatic</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>HostNames</key>
+ <dict>
+ <key>LocalHostName</key>
+ <string>rossis-Mac-mini</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>ComputerName</key>
+ <string>rossi’s Mac mini</string>
+ <key>ComputerNameEncoding</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/src/test/resources/data/osx/osx_pac.plist b/src/test/resources/data/osx/osx_pac.plist
new file mode 100644
index 0000000..5a5b64d
--- /dev/null
+++ b/src/test/resources/data/osx/osx_pac.plist
@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CurrentSet</key>
+ <string>/Sets/8458B09B-93EA-44C2-AD05-0B4E4A4D9651</string>
+ <key>NetworkServices</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>AppleTalk</key>
+ <dict/>
+ <key>DNS</key>
+ <dict/>
+ <key>IPv4</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>DHCP</string>
+ </dict>
+ <key>IPv6</key>
+ <dict>
+ <key>ConfigMethod</key>
+ <string>Automatic</string>
+ </dict>
+ <key>Interface</key>
+ <dict>
+ <key>DeviceName</key>
+ <string>en0</string>
+ <key>Hardware</key>
+ <string>Ethernet</string>
+ <key>Type</key>
+ <string>Ethernet</string>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ <key>Proxies</key>
+ <dict>
+ <key>ExceptionsList</key>
+ <array>
+ <string>*.local</string>
+ <string>169.254/16</string>
+ </array>
+ <key>ExcludeSimpleHostnames</key>
+ <integer>1</integer>
+ <key>FTPEnable</key>
+ <integer>1</integer>
+ <key>FTPPassive</key>
+ <integer>1</integer>
+ <key>FTPPort</key>
+ <integer>8092</integer>
+ <key>FTPProxy</key>
+ <string>ftp_proxy.unit-test.invalid</string>
+ <key>GopherEnable</key>
+ <integer>1</integer>
+ <key>GopherPort</key>
+ <integer>8090</integer>
+ <key>GopherProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPEnable</key>
+ <integer>1</integer>
+ <key>HTTPPort</key>
+ <integer>8090</integer>
+ <key>HTTPProxy</key>
+ <string>http_proxy.unit-test.invalid</string>
+ <key>HTTPSEnable</key>
+ <integer>1</integer>
+ <key>HTTPSPort</key>
+ <integer>8091</integer>
+ <key>HTTPSProxy</key>
+ <string>https_proxy.unit-test.invalid</string>
+ <key>ProxyAutoConfigEnable</key>
+ <integer>1</integer>
+ <key>ProxyAutoConfigURLString</key>
+ <string>http://http_proxy.unit-test.invalid/wpad.pac</string>
+ <key>ProxyAutoDiscoveryEnable</key>
+ <integer>0</integer>
+ <key>RTSPEnable</key>
+ <integer>1</integer>
+ <key>RTSPPort</key>
+ <integer>8094</integer>
+ <key>RTSPProxy</key>
+ <string>rtsp_proxy.unit-test.invalid</string>
+ <key>SOCKSEnable</key>
+ <integer>1</integer>
+ <key>SOCKSPort</key>
+ <integer>8095</integer>
+ <key>SOCKSProxy</key>
+ <string>socks_proxy.unit-test.invalid</string>
+ </dict>
+ <key>SMB</key>
+ <dict/>
+ <key>UserDefinedName</key>
+ <string>Ethernet</string>
+ </dict>
+ </dict>
+ <key>Sets</key>
+ <dict>
+ <key>8458B09B-93EA-44C2-AD05-0B4E4A4D9651</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>Global</key>
+ <dict>
+ <key>IPv4</key>
+ <dict>
+ <key>ServiceOrder</key>
+ <array>
+ <string>299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </array>
+ </dict>
+ </dict>
+ <key>Service</key>
+ <dict>
+ <key>299B07C0-D0E0-4840-8486-9E77B9ED84DB</key>
+ <dict>
+ <key>__LINK__</key>
+ <string>/NetworkServices/299B07C0-D0E0-4840-8486-9E77B9ED84DB</string>
+ </dict>
+ </dict>
+ </dict>
+ <key>UserDefinedName</key>
+ <string>Automatic</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>Network</key>
+ <dict>
+ <key>HostNames</key>
+ <dict>
+ <key>LocalHostName</key>
+ <string>rossis-Mac-mini</string>
+ </dict>
+ </dict>
+ <key>System</key>
+ <dict>
+ <key>ComputerName</key>
+ <string>rossi’s Mac mini</string>
+ <key>ComputerNameEncoding</key>
+ <integer>0</integer>
+ </dict>
+ </dict>
+</dict>
+</plist>
diff --git a/src/test/resources/data/pac/test1.pac b/src/test/resources/data/pac/test1.pac
new file mode 100644
index 0000000..4d8c2d6
--- /dev/null
+++ b/src/test/resources/data/pac/test1.pac
@@ -0,0 +1,4 @@
+
+function FindProxyForURL(url, host) {
+ return "PROXY http_proxy.unit-test.invalid:8090";
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/test2.pac b/src/test/resources/data/pac/test2.pac
new file mode 100644
index 0000000..f8a846e
--- /dev/null
+++ b/src/test/resources/data/pac/test2.pac
@@ -0,0 +1,10 @@
+// Test comments in scripts
+
+function FindProxyForURL(url, host) {
+
+ /*
+ * This is a multiline comment
+ */
+
+ return "DIRECT"; // This returns always DIRECT
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/testDateRange.pac b/src/test/resources/data/pac/testDateRange.pac
new file mode 100644
index 0000000..305b783
--- /dev/null
+++ b/src/test/resources/data/pac/testDateRange.pac
@@ -0,0 +1,11 @@
+// Test date range functions
+
+function FindProxyForURL(url, host) {
+ dateRange(1, 30);
+ dateRange("JUN", "JUL");
+ dateRange(2008, 2009);
+ dateRange("JUN", "JUL", "GMT");
+ dateRange(1, "JUN", 2008, 30, "JUL", 2099, "GMT");
+
+ return "DIRECT";
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/testLocalIP.pac b/src/test/resources/data/pac/testLocalIP.pac
new file mode 100644
index 0000000..ace486c
--- /dev/null
+++ b/src/test/resources/data/pac/testLocalIP.pac
@@ -0,0 +1,4 @@
+
+function FindProxyForURL(url, host) {
+ return "PROXY "+ myIpAddress()+":8080";
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/testMultiProxy.pac b/src/test/resources/data/pac/testMultiProxy.pac
new file mode 100644
index 0000000..9f5a2d1
--- /dev/null
+++ b/src/test/resources/data/pac/testMultiProxy.pac
@@ -0,0 +1,4 @@
+function FindProxyForURL(url, host)
+{
+ return "PROXY my-proxy.com:80 ; PROXY my-proxy2.com: 8080; ";
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/testTimeRange.pac b/src/test/resources/data/pac/testTimeRange.pac
new file mode 100644
index 0000000..21138ac
--- /dev/null
+++ b/src/test/resources/data/pac/testTimeRange.pac
@@ -0,0 +1,11 @@
+// Test weekday functions
+
+function FindProxyForURL(url, host) {
+ timeRange(12);
+ timeRange(11, 16);
+ timeRange(10, 30, 17, 30, "gmt");
+ timeRange(10, 30, 00, 17, 30, 30, "GMT");
+ timeRange(19, 9);
+
+ return "DIRECT";
+} \ No newline at end of file
diff --git a/src/test/resources/data/pac/testWeekDay.pac b/src/test/resources/data/pac/testWeekDay.pac
new file mode 100644
index 0000000..1b37cb1
--- /dev/null
+++ b/src/test/resources/data/pac/testWeekDay.pac
@@ -0,0 +1,10 @@
+// Test weekday functions
+
+function FindProxyForURL(url, host) {
+ weekdayRange("MON");
+ weekdayRange("MON", "GMT");
+ weekdayRange("FRI", "MON");
+ weekdayRange("MON", "WED", "GMT");
+
+ return "DIRECT";
+} \ No newline at end of file
diff --git a/src/test/resources/data/win/proxy_util_amd64.dll b/src/test/resources/data/win/proxy_util_amd64.dll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/resources/data/win/proxy_util_amd64.dll
diff --git a/src/test/resources/data/win/proxy_util_ia64.dll b/src/test/resources/data/win/proxy_util_ia64.dll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/resources/data/win/proxy_util_ia64.dll
diff --git a/src/test/resources/data/win/proxy_util_w32.dll b/src/test/resources/data/win/proxy_util_w32.dll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/test/resources/data/win/proxy_util_w32.dll
diff --git a/src/test/resources/data/wpad/wpad.pac b/src/test/resources/data/wpad/wpad.pac
new file mode 100644
index 0000000..4d8c2d6
--- /dev/null
+++ b/src/test/resources/data/wpad/wpad.pac
@@ -0,0 +1,4 @@
+
+function FindProxyForURL(url, host) {
+ return "PROXY http_proxy.unit-test.invalid:8090";
+} \ No newline at end of file
diff --git a/src_native/gnome/ProxySchemasGSettingsAccess.c b/src_native/gnome/ProxySchemasGSettingsAccess.c
new file mode 100644
index 0000000..b6e08a9
--- /dev/null
+++ b/src_native/gnome/ProxySchemasGSettingsAccess.c
@@ -0,0 +1,117 @@
+#include "ProxySchemasGSettingsAccess.h"
+#include <gio/gio.h>
+#include <string.h>
+#include <stdlib.h>
+
+struct GSettingsList {
+ const char * schemaName;
+ GSettings* client;
+ struct GSettingsList * next;
+};
+
+struct GSettingsList * proxySchemas;
+
+int startsWith(const char *pre, const char *str) {
+ size_t lenpre = strlen(pre),
+ lenstr = strlen(str);
+ return lenstr < lenpre ? 0 : strncmp(pre, str, lenpre) == 0;
+}
+
+__attribute__((constructor)) void init() {
+ g_type_init();
+ proxySchemas = 0;
+ const gchar* const* schemas = g_settings_list_schemas();
+ while (*schemas) {
+ if (startsWith("org.gnome.system.proxy", (const char *)(*schemas))) {
+ struct GSettingsList * nclients = (struct GSettingsList *) malloc(sizeof(struct GSettingsList));
+ nclients->next = proxySchemas;
+ nclients->schemaName = *schemas;
+ nclients->client = g_settings_new(*schemas);
+ proxySchemas = nclients;
+ }
+ schemas++;
+ }
+}
+
+__attribute__((destructor)) void destroy() {
+ struct GSettingsList * proxySchemasIt = proxySchemas;
+ while (proxySchemasIt != 0) {
+ struct GSettingsList * next = proxySchemasIt->next;
+ free(proxySchemasIt);
+ proxySchemasIt = next;
+ }
+ proxySchemas = 0;
+}
+
+void convertKey(JNIEnv *env, jmethodID put, jobject hashMap, gchar* gkey, GSettings* schema, GVariant * gvalue) {
+ jstring key = (*env)->NewStringUTF(env, gkey);
+ jobject value = 0;
+
+ const GVariantType * t = g_variant_get_type(gvalue);
+ if (g_variant_type_equal(t, G_VARIANT_TYPE_STRING)) {
+ const gchar * gstring = g_variant_get_string(gvalue, 0);
+ value = (*env)->NewStringUTF(env, gstring);
+ } else if (g_variant_type_equal(t, G_VARIANT_TYPE_BOOLEAN)) {
+ jclass bClass = (*env)->FindClass(env, "java/lang/Boolean");
+ jmethodID valueOf = (*env)->GetStaticMethodID(env, bClass, "valueOf", "(Z)Ljava/lang/Boolean;");
+ value = (*env)->CallStaticObjectMethod(env, bClass, valueOf, g_variant_get_boolean(gvalue));
+ (*env)->DeleteLocalRef(env, bClass);
+ } else if (g_variant_type_equal(t, G_VARIANT_TYPE_INT32)) {
+ jclass bClass = (*env)->FindClass(env, "java/lang/Integer");
+ jmethodID valueOf = (*env)->GetStaticMethodID(env, bClass, "valueOf", "(I)Ljava/lang/Integer;");
+ value = (*env)->CallStaticObjectMethod(env, bClass, valueOf, g_variant_get_int32(gvalue));
+ (*env)->DeleteLocalRef(env, bClass);
+ } else if (g_variant_type_equal(t, G_VARIANT_TYPE_STRING_ARRAY)) {
+ int size = g_variant_n_children(gvalue);
+ jclass bClass = (*env)->FindClass(env, "java/util/ArrayList");
+ jmethodID init = (*env)->GetMethodID(env, bClass, "<init>", "(I)V");
+ jmethodID add = (*env)->GetMethodID(env, bClass, "add", "(Ljava/lang/Object;)Z");
+ value = (*env)->NewObject(env, bClass, init, size);
+ int i;
+ for (i = 0; i < size; i++) {
+ GVariant * gsvalue = g_variant_get_child_value(gvalue, i);
+ const gchar * gstring = g_variant_get_string(gsvalue, 0);
+ jobject svalue = (*env)->NewStringUTF(env, gstring);
+ (*env)->CallObjectMethod(env, value, add, svalue);
+ (*env)->DeleteLocalRef(env, svalue);
+ }
+ (*env)->DeleteLocalRef(env, bClass);
+ }
+
+ if (value != 0) {
+ (*env)->CallObjectMethod(env, hashMap, put, key, value);
+ (*env)->DeleteLocalRef(env, value);
+ }
+ (*env)->DeleteLocalRef(env, key);
+}
+
+void convertSchema(JNIEnv *env, jclass mapClass, jmethodID init, jmethodID put, jobject hashMap, const char * schemaName, GSettings* schema) {
+ jobject subHashMap = (*env)->NewObject(env, mapClass, init);
+ jstring jschemaName = (*env)->NewStringUTF(env, schemaName);
+ gchar** keys = g_settings_list_keys(schema);
+ while (*keys) {
+ convertKey(env, put, subHashMap, *keys, schema, g_settings_get_value(schema, *keys));
+ keys++;
+ }
+ (*env)->CallObjectMethod(env, hashMap, put, jschemaName, subHashMap);
+ (*env)->DeleteLocalRef(env, subHashMap);
+ (*env)->DeleteLocalRef(env, jschemaName);
+}
+
+JNIEXPORT jobject JNICALL Java_com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess_getValueByKeyBySchema(JNIEnv *env, jclass c) {
+ jclass mapClass = (*env)->FindClass(env, "java/util/HashMap");
+
+ jmethodID init = (*env)->GetMethodID(env, mapClass, "<init>", "()V");
+ jobject hashMap = (*env)->NewObject(env, mapClass, init);
+
+ jmethodID put = (*env)->GetMethodID(env, mapClass, "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+ struct GSettingsList * proxySchemasIt = proxySchemas;
+ while (proxySchemasIt != 0) {
+ convertSchema(env, mapClass, init, put, hashMap, proxySchemasIt->schemaName, proxySchemasIt->client);
+ proxySchemasIt = proxySchemasIt->next;
+ }
+
+ (*env)->DeleteLocalRef(env, mapClass);
+ return hashMap;
+}
+
diff --git a/src_native/gnome/ProxySchemasGSettingsAccess.h b/src_native/gnome/ProxySchemasGSettingsAccess.h
new file mode 100644
index 0000000..84c112d
--- /dev/null
+++ b/src_native/gnome/ProxySchemasGSettingsAccess.h
@@ -0,0 +1,21 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess */
+
+#ifndef _Included_com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess
+#define _Included_com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess
+#ifdef __cplusplus
+extern "C" {
+#endif
+/*
+ * Class: com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess
+ * Method: getValueByKeyBySchema
+ * Signature: ()Ljava/util/Map;
+ */
+JNIEXPORT jobject JNICALL Java_com_btr_proxy_search_desktop_gnome_ProxySchemasGSettingsAccess_getValueByKeyBySchema
+ (JNIEnv *, jclass);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src_native/gnome/make b/src_native/gnome/make
new file mode 100644
index 0000000..dd2235d
--- /dev/null
+++ b/src_native/gnome/make
@@ -0,0 +1,2 @@
+gcc -fPIC -m32 -shared `pkg-config --cflags gio-2.0` ProxySchemasGSettingsAccess.c `pkg-config --libs gio-2.0` -I /usr/lib/jvm/java-6-openjdk-i386/include/ -o gsettings-x86.so
+gcc -fPIC -m64 -shared `pkg-config --cflags gio-2.0` ProxySchemasGSettingsAccess.c `pkg-config --libs gio-2.0` -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.25.x86_64/include/linux/ -I /usr/lib/jvm/java-1.7.0-openjdk-1.7.0.25.x86_64/include -o gsettings-amd64.so \ No newline at end of file
diff --git a/src_native/win/proxy_util_w23/compile.bat b/src_native/win/proxy_util_w23/compile.bat
new file mode 100755
index 0000000..15e0669
--- /dev/null
+++ b/src_native/win/proxy_util_w23/compile.bat
@@ -0,0 +1 @@
+cl "-IC:/Program Files/Java/jdk1.6.0_21/include" "-IC:/Program Files/Java/jdk1.6.0_21/include/win32" -LD proxy_util_w32.cpp dllmain.cpp stdafx.cpp -Feproxy_util_amd64.dll winhttp.lib Advapi32.lib
diff --git a/src_native/win/proxy_util_w23/dllmain.cpp b/src_native/win/proxy_util_w23/dllmain.cpp
new file mode 100644
index 0000000..c54fd32
--- /dev/null
+++ b/src_native/win/proxy_util_w23/dllmain.cpp
@@ -0,0 +1,19 @@
+// dllmain.cpp : Entry point code.
+#include "stdafx.h"
+
+BOOL APIENTRY DllMain( HMODULE hModule,
+ DWORD ul_reason_for_call,
+ LPVOID lpReserved
+ )
+{
+ switch (ul_reason_for_call)
+ {
+ case DLL_PROCESS_ATTACH:
+ case DLL_THREAD_ATTACH:
+ case DLL_THREAD_DETACH:
+ case DLL_PROCESS_DETACH:
+ break;
+ }
+ return TRUE;
+}
+
diff --git a/src_native/win/proxy_util_w23/proxy_util_w23.vcproj b/src_native/win/proxy_util_w23/proxy_util_w23.vcproj
new file mode 100644
index 0000000..5227b98
--- /dev/null
+++ b/src_native/win/proxy_util_w23/proxy_util_w23.vcproj
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="9,00"
+ Name="proxy_util_w32"
+ ProjectGUID="{9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}"
+ RootNamespace="proxy_util_w23"
+ Keyword="Win32Proj"
+ TargetFrameworkVersion="196613"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="C:\Programme\Java\jdk1.6.0_14\include\win32;C:\Programme\Java\jdk1.6.0_14\include"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;PROXY_UTIL_W23_EXPORTS"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Winhttp.lib AdvAPI32.Lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(SolutionDir)$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="2"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ EnableIntrinsicFunctions="true"
+ AdditionalIncludeDirectories="C:\Programme\Java\jdk1.6.0_14\include\win32;C:\Programme\Java\jdk1.6.0_14\include"
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;PROXY_UTIL_W23_EXPORTS"
+ RuntimeLibrary="0"
+ EnableFunctionLevelLinking="true"
+ UsePrecompiledHeader="2"
+ WarningLevel="3"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Winhttp.lib AdvAPI32.Lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Quelldateien"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\dllmain.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ CompileAsManaged="0"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="0"
+ CompileAsManaged="0"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath=".\proxy_util_w32.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.cpp"
+ >
+ <FileConfiguration
+ Name="Debug|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Release|Win32"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ UsePrecompiledHeader="1"
+ />
+ </FileConfiguration>
+ </File>
+ </Filter>
+ <Filter
+ Name="Headerdateien"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\proxy_util_w32.h"
+ >
+ </File>
+ <File
+ RelativePath=".\stdafx.h"
+ >
+ </File>
+ <File
+ RelativePath=".\targetver.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Ressourcendateien"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ <File
+ RelativePath=".\ReadMe.txt"
+ >
+ </File>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/src_native/win/proxy_util_w23/proxy_util_w32.cpp b/src_native/win/proxy_util_w23/proxy_util_w32.cpp
new file mode 100644
index 0000000..a62ed08
--- /dev/null
+++ b/src_native/win/proxy_util_w23/proxy_util_w32.cpp
@@ -0,0 +1,205 @@
+// proxy_util_w23.cpp : Main methods of the DLL.
+
+#include "stdafx.h"
+#include "proxy_util_w32.h"
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: winHttpDetectAutoProxyConfigUrl
+ * Signature: (I)Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpDetectAutoProxyConfigUrl
+(JNIEnv *env, jobject source, jint mode) {
+
+
+ LPWSTR ppwszAutoConfigUrl = NULL;
+ BOOL result = WinHttpDetectAutoProxyConfigUrl( mode, &ppwszAutoConfigUrl );
+ if (ppwszAutoConfigUrl == NULL) {
+ return NULL;
+ }
+
+ jstring retValue = env->NewString((jchar*)ppwszAutoConfigUrl,
+ wcslen(ppwszAutoConfigUrl));
+
+ GlobalFree( ppwszAutoConfigUrl );
+
+ return retValue;
+}
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: winHttpGetDefaultProxyConfiguration
+ * Signature: ()Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpGetDefaultProxyConfiguration
+(JNIEnv *env, jobject source) {
+
+ WINHTTP_PROXY_INFO proxyInfo;
+
+ // Retrieve the default proxy configuration.
+ BOOL result = WinHttpGetDefaultProxyConfiguration( &proxyInfo );
+ if (result == FALSE) {
+ // TODO what to do in case of error.
+ DWORD errorCode = GetLastError();
+ }
+
+ int proxyTypeLen = 0;
+ int proxyLen = 0;
+ int proxyBypassLen = 0;
+
+ LPWSTR proxyType = NULL;
+ if (proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NAMED_PROXY) {
+ proxyType = L"PROXY ";
+ proxyTypeLen = wcslen(proxyType);
+ } else
+ if (proxyInfo.dwAccessType == WINHTTP_ACCESS_TYPE_NO_PROXY) {
+ proxyType = L"DIRECT ";
+ proxyTypeLen = wcslen(proxyType);
+ }
+ if (proxyInfo.lpszProxy != NULL) {
+ proxyLen += wcslen(proxyInfo.lpszProxy);
+ }
+ if (proxyInfo.lpszProxyBypass != NULL) {
+ proxyBypassLen += wcslen(proxyInfo.lpszProxyBypass);
+ }
+
+ jstring retVal = proxyInfo.lpszProxy == NULL? NULL
+ : env->NewString((jchar*)proxyInfo.lpszProxy, wcslen(proxyInfo.lpszProxy));
+
+ if (proxyInfo.lpszProxy != NULL) {
+ GlobalFree( proxyInfo.lpszProxy );
+ }
+ if (proxyInfo.lpszProxyBypass != NULL) {
+ GlobalFree( proxyInfo.lpszProxyBypass );
+ }
+ return retVal;
+
+
+ //int retValueLen = proxyTypeLen+proxyLen+1+proxyBypassLen+1;
+ //int insertPos = 0;
+ //LPWSTR combined = new WCHAR[retValueLen];
+ //combined[retValueLen] = 0;
+
+ //wcsncat_s(combined, retValueLen, proxyType, proxyTypeLen);
+ //insertPos += proxyTypeLen;
+ //retValueLen -= proxyTypeLen;
+
+ //wcsncat_s(combined, retValueLen, proxyInfo.lpszProxy, proxyLen);
+ //insertPos += proxyLen;
+ //retValueLen -= proxyLen;
+
+ //wcsncat_s(combined, retValueLen, TEXT("|"), 1);
+ //insertPos += proxyLen;
+ //retValueLen -= proxyLen;
+
+ //wcsncat_s(combined, retValueLen, proxyInfo.lpszProxyBypass, proxyBypassLen);
+ //insertPos += proxyBypassLen;
+ //retValueLen -= proxyBypassLen;
+
+ // if (proxyInfo.lpszProxy != NULL) {
+ // GlobalFree( proxyInfo.lpszProxy );
+ // }
+ // if (proxyInfo.lpszProxyBypass != NULL) {
+ // GlobalFree( proxyInfo.lpszProxyBypass );
+ // }
+
+ //jstring retVal = env->NewString((jchar*)combined, wcslen(combined));
+
+ //return retVal;
+}
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: WinHttpGetIEProxyConfigForCurrentUser
+ * Signature: ()Lcom/btr/proxy/search/desktop/win/Win32IESettings;
+ ****************************************************************************/
+
+JNIEXPORT jobject JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpGetIEProxyConfigForCurrentUser
+(JNIEnv *env, jobject source) {
+
+ WINHTTP_CURRENT_USER_IE_PROXY_CONFIG ieProxyInfo;
+
+ // Retrieve the IE proxy configuration.
+ BOOL result = WinHttpGetIEProxyConfigForCurrentUser( &ieProxyInfo );
+ if (result == FALSE) {
+ DWORD errorCode = GetLastError();
+ return NULL;
+ }
+
+ jboolean autoDetect = ieProxyInfo.fAutoDetect;
+ jstring autoConfigUrl = NULL;
+ jstring proxy = NULL;
+ jstring proxyBypass = NULL;
+
+ if (ieProxyInfo.lpszAutoConfigUrl != NULL) {
+ autoConfigUrl = env->NewString((jchar*)ieProxyInfo.lpszAutoConfigUrl, wcslen(ieProxyInfo.lpszAutoConfigUrl));
+ GlobalFree( ieProxyInfo.lpszAutoConfigUrl );
+ }
+ if (ieProxyInfo.lpszProxy != NULL) {
+ proxy = env->NewString((jchar*)ieProxyInfo.lpszProxy, wcslen(ieProxyInfo.lpszProxy));
+ GlobalFree( ieProxyInfo.lpszProxy );
+ }
+ if (ieProxyInfo.lpszProxyBypass != NULL) {
+ proxyBypass = env->NewString((jchar*)ieProxyInfo.lpszProxyBypass, wcslen(ieProxyInfo.lpszProxyBypass));
+ GlobalFree( ieProxyInfo.lpszProxyBypass );
+ }
+
+ // Build result container object.
+ jclass retValueClass = env->FindClass("com/btr/proxy/search/desktop/win/Win32IESettings");
+ if ( retValueClass == NULL ) {
+ return NULL;
+ }
+
+ jmethodID jmid = env->GetMethodID(retValueClass, "<init>", "(ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ if (jmid == NULL) {
+ return NULL;
+ }
+
+ // Win32IESettings(boolean autoDetect, String autoConfigUrl, String proxy, String proxyBypass)
+ jobject retValue = env->NewObject(retValueClass, jmid, autoDetect, autoConfigUrl, proxy, proxyBypass);
+
+ return retValue;
+}
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: readUserHomedir
+ * Signature: ()Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_readUserHomedir
+(JNIEnv *env, jobject source) {
+ HKEY key;
+ int result = RegOpenKeyEx(HKEY_CURRENT_USER,
+#ifdef _WIN64
+ "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
+#else
+ L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
+#endif
+ 0, KEY_QUERY_VALUE, &key);
+ if (0 != ERROR_SUCCESS) {
+ LPWSTR errorMsg = L"ERROR: Key open failed";
+ return env->NewString((jchar*)errorMsg, wcslen(errorMsg));
+ }
+
+ BYTE pvData[1000];
+ DWORD dataSize = 1000;
+
+#ifdef _WIN64
+ result = RegQueryValueEx(key, "AppData", NULL, NULL, pvData, &dataSize);
+#else
+ result = RegQueryValueEx(key, L"AppData", NULL, NULL, pvData, &dataSize);
+#endif
+ RegCloseKey(key);
+ if (result != ERROR_SUCCESS) {
+ LPWSTR errorMsg = L"ERROR: Read value failed";
+ return env->NewString((jchar*)errorMsg, wcslen(errorMsg));
+ }
+
+ jstring retValue = env->NewString((jchar*)pvData, (dataSize-1)/2);
+ return retValue;
+}
+
+
diff --git a/src_native/win/proxy_util_w23/proxy_util_w32.h b/src_native/win/proxy_util_w23/proxy_util_w32.h
new file mode 100644
index 0000000..381b1e9
--- /dev/null
+++ b/src_native/win/proxy_util_w23/proxy_util_w32.h
@@ -0,0 +1,50 @@
+/* DO NOT EDIT THIS FILE - it is machine generated */
+#include <jni.h>
+/* Header for class com_btr_proxy_search_desktop_win_Win32ProxyUtils */
+
+#ifndef _Included_com_btr_proxy_search_desktop_win_Win32ProxyUtils
+#define _Included_com_btr_proxy_search_desktop_win_Win32ProxyUtils
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: winHttpDetectAutoProxyConfigUrl
+ * Signature: (I)Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpDetectAutoProxyConfigUrl
+ (JNIEnv *, jobject, jint);
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: winHttpGetDefaultProxyConfiguration
+ * Signature: ()Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpGetDefaultProxyConfiguration
+ (JNIEnv *, jobject);
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: WinHttpGetIEProxyConfigForCurrentUser
+ * Signature: ()Lcom/btr/proxy/search/desktop/win/Win32IESettings;
+ ****************************************************************************/
+
+JNIEXPORT jobject JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_winHttpGetIEProxyConfigForCurrentUser
+(JNIEnv *, jobject);
+
+/*****************************************************************************
+ * Class: com_btr_proxy_search_desktop_win_Win32ProxyUtils
+ * Method: readUserHomedir
+ * Signature: ()Ljava/lang/String;
+ ****************************************************************************/
+
+JNIEXPORT jstring JNICALL Java_com_btr_proxy_search_desktop_win_Win32ProxyUtils_readUserHomedir
+(JNIEnv *, jobject);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/src_native/win/proxy_util_w23/stdafx.cpp b/src_native/win/proxy_util_w23/stdafx.cpp
new file mode 100644
index 0000000..aa9cf23
--- /dev/null
+++ b/src_native/win/proxy_util_w23/stdafx.cpp
@@ -0,0 +1,2 @@
+// Used for precompiled headers.
+#include "stdafx.h"
diff --git a/src_native/win/proxy_util_w23/stdafx.h b/src_native/win/proxy_util_w23/stdafx.h
new file mode 100644
index 0000000..66875d0
--- /dev/null
+++ b/src_native/win/proxy_util_w23/stdafx.h
@@ -0,0 +1,11 @@
+// stdafx.h : Includefiles for Standardsystem-Includefiles
+
+#pragma once
+
+#include "targetver.h"
+
+#define WIN32_LEAN_AND_MEAN // No not include rarely used parts of Windows-Header .
+// Windows-Headerfiles:
+#include <windows.h>
+#include <winhttp.h>
+#include <Winreg.h> \ No newline at end of file
diff --git a/src_native/win/proxy_util_w23/targetver.h b/src_native/win/proxy_util_w23/targetver.h
new file mode 100644
index 0000000..24051b3
--- /dev/null
+++ b/src_native/win/proxy_util_w23/targetver.h
@@ -0,0 +1,28 @@
+#pragma once
+
+// This is autogenerated crap from Visual Studio. As I have the German version,
+// the comments are in German. Sorry for this.
+// -Bernd Rosstauscher
+
+// Die folgenden Makros definieren die mindestens erforderliche Plattform. Die mindestens erforderliche Plattform
+// ist die früheste Windows-, Internet Explorer-Version usw., die über die erforderlichen Features zur Ausführung
+// Ihrer Anwendung verfügt. Die Makros aktivieren alle Funktionen, die auf den Plattformversionen bis
+// einschließlich der angegebenen Version verfügbar sind.
+
+// Ändern Sie folgende Definitionen für Plattformen, die älter als die unten angegebenen sind.
+// Unter MSDN finden Sie die neuesten Informationen über die entsprechenden Werte für die unterschiedlichen Plattformen.
+#ifndef WINVER // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist.
+#define WINVER 0x0600 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen.
+#endif
+
+#ifndef _WIN32_WINNT // Gibt an, dass Windows Vista die mindestens erforderliche Plattform ist.
+#define _WIN32_WINNT 0x0600 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von Windows abzuzielen.
+#endif
+
+#ifndef _WIN32_WINDOWS // Gibt an, dass Windows 98 die mindestens erforderliche Plattform ist.
+#define _WIN32_WINDOWS 0x0410 // Ändern Sie den entsprechenden Wert, um auf mindestens Windows Me abzuzielen.
+#endif
+
+#ifndef _WIN32_IE // Gibt an, dass Internet Explorer 7.0 die mindestens erforderliche Plattform ist.
+#define _WIN32_IE 0x0700 // Ändern Sie den entsprechenden Wert, um auf andere Versionen von IE abzuzielen.
+#endif
diff --git a/src_native/win/proxy_util_w32.sln b/src_native/win/proxy_util_w32.sln
new file mode 100644
index 0000000..cc2ecd8
--- /dev/null
+++ b/src_native/win/proxy_util_w32.sln
@@ -0,0 +1,20 @@
+
+Microsoft Visual Studio Solution File, Format Version 10.00
+# Visual C++ Express 2008
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "proxy_util_w23", "proxy_util_w23\proxy_util_w23.vcproj", "{9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}.Debug|Win32.ActiveCfg = Debug|Win32
+ {9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}.Debug|Win32.Build.0 = Debug|Win32
+ {9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}.Release|Win32.ActiveCfg = Release|Win32
+ {9A61BBAC-DD8E-4952-AAE6-26C03CF474F2}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src_native/win/proxy_util_w32.suo b/src_native/win/proxy_util_w32.suo
new file mode 100644
index 0000000..049b84e
--- /dev/null
+++ b/src_native/win/proxy_util_w32.suo
Binary files differ