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