001    // License: GPL. For details, see LICENSE file.
002    package org.openstreetmap.josm.io;
003    
004    import static org.openstreetmap.josm.tools.I18n.tr;
005    
006    import java.io.IOException;
007    import java.net.InetSocketAddress;
008    import java.net.Proxy;
009    import java.net.ProxySelector;
010    import java.net.SocketAddress;
011    import java.net.URI;
012    import java.net.Proxy.Type;
013    import java.util.Collections;
014    import java.util.List;
015    
016    import org.openstreetmap.josm.Main;
017    import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel;
018    import org.openstreetmap.josm.gui.preferences.server.ProxyPreferencesPanel.ProxyPolicy;
019    
020    /**
021     * This is the default proxy selector used in JOSM.
022     *
023     */
024    public 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        {
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                System.err.println(tr("Unexpected format for port number in in preference ''{0}''. Got ''{1}''.", property, value));
082                System.err.println(tr("The proxy will not be used."));
083                return 0;
084            }
085            if (port <= 0 || port >  65535) {
086                System.err.println(tr("Illegal port number in preference ''{0}''. Got {1}.", property, port));
087                System.err.println(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                    System.err.println(tr("Warning: 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            if (host != null && ! host.trim().equals("") && port > 0) {
111                httpProxySocketAddress = new InetSocketAddress(host,port);
112            } else {
113                httpProxySocketAddress = null;
114                if (proxyPolicy.equals(ProxyPolicy.USE_HTTP_PROXY)) {
115                    System.err.println(tr("Warning: Unexpected parameters for HTTP proxy. Got host ''{0}'' and port ''{1}''.", host, port));
116                    System.err.println(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            if (host != null && ! host.trim().equals("") && port > 0) {
123                socksProxySocketAddress = new InetSocketAddress(host,port);
124            } else {
125                socksProxySocketAddress = null;
126                if (proxyPolicy.equals(ProxyPolicy.USE_SOCKS_PROXY)) {
127                    System.err.println(tr("Warning: Unexpected parameters for SOCKS proxy. Got host ''{0}'' and port ''{1}''.", host, port));
128                    System.err.println(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
136            // somewhere else
137            //
138            System.out.println(tr("Error: Connection to proxy ''{0}'' for URI ''{1}'' failed. Exception was: {2}", sa.toString(), uri.toString(), ioe.toString()));
139        }
140    
141        @Override
142        public List<Proxy> select(URI uri) {
143            Proxy proxy;
144            switch(proxyPolicy) {
145            case USE_SYSTEM_SETTINGS:
146                if (!JVM_WILL_USE_SYSTEM_PROXIES) {
147                    System.err.println(tr("Warning: 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."));
148                    return Collections.singletonList(Proxy.NO_PROXY);
149                }
150                // delegate to the former proxy selector
151                List<Proxy> ret = delegate.select(uri);
152                return ret;
153            case NO_PROXY:
154                return Collections.singletonList(Proxy.NO_PROXY);
155            case USE_HTTP_PROXY:
156                if (httpProxySocketAddress == null)
157                    return Collections.singletonList(Proxy.NO_PROXY);
158                proxy = new Proxy(Type.HTTP, httpProxySocketAddress);
159                return Collections.singletonList(proxy);
160            case USE_SOCKS_PROXY:
161                if (socksProxySocketAddress == null)
162                    return Collections.singletonList(Proxy.NO_PROXY);
163                proxy = new Proxy(Type.SOCKS, socksProxySocketAddress);
164                return Collections.singletonList(proxy);
165            }
166            // should not happen
167            return null;
168        }
169    }