Added support for proxy auth.
This also makes the use of a Proxy instance explicit in the Getdown code, rather than trying to thread things through the proxy-related system properties. It introduces a ProxyAuth SPI which allows an app installation to save proxy credentials in whatever way it deems fit. Otherwise the credentials are unsaved and the user effectively has to enter them every time Getdown attempts to access the internet. This is obviously a terrible design, but there's no good (cross-platform) way to securely store a username and password. So anyone using this feature is likely going to need to provide an implementation of ProxyAuth that does whatever auth storage they deem appropriate. Adapted from PR from @ThomasG-AI, thanks!
This commit is contained in:
@@ -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 <code>getdown.txt</code>
|
||||
* 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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
|
||||
Iterator<ProxyAuth> 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<ProxyAuth> loader = ServiceLoader.load(ProxyAuth.class);
|
||||
Iterator<ProxyAuth> 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";
|
||||
}
|
||||
@@ -11,15 +11,11 @@ m.abort_cancel = Continue installation
|
||||
|
||||
m.detecting_proxy = Trying to auto-detect proxy settings
|
||||
|
||||
m.configure_proxy = <html>We were unable to connect to our servers to download data. \
|
||||
<ul><li> If Windows Firewall or Norton Internet Security was instructed \
|
||||
to block <code>javaw.exe</code> we cannot download the data. You will need \
|
||||
to allow <code>javaw.exe</code> 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 ).</ul> \
|
||||
<p> 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.</html>
|
||||
m.configure_proxy = <html>We were unable to connect to the application server to download data. \
|
||||
<p> Please make sure that no virus scanner or firewall is blocking network communicaton with \
|
||||
the server. \
|
||||
<p> 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.</html>
|
||||
|
||||
m.proxy_extra = <html>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 = <html>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
|
||||
|
||||
|
||||
@@ -13,27 +13,21 @@ m.abort_cancel = Installation fortsetzen
|
||||
|
||||
m.detecting_proxy = Versuche Proxy-Einstellungen automatisch zu ermitteln
|
||||
|
||||
m.configure_proxy = <html>Es war nicht m\u00f6glich, eine Verbindung zu unserem \
|
||||
Server herzustellen, um die Anwendungsdaten herunterzuladen. \
|
||||
<ul><li> Falls Windows Firewall oder Norton Internet Security angewiesen \
|
||||
wurden, <code>javaw.exe</code> zu blockieren, kann die Anwendung nicht \
|
||||
heruntergeladen werden. Du musst <code>javaw.exe</code> 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). </ul> \
|
||||
<p> 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.</html>
|
||||
m.configure_proxy = <html>Es konnte keine Verbindung zum Applikations-Server aufgebaut werden. \
|
||||
<p>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.<br> \
|
||||
Wenn kein Proxy verwendet werden soll, l\u00f6schen Sie bitte alle Eintr\u00e4ge in den unten \
|
||||
stehenden Feldern und klicken sie auf OK.</html>
|
||||
|
||||
m.proxy_extra = <html>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.</html>
|
||||
m.proxy_extra = <html>Sollten Sie keine Proxyeinstellungen gesetzt haben wenden Sie sich bitte \
|
||||
an Ihren Administrator.</html>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -29,6 +29,9 @@ m.proxy_extra = <html>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
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ m.proxy_extra =<html>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
|
||||
|
||||
|
||||
@@ -28,6 +28,9 @@ m.proxy_extra = <html>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
|
||||
|
||||
|
||||
@@ -28,7 +28,10 @@ m.proxy_extra = <html>\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</html>
|
||||
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -31,6 +31,9 @@ m.proxy_extra = <html>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
|
||||
|
||||
|
||||
@@ -16,6 +16,9 @@ m.proxy_extra = <html>\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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user