Remove obsolete Applet code.
This commit is contained in:
@@ -1,51 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>com.threerings</groupId>
|
|
||||||
<artifactId>getdown</artifactId>
|
|
||||||
<version>1.7.2-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
|
|
||||||
<artifactId>getdown-app</artifactId>
|
|
||||||
<packaging>jar</packaging>
|
|
||||||
<name>Getdown Applet</name>
|
|
||||||
<description>The Getdown installer applet (obsolete)</description>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.threerings</groupId>
|
|
||||||
<artifactId>getdown-launcher</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>java</groupId>
|
|
||||||
<artifactId>plugin</artifactId>
|
|
||||||
<version>1.5</version>
|
|
||||||
<scope>system</scope>
|
|
||||||
<systemPath>${plugin.jar.path}</systemPath>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<profiles>
|
|
||||||
<!-- finagling to deal with plugin.jar not being bundled with OpenJDK -->
|
|
||||||
<profile>
|
|
||||||
<id>standard-jdk</id>
|
|
||||||
<activation>
|
|
||||||
<file><exists>${java.home}/lib/plugin.jar</exists></file>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<plugin.jar.path>${java.home}/lib/plugin.jar</plugin.jar.path>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
<profile>
|
|
||||||
<id>icedtea-web</id>
|
|
||||||
<activation>
|
|
||||||
<file><exists>/usr/share/icedtea-web/plugin.jar</exists></file>
|
|
||||||
</activation>
|
|
||||||
<properties>
|
|
||||||
<plugin.jar.path>/usr/share/icedtea-web/plugin.jar</plugin.jar.path>
|
|
||||||
</properties>
|
|
||||||
</profile>
|
|
||||||
</profiles>
|
|
||||||
</project>
|
|
||||||
@@ -1,348 +0,0 @@
|
|||||||
//
|
|
||||||
// Getdown - application installer, patcher and launcher
|
|
||||||
// Copyright (C) 2004-2016 Getdown authors
|
|
||||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
|
||||||
|
|
||||||
package com.threerings.getdown.launcher;
|
|
||||||
|
|
||||||
import java.awt.Container;
|
|
||||||
import java.awt.Image;
|
|
||||||
import java.io.DataInputStream;
|
|
||||||
import java.io.DataOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.Socket;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import java.security.cert.Certificate;
|
|
||||||
import java.security.cert.CertificateException;
|
|
||||||
import java.security.cert.CertificateFactory;
|
|
||||||
|
|
||||||
import javax.swing.JApplet;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
|
|
||||||
import netscape.javascript.JSObject;
|
|
||||||
|
|
||||||
import com.threerings.getdown.data.Application;
|
|
||||||
import com.threerings.getdown.data.Properties;
|
|
||||||
|
|
||||||
import static com.threerings.getdown.Log.log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An applet that can be used to launch a Getdown application (when signed and given privileges).
|
|
||||||
*/
|
|
||||||
public class GetdownApplet extends JApplet
|
|
||||||
implements ImageLoader
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Sets the JavaScript callback to invoke when a message is received from the launched app.
|
|
||||||
* The callback should be a function that accepts a single string parameter (the received
|
|
||||||
* message).
|
|
||||||
*/
|
|
||||||
public synchronized void setMessageCallback (JSObject callback)
|
|
||||||
{
|
|
||||||
_messageCallback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to send a message to the launched app.
|
|
||||||
*
|
|
||||||
* @return true if we succeeded in sending the message, false if the launched app has not (yet)
|
|
||||||
* established a connection to Getdown, or the send failed.
|
|
||||||
*/
|
|
||||||
public synchronized boolean sendMessage (String message)
|
|
||||||
{
|
|
||||||
if (_connectOut != null) {
|
|
||||||
try {
|
|
||||||
_connectOut.writeUTF(message);
|
|
||||||
return true;
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warning("Error sending message to app.", "message", message, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void init ()
|
|
||||||
{
|
|
||||||
_config = new GetdownAppletConfig(this);
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check our permissions, download getdown.txt, etc.
|
|
||||||
_config.init();
|
|
||||||
} catch (Exception e) {
|
|
||||||
_errmsg = e.getMessage();
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Certificate> signers = new ArrayList<>();
|
|
||||||
Certificate cert = loadCertificate("resource.crt");
|
|
||||||
if (cert != null) {
|
|
||||||
signers.add(cert);
|
|
||||||
} else {
|
|
||||||
// getSigners() returns all certificates used to sign this applet which may allow a
|
|
||||||
// third party to insert a trusted certificate. This should be avoided.
|
|
||||||
log.warning("No resource certificate found, falling back to class signers");
|
|
||||||
Object[] candidates = GetdownApplet.class.getSigners();
|
|
||||||
if (candidates != null) {
|
|
||||||
for (Object signer : candidates) {
|
|
||||||
if (signer instanceof Certificate) {
|
|
||||||
signers.add((Certificate)signer);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_getdown = new Getdown(_config.appdir, null, signers,
|
|
||||||
_config.jvmargs, _config.appargs) {
|
|
||||||
@Override
|
|
||||||
protected Container createContainer () {
|
|
||||||
getContentPane().removeAll();
|
|
||||||
getContentPane().setBackground(_ifc.background);
|
|
||||||
return getContentPane();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected RotatingBackgrounds getBackground () {
|
|
||||||
return _config.getBackgroundImages(GetdownApplet.this);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void showContainer () {
|
|
||||||
((JPanel)getContentPane()).revalidate();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void disposeContainer () {
|
|
||||||
// nothing to do as we're in an applet
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected boolean invokeDirect () {
|
|
||||||
return _config.invokeDirect;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected JApplet getApplet () {
|
|
||||||
return GetdownApplet.this;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void showDocument (String url) {
|
|
||||||
try {
|
|
||||||
getAppletContext().showDocument(new URL(url), "_blank");
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
log.warning("Invalid document url.", "url", url, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void launch () {
|
|
||||||
// if so configured, create a server socket to listen
|
|
||||||
// for a connection from the app
|
|
||||||
if (_config.allowConnect) {
|
|
||||||
try {
|
|
||||||
startConnectServer();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warning("Failed to start connect server.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.launch();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected void exit (int exitCode) {
|
|
||||||
_status.stopThrob();
|
|
||||||
_app.releaseLock();
|
|
||||||
_config.redirect();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// set up our user interface
|
|
||||||
_config.config(_getdown);
|
|
||||||
_getdown.preInit();
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
// assume that if we already encountered an error, that is the root cause that we want
|
|
||||||
// to report back to the user
|
|
||||||
if (_errmsg == null) {
|
|
||||||
_errmsg = e.getMessage();
|
|
||||||
}
|
|
||||||
log.warning("init() failed.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to start the server that will accept a connection from the launched app, allowing
|
|
||||||
* it to exchange messages with the JavaScript context.
|
|
||||||
*/
|
|
||||||
protected void startConnectServer ()
|
|
||||||
throws IOException
|
|
||||||
{
|
|
||||||
// bind and set a property with the local port that will be passed through to the app
|
|
||||||
_serverSocket = new ServerSocket(0, 0, InetAddress.getByName(null));
|
|
||||||
String port = String.valueOf(_serverSocket.getLocalPort());
|
|
||||||
log.info("Listening for connections from launched app.", "port", port);
|
|
||||||
System.setProperty(Application.PROP_PASSTHROUGH_PREFIX + Properties.CONNECT_PORT, port);
|
|
||||||
Thread thread = new Thread("ConnectServer") {
|
|
||||||
@Override
|
|
||||||
public void run () {
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
acceptConnection();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (!_serverSocket.isClosed()) {
|
|
||||||
log.warning("Error accepting connection.", e);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
protected void acceptConnection () throws IOException {
|
|
||||||
Socket socket = _serverSocket.accept();
|
|
||||||
log.info("App connected.", "port", socket.getPort());
|
|
||||||
DataInputStream connectIn = new DataInputStream(socket.getInputStream());
|
|
||||||
synchronized (GetdownApplet.this) {
|
|
||||||
_connectOut = new DataOutputStream(socket.getOutputStream());
|
|
||||||
}
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
String message = connectIn.readUTF();
|
|
||||||
synchronized (GetdownApplet.this) {
|
|
||||||
if (message.equals("CLOSE")) {
|
|
||||||
socket.close();
|
|
||||||
log.info("App closed connection.");
|
|
||||||
break;
|
|
||||||
} else if (_messageCallback != null) {
|
|
||||||
_messageCallback.call("call",
|
|
||||||
new Object[] { _messageCallback, message });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (!socket.isClosed()) {
|
|
||||||
log.warning("Error reading message.", e);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
synchronized (GetdownApplet.this) {
|
|
||||||
_connectOut = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
thread.setDaemon(true);
|
|
||||||
thread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
// implemented from ImageLoader
|
|
||||||
@Override
|
|
||||||
public Image loadImage (String path)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
return getImage(new URL(getDocumentBase(), path));
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
log.warning("Failed to load background image", "path", path, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void start ()
|
|
||||||
{
|
|
||||||
if (_errmsg != null) {
|
|
||||||
_getdown.fail(_errmsg);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
_getdown.start();
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("start() failed.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public void stop ()
|
|
||||||
{
|
|
||||||
// Interrupt the getdown thread to tell it to kill its current downloading or verifying
|
|
||||||
// before launching
|
|
||||||
_getdown.interrupt();
|
|
||||||
// release the lock if the applet window is closed or replaced
|
|
||||||
_getdown._app.releaseLock();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // documentation inherited
|
|
||||||
public synchronized void destroy ()
|
|
||||||
{
|
|
||||||
if (_serverSocket != null) {
|
|
||||||
try {
|
|
||||||
_serverSocket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warning("Error closing server socket.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (_connectOut != null) {
|
|
||||||
try {
|
|
||||||
_connectOut.writeUTF("CLOSE");
|
|
||||||
_connectOut.close();
|
|
||||||
log.info("Disconnected from app.");
|
|
||||||
} catch (IOException e) {
|
|
||||||
log.warning("Error closing connect socket/output stream.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the specified file and writes the supplied contents to it.
|
|
||||||
*/
|
|
||||||
protected boolean writeToFile (File tofile, String contents)
|
|
||||||
{
|
|
||||||
try (FileOutputStream fos = new FileOutputStream(tofile);
|
|
||||||
PrintStream out = new PrintStream(fos)) {
|
|
||||||
out.println(contents);
|
|
||||||
return true;
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
log.warning("Failed to create '" + tofile + "'.", ioe);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static Certificate loadCertificate (String path)
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
ClassLoader classLoader = GetdownApplet.class.getClassLoader();
|
|
||||||
if (classLoader == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
URL keyUrl = classLoader.getResource(path);
|
|
||||||
if (keyUrl == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
try (InputStream is = keyUrl.openStream()) {
|
|
||||||
return CertificateFactory.getInstance("X.509").generateCertificate(is);
|
|
||||||
}
|
|
||||||
} catch (CertificateException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The Getdown configuration as pulled from the applet params */
|
|
||||||
protected GetdownAppletConfig _config;
|
|
||||||
|
|
||||||
/** Handles all the actual getting down. */
|
|
||||||
protected Getdown _getdown;
|
|
||||||
|
|
||||||
/** An error encountered during initialization. */
|
|
||||||
protected String _errmsg;
|
|
||||||
|
|
||||||
/** The message callback registered by JavaScript on the containing page, if any. */
|
|
||||||
protected JSObject _messageCallback;
|
|
||||||
|
|
||||||
/** The server socket on which we listen for connections, if any. */
|
|
||||||
protected ServerSocket _serverSocket;
|
|
||||||
|
|
||||||
/** The output stream to the launched app, if a connection has been established. */
|
|
||||||
protected DataOutputStream _connectOut;
|
|
||||||
}
|
|
||||||
@@ -1,416 +0,0 @@
|
|||||||
//
|
|
||||||
// Getdown - application installer, patcher and launcher
|
|
||||||
// Copyright (C) 2004-2016 Getdown authors
|
|
||||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
|
||||||
|
|
||||||
package com.threerings.getdown.launcher;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.awt.Rectangle;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import javax.swing.JApplet;
|
|
||||||
|
|
||||||
import com.samskivert.util.RunAnywhere;
|
|
||||||
import com.samskivert.util.StringUtil;
|
|
||||||
import com.threerings.getdown.launcher.ImageLoader;
|
|
||||||
import com.threerings.getdown.launcher.RotatingBackgrounds;
|
|
||||||
import com.threerings.getdown.util.Config;
|
|
||||||
|
|
||||||
import static com.threerings.getdown.Log.log;
|
|
||||||
|
|
||||||
public class GetdownAppletConfig
|
|
||||||
{
|
|
||||||
public static final String APPBASE = "appbase";
|
|
||||||
public static final String APPNAME = "appname";
|
|
||||||
public static final String BGIMAGE = "bgimage";
|
|
||||||
public static final String ERRORBGIMAGE = "errorbgimage";
|
|
||||||
public static final String PARAM_DELIMITER = ".";
|
|
||||||
public static final String DIRECT = "direct";
|
|
||||||
public static final String CONNECT = "connect";
|
|
||||||
public static final String REDIRECT_ON_FINISH = "redirect_on_finish";
|
|
||||||
public static final String REDIRECT_ON_FINISH_TARGET = "redirect_on_finish_target";
|
|
||||||
|
|
||||||
/** Used to specify the names of additional jvmargs properties. Each jvmarg property will
|
|
||||||
* be suffixed by a monotonically increasing integer starting at zero, e.g.
|
|
||||||
* <pre>{@code
|
|
||||||
* <param name="jvmarg0" value="-Xmx256M"/>
|
|
||||||
* <param name="jvmarg1" value="-Dfoo=bar"/>
|
|
||||||
* }</pre>
|
|
||||||
* When a number is reached for which no value exists, we stop looking. */
|
|
||||||
public static final String JVMARG_PREFIX = "jvmargs";
|
|
||||||
|
|
||||||
/** A property specifying the names of additional appargs properties. Each apparg property will
|
|
||||||
* be suffixed by a monotonically increasing integer starting at zero, e.g.
|
|
||||||
* <pre>{@code
|
|
||||||
* <param name="apparg0" value="my awesome value"/>
|
|
||||||
* <param name="apparg1" value="monkeys"/>
|
|
||||||
* }</pre>
|
|
||||||
* When a number is reached for which no value exists, we stop looking. */
|
|
||||||
public static final String APPARG_PREFIX = "appargs";
|
|
||||||
|
|
||||||
public String appbase;
|
|
||||||
|
|
||||||
public String appname;
|
|
||||||
|
|
||||||
/** The list of background images and their display time */
|
|
||||||
public String imgpath;
|
|
||||||
|
|
||||||
/** The error background image */
|
|
||||||
public String errorimgpath;
|
|
||||||
|
|
||||||
public File appdir;
|
|
||||||
|
|
||||||
/** Additional jvmargs to supply to the launching app. */
|
|
||||||
public String[] jvmargs;
|
|
||||||
|
|
||||||
/** Additional appargs to supply to the launching app. */
|
|
||||||
public String[] appargs;
|
|
||||||
|
|
||||||
/** An optional URL to which the applet should redirect when done. */
|
|
||||||
public URL redirectUrl;
|
|
||||||
|
|
||||||
public String redirectTarget;
|
|
||||||
|
|
||||||
public String installerFileContents;
|
|
||||||
|
|
||||||
/** Indicates whether the downloaded app should be launched in the parent applet (true) or as a
|
|
||||||
* separate java process (false). */
|
|
||||||
public boolean invokeDirect;
|
|
||||||
|
|
||||||
/** Indicates whether Getdown should allow the launched app to connect to it through a server
|
|
||||||
* socket bound to localhost on any available port in order to allow interaction with
|
|
||||||
* JavaScript code on the page containing the applet. */
|
|
||||||
public boolean allowConnect;
|
|
||||||
|
|
||||||
/** Optional default bounds for the status panel. */
|
|
||||||
public Rectangle statusBounds;
|
|
||||||
|
|
||||||
public Color statusColor;
|
|
||||||
|
|
||||||
public GetdownAppletConfig (JApplet applet)
|
|
||||||
{
|
|
||||||
this(applet, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GetdownAppletConfig (JApplet applet, String paramPrefix)
|
|
||||||
{
|
|
||||||
_applet = applet;
|
|
||||||
_prefix = paramPrefix;
|
|
||||||
|
|
||||||
appbase = getParameter(APPBASE, "");
|
|
||||||
appname = getParameter(APPNAME, "");
|
|
||||||
log.info("App Base: " + appbase);
|
|
||||||
log.info("App Name: " + appname);
|
|
||||||
|
|
||||||
imgpath = getParameter(BGIMAGE);
|
|
||||||
errorimgpath = getParameter(ERRORBGIMAGE);
|
|
||||||
|
|
||||||
// DEPRECATED LEGACY CRAP: extract system properties to set in applet JVM (not app JVM)
|
|
||||||
String props = getParameter("properties");
|
|
||||||
if (props != null) {
|
|
||||||
String[] proparray = props.split(" ");
|
|
||||||
for (String property : proparray) {
|
|
||||||
String key = property.substring(property.indexOf("-D") + 2, property.indexOf("="));
|
|
||||||
String value = property.substring(property.indexOf("=") + 1);
|
|
||||||
_properties.setProperty(key, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// END DLC
|
|
||||||
|
|
||||||
String root;
|
|
||||||
if (RunAnywhere.isWindows()) {
|
|
||||||
root = "Application Data";
|
|
||||||
String verStr = System.getProperty("os.version");
|
|
||||||
try {
|
|
||||||
if (Float.parseFloat(verStr) >= 6.0f) {
|
|
||||||
// Vista makes us write it here.... Yay.
|
|
||||||
root = "AppData" + File.separator + "LocalLow";
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("Couldn't parse OS version", "vers", verStr, "error", e);
|
|
||||||
}
|
|
||||||
} else if (RunAnywhere.isMacOS()) {
|
|
||||||
root = "Library" + File.separator + "Application Support";
|
|
||||||
} else /* isLinux() or something wacky */{
|
|
||||||
root = ".getdown";
|
|
||||||
}
|
|
||||||
appdir = new File(System.getProperty("user.home") + File.separator + root + File.separator
|
|
||||||
+ appname);
|
|
||||||
|
|
||||||
installerFileContents = getParameter("installer");
|
|
||||||
|
|
||||||
// extract arguments to be added to getdown.txt's jvmargs
|
|
||||||
List<String> jvmalist = parseArgList(JVMARG_PREFIX);
|
|
||||||
// DEPRECATED LEGACY CRAP: extract system properties to set in app JVM
|
|
||||||
String appprops = getParameter("app_properties");
|
|
||||||
if (appprops != null) {
|
|
||||||
for (String jvmarg : appprops.split(",")) {
|
|
||||||
jvmalist.add("-D" + jvmarg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// END DLC
|
|
||||||
jvmargs = jvmalist.toArray(new String[jvmalist.size()]);
|
|
||||||
|
|
||||||
// extract arguments to be added to getdown.txt's appargs
|
|
||||||
List<String> appalist = parseArgList(APPARG_PREFIX);
|
|
||||||
appargs = appalist.toArray(new String[appalist.size()]);
|
|
||||||
|
|
||||||
String direct = getParameter(DIRECT, "false");
|
|
||||||
invokeDirect = Boolean.valueOf(direct);
|
|
||||||
|
|
||||||
String connect = getParameter(CONNECT, "false");
|
|
||||||
allowConnect = Boolean.valueOf(connect);
|
|
||||||
|
|
||||||
String redirectURL = getParameter(REDIRECT_ON_FINISH);
|
|
||||||
if (redirectURL != null) {
|
|
||||||
try {
|
|
||||||
redirectUrl = new URL(redirectURL);
|
|
||||||
redirectTarget = getParameter(REDIRECT_ON_FINISH_TARGET);
|
|
||||||
} catch (MalformedURLException e) {
|
|
||||||
log.warning("URL in " + REDIRECT_ON_FINISH + " param is malformed: " + e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This allows us to configure the status panel from applet parameters in case something
|
|
||||||
// goes horribly wrong before we get a chance to read getdown.txt (like when the user
|
|
||||||
// rejects write permission for the applet)
|
|
||||||
statusBounds = Config.parseRect("ui.status", getParameter("ui.status"));
|
|
||||||
statusColor = Config.parseColor(getParameter("ui.status_text"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Does all the fiddly initialization of Getdown and throws an exception if something goes
|
|
||||||
* horribly wrong.
|
|
||||||
*/
|
|
||||||
public void init () throws Exception
|
|
||||||
{
|
|
||||||
securityCheck();
|
|
||||||
setSystemProperties();
|
|
||||||
ensureAppdirExists();
|
|
||||||
createFiles();
|
|
||||||
|
|
||||||
// record a few things for posterity
|
|
||||||
log.info("------------------ VM Info ------------------");
|
|
||||||
log.info("-- OS Name: " + System.getProperty("os.name"));
|
|
||||||
log.info("-- OS Arch: " + System.getProperty("os.arch"));
|
|
||||||
log.info("-- OS Vers: " + System.getProperty("os.version"));
|
|
||||||
log.info("-- Java Vers: " + System.getProperty("java.version"));
|
|
||||||
log.info("-- Java Home: " + System.getProperty("java.home"));
|
|
||||||
log.info("-- User Name: " + System.getProperty("user.name"));
|
|
||||||
log.info("-- User Home: " + System.getProperty("user.home"));
|
|
||||||
log.info("-- Cur dir: " + System.getProperty("user.dir"));
|
|
||||||
log.info("---------------------------------------------");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets getdown status panel size and text color from applet params.
|
|
||||||
*/
|
|
||||||
public void config (Getdown getdown)
|
|
||||||
{
|
|
||||||
if (statusBounds != null) {
|
|
||||||
getdown._ifc.status = statusBounds;
|
|
||||||
}
|
|
||||||
if (statusColor != null) {
|
|
||||||
getdown._ifc.statusText = statusColor;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of the named parameter. If the param is not set, this will return null.
|
|
||||||
*/
|
|
||||||
public String getParameter (String param)
|
|
||||||
{
|
|
||||||
return (_prefix == null) ?
|
|
||||||
_applet.getParameter(param) :
|
|
||||||
_applet.getParameter(_prefix + PARAM_DELIMITER + param);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value of an optional Applet parameter. If the param is not set, the default value is
|
|
||||||
* returned.
|
|
||||||
*/
|
|
||||||
public String getParameter (String param, String defaultValue)
|
|
||||||
{
|
|
||||||
String value = getParameter(param);
|
|
||||||
return (value == null) ? defaultValue : value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Uses the Applet context to redirect the browser to a URL (intended for use when the applet
|
|
||||||
* is done downloading). If the redirect_on_finish parameter was not set, this does nothing.
|
|
||||||
*/
|
|
||||||
public void redirect ()
|
|
||||||
{
|
|
||||||
if (redirectUrl != null) {
|
|
||||||
if (redirectTarget == null) {
|
|
||||||
_applet.getAppletContext().showDocument(redirectUrl);
|
|
||||||
} else {
|
|
||||||
_applet.getAppletContext().showDocument(redirectUrl, redirectTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the rotation background images and error image.The given image loader will be used if
|
|
||||||
* the images have not been loaded yet. The locations of the images are pulled from the Applet
|
|
||||||
* parameters.
|
|
||||||
*/
|
|
||||||
public RotatingBackgrounds getBackgroundImages (ImageLoader loader)
|
|
||||||
{
|
|
||||||
if (bgimages == null) {
|
|
||||||
// Load background images
|
|
||||||
bgimages = getBackgroundImages(imgpath, errorimgpath, loader);
|
|
||||||
}
|
|
||||||
return bgimages;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the rotation background images and error image.The given image loader will be used if
|
|
||||||
* the images have not been loaded yet.
|
|
||||||
*/
|
|
||||||
public static RotatingBackgrounds getBackgroundImages (String imageParam,
|
|
||||||
String errorImagePath, ImageLoader loader)
|
|
||||||
{
|
|
||||||
// Parse the image parameter and load the background images
|
|
||||||
RotatingBackgrounds images;
|
|
||||||
if (imageParam == null) {
|
|
||||||
images = new RotatingBackgrounds();
|
|
||||||
} else if (imageParam.indexOf(",") > -1) {
|
|
||||||
images = new RotatingBackgrounds(imageParam.split(","), errorImagePath, loader);
|
|
||||||
} else {
|
|
||||||
images = new RotatingBackgrounds(loader.loadImage(imageParam));
|
|
||||||
}
|
|
||||||
return images;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses parameters named {@code prefix0}, {@code prefix1}, ... into a list.
|
|
||||||
*/
|
|
||||||
protected List<String> parseArgList (String prefix)
|
|
||||||
{
|
|
||||||
List<String> arglist = new ArrayList<>();
|
|
||||||
String value;
|
|
||||||
for (int ii = 0; (value = getParameter(prefix + ii)) != null; ii++) {
|
|
||||||
arglist.add(value);
|
|
||||||
}
|
|
||||||
return arglist;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This checks whether the user has accepted our signed
|
|
||||||
*/
|
|
||||||
protected final void securityCheck () throws Exception
|
|
||||||
{
|
|
||||||
// getdown requires full read/write permissions to the system; if we don't have this, then
|
|
||||||
// we need to not do anything unsafe, and display a message to the user telling them they
|
|
||||||
// need to (groan) close out of the web browser entirely and re-launch the browser, go to
|
|
||||||
// our site, and accept the certificate
|
|
||||||
SecurityManager sm = System.getSecurityManager();
|
|
||||||
if (sm != null) {
|
|
||||||
try {
|
|
||||||
sm.checkPropertiesAccess();
|
|
||||||
sm.checkWrite("getdown");
|
|
||||||
} catch (SecurityException se) {
|
|
||||||
log.warning("Signed applet rejected by user", "se", se);
|
|
||||||
throw new Exception("m.insufficient_permissions_error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps the contents of the <code>properties</code> into the System properties.
|
|
||||||
*/
|
|
||||||
protected void setSystemProperties ()
|
|
||||||
{
|
|
||||||
System.getProperties().putAll(_properties);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sire that the appdir directory exists, creating it if necessary. Throws an exception if
|
|
||||||
* the directory does not exist and cannot be created.
|
|
||||||
*/
|
|
||||||
protected void ensureAppdirExists () throws Exception
|
|
||||||
{
|
|
||||||
// if our application directory does not exist, auto-create it
|
|
||||||
if ((!appdir.exists() || !appdir.isDirectory()) && !appdir.mkdirs()) {
|
|
||||||
throw new Exception("m.create_appdir_failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes the installer.txt and getdown.txt files to the local file system as needed.
|
|
||||||
*/
|
|
||||||
protected void createFiles () throws Exception
|
|
||||||
{
|
|
||||||
// if an installer.txt file is desired, create that
|
|
||||||
if (!StringUtil.isBlank(installerFileContents)) {
|
|
||||||
File infile = new File(appdir, "installer.txt");
|
|
||||||
if (!infile.exists()) {
|
|
||||||
writeToFile(infile, installerFileContents);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// if our getdown.txt file does not exist, or it is corrupt, auto-/recreate it
|
|
||||||
File gdfile = new File(appdir, "getdown.txt");
|
|
||||||
boolean createGetdown = !gdfile.exists();
|
|
||||||
if (!createGetdown) {
|
|
||||||
try {
|
|
||||||
Config config = Config.parseConfig(gdfile, Config.createOpts(false));
|
|
||||||
String oappbase = StringUtil.trim(config.getString(APPBASE));
|
|
||||||
createGetdown = (appbase != null && !appbase.trim().equals(oappbase));
|
|
||||||
if (createGetdown) {
|
|
||||||
log.warning("Recreating getdown.txt due to appbase mismatch",
|
|
||||||
"nappbase", appbase, "oappbase", oappbase);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("Failure checking validity of getdown.txt, forcing recreate.",
|
|
||||||
"error", e);
|
|
||||||
createGetdown = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (createGetdown) {
|
|
||||||
if (StringUtil.isBlank(appbase)) {
|
|
||||||
throw new Exception("m.missing_appbase");
|
|
||||||
}
|
|
||||||
if (!writeToFile(gdfile, "appbase = " + appbase)) {
|
|
||||||
throw new Exception("m.create_getdown_failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the specified file and writes the supplied contents to it.
|
|
||||||
*/
|
|
||||||
protected boolean writeToFile (File tofile, String contents)
|
|
||||||
{
|
|
||||||
try (FileOutputStream fos = new FileOutputStream(tofile);
|
|
||||||
PrintStream out = new PrintStream(fos)) {
|
|
||||||
out.println(contents);
|
|
||||||
return true;
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
log.warning("Failed to create '" + tofile + "'.", ioe);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** A reference to the Applet in which Getdown is running */
|
|
||||||
protected JApplet _applet;
|
|
||||||
|
|
||||||
/** An optional prefix to prepend when looking for Getdown Applet params */
|
|
||||||
protected String _prefix;
|
|
||||||
|
|
||||||
/** The background images displayed on the status panel as Getdown is getting down. */
|
|
||||||
protected RotatingBackgrounds bgimages;
|
|
||||||
|
|
||||||
/** System properties to set in the applet JVM. */
|
|
||||||
protected Properties _properties = new Properties();
|
|
||||||
}
|
|
||||||
@@ -25,8 +25,6 @@ import java.util.regex.Matcher;
|
|||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.GZIPInputStream;
|
import java.util.zip.GZIPInputStream;
|
||||||
|
|
||||||
import javax.swing.JApplet;
|
|
||||||
|
|
||||||
import com.samskivert.io.StreamUtil;
|
import com.samskivert.io.StreamUtil;
|
||||||
import com.samskivert.text.MessageUtil;
|
import com.samskivert.text.MessageUtil;
|
||||||
import com.samskivert.util.ArrayUtil;
|
import com.samskivert.util.ArrayUtil;
|
||||||
@@ -141,12 +139,6 @@ public class Application
|
|||||||
/** The patch notes URL. */
|
/** The patch notes URL. */
|
||||||
public String patchNotesUrl;
|
public String patchNotesUrl;
|
||||||
|
|
||||||
/** The dimensions of the play again button. */
|
|
||||||
public Rectangle playAgain;
|
|
||||||
|
|
||||||
/** The path (relative to the appdir) to a single play again image. */
|
|
||||||
public String playAgainImage;
|
|
||||||
|
|
||||||
/** Whether window decorations are hidden for the UI. */
|
/** Whether window decorations are hidden for the UI. */
|
||||||
public boolean hideDecorations;
|
public boolean hideDecorations;
|
||||||
|
|
||||||
@@ -172,7 +164,6 @@ public class Application
|
|||||||
", pb=" + progressBar + ", srect=" + status + ", st=" + statusText +
|
", pb=" + progressBar + ", srect=" + status + ", st=" + statusText +
|
||||||
", shadow=" + textShadow + ", err=" + installError + ", nrect=" + patchNotes +
|
", shadow=" + textShadow + ", err=" + installError + ", nrect=" + patchNotes +
|
||||||
", notes=" + patchNotesUrl + ", stepPercentages=" + stepPercentages +
|
", notes=" + patchNotesUrl + ", stepPercentages=" + stepPercentages +
|
||||||
", parect=" + playAgain + ", paimage=" + playAgainImage +
|
|
||||||
", hideProgressText" + hideProgressText + ", minShow=" + minShowSeconds + "]";
|
", hideProgressText" + hideProgressText + ", minShow=" + minShowSeconds + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,10 +744,6 @@ public class Application
|
|||||||
ui.patchNotes = config.getRect("ui.patch_notes", ui.patchNotes);
|
ui.patchNotes = config.getRect("ui.patch_notes", ui.patchNotes);
|
||||||
ui.patchNotesUrl = config.getUrl("ui.patch_notes_url", null);
|
ui.patchNotesUrl = config.getUrl("ui.patch_notes_url", null);
|
||||||
|
|
||||||
// the play again bits
|
|
||||||
ui.playAgain = config.getRect("ui.play_again", ui.playAgain);
|
|
||||||
ui.playAgainImage = config.getString("ui.play_again_image");
|
|
||||||
|
|
||||||
// step progress percentages
|
// step progress percentages
|
||||||
for (UpdateInterface.Step step : UpdateInterface.Step.values()) {
|
for (UpdateInterface.Step step : UpdateInterface.Step.values()) {
|
||||||
String spec = config.getString("ui.percents." + step.name());
|
String spec = config.getString("ui.percents." + step.name());
|
||||||
@@ -1058,7 +1045,7 @@ public class Application
|
|||||||
/**
|
/**
|
||||||
* Runs this application directly in the current VM.
|
* Runs this application directly in the current VM.
|
||||||
*/
|
*/
|
||||||
public void invokeDirect (JApplet applet) throws IOException
|
public void invokeDirect () throws IOException
|
||||||
{
|
{
|
||||||
ClassPath classPath = ClassPaths.buildClassPath(this);
|
ClassPath classPath = ClassPaths.buildClassPath(this);
|
||||||
URL[] jarUrls = classPath.asUrls();
|
URL[] jarUrls = classPath.asUrls();
|
||||||
@@ -1103,9 +1090,6 @@ public class Application
|
|||||||
System.setProperty(entry.getKey(), entry.getValue());
|
System.setProperty(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
// make a note that we're running in "applet" mode
|
|
||||||
System.setProperty("applet", "true");
|
|
||||||
|
|
||||||
// prepare our app arguments
|
// prepare our app arguments
|
||||||
String[] args = new String[_appargs.size()];
|
String[] args = new String[_appargs.size()];
|
||||||
for (int ii = 0; ii < args.length; ii++) args[ii] = processArg(_appargs.get(ii));
|
for (int ii = 0; ii < args.length; ii++) args[ii] = processArg(_appargs.get(ii));
|
||||||
@@ -1113,17 +1097,9 @@ public class Application
|
|||||||
try {
|
try {
|
||||||
log.info("Loading " + _class);
|
log.info("Loading " + _class);
|
||||||
Class<?> appclass = loader.loadClass(_class);
|
Class<?> appclass = loader.loadClass(_class);
|
||||||
Method main;
|
Method main = appclass.getMethod("main", SA_PROTO.getClass());
|
||||||
try {
|
|
||||||
// first see if the class has a special applet-aware main
|
|
||||||
main = appclass.getMethod("main", JApplet.class, SA_PROTO.getClass());
|
|
||||||
log.info("Invoking main(JApplet, {" + StringUtil.join(args, ", ") + "})");
|
|
||||||
main.invoke(null, new Object[] { applet, args });
|
|
||||||
} catch (NoSuchMethodException nsme) {
|
|
||||||
main = appclass.getMethod("main", SA_PROTO.getClass());
|
|
||||||
log.info("Invoking main({" + StringUtil.join(args, ", ") + "})");
|
log.info("Invoking main({" + StringUtil.join(args, ", ") + "})");
|
||||||
main.invoke(null, new Object[] { args });
|
main.invoke(null, new Object[] { args });
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace(System.err);
|
e.printStackTrace(System.err);
|
||||||
}
|
}
|
||||||
@@ -1321,9 +1297,6 @@ public class Application
|
|||||||
|
|
||||||
for (int ii = 0; ii < sizes.length; ii++) {
|
for (int ii = 0; ii < sizes.length; ii++) {
|
||||||
final Resource rsrc = rsrcs.get(ii);
|
final Resource rsrc = rsrcs.get(ii);
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException("m.applet_stopped");
|
|
||||||
}
|
|
||||||
final int index = ii;
|
final int index = ii;
|
||||||
exec.execute(new Runnable() {
|
exec.execute(new Runnable() {
|
||||||
public void run () {
|
public void run () {
|
||||||
@@ -1414,9 +1387,6 @@ public class Application
|
|||||||
|
|
||||||
ProgressAggregator pagg = new ProgressAggregator(obs, sizes);
|
ProgressAggregator pagg = new ProgressAggregator(obs, sizes);
|
||||||
for (int ii = 0; ii < sizes.length; ii++) {
|
for (int ii = 0; ii < sizes.length; ii++) {
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException("m.applet_stopped");
|
|
||||||
}
|
|
||||||
Resource rsrc = rsrcs.get(ii);
|
Resource rsrc = rsrcs.get(ii);
|
||||||
ProgressObserver pobs = pagg.startElement(ii);
|
ProgressObserver pobs = pagg.startElement(ii);
|
||||||
try {
|
try {
|
||||||
@@ -1539,12 +1509,6 @@ public class Application
|
|||||||
* Downloads a new copy of the specified control file, optionally validating its signature.
|
* Downloads a new copy of the specified control file, optionally validating its signature.
|
||||||
* If the download is successful, moves it over the old file on the filesystem.
|
* If the download is successful, moves it over the old file on the filesystem.
|
||||||
*
|
*
|
||||||
* <p> We implement simple signing of the digest.txt file for use with the Getdown applet, but
|
|
||||||
* this should never be used as-is with a non-applet getdown installation, as the signing
|
|
||||||
* format has no provisions for declaring arbitrary signing key IDs, signature algorithm, et al
|
|
||||||
* -- it is entirely reliant on the ability to upgrade the Getdown applet, and its signature
|
|
||||||
* validation implementation, at-will (ie, via an Applet).
|
|
||||||
*
|
|
||||||
* <p> TODO: Switch to PKCS #7 or CMS.
|
* <p> TODO: Switch to PKCS #7 or CMS.
|
||||||
*
|
*
|
||||||
* @param sigVersion if {@code 0} no validation will be performed, if {@code > 0} then this
|
* @param sigVersion if {@code 0} no validation will be performed, if {@code > 0} then this
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ import java.awt.image.BufferedImage;
|
|||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
import javax.swing.ImageIcon;
|
import javax.swing.ImageIcon;
|
||||||
import javax.swing.JApplet;
|
|
||||||
import javax.swing.JButton;
|
import javax.swing.JButton;
|
||||||
import javax.swing.JFrame;
|
import javax.swing.JFrame;
|
||||||
import javax.swing.JLayeredPane;
|
import javax.swing.JLayeredPane;
|
||||||
@@ -70,7 +69,7 @@ import static com.threerings.getdown.Log.log;
|
|||||||
* Manages the main control for the Getdown application updater and deployment system.
|
* Manages the main control for the Getdown application updater and deployment system.
|
||||||
*/
|
*/
|
||||||
public abstract class Getdown extends Thread
|
public abstract class Getdown extends Thread
|
||||||
implements Application.StatusDisplay, ImageLoader
|
implements Application.StatusDisplay, RotatingBackgrounds.ImageLoader
|
||||||
{
|
{
|
||||||
public static void main (String[] args)
|
public static void main (String[] args)
|
||||||
{
|
{
|
||||||
@@ -128,7 +127,7 @@ public abstract class Getdown extends Thread
|
|||||||
/**
|
/**
|
||||||
* Installs the currently pending new resources.
|
* Installs the currently pending new resources.
|
||||||
*/
|
*/
|
||||||
public void install () throws IOException, InterruptedException
|
public void install () throws IOException
|
||||||
{
|
{
|
||||||
if (SysProps.noInstall()) {
|
if (SysProps.noInstall()) {
|
||||||
log.info("Skipping install due to 'no_install' sysprop.");
|
log.info("Skipping install due to 'no_install' sysprop.");
|
||||||
@@ -136,9 +135,6 @@ public abstract class Getdown extends Thread
|
|||||||
log.info("Installing " + _toInstallResources.size() + " downloaded resources:");
|
log.info("Installing " + _toInstallResources.size() + " downloaded resources:");
|
||||||
for (Resource resource : _toInstallResources) {
|
for (Resource resource : _toInstallResources) {
|
||||||
resource.install();
|
resource.install();
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException("m.applet_stopped");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_toInstallResources.clear();
|
_toInstallResources.clear();
|
||||||
_readyToInstall = false;
|
_readyToInstall = false;
|
||||||
@@ -148,21 +144,6 @@ public abstract class Getdown extends Thread
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* This is used by the applet which always needs a user interface and wants to load it as soon
|
|
||||||
* as possible.
|
|
||||||
*/
|
|
||||||
public void preInit ()
|
|
||||||
{
|
|
||||||
try {
|
|
||||||
_ifc = _app.init(true);
|
|
||||||
createInterfaceAsync(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.warning("Failed to preinit: " + e);
|
|
||||||
createInterfaceAsync(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run ()
|
public void run ()
|
||||||
{
|
{
|
||||||
@@ -530,10 +511,6 @@ public abstract class Getdown extends Thread
|
|||||||
// Only launch if we aren't in silent mode. Some mystery program starting out
|
// Only launch if we aren't in silent mode. Some mystery program starting out
|
||||||
// of the blue would be disconcerting.
|
// of the blue would be disconcerting.
|
||||||
if (!_silent || _launchInSilent) {
|
if (!_silent || _launchInSilent) {
|
||||||
if (Thread.interrupted()) {
|
|
||||||
// One last interrupted check so we don't launch as the applet aborts
|
|
||||||
throw new InterruptedException("m.applet_stopped");
|
|
||||||
}
|
|
||||||
// And another final check for the lock. It'll already be held unless
|
// And another final check for the lock. It'll already be held unless
|
||||||
// we're in silent mode.
|
// we're in silent mode.
|
||||||
_app.lockForUpdates();
|
_app.lockForUpdates();
|
||||||
@@ -611,7 +588,7 @@ public abstract class Getdown extends Thread
|
|||||||
* running with the necessary Java version.
|
* running with the necessary Java version.
|
||||||
*/
|
*/
|
||||||
protected void updateJava ()
|
protected void updateJava ()
|
||||||
throws IOException, InterruptedException
|
throws IOException
|
||||||
{
|
{
|
||||||
Resource vmjar = _app.getJavaVMResource();
|
Resource vmjar = _app.getJavaVMResource();
|
||||||
if (vmjar == null) {
|
if (vmjar == null) {
|
||||||
@@ -652,7 +629,7 @@ public abstract class Getdown extends Thread
|
|||||||
* Called if the application is determined to be of an old version.
|
* Called if the application is determined to be of an old version.
|
||||||
*/
|
*/
|
||||||
protected void update ()
|
protected void update ()
|
||||||
throws IOException, InterruptedException
|
throws IOException
|
||||||
{
|
{
|
||||||
// first clear all validation markers
|
// first clear all validation markers
|
||||||
_app.clearValidationMarkers();
|
_app.clearValidationMarkers();
|
||||||
@@ -724,7 +701,7 @@ public abstract class Getdown extends Thread
|
|||||||
* Called if the application is determined to require resource downloads.
|
* Called if the application is determined to require resource downloads.
|
||||||
*/
|
*/
|
||||||
protected void download (Collection<Resource> resources)
|
protected void download (Collection<Resource> resources)
|
||||||
throws IOException, InterruptedException
|
throws IOException
|
||||||
{
|
{
|
||||||
// create our user interface
|
// create our user interface
|
||||||
createInterfaceAsync(false);
|
createInterfaceAsync(false);
|
||||||
@@ -746,12 +723,6 @@ public abstract class Getdown extends Thread
|
|||||||
}
|
}
|
||||||
_lastCheck = percent;
|
_lastCheck = percent;
|
||||||
}
|
}
|
||||||
if (Thread.currentThread().isInterrupted()) {
|
|
||||||
// The applet interrupts when it stops, so abort the download and quit. Use
|
|
||||||
// isInterrupted so the containing code can call interrupted outside of here
|
|
||||||
// to check if this was the reason for aborting.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
setStatusAsync("m.downloading", stepToGlobalPercent(percent), remaining, true);
|
setStatusAsync("m.downloading", stepToGlobalPercent(percent), remaining, true);
|
||||||
if (percent > 0) {
|
if (percent > 0) {
|
||||||
reportTrackingEvent("progress", percent);
|
reportTrackingEvent("progress", percent);
|
||||||
@@ -772,9 +743,6 @@ public abstract class Getdown extends Thread
|
|||||||
// start the download and wait for it to complete
|
// start the download and wait for it to complete
|
||||||
Downloader dl = new HTTPDownloader(resources, obs);
|
Downloader dl = new HTTPDownloader(resources, obs);
|
||||||
if (!dl.download()) {
|
if (!dl.download()) {
|
||||||
if (Thread.interrupted()) {
|
|
||||||
throw new InterruptedException("m.applet_stopped");
|
|
||||||
}
|
|
||||||
throw new MultipleGetdownRunning();
|
throw new MultipleGetdownRunning();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -789,11 +757,10 @@ public abstract class Getdown extends Thread
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (invokeDirect()) {
|
if (invokeDirect()) {
|
||||||
// if we're in applet mode, this will NOOP; if we're in app mode and are invoking
|
// we want to close the Getdown window, as the app is launching
|
||||||
// direct, we want to close the Getdown window, as the app is launching
|
|
||||||
disposeContainer();
|
disposeContainer();
|
||||||
_app.releaseLock();
|
_app.releaseLock();
|
||||||
_app.invokeDirect(getApplet());
|
_app.invokeDirect();
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
Process proc;
|
Process proc;
|
||||||
@@ -864,17 +831,6 @@ public abstract class Getdown extends Thread
|
|||||||
setStatusAsync(null, 100, -1L, false);
|
setStatusAsync(null, 100, -1L, false);
|
||||||
exit(0);
|
exit(0);
|
||||||
|
|
||||||
if (_playAgain != null && _playAgain.isEnabled()) {
|
|
||||||
// wait a little time before showing the button
|
|
||||||
Timer timer = new Timer("playAgain", true);
|
|
||||||
timer.schedule(new TimerTask() {
|
|
||||||
@Override public void run () {
|
|
||||||
initPlayAgain();
|
|
||||||
_playAgain.setVisible(true);
|
|
||||||
}
|
|
||||||
}, PLAY_AGAIN_TIME);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warning("launch() failed.", e);
|
log.warning("launch() failed.", e);
|
||||||
}
|
}
|
||||||
@@ -909,26 +865,6 @@ public abstract class Getdown extends Thread
|
|||||||
});
|
});
|
||||||
_patchNotes.setFont(StatusPanel.FONT);
|
_patchNotes.setFont(StatusPanel.FONT);
|
||||||
_layers.add(_patchNotes);
|
_layers.add(_patchNotes);
|
||||||
|
|
||||||
if (getApplet() != null) {
|
|
||||||
_playAgain = new JButton();
|
|
||||||
_playAgain.setEnabled(false);
|
|
||||||
_playAgain.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
|
||||||
_playAgain.setFont(StatusPanel.FONT);
|
|
||||||
_playAgain.addActionListener(new ActionListener() {
|
|
||||||
@Override public void actionPerformed (ActionEvent event) {
|
|
||||||
_playAgain.setVisible(false);
|
|
||||||
_stepMinPercent = _lastGlobalPercent = 0;
|
|
||||||
EventQueue.invokeLater(new Runnable() {
|
|
||||||
public void run () {
|
|
||||||
getdown();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_layers.add(_playAgain);
|
|
||||||
}
|
|
||||||
|
|
||||||
_status = new StatusPanel(_msgs);
|
_status = new StatusPanel(_msgs);
|
||||||
_layers.add(_status);
|
_layers.add(_status);
|
||||||
initInterface();
|
initInterface();
|
||||||
@@ -957,37 +893,12 @@ public abstract class Getdown extends Thread
|
|||||||
_patchNotes.setBounds(_ifc.patchNotes);
|
_patchNotes.setBounds(_ifc.patchNotes);
|
||||||
_patchNotes.setVisible(false);
|
_patchNotes.setVisible(false);
|
||||||
|
|
||||||
initPlayAgain();
|
|
||||||
|
|
||||||
// we were displaying progress while the UI wasn't up. Now that it is, whatever progress
|
// we were displaying progress while the UI wasn't up. Now that it is, whatever progress
|
||||||
// is left is scaled into a 0-100 DISPLAYED progress.
|
// is left is scaled into a 0-100 DISPLAYED progress.
|
||||||
_uiDisplayPercent = _lastGlobalPercent;
|
_uiDisplayPercent = _lastGlobalPercent;
|
||||||
_stepMinPercent = _lastGlobalPercent = 0;
|
_stepMinPercent = _lastGlobalPercent = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initPlayAgain ()
|
|
||||||
{
|
|
||||||
if (_playAgain != null) {
|
|
||||||
Image image = loadImage(_ifc.playAgainImage);
|
|
||||||
boolean hasImage = image != null;
|
|
||||||
if (hasImage) {
|
|
||||||
_playAgain.setIcon(new ImageIcon(image));
|
|
||||||
_playAgain.setText("");
|
|
||||||
} else {
|
|
||||||
_playAgain.setText(_msgs.getString("m.play_again"));
|
|
||||||
_playAgain.setIcon(null);
|
|
||||||
}
|
|
||||||
_playAgain.setBorderPainted(!hasImage);
|
|
||||||
_playAgain.setOpaque(!hasImage);
|
|
||||||
_playAgain.setContentAreaFilled(!hasImage);
|
|
||||||
if (_ifc.playAgain != null) {
|
|
||||||
_playAgain.setBounds(_ifc.playAgain);
|
|
||||||
_playAgain.setEnabled(true);
|
|
||||||
}
|
|
||||||
_playAgain.setVisible(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected RotatingBackgrounds getBackground ()
|
protected RotatingBackgrounds getBackground ()
|
||||||
{
|
{
|
||||||
if (_ifc.rotatingBackgrounds != null) {
|
if (_ifc.rotatingBackgrounds != null) {
|
||||||
@@ -1140,27 +1051,16 @@ public abstract class Getdown extends Thread
|
|||||||
*/
|
*/
|
||||||
protected boolean invokeDirect ()
|
protected boolean invokeDirect ()
|
||||||
{
|
{
|
||||||
// by default check a sysprop (which itself defaults to false); in applet mode this is
|
|
||||||
// overridden to check the applet config
|
|
||||||
return SysProps.direct();
|
return SysProps.direct();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to the applet that we'll pass on to our application when we're in "invoke
|
|
||||||
* direct" mode.
|
|
||||||
*/
|
|
||||||
protected JApplet getApplet ()
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests to show the document at the specified URL in a new window.
|
* Requests to show the document at the specified URL in a new window.
|
||||||
*/
|
*/
|
||||||
protected abstract void showDocument (String url);
|
protected abstract void showDocument (String url);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Requests that Getdown exit. In applet mode this does nothing.
|
* Requests that Getdown exit.
|
||||||
*/
|
*/
|
||||||
protected abstract void exit (int exitCode);
|
protected abstract void exit (int exitCode);
|
||||||
|
|
||||||
@@ -1238,7 +1138,6 @@ public abstract class Getdown extends Thread
|
|||||||
protected JLayeredPane _layers;
|
protected JLayeredPane _layers;
|
||||||
protected StatusPanel _status;
|
protected StatusPanel _status;
|
||||||
protected JButton _patchNotes;
|
protected JButton _patchNotes;
|
||||||
protected JButton _playAgain;
|
|
||||||
protected AbortPanel _abort;
|
protected AbortPanel _abort;
|
||||||
protected RotatingBackgrounds _background;
|
protected RotatingBackgrounds _background;
|
||||||
|
|
||||||
@@ -1263,7 +1162,6 @@ public abstract class Getdown extends Thread
|
|||||||
|
|
||||||
protected static final int MAX_LOOPS = 5;
|
protected static final int MAX_LOOPS = 5;
|
||||||
protected static final long FALLBACK_CHECK_TIME = 1000L;
|
protected static final long FALLBACK_CHECK_TIME = 1000L;
|
||||||
protected static final long PLAY_AGAIN_TIME = 3000L;
|
|
||||||
protected static final String PROXY_REGISTRY =
|
protected static final String PROXY_REGISTRY =
|
||||||
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// Getdown - application installer, patcher and launcher
|
|
||||||
// Copyright (C) 2004-2016 Getdown authors
|
|
||||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
|
||||||
|
|
||||||
package com.threerings.getdown.launcher;
|
|
||||||
|
|
||||||
import java.awt.Image;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Abstracts away the process of loading an image so that it can be done differently in the app and
|
|
||||||
* applet.
|
|
||||||
*/
|
|
||||||
public interface ImageLoader
|
|
||||||
{
|
|
||||||
/** Loads and returns the image with the supplied path. */
|
|
||||||
public Image loadImage (String path);
|
|
||||||
}
|
|
||||||
@@ -11,6 +11,10 @@ import static com.threerings.getdown.Log.log;
|
|||||||
|
|
||||||
public class RotatingBackgrounds
|
public class RotatingBackgrounds
|
||||||
{
|
{
|
||||||
|
public interface ImageLoader {
|
||||||
|
/** Loads and returns the image with the supplied path. */
|
||||||
|
public Image loadImage (String path);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a placeholder if there are no images. Just returns null from getImage every time.
|
* Creates a placeholder if there are no images. Just returns null from getImage every time.
|
||||||
|
|||||||
@@ -45,7 +45,6 @@
|
|||||||
<module>core</module>
|
<module>core</module>
|
||||||
<module>tools</module>
|
<module>tools</module>
|
||||||
<module>launcher</module>
|
<module>launcher</module>
|
||||||
<!-- deprecated: <module>applet</module>-->
|
|
||||||
<module>ant</module>
|
<module>ant</module>
|
||||||
</modules>
|
</modules>
|
||||||
|
|
||||||
|
|||||||
@@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// Getdown - application installer, patcher and launcher
|
|
||||||
// Copyright (C) 2004-2016 Getdown authors
|
|
||||||
// https://github.com/threerings/getdown/blob/master/LICENSE
|
|
||||||
|
|
||||||
package com.threerings.getdown.tools;
|
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.security.KeyStore;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.Signature;
|
|
||||||
|
|
||||||
import com.threerings.getdown.data.Digest;
|
|
||||||
import com.threerings.getdown.util.Base64;
|
|
||||||
|
|
||||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Produces a signed hash of the appbase, appname, and image path to ensure that signed copies of
|
|
||||||
* Getdown are not hijacked to run malicious code.
|
|
||||||
*/
|
|
||||||
public class AppletParamSigner
|
|
||||||
{
|
|
||||||
public static void main (String[] args)
|
|
||||||
{
|
|
||||||
if (args.length != 7) {
|
|
||||||
System.err.println("AppletParamSigner keystore storepass alias keypass " +
|
|
||||||
"appbase appname imgpath");
|
|
||||||
System.exit(255);
|
|
||||||
}
|
|
||||||
|
|
||||||
String keystore = args[0];
|
|
||||||
String storepass = args[1];
|
|
||||||
String alias = args[2];
|
|
||||||
String keypass = args[3];
|
|
||||||
String appbase = args[4];
|
|
||||||
String appname = args[5];
|
|
||||||
String imgpath = args[6];
|
|
||||||
String params = appbase + appname + imgpath;
|
|
||||||
|
|
||||||
try (FileInputStream fis = new FileInputStream(keystore);
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(fis)) {
|
|
||||||
|
|
||||||
KeyStore store = KeyStore.getInstance("JKS");
|
|
||||||
store.load(bis, storepass.toCharArray());
|
|
||||||
PrivateKey key = (PrivateKey)store.getKey(alias, keypass.toCharArray());
|
|
||||||
Signature sig = Signature.getInstance(Digest.sigAlgorithm(Digest.VERSION));
|
|
||||||
sig.initSign(key);
|
|
||||||
sig.update(params.getBytes(UTF_8));
|
|
||||||
String signed = Base64.encodeToString(sig.sign(), Base64.DEFAULT);
|
|
||||||
System.out.println("<param name=\"appbase\" value=\"" + appbase + "\" />");
|
|
||||||
System.out.println("<param name=\"appname\" value=\"" + appname + "\" />");
|
|
||||||
System.out.println("<param name=\"bgimage\" value=\"" + imgpath + "\" />");
|
|
||||||
System.out.println("<param name=\"signature\" value=\"" + signed + "\" />");
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("Failed to produce signature.");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user