JAL-3633 new getdown jars
[jalview.git] / getdown / src / getdown / launcher / src / main / java / com / threerings / getdown / launcher / ProxyUtil.java
1 //
2 // Getdown - application installer, patcher and launcher
3 // Copyright (C) 2004-2018 Getdown authors
4 // https://github.com/threerings/getdown/blob/master/LICENSE
5
6 package com.threerings.getdown.launcher;
7
8 import java.io.File;
9 import java.io.FileInputStream;
10 import java.io.FileOutputStream;
11 import java.io.IOException;
12 import java.io.InputStream;
13 import java.io.PrintStream;
14 import java.net.Authenticator;
15 import java.net.HttpURLConnection;
16 import java.net.InetSocketAddress;
17 import java.net.PasswordAuthentication;
18 import java.net.Proxy;
19 import java.net.URL;
20 import java.net.URLConnection;
21 import java.util.Iterator;
22 import java.util.ServiceLoader;
23
24 import jalview.bin.LaunchUtils;
25 import jalview.util.ChannelProperties;
26
27 import ca.beq.util.win32.registry.RegistryKey;
28 import ca.beq.util.win32.registry.RegistryValue;
29 import ca.beq.util.win32.registry.RootKey;
30
31 import com.threerings.getdown.data.Application;
32 import com.threerings.getdown.spi.ProxyAuth;
33 import com.threerings.getdown.util.Config;
34 import com.threerings.getdown.util.ConnectionUtil;
35 import com.threerings.getdown.util.LaunchUtil;
36 import com.threerings.getdown.util.StringUtil;
37
38 import static com.threerings.getdown.Log.log;
39
40 public class ProxyUtil {
41
42     public static boolean autoDetectProxy (Application app)
43     {
44         String host = null, port = null;
45
46         // check for a proxy configured via system properties
47         if (System.getProperty("https.proxyHost") != null) {
48             host = System.getProperty("https.proxyHost");
49             port = System.getProperty("https.proxyPort");
50         }
51         if (StringUtil.isBlank(host) && System.getProperty("http.proxyHost") != null) {
52             host = System.getProperty("http.proxyHost");
53             port = System.getProperty("http.proxyPort");
54         }
55
56         // check the Windows registry
57         if (StringUtil.isBlank(host) && LaunchUtil.isWindows()) {
58             try {
59                 String rhost = null, rport = null;
60                 boolean enabled = false;
61                 RegistryKey.initialize();
62                 RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY);
63                 for (Iterator<?> iter = r.values(); iter.hasNext(); ) {
64                     RegistryValue value = (RegistryValue)iter.next();
65                     if (value.getName().equals("ProxyEnable")) {
66                         enabled = value.getStringValue().equals("1");
67                     }
68                     if (value.getName().equals("ProxyServer")) {
69                         String strval = value.getStringValue();
70                         int cidx = strval.indexOf(":");
71                         if (cidx != -1) {
72                             rport = strval.substring(cidx+1);
73                             strval = strval.substring(0, cidx);
74                         }
75                         rhost = strval;
76                     }
77                 }
78                 if (enabled) {
79                     host = rhost;
80                     port = rport;
81                 } else {
82                     log.info("Detected no proxy settings in the registry.");
83                 }
84             } catch (Throwable t) {
85                 log.info("Failed to find proxy settings in Windows registry", "error", t);
86             }
87         }
88
89         // look for a proxy.txt file
90         if (StringUtil.isBlank(host)) {
91             String[] hostPort = loadProxy(app);
92             host = hostPort[0];
93             port = hostPort[1];
94         }
95
96         // look in jalview_properties
97         String[] hostPortAuthUser = jalviewProxyProperties(app);
98         host = hostPortAuthUser[0];
99         port = hostPortAuthUser[1];
100         boolean proxyAuth = Boolean.parseBoolean(hostPortAuthUser[2]);
101         String username = hostPortAuthUser[3];
102
103         if (StringUtil.isBlank(host)) {
104             return false;
105         }
106
107         // yay, we found a proxy configuration, configure it in the app
108         initProxy(app, host, port, username, null);
109         return true;
110     }
111
112     public static String[] jalviewProxyProperties(Application app) {
113       String host = null;
114       String port = null;
115       boolean proxyAuth = false;
116       String username = null;
117       File channelProps = app.getLocalPath(ChannelProperties.CHANNEL_PROPERTIES_FILENAME);
118       if (channelProps.exists()) {
119         try {
120           InputStream is = new FileInputStream(channelProps);
121           ChannelProperties.loadProps(is);
122         } catch (IOException e) {
123           log.error(e.getMessage());
124         }
125         if (Boolean.parseBoolean(LaunchUtils.getUserPreference("USE_PROXY"))) {
126           host = LaunchUtils.getUserPreference("PROXY_SERVER_HTTPS");
127           port = LaunchUtils.getUserPreference("PROXY_PORT_HTTPS");
128           if (StringUtil.isBlank(host)) {
129             host = LaunchUtils.getUserPreference("PROXY_SERVER");
130             port = LaunchUtils.getUserPreference("PROXY_PORT");
131           }
132           proxyAuth = Boolean.parseBoolean(LaunchUtils.getUserPreference("PROXY_AUTH"));
133           if (proxyAuth) {
134             username = LaunchUtils.getUserPreference("PROXY_AUTH_USERNAME");
135           }
136         }
137       }
138       return new String[]{ host, port, String.valueOf(proxyAuth), username };
139     }
140
141     public static boolean canLoadWithoutProxy (URL rurl)
142     {
143         log.info("Testing whether proxy is needed, via: " + rurl);
144         try {
145             // try to make a HEAD request for this URL (use short connect and read timeouts)
146             URLConnection conn = ConnectionUtil.open(Proxy.NO_PROXY, rurl, 5, 5);
147             if (conn instanceof HttpURLConnection) {
148                 HttpURLConnection hcon = (HttpURLConnection)conn;
149                 try {
150                     hcon.setRequestMethod("HEAD");
151                     hcon.connect();
152                     // make sure we got a satisfactory response code
153                     int rcode = hcon.getResponseCode();
154                     if (rcode == HttpURLConnection.HTTP_PROXY_AUTH ||
155                         rcode == HttpURLConnection.HTTP_FORBIDDEN) {
156                         log.warning("Got an 'HTTP credentials needed' response", "code", rcode);
157                     } else {
158                         return true;
159                     }
160                 } finally {
161                     hcon.disconnect();
162                 }
163             } else {
164                 // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy
165                 return true;
166             }
167         } catch (IOException ioe) {
168             log.info("Failed to HEAD " + rurl + ": " + ioe);
169             log.info("We probably need a proxy, but auto-detection failed.");
170         }
171         return false;
172     }
173
174     public static void configProxy (Application app, String host, String port,
175                                     String username, String password) {
176         // save our proxy host and port in a local file
177         saveProxy(app, host, port);
178
179         // save our credentials via the SPI
180         if (!StringUtil.isBlank(username) && !StringUtil.isBlank(password)) {
181             ServiceLoader<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
182             Iterator<ProxyAuth> iterator = loader.iterator();
183             String appDir = app.getAppDir().getAbsolutePath();
184             while (iterator.hasNext()) {
185                 iterator.next().saveCredentials(appDir, username, password);
186             }
187         }
188
189         // also configure them in the app
190         initProxy(app, host, port, username, password);
191     }
192
193     public static String[] loadProxy (Application app) {
194         File pfile = app.getLocalPath("proxy.txt");
195         if (pfile.exists()) {
196             try {
197                 Config pconf = Config.parseConfig(pfile, Config.createOpts(false));
198                 return new String[] { pconf.getString("host"), pconf.getString("port") };
199             } catch (IOException ioe) {
200                 log.warning("Failed to read '" + pfile + "': " + ioe);
201             }
202         }
203         return new String[] { null, null};
204     }
205
206     public static void saveProxy (Application app, String host, String port) {
207         File pfile = app.getLocalPath("proxy.txt");
208         try (PrintStream pout = new PrintStream(new FileOutputStream(pfile))) {
209             if (!StringUtil.isBlank(host)) {
210                 pout.println("host = " + host);
211             }
212             if (!StringUtil.isBlank(port)) {
213                 pout.println("port = " + port);
214             }
215         } catch (IOException ioe) {
216             log.warning("Error creating proxy file '" + pfile + "': " + ioe);
217         }
218     }
219
220     public static void initProxy (Application app, String host, String port,
221                                   String username, String password)
222     {
223 System.out.println("**** initProxy(app, '"+host+"', "+port+", '"+username+"', "+(password==null?"null":"*x"+password.length())+")");
224         // check whether we have saved proxy credentials
225         String appDir = app.getAppDir().getAbsolutePath();
226         ServiceLoader<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
227         Iterator<ProxyAuth> iter = loader.iterator();
228         ProxyAuth.Credentials creds = iter.hasNext() ? iter.next().loadCredentials(appDir) : null;
229         if (creds != null) {
230             username = creds.username;
231             password = creds.password;
232         }
233         boolean haveCreds = !StringUtil.isBlank(username) && !StringUtil.isBlank(password);
234
235         int pport = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port);
236         log.info("Using proxy", "host", host, "port", pport, "haveCreds", haveCreds);
237         app.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pport));
238
239         if (haveCreds) {
240             final String fuser = username;
241             final char[] fpass = password.toCharArray();
242             Authenticator.setDefault(new Authenticator() {
243                 @Override protected PasswordAuthentication getPasswordAuthentication () {
244                     return new PasswordAuthentication(fuser, fpass);
245                 }
246             });
247         }
248     }
249
250     protected static final String PROXY_REGISTRY =
251         "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
252 }