package com.github.markusbernhardt.proxy.search.desktop.gnome; import java.io.IOException; import java.net.ProxySelector; import java.util.Properties; import com.github.markusbernhardt.proxy.ProxySearchStrategy; import com.github.markusbernhardt.proxy.selector.direct.NoProxySelector; import com.github.markusbernhardt.proxy.selector.fixed.FixedProxySelector; import com.github.markusbernhardt.proxy.selector.misc.ProtocolDispatchSelector; import com.github.markusbernhardt.proxy.selector.whitelist.ProxyBypassListSelector; import com.github.markusbernhardt.proxy.util.Logger; import com.github.markusbernhardt.proxy.util.ProxyException; import com.github.markusbernhardt.proxy.util.ProxyUtil; import com.github.markusbernhardt.proxy.util.Logger.LogLevel; import java.io.BufferedReader; import java.io.InputStreamReader; /***************************************************************************** * 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>org.gnome.system.proxy.http enabled</i> -> bool used only by * gnome-vfs</li> * <li><i>org.gnome.system.proxy.http host</i> -> string * "my-proxy.example.com" without "http://"</li> * <li><i>org.gnome.system.proxy.http port</i> -> int</li> * <li><i>org.gnome.system.proxy.http use-authentication</i> -> bool</li> * <li><i>org.gnome.system.proxy.http authentication-user</i> -> string</li> * <li><i>org.gnome.system.proxy.http authentication-password</i> -> string * </li> * <li><i>org.gnome.system.proxy ignore-hosts</i> -> list-of-string</li> * <li><i>org.gnome.system.proxy mode</i> -> string THIS IS THE CANONICAL * KEY; SEE BELOW</li> * <li><i>org.gnome.system.proxy use-same-proxy</i> -> bool</li> * <li><i>org.gnome.system.proxy.https host</i> -> string * "proxy-for-https.example.com"</li> * <li><i>org.gnome.system.proxy.https port</i> -> int</li> * <li><i>org.gnome.system.proxy.ftp host</i> -> string * "proxy-for-ftp.example.com"</li> * <li><i>org.gnome.system.proxy.ftp port</i> -> int</li> * <li><i>org.gnome.system.proxy.socks host</i> -> string * "proxy-for-socks.example.com"</li> * <li><i>org.gnome.system.proxy.socks port</i> -> int</li> * <li><i>org.gnome.system.proxy autoconfig-url</i> -> string * "http://proxy-autoconfig.example.com"</li> * </ul> * <i>org.gnome.system.proxy mode</i> can be either:<br> * "none" -> No proxy is used<br> * "manual" -> The user's configuration values are used * (org.gnome.system.http_proxy/{host,port,etc.})<br> * "auto" -> The "org.gnome.system.proxy/autoconfig_url" key is used <br> * <p> * GNOME Proxy_configuration settings are explained * <a href="https://developer.gnome.org/ProxyConfiguration/">here</a> in detail * </p> * * @author Markus Bernhardt, Copyright 2016 * @author Bernd Rosstauscher, Copyright 2009 ****************************************************************************/ // TODO Work in progress: Needs unit tests and testing and code review public class GnomeDConfProxySearchStrategy implements ProxySearchStrategy { /************************************************************************* * ProxySelector * * @see java.net.ProxySelector#ProxySelector() ************************************************************************/ public GnomeDConfProxySearchStrategy() { 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. ************************************************************************/ @Override public ProxySelector getProxySelector() throws ProxyException { Logger.log(getClass(), LogLevel.TRACE, "Detecting Gnome proxy settings"); Properties settings = readSettings(); String type = settings.getProperty("org.gnome.system.proxy mode"); Logger.log(getClass(), LogLevel.TRACE, "Mode is :{0}", type); ProxySelector result = null; if (type == null) { String useProxy = settings.getProperty("org.gnome.system.proxy.http enabled"); 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("org.gnome.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("org.gnome.system.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; } /************************************************************************* * Gets the printable name of the search strategy. * * @return the printable name of the search strategy ************************************************************************/ @Override public String getName() { return "gnome"; } /************************************************************************* * 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(settings); } catch (IOException e) { Logger.log(getClass(), LogLevel.ERROR, "Gnome settings read error.", e); throw new ProxyException(e); } return settings; } /************************************************************************* * 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("org.gnome.system.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("org.gnome.system.proxy.http 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("org.gnome.system.proxy.http host", null); int proxyPort = Integer.parseInt(settings.getProperty("org.gnome.system.proxy.http 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("org.gnome.system.proxy.socks host", null); int proxyPort = Integer.parseInt(settings.getProperty("org.gnome.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("org.gnome.system.proxy.ftp host", null); int proxyPort = Integer.parseInt(settings.getProperty("org.gnome.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("org.gnome.system.proxy.https host", null); int proxyPort = Integer.parseInt(settings.getProperty("org.gnome.system.proxy.https 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(Properties settings) throws IOException { String line; Logger.log(getClass(), LogLevel.TRACE, "exec gsettings list-recursively org.gnome.system.proxy"); Process p = Runtime.getRuntime().exec("gsettings list-recursively org.gnome.system.proxy"); BufferedReader bri = new BufferedReader(new InputStreamReader(p.getInputStream())); while ((line = bri.readLine()) != null) { Logger.log(getClass(), LogLevel.TRACE, line); int schemaSep = line.indexOf(" "); int keySep = line.indexOf(" ", schemaSep + 1); String entry = line.substring(0, keySep); String value = line.substring(keySep + 1).replaceAll("'", ""); // TODO 30.03.2015 bros Test for IP6 compatibility if (value.matches("\\[.*\\]")) { value = value.replaceAll("\\[|\\]| ", ""); } Logger.log(getClass(), LogLevel.TRACE, "prop is: {0}|{1}", entry, value); settings.setProperty(entry, value); } try { p.waitFor(); } catch (InterruptedException ex) { Logger.log(getClass(), LogLevel.ERROR, ex.getMessage()); } return settings; } }