diff --git a/core/src/main/java/com/threerings/getdown/data/Application.java b/core/src/main/java/com/threerings/getdown/data/Application.java index 77e14fa..90cf59b 100644 --- a/core/src/main/java/com/threerings/getdown/data/Application.java +++ b/core/src/main/java/com/threerings/getdown/data/Application.java @@ -8,6 +8,7 @@ package com.threerings.getdown.data; import java.io.*; import java.lang.reflect.Method; import java.net.MalformedURLException; +import java.net.Proxy; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; @@ -231,6 +232,11 @@ public class Application } } + /** The proxy that should be used to do HTTP downloads. This must be configured prior to using + * the application instance. Yes this is a public mutable field, no I'm not going to create a + * getter and setter just to pretend like that's not the case. */ + public Proxy proxy = Proxy.NO_PROXY; + /** * Creates an application instance which records the location of the getdown.txt * configuration file from the supplied application directory. @@ -1208,7 +1214,7 @@ public class Application } if (_latest != null) { - try (InputStream in = ConnectionUtil.open(_latest, 0, 0).getInputStream(); + try (InputStream in = ConnectionUtil.open(proxy, _latest, 0, 0).getInputStream(); InputStreamReader reader = new InputStreamReader(in, UTF_8); BufferedReader bin = new BufferedReader(reader)) { for (String[] pair : Config.parsePairs(bin, Config.createOpts(false))) { @@ -1592,7 +1598,7 @@ public class Application log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'."); // stream the URL into our temporary file - URLConnection uconn = ConnectionUtil.open(targetURL, 0, 0); + URLConnection uconn = ConnectionUtil.open(proxy, targetURL, 0, 0); // we have to tell Java not to use caches here, otherwise it will cache any request for // same URL for the lifetime of this JVM (based on the URL string, not the URL object); // if the getdown.txt file, for example, changes in the meanwhile, we would never hear diff --git a/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java b/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java index f141366..a7a3287 100644 --- a/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java +++ b/core/src/main/java/com/threerings/getdown/net/HTTPDownloader.java @@ -10,6 +10,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; +import java.net.Proxy; import java.net.URL; import java.net.URLConnection; import java.nio.channels.Channels; @@ -25,9 +26,14 @@ import static com.threerings.getdown.Log.log; */ public class HTTPDownloader extends Downloader { + public HTTPDownloader (Proxy proxy) + { + _proxy = proxy; + } + @Override protected long checkSize (Resource rsrc) throws IOException { - URLConnection conn = ConnectionUtil.open(rsrc.getRemote(), 0, 0); + URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0); try { // if we're accessing our data via HTTP, we only need a HEAD request if (conn instanceof HttpURLConnection) { @@ -54,7 +60,7 @@ public class HTTPDownloader extends Downloader // system property if (true) { // download the resource from the specified URL - URLConnection conn = ConnectionUtil.open(rsrc.getRemote(), 0, 0); + URLConnection conn = ConnectionUtil.open(_proxy, rsrc.getRemote(), 0, 0); conn.connect(); // make sure we got a satisfactory response code @@ -65,7 +71,6 @@ public class HTTPDownloader extends Downloader hcon.getResponseCode()); } } - long actualSize = conn.getContentLength(); log.info("Downloading resource", "url", rsrc.getRemote(), "size", actualSize); long currentSize = 0L; @@ -105,4 +110,6 @@ public class HTTPDownloader extends Downloader } } } + + protected final Proxy _proxy; } diff --git a/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java b/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java new file mode 100644 index 0000000..22446ec --- /dev/null +++ b/core/src/main/java/com/threerings/getdown/spi/ProxyAuth.java @@ -0,0 +1,32 @@ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2018 Getdown authors +// https://github.com/threerings/getdown/blob/master/LICENSE + +package com.threerings.getdown.spi; + +/** + * A service provider interface that handles the storage of proxy credentials. + */ +public interface ProxyAuth +{ + /** Credentials for a proxy server. */ + public static class Credentials { + public final String username; + public final String password; + public Credentials (String username, String password) { + this.username = username; + this.password = password; + } + } + + /** + * Loads the credentials for the app installed in {@code appDir}. + */ + public Credentials loadCredentials (String appDir); + + /** + * Encrypts and saves the credentials for the app installed in {@code appDir}. + */ + public void saveCredentials (String appDir, String username, String password); +} diff --git a/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java b/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java index 7f5a6aa..21b0569 100644 --- a/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java +++ b/core/src/main/java/com/threerings/getdown/util/ConnectionUtil.java @@ -7,11 +7,12 @@ package com.threerings.getdown.util; import java.io.IOException; import java.net.HttpURLConnection; +import java.net.Proxy; import java.net.URL; import java.net.URLConnection; import java.net.URLDecoder; + import com.threerings.getdown.data.SysProps; -import com.threerings.getdown.util.Base64; import static java.nio.charset.StandardCharsets.UTF_8; @@ -19,6 +20,7 @@ public class ConnectionUtil { /** * Opens a connection to a URL, setting the authentication header if user info is present. + * @param proxy the proxy via which to perform HTTP connections. * @param url the URL to which to open a connection. * @param connectTimeout if {@code > 0} then a timeout, in seconds, to use when opening the * connection. If {@code 0} is supplied, the connection timeout specified via system properties @@ -27,10 +29,10 @@ public class ConnectionUtil * the connection. If {@code 0} is supplied, the read timeout specified via system properties * will be used instead. */ - public static URLConnection open (URL url, int connectTimeout, int readTimeout) + public static URLConnection open (Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException { - URLConnection conn = url.openConnection(); + URLConnection conn = url.openConnection(proxy); // configure a connect timeout, if requested int ctimeout = connectTimeout > 0 ? connectTimeout : SysProps.connectTimeout(); @@ -63,9 +65,9 @@ public class ConnectionUtil * present. Throws a class cast exception if the connection returned is not the right type. See * {@link #open} for parameter documentation. */ - public static HttpURLConnection openHttp (URL url, int connectTimeout, int readTimeout) - throws IOException + public static HttpURLConnection openHttp ( + Proxy proxy, URL url, int connectTimeout, int readTimeout) throws IOException { - return (HttpURLConnection)open(url, connectTimeout, readTimeout); + return (HttpURLConnection)open(proxy, url, connectTimeout, readTimeout); } } diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java b/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java index 20ad0b0..377e5cd 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/Getdown.java @@ -13,6 +13,16 @@ import java.awt.GraphicsEnvironment; import java.awt.Image; import java.awt.event.ActionEvent; import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.PrintStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.*; import javax.imageio.ImageIO; import javax.swing.AbstractAction; @@ -20,29 +30,9 @@ import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JLayeredPane; -import java.io.BufferedReader; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.PrintStream; - -import java.net.HttpURLConnection; -import java.net.URL; -import java.net.URLConnection; - -import java.util.*; - -import ca.beq.util.win32.registry.RegistryKey; -import ca.beq.util.win32.registry.RegistryValue; -import ca.beq.util.win32.registry.RootKey; - import com.samskivert.swing.util.SwingUtil; - -import com.threerings.getdown.data.Application.UpdateInterface.Step; import com.threerings.getdown.data.*; +import com.threerings.getdown.data.Application.UpdateInterface.Step; import com.threerings.getdown.net.Downloader; import com.threerings.getdown.net.HTTPDownloader; import com.threerings.getdown.tools.Patcher; @@ -186,24 +176,12 @@ public abstract class Getdown extends Thread /** * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher. */ - public void configureProxy (String host, String port) + public void configProxy (String host, String port, String username, String password) { log.info("User configured proxy", "host", host, "port", port); - // if we're provided with valid values, create a proxy.txt file if (!StringUtil.isBlank(host)) { - File pfile = _app.getLocalPath("proxy.txt"); - try (PrintStream pout = new PrintStream(new FileOutputStream(pfile))) { - pout.println("host = " + host); - if (!StringUtil.isBlank(port)) { - pout.println("port = " + port); - } - } catch (IOException ioe) { - log.warning("Error creating proxy file '" + pfile + "': " + ioe); - } - - // also configure them in the JVM - setProxyProperties(host, port); + ProxyUtil.configProxy(_app, host, port, username, password); } // clear out our UI @@ -214,67 +192,11 @@ public abstract class Getdown extends Thread new Thread(this).start(); } - /** - * Reads and/or autodetects our proxy settings. - * - * @return true if we should proceed with running the launcher, false if we need to wait for - * the user to enter proxy settings. - */ - protected boolean detectProxy () - { - // we may already have a proxy configured - if (System.getProperty("http.proxyHost") != null || - System.getProperty("https.proxyHost") != null) { + protected boolean detectProxy () { + if (ProxyUtil.autoDetectProxy(_app)) { return true; } - // look in the Vinders registry - if (LaunchUtil.isWindows()) { - try { - String host = null, port = null; - boolean enabled = false; - RegistryKey.initialize(); - RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY); - for (Iterator iter = r.values(); iter.hasNext(); ) { - RegistryValue value = (RegistryValue)iter.next(); - if (value.getName().equals("ProxyEnable")) { - enabled = value.getStringValue().equals("1"); - } - if (value.getName().equals("ProxyServer")) { - String strval = value.getStringValue(); - int cidx = strval.indexOf(":"); - if (cidx != -1) { - port = strval.substring(cidx+1); - strval = strval.substring(0, cidx); - } - host = strval; - } - } - - if (enabled) { - setProxyProperties(host, port); - return true; - } else { - log.info("Detected no proxy settings in the registry."); - } - - } catch (Throwable t) { - log.info("Failed to find proxy settings in Windows registry", "error", t); - } - } - - // otherwise look for and read our proxy.txt file - File pfile = _app.getLocalPath("proxy.txt"); - if (pfile.exists()) { - try { - Config pconf = Config.parseConfig(pfile, Config.createOpts(false)); - setProxyProperties(pconf.getString("host"), pconf.getString("port")); - return true; - } catch (IOException ioe) { - log.warning("Failed to read '" + pfile + "': " + ioe); - } - } - // otherwise see if we actually need a proxy; first we have to initialize our application // to get some sort of interface configuration and the appbase URL log.info("Checking whether we need to use a proxy..."); @@ -284,59 +206,15 @@ public abstract class Getdown extends Thread // no worries } updateStatus("m.detecting_proxy"); - - URL rurl = _app.getConfigResource().getRemote(); - try { - // try to make a HEAD request for this URL (use short connect and read timeouts) - URLConnection conn = ConnectionUtil.open(rurl, 5, 5); - if (conn instanceof HttpURLConnection) { - HttpURLConnection hcon = (HttpURLConnection)conn; - try { - hcon.setRequestMethod("HEAD"); - hcon.connect(); - // make sure we got a satisfactory response code - if (hcon.getResponseCode() != HttpURLConnection.HTTP_OK) { - log.warning("Got a non-200 response but assuming we're OK because we got " + - "something...", "url", rurl, "rsp", hcon.getResponseCode()); - } - } finally { - hcon.disconnect(); - } - } - - // we got through, so we appear not to require a proxy; make a blank proxy config and - // get on gettin' down - log.info("No proxy appears to be needed."); - try { - pfile.createNewFile(); - } catch (IOException ioe) { - log.warning("Failed to create blank proxy file '" + pfile + "': " + ioe); - } - return true; - - } catch (IOException ioe) { - log.info("Failed to HEAD " + rurl + ": " + ioe); - log.info("We probably need a proxy, but auto-detection failed."); + if (!ProxyUtil.canLoadWithoutProxy(_app.getConfigResource().getRemote())) { + return false; } - // let the caller know that we need a proxy but can't detect it - return false; - } - - /** - * Configures the JVM proxy system properties. - */ - protected void setProxyProperties (String host, String port) - { - if (!StringUtil.isBlank(host)) { - System.setProperty("http.proxyHost", host); - System.setProperty("https.proxyHost", host); - if (!StringUtil.isBlank(port)) { - System.setProperty("http.proxyPort", port); - System.setProperty("https.proxyPort", port); - } - log.info("Using proxy", "host", host, "port", port); - } + // we got through, so we appear not to require a proxy; make a blank proxy config so that + // we don't go through this whole detection process again next time + log.info("No proxy appears to be needed."); + ProxyUtil.saveProxy(_app, null, null); + return true; } protected void readConfig (boolean preloads) throws IOException { @@ -374,10 +252,6 @@ public abstract class Getdown extends Thread */ protected void getdown () { - log.info("---------------- Proxy Info -----------------"); - log.info("-- Proxy Host: " + System.getProperty("http.proxyHost")); - log.info("-- Proxy Port: " + System.getProperty("http.proxyPort")); - log.info("---------------------------------------------"); try { // first parses our application deployment file @@ -723,7 +597,7 @@ public abstract class Getdown extends Thread // create our user interface createInterfaceAsync(false); - Downloader dl = new HTTPDownloader() { + Downloader dl = new HTTPDownloader(_app.proxy) { @Override protected void resolvingDownloads () { updateStatus("m.resolving"); } @@ -1113,7 +987,7 @@ public abstract class Getdown extends Thread @Override public void run () { try { - HttpURLConnection ucon = ConnectionUtil.openHttp(_url, 0, 0); + HttpURLConnection ucon = ConnectionUtil.openHttp(_app.proxy, _url, 0, 0); // if we have a tracking cookie configured, configure the request with it if (_app.getTrackingCookieName() != null && @@ -1182,6 +1056,4 @@ public abstract class Getdown extends Thread protected static final int MAX_LOOPS = 5; protected static final long FALLBACK_CHECK_TIME = 1000L; - protected static final String PROXY_REGISTRY = - "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; } diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java b/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java index 939b243..5a620dd 100644 --- a/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java +++ b/launcher/src/main/java/com/threerings/getdown/launcher/ProxyPanel.java @@ -7,22 +7,25 @@ package com.threerings.getdown.launcher; import java.awt.BorderLayout; import java.awt.Dimension; +import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; - +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; import java.util.MissingResourceException; import java.util.ResourceBundle; import javax.swing.BorderFactory; import javax.swing.JButton; +import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JPasswordField; import javax.swing.JTextField; import com.samskivert.swing.GroupLayout; import com.samskivert.swing.Spacer; import com.samskivert.swing.VGroupLayout; - import com.threerings.getdown.util.MessageUtil; import static com.threerings.getdown.Log.log; @@ -30,8 +33,7 @@ import static com.threerings.getdown.Log.log; * Displays an interface with which the user can configure their proxy * settings. */ -public final class ProxyPanel extends JPanel - implements ActionListener +public final class ProxyPanel extends JPanel implements ActionListener { public ProxyPanel (Getdown getdown, ResourceBundle msgs) { @@ -40,21 +42,50 @@ public final class ProxyPanel extends JPanel setLayout(new VGroupLayout()); setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); - add(new JLabel(get("m.configure_proxy"))); + add(new SaneLabelField(get("m.configure_proxy"))); add(new Spacer(5, 5)); - JPanel row = new JPanel(new BorderLayout(5, 5)); - row.add(new JLabel(get("m.proxy_host")), BorderLayout.WEST); + JPanel row = new JPanel(new GridLayout()); + row.add(new SaneLabelField(get("m.proxy_host")), BorderLayout.WEST); row.add(_host = new SaneTextField()); add(row); - row = new JPanel(new BorderLayout(5, 5)); - row.add(new JLabel(get("m.proxy_port")), BorderLayout.WEST); + row = new JPanel(new GridLayout()); + row.add(new SaneLabelField(get("m.proxy_port")), BorderLayout.WEST); row.add(_port = new SaneTextField()); add(row); add(new Spacer(5, 5)); - add(new JLabel(get("m.proxy_extra"))); + + row = new JPanel(new GridLayout()); + row.add(new SaneLabelField(get("m.proxy_auth_required")), BorderLayout.WEST); + _useAuth = new JCheckBox(); + row.add(_useAuth); + add(row); + + row = new JPanel(new GridLayout()); + row.add(new SaneLabelField(get("m.proxy_username")), BorderLayout.WEST); + _username = new SaneTextField(); + _username.setEnabled(false); + row.add(_username); + add(row); + + row = new JPanel(new GridLayout()); + row.add(new SaneLabelField(get("m.proxy_password")), BorderLayout.WEST); + _password = new SanePasswordField(); + _password.setEnabled(false); + row.add(_password); + add(row); + + _useAuth.addItemListener(new ItemListener() { + @Override public void itemStateChanged (ItemEvent event) { + boolean selected = (event.getStateChange() == ItemEvent.SELECTED); + _username.setEnabled(selected); + _password.setEnabled(selected); + } + }); + + add(new Spacer(5, 5)); row = GroupLayout.makeButtonBox(GroupLayout.CENTER); JButton button; @@ -79,7 +110,7 @@ public final class ProxyPanel extends JPanel // documentation inherited @Override - public void addNotify () + public void addNotify () { super.addNotify(); _host.requestFocusInWindow(); @@ -93,17 +124,23 @@ public final class ProxyPanel extends JPanel // or the JLabel will claim a bogus height thinking it can lay its // text out all on one line which will booch the whole UI's // preferred size - return new Dimension(500, 350); + return new Dimension(500, 320); } // documentation inherited from interface + @Override public void actionPerformed (ActionEvent e) { String cmd = e.getActionCommand(); if (cmd.equals("ok")) { - // communicate this info back to getdown - _getdown.configureProxy(_host.getText(), _port.getText()); - + String user = null, pass = null; + if (_useAuth.isSelected()) { + user = _username.getText(); + // we have to keep the proxy password around for every HTTP request, so having it + // in a char[] that gets zeroed out after use is not viable for this use case + pass = new String(_password.getPassword()); + } + _getdown.configProxy(_host.getText(), _port.getText(), user, pass); } else { // they canceled, we're outta here System.exit(0); @@ -126,19 +163,34 @@ public final class ProxyPanel extends JPanel } } - protected static class SaneTextField extends JTextField - { - @Override - public Dimension getPreferredSize () { - Dimension d = super.getPreferredSize(); - d.width = Math.max(d.width, 150); - return d; + protected static class SaneLabelField extends JLabel { + public SaneLabelField(String message) { super(message); } + @Override public Dimension getPreferredSize () { + return clampWidth(super.getPreferredSize(), 200); } } + protected static class SaneTextField extends JTextField { + @Override public Dimension getPreferredSize () { + return clampWidth(super.getPreferredSize(), 150); + } + } + protected static class SanePasswordField extends JPasswordField { + @Override public Dimension getPreferredSize () { + return clampWidth(super.getPreferredSize(), 150); + } + } + + protected static Dimension clampWidth (Dimension dim, int minWidth) { + dim.width = Math.max(dim.width, minWidth); + return dim; + } protected Getdown _getdown; protected ResourceBundle _msgs; protected JTextField _host; protected JTextField _port; + protected JCheckBox _useAuth; + protected JTextField _username; + protected JPasswordField _password; } diff --git a/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java b/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java new file mode 100644 index 0000000..a36b5fa --- /dev/null +++ b/launcher/src/main/java/com/threerings/getdown/launcher/ProxyUtil.java @@ -0,0 +1,210 @@ +// +// Getdown - application installer, patcher and launcher +// Copyright (C) 2004-2018 Getdown authors +// https://github.com/threerings/getdown/blob/master/LICENSE + +package com.threerings.getdown.launcher; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.Authenticator; +import java.net.HttpURLConnection; +import java.net.InetSocketAddress; +import java.net.PasswordAuthentication; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.util.Iterator; +import java.util.ServiceLoader; + +import ca.beq.util.win32.registry.RegistryKey; +import ca.beq.util.win32.registry.RegistryValue; +import ca.beq.util.win32.registry.RootKey; + +import com.threerings.getdown.data.Application; +import com.threerings.getdown.spi.ProxyAuth; +import com.threerings.getdown.util.Config; +import com.threerings.getdown.util.ConnectionUtil; +import com.threerings.getdown.util.LaunchUtil; +import com.threerings.getdown.util.StringUtil; + +import static com.threerings.getdown.Log.log; + +public class ProxyUtil { + + public static boolean autoDetectProxy (Application app) + { + String host = null, port = null; + + // check for a proxy configured via system properties + if (System.getProperty("https.proxyHost") != null) { + host = System.getProperty("https.proxyHost"); + port = System.getProperty("https.proxyPort"); + } + if (StringUtil.isBlank(host) && System.getProperty("http.proxyHost") != null) { + host = System.getProperty("http.proxyHost"); + port = System.getProperty("http.proxyPort"); + } + + // check the Windows registry + if (StringUtil.isBlank(host) && LaunchUtil.isWindows()) { + try { + String rhost = null, rport = null; + boolean enabled = false; + RegistryKey.initialize(); + RegistryKey r = new RegistryKey(RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY); + for (Iterator iter = r.values(); iter.hasNext(); ) { + RegistryValue value = (RegistryValue)iter.next(); + if (value.getName().equals("ProxyEnable")) { + enabled = value.getStringValue().equals("1"); + } + if (value.getName().equals("ProxyServer")) { + String strval = value.getStringValue(); + int cidx = strval.indexOf(":"); + if (cidx != -1) { + rport = strval.substring(cidx+1); + strval = strval.substring(0, cidx); + } + rhost = strval; + } + } + if (enabled) { + host = rhost; + port = rport; + } else { + log.info("Detected no proxy settings in the registry."); + } + } catch (Throwable t) { + log.info("Failed to find proxy settings in Windows registry", "error", t); + } + } + + // look for a proxy.txt file + if (StringUtil.isBlank(host)) { + String[] hostPort = loadProxy(app); + host = hostPort[0]; + port = hostPort[1]; + } + + if (StringUtil.isBlank(host)) { + return false; + } + + // yay, we found a proxy configuration, configure it in the app + initProxy(app, host, port, null, null); + return true; + } + + public static boolean canLoadWithoutProxy (URL rurl) + { + log.info("Testing whether proxy is needed, via: " + rurl); + try { + // try to make a HEAD request for this URL (use short connect and read timeouts) + URLConnection conn = ConnectionUtil.open(Proxy.NO_PROXY, rurl, 5, 5); + if (conn instanceof HttpURLConnection) { + HttpURLConnection hcon = (HttpURLConnection)conn; + try { + hcon.setRequestMethod("HEAD"); + hcon.connect(); + // make sure we got a satisfactory response code + int rcode = hcon.getResponseCode(); + if (rcode == HttpURLConnection.HTTP_PROXY_AUTH || + rcode == HttpURLConnection.HTTP_FORBIDDEN) { + log.warning("Got an 'HTTP credentials needed' response", "code", rcode); + } else { + return true; + } + } finally { + hcon.disconnect(); + } + } else { + // if the appbase is not an HTTP/S URL (like file:), then we don't need a proxy + return true; + } + } catch (IOException ioe) { + log.info("Failed to HEAD " + rurl + ": " + ioe); + log.info("We probably need a proxy, but auto-detection failed."); + } + return false; + } + + public static void configProxy (Application app, String host, String port, + String username, String password) { + // save our proxy host and port in a local file + saveProxy(app, host, port); + + // save our credentials via the SPI + if (!StringUtil.isBlank(username) && !StringUtil.isBlank(password)) { + ServiceLoader loader = ServiceLoader.load(ProxyAuth.class); + Iterator iterator = loader.iterator(); + String appDir = app.getAppDir().getAbsolutePath(); + while (iterator.hasNext()) { + iterator.next().saveCredentials(appDir, username, password); + } + } + + // also configure them in the app + initProxy(app, host, port, username, password); + } + + public static String[] loadProxy (Application app) { + File pfile = app.getLocalPath("proxy.txt"); + if (pfile.exists()) { + try { + Config pconf = Config.parseConfig(pfile, Config.createOpts(false)); + return new String[] { pconf.getString("host"), pconf.getString("port") }; + } catch (IOException ioe) { + log.warning("Failed to read '" + pfile + "': " + ioe); + } + } + return new String[] { null, null}; + } + + public static void saveProxy (Application app, String host, String port) { + File pfile = app.getLocalPath("proxy.txt"); + try (PrintStream pout = new PrintStream(new FileOutputStream(pfile))) { + if (!StringUtil.isBlank(host)) { + pout.println("host = " + host); + } + if (!StringUtil.isBlank(port)) { + pout.println("port = " + port); + } + } catch (IOException ioe) { + log.warning("Error creating proxy file '" + pfile + "': " + ioe); + } + } + + public static void initProxy (Application app, String host, String port, + String username, String password) + { + // check whether we have saved proxy credentials + String appDir = app.getAppDir().getAbsolutePath(); + ServiceLoader loader = ServiceLoader.load(ProxyAuth.class); + Iterator iter = loader.iterator(); + ProxyAuth.Credentials creds = iter.hasNext() ? iter.next().loadCredentials(appDir) : null; + if (creds != null) { + username = creds.username; + password = creds.password; + } + boolean haveCreds = !StringUtil.isBlank(username) && !StringUtil.isBlank(password); + + int pport = StringUtil.isBlank(port) ? 80 : Integer.valueOf(port); + log.info("Using proxy", "host", host, "port", pport, "haveCreds", haveCreds); + app.proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(host, pport)); + + if (haveCreds) { + final String fuser = username; + final char[] fpass = password.toCharArray(); + Authenticator.setDefault(new Authenticator() { + @Override protected PasswordAuthentication getPasswordAuthentication () { + return new PasswordAuthentication(fuser, fpass); + } + }); + } + } + + protected static final String PROXY_REGISTRY = + "Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings"; +} diff --git a/launcher/src/main/resources/com/threerings/getdown/messages.properties b/launcher/src/main/resources/com/threerings/getdown/messages.properties index 1c57165..19b2999 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages.properties @@ -11,15 +11,11 @@ m.abort_cancel = Continue installation m.detecting_proxy = Trying to auto-detect proxy settings -m.configure_proxy = We were unable to connect to our servers to download data. \ -
  • If Windows Firewall or Norton Internet Security was instructed \ - to block javaw.exe we cannot download the data. You will need \ - to allow javaw.exe to access the internet. You can try \ - running the app again, but you may need to clear javaw.exe from your \ - firewall configuration ( Start -> Control Panel -> Windows Firewall ).
\ -

Your computer may access the Internet through a proxy and we were \ - unable to automatically detect your proxy settings. If you know your \ - proxy settings, you can enter them below. +m.configure_proxy = We were unable to connect to the application server to download data. \ +

Please make sure that no virus scanner or firewall is blocking network communicaton with \ + the server. \ +

Your computer may access the Internet through a proxy and we were unable to automatically \ + detect your proxy settings. If you know your proxy settings, you can enter them below. m.proxy_extra = If you are sure that you don't use a proxy then \ perhaps there is a temporary Internet outage that is preventing us from \ @@ -28,6 +24,9 @@ m.proxy_extra = If you are sure that you don't use a proxy then \ m.proxy_host = Proxy IP m.proxy_port = Proxy port +m.proxy_username = Username +m.proxy_password = Password +m.proxy_auth_required = Authentication required m.proxy_ok = OK m.proxy_cancel = Cancel diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_de.properties b/launcher/src/main/resources/com/threerings/getdown/messages_de.properties index 72c8626..8e36835 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_de.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_de.properties @@ -13,27 +13,21 @@ m.abort_cancel = Installation fortsetzen m.detecting_proxy = Versuche Proxy-Einstellungen automatisch zu ermitteln -m.configure_proxy = Es war nicht m\u00f6glich, eine Verbindung zu unserem \ -Server herzustellen, um die Anwendungsdaten herunterzuladen. \ -

  • Falls Windows Firewall oder Norton Internet Security angewiesen \ -wurden, javaw.exe zu blockieren, kann die Anwendung nicht \ -heruntergeladen werden. Du musst javaw.exe erlauben, \ -Verbindung zum Internet herzustellen. Du kannst nochmals versuchen, die \ -Anwendung zu starten, aber eventuell musst du javaw.exe aus der \ -Firewall-Konfiguration entfernen (Start -> Systemsteuerung -> Windows \ -Firewall).
\ -

Eventuell ist der Computer durch einen Proxy mit dem Internet \ -verbunden. Es war uns nicht m\u00f6glich, die Proxy-Einstellungen automatisch \ -zu ermitteln. Falls bekannt, k\u00f6nnen diese unten eingetragen \ -werden. +m.configure_proxy = Es konnte keine Verbindung zum Applikations-Server aufgebaut werden. \ +

Bitte kontrollieren Sie die Proxyeinstellungen und stellen Sie sicher, dass keine lokal oder \ + im Netzwerk betriebene Sicherheitsanwendung (Virenscanner, Firewall, etc.) die Kommunikation \ + mit dem Server blockiert.
\ + Wenn kein Proxy verwendet werden soll, l\u00f6schen Sie bitte alle Eintr\u00e4ge in den unten \ + stehenden Feldern und klicken sie auf OK. -m.proxy_extra = Falls kein Proxy verwendet wird, liegt \ -m\u00f6glicherweise eine St\u00f6rung in der Internetverbindung zu unserem Server \ -vor. In diesem Fall bitte abbrechen und sp\u00e4ter noch einmal \ -versuchen. +m.proxy_extra = Sollten Sie keine Proxyeinstellungen gesetzt haben wenden Sie sich bitte \ + an Ihren Administrator. -m.proxy_host = Proxy IP -m.proxy_port = Proxy port +m.proxy_host = Proxy-Adresse +m.proxy_port = Proxy-Port +m.proxy_username = Benutzername +m.proxy_password = Passwort +m.proxy_auth_required = Authentisierung erforderlich m.proxy_ok = OK m.proxy_cancel = Abbrechen diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_es.properties b/launcher/src/main/resources/com/threerings/getdown/messages_es.properties index 7ad2777..609b025 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_es.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_es.properties @@ -29,6 +29,9 @@ m.proxy_extra = Si est\u00e1s seguro de que no tienes un proxy entonces \ m.proxy_host = IP proxy m.proxy_port = Puerto proxy +m.proxy_username = Nombre de usuario +m.proxy_password = Contrase\u00f1a +m.proxy_auth_required = Autenticacion requerida m.proxy_ok = OK m.proxy_cancel = Cancelar diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties b/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties index dd31f3e..3666204 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_fr.properties @@ -27,6 +27,9 @@ m.proxy_extra =Si vous \u00eates certain de ne pas utiliser de proxy, il e m.proxy_host = Proxy IP m.proxy_port = Proxy port +m.proxy_username = Nom d'utilisateur +m.proxy_password = Mot de passe +m.proxy_auth_required = Identification requise m.proxy_ok = OK m.proxy_cancel = Annuler diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_it.properties b/launcher/src/main/resources/com/threerings/getdown/messages_it.properties index 4592d86..33b3260 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_it.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_it.properties @@ -28,6 +28,9 @@ m.proxy_extra = Se sei sicuro di non usare proxy \ m.proxy_host = IP Proxy m.proxy_port = Porta Proxy +m.proxy_username = Nome utente +m.proxy_password = Parola d'ordine +m.proxy_auth_required = Autenticazione richiesta m.proxy_ok = OK m.proxy_cancel = Annulla diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties b/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties index 4b98391..c344c16 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_ja.properties @@ -28,7 +28,10 @@ m.proxy_extra = \u30d7\u30ed\u30ad\u30b7\u3092\u4f7f\u7528\u3057\u3066\u30 \u5f8c\u307b\u3069\u6539\u3081\u3066\u5b9f\u884c\u3057\u3066\u304f\u3060\u3055\u3044\u3002 m.proxy_host = \u30d7\u30ed\u30ad\u30b7IP -m.proxy_port = \u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8 +m.proxy_port = \u30d7\u30ed\u30ad\u30b7\u30dd\u30fc\u30c8 +m.proxy_username = Username +m.proxy_password = Password +m.proxy_auth_required = Authentication required m.proxy_ok = OK m.proxy_cancel = \u30ad\u30e3\u30f3\u30bb\u30eb diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties b/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties index 000538f..3f8a47f 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_ko.properties @@ -24,6 +24,9 @@ m.proxy_extra = \uC790\uB3D9 \uD504\uB85D\uC2DC\uB97C \uC124\uC815\uC744 \uC2DC\ m.proxy_host = \uD504\uB85D\uC2DC IP m.proxy_port = \uD504\uB85D\uC2DC \uD3EC\uD2B8 +m.proxy_username = Username +m.proxy_password = Password +m.proxy_auth_required = Authentication required m.proxy_ok = OK m.proxy_cancel = \uCDE8\uC18C diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties b/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties index 87cc1a4..47db91c 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_pt.properties @@ -31,6 +31,9 @@ m.proxy_extra = Se voc\u00EA tem certeza que n\u00E3o usa um proxy, ent\u0 m.proxy_host = IP do Proxy m.proxy_port = Porta do Proxy +m.proxy_username = Nome de usu\u00e1rio +m.proxy_password = Senha +m.proxy_auth_required = Autentifica\u00e7\u00e3o requerida m.proxy_ok = OK m.proxy_cancel = Cancelar diff --git a/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties b/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties index 986ebb2..2c27543 100644 --- a/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties +++ b/launcher/src/main/resources/com/threerings/getdown/messages_zh.properties @@ -16,6 +16,9 @@ m.proxy_extra = \u5982\u679c\u60a8\u786e\u5b9a\u60a8\u6ca1\u6709\u4f7f\u75 m.proxy_host = \u4ee3\u7406\u670d\u52a1\u5668\u7684IP\u5730\u5740 m.proxy_port = \u4ee3\u7406\u670d\u52a1\u5668\u7684\u7aef\u53e3\u53f7 +m.proxy_username = Username +m.proxy_password = Password +m.proxy_auth_required = Authentication required m.proxy_ok = \u786e\u5b9a m.proxy_cancel = \u53d6\u6d88