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;
	
} 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;
	}

} 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.");

		int index = fqdn.indexOf('.');
		while (index != -1 && result == null) {
			fqdn = fqdn.substring(index+1);
			
			// if we are already on TLD level then escape 
			if (fqdn.indexOf('.') == -1) {
				break;
			}
			
			// Try to connect to URL
			try {
				URL lookupURL = new URL("http://wpad."+ fqdn +"/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();
				}
				con.disconnect();
			} catch (UnknownHostException e) {
				Logger.log(getClass(), LogLevel.DEBUG, "Not available!");
				// Not a real error, try next address
			}

			index = fqdn.indexOf('.');
		}
			
		return result;
	}

	/*************************************************************************
	 * 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;
	}

} 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();
	}

} 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;
	}

} 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);
	}

} 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) {
					proxies.add(buildProxyFromPacResult(proxyDef));
				}
			}
			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);
	}
	
}
 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 {
				// Reset it again with next download we should get a new expire info
				this.expireAtMillis = 0;   
				
				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) {
			System.out.println(System.getProperty("user.dir"));
			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;
		}
	}
	
} 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);
	}

} 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();
	}

} 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;
	}
	

}
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;
package com.btr.proxy.search.desktop.kde;



/*****************************************************************************
 * 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();