001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.io;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.io.IOException;
007import java.net.InetSocketAddress;
008import java.net.Proxy;
009import java.net.Proxy.Type;
010import java.net.ProxySelector;
011import java.net.SocketAddress;
012import java.net.URI;
013import java.util.Collections;
014import java.util.List;
015
016import org.openstreetmap.josm.Main;
017import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel;
018import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel.ProxyPolicy;
019
020/**
021 * This is the default proxy selector used in JOSM.
022 *
023 */
024public class DefaultProxySelector extends ProxySelector {
025    /**
026     * The {@link ProxySelector} provided by the JDK will retrieve proxy information
027     * from the system settings, if the system property <tt>java.net.useSystemProxies</tt>
028     * is defined <strong>at startup</strong>. It has no effect if the property is set
029     * later by the application.
030     *
031     * We therefore read the property at class loading time and remember it's value.
032     */
033    private static boolean JVM_WILL_USE_SYSTEM_PROXIES = false;
034    static {
035        String v = System.getProperty("java.net.useSystemProxies");
036        if (v != null && v.equals(Boolean.TRUE.toString())) {
037            JVM_WILL_USE_SYSTEM_PROXIES = true;
038        }
039    }
040
041    /**
042     * The {@link ProxySelector} provided by the JDK will retrieve proxy information
043     * from the system settings, if the system property <tt>java.net.useSystemProxies</tt>
044     * is defined <strong>at startup</strong>. If the property is set later by the application,
045     * this has no effect.
046     *
047     * @return true, if <tt>java.net.useSystemProxies</tt> was set to true at class initialization time
048     *
049     */
050    public static boolean willJvmRetrieveSystemProxies() {
051        return JVM_WILL_USE_SYSTEM_PROXIES;
052    }
053
054    private ProxyPolicy proxyPolicy;
055    private InetSocketAddress httpProxySocketAddress;
056    private InetSocketAddress socksProxySocketAddress;
057    private ProxySelector delegate;
058
059    /**
060     * A typical example is:
061     * <pre>
062     *    PropertySelector delegate = PropertySelector.getDefault();
063     *    PropertySelector.setDefault(new DefaultPropertySelector(delegate));
064     * </pre>
065     *
066     * @param delegate the proxy selector to delegate to if system settings are used. Usually
067     * this is the proxy selector found by ProxySelector.getDefault() before this proxy
068     * selector is installed
069     */
070    public DefaultProxySelector(ProxySelector delegate) {
071        this.delegate = delegate;
072        initFromPreferences();
073    }
074
075    protected int parseProxyPortValue(String property, String value) {
076        if (value == null) return 0;
077        int port = 0;
078        try {
079            port = Integer.parseInt(value);
080        } catch (NumberFormatException e) {
081            Main.error(tr("Unexpected format for port number in in preference ''{0}''. Got ''{1}''.", property, value));
082            Main.error(tr("The proxy will not be used."));
083            return 0;
084        }
085        if (port <= 0 || port >  65535) {
086            Main.error(tr("Illegal port number in preference ''{0}''. Got {1}.", property, port));
087            Main.error(tr("The proxy will not be used."));
088            return 0;
089        }
090        return port;
091    }
092
093    /**
094     * Initializes the proxy selector from the setting in the preferences.
095     *
096     */
097    public void initFromPreferences() {
098        String value = Main.pref.get(ProxyPreferencesPanel.PROXY_POLICY);
099        if (value.length() == 0) {
100            proxyPolicy = ProxyPolicy.NO_PROXY;
101        } else {
102            proxyPolicy = ProxyPolicy.fromName(value);
103            if (proxyPolicy == null) {
104                Main.warn(tr("Unexpected value for preference ''{0}'' found. Got ''{1}''. Will use no proxy.", ProxyPreferencesPanel.PROXY_POLICY, value));
105                proxyPolicy = ProxyPolicy.NO_PROXY;
106            }
107        }
108        String host = Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_HOST, null);
109        int port = parseProxyPortValue(ProxyPreferencesPanel.PROXY_HTTP_PORT, Main.pref.get(ProxyPreferencesPanel.PROXY_HTTP_PORT, null));
110        httpProxySocketAddress = null;
111        if (proxyPolicy.equals(ProxyPolicy.USE_HTTP_PROXY)) {
112            if (host != null && !host.trim().isEmpty() && port > 0) {
113                httpProxySocketAddress = new InetSocketAddress(host, port);
114            } else {
115                Main.warn(tr("Unexpected parameters for HTTP proxy. Got host ''{0}'' and port ''{1}''.", host, port));
116                Main.warn(tr("The proxy will not be used."));
117            }
118        }
119
120        host = Main.pref.get(ProxyPreferencesPanel.PROXY_SOCKS_HOST, null);
121        port = parseProxyPortValue(ProxyPreferencesPanel.PROXY_SOCKS_PORT, Main.pref.get(ProxyPreferencesPanel.PROXY_SOCKS_PORT, null));
122        socksProxySocketAddress = null;
123        if (proxyPolicy.equals(ProxyPolicy.USE_SOCKS_PROXY)) {
124            if (host != null && !host.trim().isEmpty() && port > 0) {
125                socksProxySocketAddress = new InetSocketAddress(host,port);
126            } else {
127                Main.warn(tr("Unexpected parameters for SOCKS proxy. Got host ''{0}'' and port ''{1}''.", host, port));
128                Main.warn(tr("The proxy will not be used."));
129            }
130        }
131    }
132
133    @Override
134    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
135        // Just log something. The network stack will also throw an exception which will be caught somewhere else
136        //
137        Main.error(tr("Connection to proxy ''{0}'' for URI ''{1}'' failed. Exception was: {2}", sa.toString(), uri.toString(), ioe.toString()));
138    }
139
140    @Override
141    public List<Proxy> select(URI uri) {
142        Proxy proxy;
143        switch(proxyPolicy) {
144        case USE_SYSTEM_SETTINGS:
145            if (!JVM_WILL_USE_SYSTEM_PROXIES) {
146                Main.warn(tr("The JVM is not configured to lookup proxies from the system settings. The property ''java.net.useSystemProxies'' was missing at startup time.  Will not use a proxy."));
147                return Collections.singletonList(Proxy.NO_PROXY);
148            }
149            // delegate to the former proxy selector
150            List<Proxy> ret = delegate.select(uri);
151            return ret;
152        case NO_PROXY:
153            return Collections.singletonList(Proxy.NO_PROXY);
154        case USE_HTTP_PROXY:
155            if (httpProxySocketAddress == null)
156                return Collections.singletonList(Proxy.NO_PROXY);
157            proxy = new Proxy(Type.HTTP, httpProxySocketAddress);
158            return Collections.singletonList(proxy);
159        case USE_SOCKS_PROXY:
160            if (socksProxySocketAddress == null)
161                return Collections.singletonList(Proxy.NO_PROXY);
162            proxy = new Proxy(Type.SOCKS, socksProxySocketAddress);
163            return Collections.singletonList(proxy);
164        }
165        // should not happen
166        return null;
167    }
168}