summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorBjörn Hagemeister2014-11-11 14:40:18 +0100
committerBjörn Hagemeister2014-11-11 14:40:18 +0100
commite70ee5b59306ea37dd0c72603c61b33b1555def9 (patch)
treee6f09d76449da54463b9b4fa408b7dfba4b4b7bf /src
downloadproxy-vole-e70ee5b59306ea37dd0c72603c61b33b1555def9.tar.gz
proxy-vole-e70ee5b59306ea37dd0c72603c61b33b1555def9.tar.xz
proxy-vole-e70ee5b59306ea37dd0c72603c61b33b1555def9.zip
Added proxy java classes.
Diffstat (limited to 'src')
-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
116 files changed, 12011 insertions, 0 deletions
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