diff --git a/src/java/com/threerings/getdown/data/Application.java b/src/java/com/threerings/getdown/data/Application.java
index 39812fb..5c1da21 100644
--- a/src/java/com/threerings/getdown/data/Application.java
+++ b/src/java/com/threerings/getdown/data/Application.java
@@ -64,8 +64,8 @@ import com.threerings.getdown.util.MetaProgressObserver;
import com.threerings.getdown.util.ProgressObserver;
/**
- * Parses and provide access to the information contained in the
- * getdown.txt configuration file.
+ * Parses and provide access to the information contained in the getdown.txt
+ * configuration file.
*/
public class Application
{
@@ -75,12 +75,11 @@ public class Application
/** The name of our target version file. */
public static final String VERSION_FILE = "version.txt";
- /** System properties that are prefixed with this string will be
- * passed through to our application (minus this prefix). */
+ /** System properties that are prefixed with this string will be passed through to our
+ * application (minus this prefix). */
public static final String PROP_PASSTHROUGH_PREFIX = "app.";
- /** Used to communicate information about the UI displayed when
- * updating the application. */
+ /** Used to communicate information about the UI displayed when updating the application. */
public static class UpdateInterface
{
/** The human readable name of this application. */
@@ -116,16 +115,17 @@ public class Application
/** Generates a string representation of this instance. */
public String toString ()
{
- return "[name=" + name + ", bg=" + backgroundImage +
- ", pi=" + progressImage + ", prect=" + progress +
- ", pt=" + progressText + ", pb=" + progressBar +
- ", srect=" + status + ", st=" + statusText +
- ", shadow=" + textShadow + ", err=" + installError + "]";
+ return "[name=" + name + ", bg=" + backgroundImage + ", pi=" + progressImage +
+ ", prect=" + progress + ", pt=" + progressText + ", pb=" + progressBar +
+ ", srect=" + status + ", st=" + statusText + ", shadow=" + textShadow +
+ ", err=" + installError + "]";
}
}
- /** Used by {@link #verifyMetadata} to communicate status in
- * circumstances where it needs to take network actions. */
+ /**
+ * Used by {@link #verifyMetadata} to communicate status in circumstances where it needs to
+ * take network actions.
+ */
public static interface StatusDisplay
{
/** Requests that the specified status message be displayed. */
@@ -133,15 +133,13 @@ public class Application
}
/**
- * Creates an application instance which records the location of the
- * getdown.txt configuration file from the supplied
- * application directory.
+ * Creates an application instance which records the location of the getdown.txt
+ * configuration file from the supplied application directory.
*
- * @param appid usually null but a string identifier if a secondary
- * application is desired to be launched. That application will use *
- * appid.class and appid.apparg to configure
- * itself but all other parameters will be the same as the primary
- * application.
+ * @param appid usually null but a string identifier if a secondary application is desired to
+ * be launched. That application will use appid.class and
+ * appid.apparg to configure itself but all other parameters will be the same as
+ * the primary application.
*/
public Application (File appdir, String appid)
{
@@ -151,8 +149,8 @@ public class Application
}
/**
- * Indicates whether or not we support downloading of our resources using
- * the Bittorrent protocol.
+ * Indicates whether or not we support downloading of our resources using the Bittorrent
+ * protocol.
*/
public boolean getUseTorrent ()
{
@@ -160,8 +158,7 @@ public class Application
}
/**
- * Returns a resource that refers to the application configuration
- * file itself.
+ * Returns a resource that refers to the application configuration file itself.
*/
public Resource getConfigResource ()
{
@@ -173,8 +170,7 @@ public class Application
}
/**
- * Returns a list of the code {@link Resource} objects used by this
- * application.
+ * Returns a list of the code {@link Resource} objects used by this application.
*/
public List getCodeResources ()
{
@@ -182,8 +178,7 @@ public class Application
}
/**
- * Returns a list of the non-code {@link Resource} objects used by
- * this application.
+ * Returns a list of the non-code {@link Resource} objects used by this application.
*/
public List getResources ()
{
@@ -191,8 +186,7 @@ public class Application
}
/**
- * Returns a list of all the {@link Resource} objects used by
- * this application.
+ * Returns a list of all the {@link Resource} objects used by this application.
*/
public List getAllResources ()
{
@@ -203,10 +197,9 @@ public class Application
}
/**
- * Returns a list of all auxiliary resource groups defined by the
- * application. An auxiliary resource group is a collection of resource
- * files that are not downloaded unless a group token file is present in
- * the application directory.
+ * Returns a list of all auxiliary resource groups defined by the application. An auxiliary
+ * resource group is a collection of resource files that are not downloaded unless a group
+ * token file is present in the application directory.
*/
public List getAuxGroups ()
{
@@ -214,16 +207,16 @@ public class Application
}
/**
- * Returns true if the specified auxgroup has been "activated", false if
- * not. Non-activated groups should be ignored, activated groups should be
- * downloaded and patched along with the main resources.
+ * Returns true if the specified auxgroup has been "activated", false if not. Non-activated
+ * groups should be ignored, activated groups should be downloaded and patched along with the
+ * main resources.
*/
public boolean isAuxGroupActive (String auxgroup)
{
Boolean active = _auxactive.get(auxgroup);
if (active == null) {
- // TODO: compare the contents with the MD5 hash of the auxgroup
- // name and the client's machine ident
+ // TODO: compare the contents with the MD5 hash of the auxgroup name and the client's
+ // machine ident
active = getLocalPath(auxgroup + ".dat").exists();
_auxactive.put(auxgroup, active);
}
@@ -231,9 +224,9 @@ public class Application
}
/**
- * Returns a list of the non-code {@link Resource} objects included in the
- * specified auxiliary resource group. If the group does not exist or has
- * no resources, an empty list will be returned.
+ * Returns a list of the non-code {@link Resource} objects included in the specified auxiliary
+ * resource group. If the group does not exist or has no resources, an empty list will be
+ * returned.
*/
public List getResources (String group)
{
@@ -242,8 +235,7 @@ public class Application
}
/**
- * Returns all non-code resources and all resources from active auxiliary
- * resource groups.
+ * Returns all non-code resources and all resources from active auxiliary resource groups.
*/
public List getActiveResources ()
{
@@ -258,19 +250,17 @@ public class Application
}
/**
- * Returns a resource that can be used to download a patch file that
- * will bring this application from its current version to the target
- * version.
+ * Returns a resource that can be used to download a patch file that will bring this
+ * application from its current version to the target version.
*
- * @param auxgroup the auxiliary resource group for which a patch resource
- * is desired or null for the main application patch resource.
+ * @param auxgroup the auxiliary resource group for which a patch resource is desired or null
+ * for the main application patch resource.
*/
public Resource getPatchResource (String auxgroup)
{
if (_targetVersion <= _version) {
- Log.warning("Requested patch resource for up-to-date or " +
- "non-versioned application [cvers=" + _version +
- ", tvers=" + _targetVersion + "].");
+ Log.warning("Requested patch resource for up-to-date or non-versioned application " +
+ "[cvers=" + _version + ", tvers=" + _targetVersion + "].");
return null;
}
@@ -288,8 +278,8 @@ public class Application
}
/**
- * Returns a resource that can be used to download an archive containing
- * all files belonging to the application.
+ * Returns a resource that can be used to download an archive containing all files belonging to
+ * the application.
*/
public Resource getFullResource ()
{
@@ -306,17 +296,15 @@ public class Application
}
/**
- * Instructs the application to parse its getdown.txt
- * configuration and prepare itself for operation. The application
- * base URL will be parsed first so that if there are errors
- * discovered later, the caller can use the application base to
- * download a new config.txt file and try again.
+ * Instructs the application to parse its getdown.txt configuration and prepare
+ * itself for operation. The application base URL will be parsed first so that if there are
+ * errors discovered later, the caller can use the application base to download a new
+ * config.txt file and try again.
*
- * @return a configured UpdateInterface instance that will be used to
- * configure the update UI.
+ * @return a configured UpdateInterface instance that will be used to configure the update UI.
*
- * @exception IOException thrown if there is an error reading the file
- * or an error encountered during its parsing.
+ * @exception IOException thrown if there is an error reading the file or an error encountered
+ * during its parsing.
*/
public UpdateInterface init (boolean checkPlatform)
throws IOException
@@ -326,9 +314,8 @@ public class Application
try {
cdata = ConfigUtil.parseConfig(_config, checkPlatform);
} catch (FileNotFoundException fnfe) {
- // thanks to funny windows bullshit, we have to do this backup
- // file fiddling in case we got screwed while updating our
- // very critical getdown config file
+ // thanks to funny windows bullshit, we have to do this backup file fiddling in case we
+ // got screwed while updating our very critical getdown config file
File cbackup = getLocalPath(CONFIG_FILE + "_old");
if (cbackup.exists()) {
cdata = ConfigUtil.parseConfig(cbackup, checkPlatform);
@@ -337,9 +324,8 @@ public class Application
}
}
- // first determine our application base, this way if anything goes
- // wrong later in the process, our caller can use the appbase to
- // download a new configuration file
+ // first determine our application base, this way if anything goes wrong later in the
+ // process, our caller can use the appbase to download a new configuration file
_appbase = (String)cdata.get("appbase");
if (_appbase == null) {
throw new IOException("m.missing_appbase");
@@ -380,6 +366,18 @@ public class Application
throw new IOException("m.missing_class");
}
+ // check to see if we require a particular JVM version and have a supplied JVM
+ vstr = (String)cdata.get("java_version");
+ if (vstr != null) {
+ try {
+ _javaVersion = Integer.parseInt(vstr);
+ } catch (Exception e) {
+ String err = MessageUtil.tcompose("m.invalid_java_version", vstr);
+ throw (IOException) new IOException(err).initCause(e);
+ }
+ }
+ _javaLocation = (String)cdata.get("java_location");
+
// clear our arrays as we may be reinitializing
_codes.clear();
_resources.clear();
@@ -428,8 +426,7 @@ public class Application
if (file.exists()) {
try {
List args = ConfigUtil.parsePairs(file, false);
- for (Iterator iter = args.iterator();
- iter.hasNext();) {
+ for (Iterator iter = args.iterator(); iter.hasNext();) {
String[] pair = iter.next();
_jvmargs.add(pair[0] + "=" + pair[1]);
}
@@ -439,21 +436,18 @@ public class Application
}
// determine whether or not we should be using bit torrent
- _useTorrent = (cdata.get("torrent") != null) ||
- (System.getProperty("torrent") != null);
+ _useTorrent = (cdata.get("torrent") != null) || (System.getProperty("torrent") != null);
- // look for a debug.txt file which causes us to run in java.exe on
- // Windows so that we can obtain a thread dump of the running JVM
+ // look for a debug.txt file which causes us to run in java.exe on Windows so that we can
+ // obtain a thread dump of the running JVM
_windebug = getLocalPath("debug.txt").exists();
// parse and return our application config
UpdateInterface ui = new UpdateInterface();
_name = ui.name = (String)cdata.get("ui.name");
ui.progress = parseRect(cdata, "ui.progress", ui.progress);
- ui.progressText = parseColor(
- cdata, "ui.progress_text", ui.progressText);
- ui.progressBar = parseColor(
- cdata, "ui.progress_bar", ui.progressBar);
+ ui.progressText = parseColor(cdata, "ui.progress_text", ui.progressText);
+ ui.progressBar = parseColor(cdata, "ui.progress_bar", ui.progressBar);
ui.status = parseRect(cdata, "ui.status", ui.status);
ui.statusText = parseColor(cdata, "ui.status_text", ui.statusText);
ui.textShadow = parseColor(cdata, "ui.text_shadow", ui.textShadow);
@@ -475,9 +469,8 @@ public class Application
}
/**
- * Returns a URL from which the specified path can be fetched. Our
- * application base URL is properly versioned and combined with the
- * supplied path.
+ * Returns a URL from which the specified path can be fetched. Our application base URL is
+ * properly versioned and combined with the supplied path.
*/
public URL getRemoteURL (String path)
throws MalformedURLException
@@ -494,8 +487,18 @@ public class Application
}
/**
- * Attempts to redownload the getdown.txt file based on
- * information parsed from a previous call to {@link #init}.
+ * Returns true if we either have no version requirement, are running in a JVM that meets our
+ * version requirements or have what appears to be a version of the JVM that meets our
+ * requirements.
+ */
+ public boolean haveValidJavaVersion ()
+ {
+ return true; // TODO
+ }
+
+ /**
+ * Attempts to redownload the getdown.txt file based on information parsed from a
+ * previous call to {@link #init}.
*/
public void attemptRecovery (StatusDisplay status)
throws IOException
@@ -505,9 +508,8 @@ public class Application
}
/**
- * Downloads and replaces the getdown.txt and
- * digest.txt files with those for the target version of
- * our application.
+ * Downloads and replaces the getdown.txt and digest.txt files with
+ * those for the target version of our application.
*/
public void updateMetadata ()
throws IOException
@@ -520,10 +522,9 @@ public class Application
throw (IOException) new IOException(err).initCause(mue);
}
- // now re-download our control files; we download the digest first
- // so that if it fails, our config file will still reference the
- // old version and re-running the updater will start the whole
- // process over again
+ // now re-download our control files; we download the digest first so that if it fails, our
+ // config file will still reference the old version and re-running the updater will start
+ // the whole process over again
downloadControlFile(Digest.DIGEST_FILE);
downloadControlFile(CONFIG_FILE);
}
@@ -553,11 +554,9 @@ public class Application
args.add("-classpath");
args.add(cpbuf.toString());
- // we love our Mac users, so we do nice things to preserve our
- // application identity
+ // we love our Mac users, so we do nice things to preserve our application identity
if (RunAnywhere.isMacOS()) {
- args.add("-Xdock:icon=" + _appdir.getAbsolutePath() +
- "/../desktop.icns");
+ args.add("-Xdock:icon=" + _appdir.getAbsolutePath() + "/../desktop.icns");
args.add("-Xdock:name=" + _name);
}
@@ -565,8 +564,7 @@ public class Application
String proxyHost;
if ((proxyHost = System.getProperty("http.proxyHost")) != null) {
args.add("-Dhttp.proxyHost=" + proxyHost);
- args.add("-Dhttp.proxyPort=" +
- System.getProperty("http.proxyPort"));
+ args.add("-Dhttp.proxyPort=" + System.getProperty("http.proxyPort"));
}
// pass along any pass-through arguments
@@ -607,8 +605,7 @@ public class Application
ArrayList jars = new ArrayList();
for (Resource rsrc : _codes) {
try {
- jars.add(
- new URL("file", "", rsrc.getLocal().getAbsolutePath()));
+ jars.add(new URL("file", "", rsrc.getLocal().getAbsolutePath()));
} catch (Exception e) {
e.printStackTrace(System.err);
}
@@ -631,8 +628,7 @@ public class Application
if (eqidx == -1) {
Log.warning("Bogus system property: '" + jvmarg + "'?");
} else {
- System.setProperty(jvmarg.substring(0, eqidx),
- jvmarg.substring(eqidx+1));
+ System.setProperty(jvmarg.substring(0, eqidx), jvmarg.substring(eqidx+1));
}
}
}
@@ -655,8 +651,7 @@ public class Application
Method main;
try {
// first see if the class has a special applet-aware main
- main = appclass.getMethod(
- "main", JApplet.class, SA_PROTO.getClass());
+ main = appclass.getMethod("main", JApplet.class, SA_PROTO.getClass());
main.invoke(null, new Object[] { applet, args });
} catch (NoSuchMethodException nsme) {
main = appclass.getMethod("main", SA_PROTO.getClass());
@@ -676,17 +671,16 @@ public class Application
}
/**
- * Loads the digest.txt file and verifies the contents of
- * both that file and the getdown.text file. Then it
- * loads the version.txt and decides whether or not the
- * application needs to be updated or whether we can proceed to
- * verification and execution.
+ * Loads the digest.txt file and verifies the contents of both that file and the
+ * getdown.text file. Then it loads the version.txt and decides
+ * whether or not the application needs to be updated or whether we can proceed to verification
+ * and execution.
*
- * @return true if the application needs to be updated, false if it is
- * up to date and can be verified and executed.
+ * @return true if the application needs to be updated, false if it is up to date and can be
+ * verified and executed.
*
- * @exception IOException thrown if we encounter an unrecoverable
- * error while verifying the metadata.
+ * @exception IOException thrown if we encounter an unrecoverable error while verifying the
+ * metadata.
*/
public boolean verifyMetadata (StatusDisplay status)
throws IOException
@@ -701,21 +695,18 @@ public class Application
// Log.info("JVM Args: " + StringUtil.toString(_jvmargs.iterator()));
// Log.info("App Args: " + StringUtil.toString(_appargs.iterator()));
- // create our digester which will read in the contents of the
- // digest file and validate itself
+ // this will read in the contents of the digest file and validate itself
try {
_digest = new Digest(_appdir);
} catch (IOException ioe) {
- Log.info("Failed to load digest: " + ioe.getMessage() + ". " +
- "Attempting recovery...");
+ Log.info("Failed to load digest: " + ioe.getMessage() + ". Attempting recovery...");
}
- // if we have no version, then we are running in unversioned mode
- // so we need to download our digest.txt file on every invocation
+ // if we have no version, then we are running in unversioned mode so we need to download
+ // our digest.txt file on every invocation
if (_version == -1) {
- // make a note of the old meta-digest, if this changes we need
- // to revalidate all of our resources as one or more of them
- // have also changed
+ // make a note of the old meta-digest, if this changes we need to revalidate all of our
+ // resources as one or more of them have also changed
String olddig = (_digest == null) ? "" : _digest.getMetaDigest();
try {
status.updateStatus("m.checking");
@@ -732,10 +723,9 @@ public class Application
}
}
- // regardless of whether we're versioned, if we failed to read the
- // digest from disk, try to redownload the digest file and give it
- // another good college try; this time we allow exceptions to
- // propagate up to the caller as there is nothing else we can do
+ // regardless of whether we're versioned, if we failed to read the digest from disk, try to
+ // redownload the digest file and give it another good college try; this time we allow
+ // exceptions to propagate up to the caller as there is nothing else we can do
if (_digest == null) {
status.updateStatus("m.updating_metadata");
downloadControlFile(Digest.DIGEST_FILE);
@@ -746,16 +736,14 @@ public class Application
Resource crsrc = getConfigResource();
if (!_digest.validateResource(crsrc, null)) {
status.updateStatus("m.updating_metadata");
- // attempt to redownload both of our metadata files; again we
- // pass errors up to our caller because there's nothing we can
- // do to automatically recover
+ // attempt to redownload both of our metadata files; again we pass errors up to our
+ // caller because there's nothing we can do to automatically recover
downloadControlFile(CONFIG_FILE);
downloadControlFile(Digest.DIGEST_FILE);
_digest = new Digest(_appdir);
// revalidate everything if we end up downloading new metadata
clearValidationMarkers();
- // if the new copy validates, reinitialize ourselves;
- // otherwise report baffling hoseage
+ // if the new copy validates, reinitialize ourselves; otherwise report baffling hoseage
if (_digest.validateResource(crsrc, null)) {
init(true);
} else {
@@ -767,15 +755,13 @@ public class Application
// start by assuming we are happy with our version
_targetVersion = _version;
- // if we are a versioned application, read in the contents of the
- // version.txt file
+ // if we are a versioned application, read in the contents of the version.txt file
if (_version != -1) {
File vfile = getLocalPath(VERSION_FILE);
FileInputStream fin = null;
try {
fin = new FileInputStream(vfile);
- BufferedReader bin = new BufferedReader(
- new InputStreamReader(fin));
+ BufferedReader bin = new BufferedReader(new InputStreamReader(fin));
String vstr = bin.readLine();
if (!StringUtil.isBlank(vstr)) {
_targetVersion = Long.parseLong(vstr);
@@ -792,11 +778,9 @@ public class Application
}
/**
- * Verifies the code and media resources associated with this
- * application. A list of resources that do not exist or fail the
- * verification process will be returned. If all resources are ready
- * to go, null will be returned and the application is considered
- * ready to run.
+ * Verifies the code and media resources associated with this application. A list of resources
+ * that do not exist or fail the verification process will be returned. If all resources are
+ * ready to go, null will be returned and the application is considered ready to run.
*/
public List verifyResources (ProgressObserver obs)
{
@@ -830,8 +814,8 @@ public class Application
}
} catch (Exception e) {
- Log.info("Failure validating resource [rsrc=" + rsrc +
- ", error=" + e + "]. Requesting redownload...");
+ Log.info("Failure validating resource [rsrc=" + rsrc + ", error=" + e + "]. " +
+ "Requesting redownload...");
} finally {
mpobs.progress(100);
@@ -856,12 +840,12 @@ public class Application
protected URL createVAppBase (long version)
throws MalformedURLException
{
- return new URL(
- StringUtil.replace(_appbase, "%VERSION%", "" + version));
+ return new URL(StringUtil.replace(_appbase, "%VERSION%", "" + version));
}
- /** Clears all validation marker files for the resources in the
- * supplied iterator. */
+ /**
+ * Clears all validation marker files for the resources in the supplied iterator.
+ */
protected void clearValidationMarkers (Iterator iter)
{
while (iter.hasNext()) {
@@ -870,9 +854,8 @@ public class Application
}
/**
- * Downloads a new copy of the specified control file and, if the
- * download is successful, moves it over the old file on the
- * filesystem.
+ * Downloads a new copy of the specified control file and, if the download is successful, moves
+ * it over the old file on the filesystem.
*/
protected void downloadControlFile (String path)
throws IOException
@@ -882,15 +865,12 @@ public class Application
try {
targetURL = getRemoteURL(path);
} catch (Exception e) {
- Log.warning("Requested to download invalid control file " +
- "[appbase=" + _vappbase + ", path=" + path +
- ", error=" + e + "].");
- String msg = "Invalid path '" + path + "'.";
- throw (IOException) new IOException(msg).initCause(e);
+ Log.warning("Requested to download invalid control file [appbase=" + _vappbase +
+ ", path=" + path + ", error=" + e + "].");
+ throw (IOException) new IOException("Invalid path '" + path + "'.").initCause(e);
}
- Log.info("Attempting to refetch '" + path + "' from '" +
- targetURL + "'.");
+ Log.info("Attempting to refetch '" + path + "' from '" + targetURL + "'.");
// stream the URL into our temporary file
InputStream fin = null;
@@ -904,12 +884,10 @@ public class Application
StreamUtil.close(fout);
}
- // Windows is a wonderful operating system, it won't let you
- // rename a file overtop of another one; thus to avoid running the
- // risk of getting royally fucked, we have to do this complicated
- // backup bullshit; this way if the shit hits the fan before we
- // get the new copy into place, we should be able to read from the
- // backup copy; yay!
+ // Windows is a wonderful operating system, it won't let you rename a file overtop of
+ // another one; thus to avoid running the risk of getting royally fucked, we have to do
+ // this complicated backup bullshit; this way if the shit hits the fan before we get the
+ // new copy into place, we should be able to read from the backup copy; yay!
File original = getLocalPath(path);
if (RunAnywhere.isWindows() && original.exists()) {
File backup = getLocalPath(path + "_old");
@@ -917,16 +895,14 @@ public class Application
Log.warning("Failed to delete " + backup + ".");
}
if (!original.renameTo(backup)) {
- Log.warning("Failed to move " + original + " to backup. " +
- "We will likely fail to replace it with " +
- target + ".");
+ Log.warning("Failed to move " + original + " to backup. We will likely fail " +
+ "to replace it with " + target + ".");
}
}
// now attempt to replace the current file with the new one
if (!target.renameTo(original)) {
- throw new IOException(
- "Failed to rename(" + target + ", " + original + ")");
+ throw new IOException("Failed to rename(" + target + ", " + original + ")");
}
}
@@ -934,14 +910,12 @@ public class Application
protected Resource createResource (String path, boolean unpack)
throws MalformedURLException
{
- return new Resource(
- path, getRemoteURL(path), getLocalPath(path), unpack);
+ return new Resource(path, getRemoteURL(path), getLocalPath(path), unpack);
}
/** Used to parse resources with the specfied name. */
- protected void parseResources (
- HashMap cdata, String name, boolean unpack,
- ArrayList list)
+ protected void parseResources (HashMap cdata, String name, boolean unpack,
+ ArrayList list)
{
String[] rsrcs = ConfigUtil.getMultiValue(cdata, name);
if (rsrcs == null) {
@@ -957,8 +931,7 @@ public class Application
}
/** Used to parse rectangle specifications from the config file. */
- protected Rectangle parseRect (
- HashMap cdata, String name, Rectangle def)
+ protected Rectangle parseRect (HashMap cdata, String name, Rectangle def)
{
String value = (String)cdata.get(name);
if (!StringUtil.isBlank(value)) {
@@ -966,24 +939,21 @@ public class Application
if (v != null && v.length == 4) {
return new Rectangle(v[0], v[1], v[2], v[3]);
} else {
- Log.warning("Ignoring invalid '" + name + "' config '" +
- value + "'.");
+ Log.warning("Ignoring invalid '" + name + "' config '" + value + "'.");
}
}
return def;
}
/** Used to parse color specifications from the config file. */
- protected Color parseColor (
- HashMap cdata, String name, Color def)
+ protected Color parseColor (HashMap cdata, String name, Color def)
{
String value = (String)cdata.get(name);
if (!StringUtil.isBlank(value)) {
try {
return new Color(Integer.parseInt(value, 16));
} catch (Exception e) {
- Log.warning("Ignoring invalid '" + name + "' config '" +
- value + "'.");
+ Log.warning("Ignoring invalid '" + name + "' config '" + value + "'.");
}
}
return def;
@@ -993,8 +963,7 @@ public class Application
protected String[] parseList (HashMap cdata, String name)
{
String value = (String)cdata.get(name);
- return (value == null) ? new String[0] :
- StringUtil.parseStringArray(value);
+ return (value == null) ? new String[0] : StringUtil.parseStringArray(value);
}
protected File _appdir;
@@ -1011,14 +980,16 @@ public class Application
protected boolean _windebug;
protected boolean _useTorrent = false;
+ protected int _javaVersion;
+ protected String _javaLocation;
+
protected ArrayList _codes = new ArrayList();
protected ArrayList _resources = new ArrayList();
protected ArrayList _auxgroups = new ArrayList();
protected HashMap> _auxrsrcs =
new HashMap>();
- protected HashMap _auxactive =
- new HashMap();
+ protected HashMap _auxactive = new HashMap();
protected ArrayList _jvmargs = new ArrayList();
protected ArrayList _appargs = new ArrayList();
diff --git a/src/java/com/threerings/getdown/launcher/Getdown.java b/src/java/com/threerings/getdown/launcher/Getdown.java
index 722c754..161a3d6 100644
--- a/src/java/com/threerings/getdown/launcher/Getdown.java
+++ b/src/java/com/threerings/getdown/launcher/Getdown.java
@@ -68,8 +68,7 @@ import com.threerings.getdown.util.LaunchUtil;
import com.threerings.getdown.util.ProgressObserver;
/**
- * 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
implements Application.StatusDisplay
@@ -86,16 +85,14 @@ public abstract class Getdown extends Thread
try {
_msgs = ResourceBundle.getBundle("com.threerings.getdown.messages");
} catch (Exception e) {
- // welcome to hell, where java can't cope with a classpath
- // that contains jars that live in a directory that contains a
- // !, at least the same bug happens on all platforms
+ // welcome to hell, where java can't cope with a classpath that contains jars that live
+ // in a directory that contains a !, at least the same bug happens on all platforms
String dir = appDir.toString();
if (dir.equals(".")) {
dir = System.getProperty("user.dir");
}
- String errmsg = "The directory in which this application is " +
- "installed:\n" + dir + "\nis invalid. The directory " +
- "must not contain the '!' character. Please reinstall.";
+ String errmsg = "The directory in which this application is installed:\n" + dir +
+ "\nis invalid. The directory must not contain the '!' character. Please reinstall.";
updateStatus(errmsg);
_dead = true;
}
@@ -104,8 +101,8 @@ 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.
+ * This is used by the applet which always needs a user interface and wants to load it as soon
+ * as possible.
*/
public void preInit ()
{
@@ -120,8 +117,8 @@ public abstract class Getdown extends Thread
public void run ()
{
- // if we have no messages, just bail because we're hosed; the
- // error message will be displayed to the user already
+ // if we have no messages, just bail because we're hosed; the error message will be
+ // displayed to the user already
if (_msgs == null) {
return;
}
@@ -145,11 +142,9 @@ public abstract class Getdown extends Thread
} else {
// create a panel they can use to configure the proxy settings
_container = createContainer();
- _container.add(
- new ProxyPanel(this, _msgs), BorderLayout.CENTER);
+ _container.add(new ProxyPanel(this, _msgs), BorderLayout.CENTER);
showContainer();
- // allow them to close the window to abort the proxy
- // configuration
+ // allow them to close the window to abort the proxy configuration
_dead = true;
}
@@ -161,11 +156,11 @@ public abstract class Getdown extends Thread
} else if (!msg.startsWith("m.")) {
// try to do something sensible based on the type of error
if (e instanceof FileNotFoundException) {
- msg = MessageUtil.compose("m.missing_resource",
- MessageUtil.taint(msg), _ifc.installError);
+ msg = MessageUtil.compose(
+ "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
} else {
- msg = MessageUtil.compose("m.init_error",
- MessageUtil.taint(msg), _ifc.installError);
+ msg = MessageUtil.compose(
+ "m.init_error", MessageUtil.taint(msg), _ifc.installError);
}
}
updateStatus(msg);
@@ -174,13 +169,11 @@ public abstract class Getdown extends Thread
}
/**
- * Configures our proxy settings (called by {@link ProxyPanel}) and
- * fires up the launcher.
+ * Configures our proxy settings (called by {@link ProxyPanel}) and fires up the launcher.
*/
public void configureProxy (String host, String port)
{
- Log.info("User configured proxy [host=" + host +
- ", port=" + port + "].");
+ 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)) {
@@ -193,8 +186,7 @@ public abstract class Getdown extends Thread
}
pout.close();
} catch (IOException ioe) {
- Log.warning("Error creating proxy file '" + pfile +
- "': " + ioe);
+ Log.warning("Error creating proxy file '" + pfile + "': " + ioe);
}
// also configure them in the JVM
@@ -212,8 +204,8 @@ public abstract class Getdown extends Thread
/**
* 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.
+ * @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 ()
{
@@ -228,8 +220,7 @@ public abstract class Getdown extends Thread
String host = null, port = null;
boolean enabled = false;
RegistryKey.initialize();
- RegistryKey r = new RegistryKey(
- RootKey.HKEY_CURRENT_USER, PROXY_REGISTRY);
+ 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")) {
@@ -254,8 +245,7 @@ public abstract class Getdown extends Thread
}
} catch (Throwable t) {
- Log.info("Failed to find proxy settings in Windows registry " +
- "[error=" + t + "].");
+ Log.info("Failed to find proxy settings in Windows registry [error=" + t + "].");
}
}
@@ -264,18 +254,15 @@ public abstract class Getdown extends Thread
if (pfile.exists()) {
try {
HashMap pconf = ConfigUtil.parseConfig(pfile, false);
- setProxyProperties((String)pconf.get("host"),
- (String)pconf.get("port"));
+ setProxyProperties((String)pconf.get("host"), (String)pconf.get("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
+ // 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...");
try {
_ifc = _app.init(true);
@@ -287,26 +274,23 @@ public abstract class Getdown extends Thread
URL rurl = _app.getConfigResource().getRemote();
try {
// try to make a HEAD request for this URL
- HttpURLConnection ucon = (HttpURLConnection)
- rurl.openConnection();
+ HttpURLConnection ucon = (HttpURLConnection)rurl.openConnection();
ucon.setRequestMethod("HEAD");
ucon.connect();
// make sure we got a satisfactory response code
if (ucon.getResponseCode() != HttpURLConnection.HTTP_OK) {
- Log.warning("Got a non-200 response but assuming we're OK " +
- "because we got something... [url=" + rurl +
- ", rsp=" + ucon.getResponseCode() + "].");
+ Log.warning("Got a non-200 response but assuming we're OK because we got " +
+ "something... [url=" + rurl + ", rsp=" + ucon.getResponseCode() + "].");
}
- // we got through, so we appear not to require a proxy; make a
- // blank proxy config and get on gettin' down
+ // 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);
+ Log.warning("Failed to create blank proxy file '" + pfile + "': " + ioe);
}
return true;
@@ -334,8 +318,7 @@ public abstract class Getdown extends Thread
}
/**
- * Does the actual application validation, update and launching
- * business.
+ * Does the actual application validation, update and launching business.
*/
protected void getdown ()
{
@@ -357,9 +340,11 @@ public abstract class Getdown extends Thread
createInterface(true);
}
+ // if we aren't running in a JVM that meets our version requirements, either complain
+ // or attempt to download and install the appropriate version
+
for (int ii = 0; ii < MAX_LOOPS; ii++) {
- // make sure we have the desired version and that the
- // metadata files are valid...
+ // make sure we have the desired version and that the metadata files are valid...
setStatus("m.validating", -1, -1L, false);
if (_app.verifyMetadata(this)) {
Log.info("Application requires update.");
@@ -394,11 +379,11 @@ public abstract class Getdown extends Thread
} else if (!msg.startsWith("m.")) {
// try to do something sensible based on the type of error
if (e instanceof FileNotFoundException) {
- msg = MessageUtil.compose("m.missing_resource",
- MessageUtil.taint(msg), _ifc.installError);
+ msg = MessageUtil.compose(
+ "m.missing_resource", MessageUtil.taint(msg), _ifc.installError);
} else {
- msg = MessageUtil.compose("m.init_error", msg,
- MessageUtil.taint(msg), _ifc.installError);
+ msg = MessageUtil.compose(
+ "m.init_error", msg, MessageUtil.taint(msg), _ifc.installError);
}
}
updateStatus(msg);
@@ -445,8 +430,7 @@ public abstract class Getdown extends Thread
for (Resource prsrc : list) {
try {
Patcher patcher = new Patcher();
- patcher.patch(prsrc.getLocal().getParentFile(),
- prsrc.getLocal(), _progobs);
+ patcher.patch(prsrc.getLocal().getParentFile(), prsrc.getLocal(), _progobs);
} catch (Exception e) {
Log.warning("Failed to apply patch [prsrc=" + prsrc + "].");
Log.logStackTrace(e);
@@ -460,9 +444,9 @@ public abstract class Getdown extends Thread
}
}
- // if the patch resource is null, that means something was booched
- // in the application, so we skip the patching process but update
- // the metadata which will result in a "brute force" upgrade
+ // if the patch resource is null, that means something was booched in the application, so
+ // we skip the patching process but update the metadata which will result in a "brute
+ // force" upgrade
// finally update our metadata files...
_app.updateMetadata();
@@ -471,8 +455,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 (List resources)
{
@@ -510,23 +493,19 @@ public abstract class Getdown extends Thread
// assume we're going to use an HTTP downloader
Downloader dl = new HTTPDownloader(resources, obs);
- // if torrent downloading is enabled and we are downloading the right
- // set of resources (a single patch file or the entire app from
- // scratch), then use a torrent downloader instead.
- // Because many of our installers also bundle background.png,
- // and might bundle more required files, we need to allow a
- // 'fudge factor' threshhold for determining at which point it is
- // faster to torrent, and at which point we should use HTTP.
+ // if torrent downloading is enabled and we are downloading the right set of resources (a
+ // single patch file or the entire app from scratch), then use a torrent downloader
+ // instead. Because many of our installers also bundle background.png, and might bundle
+ // more required files, we need to allow a 'fudge factor' threshhold for determining at
+ // which point it is faster to torrent, and at which point we should use HTTP.
if (_app.getUseTorrent()) {
- int verifiedResources = _app.getAllResources().size() -
- resources.size();
+ int verifiedResources = _app.getAllResources().size() - resources.size();
if (verifiedResources <= MAX_TORRENT_VERIFIED_RESOURCES) {
ArrayList full = new ArrayList();
full.add(_app.getFullResource());
full.addAll(resources);
dl = new TorrentDownloader(full, obs);
- } else if (resources.size() == 1 &&
- resources.get(0).getPath().startsWith("patch")) {
+ } else if (resources.size() == 1 && resources.get(0).getPath().startsWith("patch")) {
dl = new TorrentDownloader(resources, obs);
}
}
@@ -543,8 +522,7 @@ public abstract class Getdown extends Thread
}
/**
- * Called to launch the application if everything is determined to be
- * ready to go.
+ * Called to launch the application if everything is determined to be ready to go.
*/
protected void launch ()
{
@@ -557,28 +535,27 @@ public abstract class Getdown extends Thread
} else {
Process proc = _app.createProcess();
- // on Windows 98 and ME we need to stick around and read the
- // output of stderr lest the process fill its output buffer and
- // choke, yay!
+ // on Windows 98 and ME we need to stick around and read the output of stderr lest
+ // the process fill its output buffer and choke, yay!
final InputStream stderr = proc.getErrorStream();
if (LaunchUtil.mustMonitorChildren()) {
// close our window if it's around
disposeContainer();
_status = null;
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(stderr));
+ BufferedReader reader = new BufferedReader(new InputStreamReader(stderr));
while (reader.readLine() != null) {
// nothing doing!
}
Log.info("Process exited: " + proc.waitFor());
+
} else {
- // spawn a daemon thread that will catch the early bits of
- // stderr in case the launch fails
+ // spawn a daemon thread that will catch the early bits of stderr in case the
+ // launch fails
Thread t = new Thread() {
public void run () {
try {
- BufferedReader reader = new BufferedReader(
- new InputStreamReader(stderr));
+ BufferedReader reader =
+ new BufferedReader(new InputStreamReader(stderr));
String line;
while ((line = reader.readLine()) != null) {
Log.warning(line);
@@ -593,9 +570,9 @@ public abstract class Getdown extends Thread
}
}
- // if we have a UI open and we haven't been around for at least 5
- // seconds, don't stick a fork in ourselves straight away but give
- // our lovely user a chance to see what we're doing
+ // if we have a UI open and we haven't been around for at least 5 seconds, don't stick
+ // a fork in ourselves straight away but give our lovely user a chance to see what
+ // we're doing
long uptime = System.currentTimeMillis() - _startup;
if (_container != null && uptime < MIN_EXIST_TIME) {
try {
@@ -611,8 +588,8 @@ public abstract class Getdown extends Thread
}
/**
- * Creates our user interface, which we avoid doing unless we actually
- * have to update something.
+ * Creates our user interface, which we avoid doing unless we actually have to update
+ * something.
*/
protected void createInterface (boolean force)
{
@@ -627,8 +604,7 @@ public abstract class Getdown extends Thread
_status = new StatusPanel(_msgs);
_container.add(_status, BorderLayout.CENTER);
}
- _status.init(_ifc, getBackgroundImage(),
- getProgressImage());
+ _status.init(_ifc, getBackgroundImage(), getProgressImage());
showContainer();
}
});
@@ -660,8 +636,8 @@ public abstract class Getdown extends Thread
}
}
- protected void setStatus (final String message, final int percent,
- final long remaining, boolean createUI)
+ protected void setStatus (
+ final String message, final int percent, final long remaining, boolean createUI)
{
if (_status == null && createUI) {
createInterface(false);
@@ -712,8 +688,7 @@ public abstract class Getdown extends Thread
imgpath = _app.getLocalPath(path);
return ImageIO.read(imgpath);
} catch (IOException ioe2) {
- Log.warning("Failed to load image [path=" + imgpath +
- ", error=" + ioe2 + "].");
+ Log.warning("Failed to load image [path=" + imgpath + ", error=" + ioe2 + "].");
return null;
}
}
@@ -734,9 +709,8 @@ public abstract class Getdown extends Thread
protected abstract void disposeContainer ();
/**
- * If this method returns true we will run the application in the same JVM,
- * otherwise we will fork off a new JVM. Some options are not supported if
- * we do not fork off a new JVM.
+ * If this method returns true we will run the application in the same JVM, otherwise we will
+ * fork off a new JVM. Some options are not supported if we do not fork off a new JVM.
*/
protected boolean invokeDirect ()
{
@@ -744,8 +718,8 @@ public abstract class Getdown extends Thread
}
/**
- * Provides access to the applet that we'll pass on to our application when
- * we're in "invoke direct" mode.
+ * Provides access to the applet that we'll pass on to our application when we're in "invoke
+ * direct" mode.
*/
protected JApplet getApplet ()
{
@@ -765,8 +739,7 @@ public abstract class Getdown extends Thread
};
protected Application _app;
- protected Application.UpdateInterface _ifc =
- new Application.UpdateInterface();
+ protected Application.UpdateInterface _ifc = new Application.UpdateInterface();
protected ResourceBundle _msgs;
protected Container _container;
@@ -776,10 +749,7 @@ public abstract class Getdown extends Thread
protected boolean _dead;
protected long _startup;
- /**
- * The maximum number of resources that can be already present for
- * bittorrent to be used.
- */
+ /** The maximum number of resources that can be already present for bittorrent to be used. */
protected static final int MAX_TORRENT_VERIFIED_RESOURCES = 1;
protected static final int MAX_LOOPS = 5;
diff --git a/src/java/com/threerings/getdown/launcher/GetdownApplet.java b/src/java/com/threerings/getdown/launcher/GetdownApplet.java
index 8b20381..c5f780d 100644
--- a/src/java/com/threerings/getdown/launcher/GetdownApplet.java
+++ b/src/java/com/threerings/getdown/launcher/GetdownApplet.java
@@ -54,23 +54,8 @@ public class GetdownApplet extends JApplet
@Override // documentation inherited
public void init ()
{
- // Getdown absolutely 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.checkWrite("getdown");
- sm.checkPropertiesAccess();
- } catch (SecurityException se) {
- Log.warning("Signed applet rejected by user [se=" + se + "].");
- _permissioned = false;
- }
- }
-
- // First off, verify that we are not being hijacked to execute
- // malicious code in the name of the signer.
+ // verify that we are not being hijacked to execute malicious code in the name of the
+ // signer
String appbase = getParameter("appbase");
String appname = getParameter("appname");
String imgpath = getParameter("bgimage");
@@ -83,127 +68,39 @@ public class GetdownApplet extends JApplet
if (imgpath == null) {
imgpath = "";
}
- String params = appbase + appname + imgpath;
- String signature = getParameter("signature");
- if (signature == null) {
- signature = "";
- }
- Object[] signers = GetdownApplet.class.getSigners();
- if (signers.length == 0) {
- _safe = true;
- }
- for (Object signer : signers) {
- if (!_safe && signer instanceof Certificate) {
- Certificate cert = (Certificate)signer;
- try {
- Signature sig = Signature.getInstance("SHA1withRSA");
- sig.initVerify(cert);
- sig.update(params.getBytes());
- if (sig.verify(Base64.decodeBase64(
- signature.getBytes()))) {
- _safe = true;
- }
- } catch (GeneralSecurityException gse) {
- // ignore the error - the default is to not launch.
- }
- }
- }
-
- if (!_safe) {
- Log.warning("Signed getdown invoked on unsigned application; " +
- "aborting installation.");
- }
-
- // Pass through properties parameter.
- String properties = getParameter("properties");
- if (properties != null && _permissioned) {
- String[] proparray = properties.split(" ");
- for (String property : proparray) {
- String key = property.substring(property.indexOf("-D") + 2,
- property.indexOf("="));
- String value = property.substring(property.indexOf("=") + 1);
- System.setProperty(key, value);
- }
- }
-
- // when run from an applet, we install
- String root;
- if (RunAnywhere.isWindows()) {
- root = "Application Data";
- } else if (RunAnywhere.isMacOS()) {
- root = "Library" + File.separator + "Application Support";
- } else /* isLinux() or something wacky */ {
- root = ".getdown";
- }
-
- String appdir = root + File.separator + appname;
- if (_permissioned) {
- appdir = System.getProperty("user.home") + File.separator + appdir;
- }
-
- // if our application directory does not exist, auto-create it
- File appDir = new File(appdir);
- if (_permissioned && (!appDir.exists() || !appDir.isDirectory())) {
- if (!appDir.mkdirs()) {
- Log.warning("Unable to create app_dir '" + appdir + "'.");
- // TODO: report error
- return;
- }
- }
-
- // if an installer.txt file is desired, create that
- String inststr = getParameter("installer");
- if (_permissioned && !StringUtil.isBlank(inststr)) {
- File infile = new File(appDir, "installer.txt");
- if (!infile.exists()) {
- writeToFile(infile, inststr);
- }
- }
-
- // if our getdown.txt file does not exist, auto-create it
- if (_permissioned) {
- File gdfile = new File(appDir, "getdown.txt");
- if (!gdfile.exists()) {
- if (StringUtil.isBlank(appbase)) {
- Log.warning("Missing 'appbase' cannot auto-create " +
- "application directory.");
- // TODO: report
- return;
- }
- if (!writeToFile(gdfile, "appbase = " + appbase)) {
- // TODO: report the error
- return;
- }
- }
- }
-
- // if a background image was specified, grabbit
+ File appdir = null;
try {
- if (!StringUtil.isBlank(imgpath)) {
- _bgimage = getImage(new URL(getDocumentBase(), imgpath));
- }
- } catch (Exception e) {
- Log.info("Failed to load background image [path=" + imgpath + "].");
- Log.logStackTrace(e);
- }
+ appdir = initGetdown(appbase, appname, imgpath);
- // 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"));
- if (_permissioned) {
+ // if a background image was specified, grabbit
+ try {
+ if (!StringUtil.isBlank(imgpath)) {
+ _bgimage = getImage(new URL(getDocumentBase(), imgpath));
+ }
+ } catch (Exception e) {
+ Log.info("Failed to load background image [path=" + imgpath + "].");
+ Log.logStackTrace(e);
+ }
+
+ // 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("---------------------------------------------");
+
+ } catch (Exception e) {
+ _errmsg = e.getMessage();
}
- Log.info("---------------------------------------------");
try {
- _getdown = new Getdown(appDir, null) {
+ _getdown = new Getdown(appdir, null) {
protected Container createContainer () {
getContentPane().removeAll();
return getContentPane();
@@ -227,14 +124,13 @@ public class GetdownApplet extends JApplet
protected void exit (int exitCode) {
// don't exit as we're in an applet
}
- @Override // documentation inherited
- protected void setStatus (final String message, final int percent,
- final long remaining, boolean createUI)
+ @Override protected void setStatus (
+ final String message, final int percent, final long remaining, boolean createUI)
{
super.setStatus(message, percent, remaining, createUI);
try {
- JSObject.getWindow(GetdownApplet.this).call("getdownStatus",
- new Object[] {message, percent, remaining});
+ JSObject.getWindow(GetdownApplet.this).call(
+ "getdownStatus", new Object[] { message, percent, remaining });
} catch (JSException jse) {
// don't sweat it.
}
@@ -252,18 +148,14 @@ public class GetdownApplet extends JApplet
@Override // documentation inherited
public void start ()
{
- if (!_safe) {
- _getdown.updateStatus("m.corrupt_param_signature_error");
- return;
- }
- if (!_permissioned) {
- _getdown.updateStatus("m.insufficient_permissions_error");
- return;
- }
- try {
- _getdown.start();
- } catch (Exception e) {
- Log.logStackTrace(e);
+ if (_errmsg != null) {
+ _getdown.updateStatus(_errmsg);
+ } else {
+ try {
+ _getdown.start();
+ } catch (Exception e) {
+ Log.logStackTrace(e);
+ }
}
}
@@ -273,6 +165,110 @@ public class GetdownApplet extends JApplet
// TODO
}
+ /**
+ * Does all the fiddly initialization of Getdown and throws an exception if something goes
+ * horribly wrong. If an exception is thrown we will abort the whole process and display an
+ * error message to the user.
+ */
+ protected File initGetdown (String appbase, String appname, String imgpath)
+ 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.checkWrite("getdown");
+ sm.checkPropertiesAccess();
+ } catch (SecurityException se) {
+ Log.warning("Signed applet rejected by user [se=" + se + "].");
+ throw new Exception("m.insufficient_permissions_error");
+ }
+ }
+
+
+ Object[] signers = GetdownApplet.class.getSigners();
+ if (signers.length == 0) {
+ Log.info("No signers, not verifying param signature.");
+
+ } else {
+ String signature = getParameter("signature");
+ if (signature == null) {
+ signature = "";
+ }
+ String params = appbase + appname + imgpath;
+ for (Object signer : signers) {
+ if (signer instanceof Certificate) {
+ Certificate cert = (Certificate)signer;
+ try {
+ Signature sig = Signature.getInstance("SHA1withRSA");
+ sig.initVerify(cert);
+ sig.update(params.getBytes());
+ if (!sig.verify(Base64.decodeBase64(signature.getBytes()))) {
+ throw new Exception("m.corrupt_param_signature_error");
+ }
+ } catch (GeneralSecurityException gse) {
+ throw new Exception("m.corrupt_param_signature_error");
+ }
+ }
+ }
+ }
+
+ // pass through properties parameters
+ String properties = getParameter("properties");
+ if (properties != null) {
+ String[] proparray = properties.split(" ");
+ for (String property : proparray) {
+ String key = property.substring(property.indexOf("-D") + 2, property.indexOf("="));
+ String value = property.substring(property.indexOf("=") + 1);
+ System.setProperty(key, value);
+ }
+ }
+
+ // when run from an applet, we install to the user's home directory
+ String root;
+ if (RunAnywhere.isWindows()) {
+ root = "Application Data";
+ } else if (RunAnywhere.isMacOS()) {
+ root = "Library" + File.separator + "Application Support";
+ } else /* isLinux() or something wacky */ {
+ root = ".getdown";
+ }
+ File appdir = new File(System.getProperty("user.home") + File.separator + root +
+ File.separator + appname);
+
+ // if our application directory does not exist, auto-create it
+ if (!appdir.exists() || !appdir.isDirectory()) {
+ if (!appdir.mkdirs()) {
+ throw new Exception("m.create_appdir_failed");
+ }
+ }
+
+ // if an installer.txt file is desired, create that
+ String inststr = getParameter("installer");
+ if (!StringUtil.isBlank(inststr)) {
+ File infile = new File(appdir, "installer.txt");
+ if (!infile.exists()) {
+ writeToFile(infile, inststr);
+ }
+ }
+
+ // if our getdown.txt file does not exist, auto-create it
+ File gdfile = new File(appdir, "getdown.txt");
+ if (!gdfile.exists()) {
+ if (StringUtil.isBlank(appbase)) {
+ throw new Exception("m.missing_appbase");
+ }
+ if (!writeToFile(gdfile, "appbase = " + appbase)) {
+ throw new Exception("m.create_getdown_failed");
+ }
+ }
+
+ return appdir;
+ }
+
/**
* Creates the specified file and writes the supplied contents to it.
*/
@@ -290,15 +286,12 @@ public class GetdownApplet extends JApplet
}
}
+ /** Handles all the actual getting down. */
protected Getdown _getdown;
+
+ /** A background image drawn to make things look purdy. */
protected Image _bgimage;
- /**
- * Getdown will refuse to initialize if the jar is signed but the
- * parameters are not validated to prevent malicious code from being run.
- */
- protected boolean _safe = false;
-
- /** Whether Getdown has been trusted by the user with system access */
- protected boolean _permissioned = true;
+ /** An error encountered during initialization. */
+ protected String _errmsg;
}
diff --git a/src/java/com/threerings/getdown/launcher/StatusPanel.java b/src/java/com/threerings/getdown/launcher/StatusPanel.java
index 4c70906..1dd8bc9 100644
--- a/src/java/com/threerings/getdown/launcher/StatusPanel.java
+++ b/src/java/com/threerings/getdown/launcher/StatusPanel.java
@@ -118,7 +118,7 @@ public class StatusPanel extends JComponent
{
status = xlate(status);
_newlab = new Label(status, _ifc.statusText, _font);
- _newlab.setTargetWidth(_ifc.status.width);
+ _newlab.setTargetWidth(Math.min(_ifc.status.width, getWidth() - _ifc.status.x*2));
if (_ifc.textShadow != null) {
_newlab.setAlternateColor(_ifc.textShadow);
_newlab.setStyle(Label.SHADOW);
diff --git a/src/java/com/threerings/getdown/messages.properties b/src/java/com/threerings/getdown/messages.properties
index 1273c1e..5c5bcd6 100644
--- a/src/java/com/threerings/getdown/messages.properties
+++ b/src/java/com/threerings/getdown/messages.properties
@@ -64,14 +64,20 @@ m.readonly_error = The directory in which this application is installed: \
\n{0}\nis read-only. Please install the applicaton into a directory where \
you have write access.
-m.missing_resource = The application has failed to launch due to a \
- missing resource:\n{0}\n\nPlease visit\n{1} for information on how to handle such problems.
+m.missing_resource = The application has failed to launch due to a missing \
+ resource:\n{0}\n\nPlease visit\n{1} for information on how to handle such \
+ problems.
-m.insufficient_permissions_error = \n\n\n\n\n\n\nYou did not accept the application's digital signature. \
- \nPlease quit and re-open your web browser and attempt to \nre-launch the application, \
- making sure that you accept \nthe application's digital signature.
-m.corrupt_param_signature_error = We couldn't verify the application's digital signature.\n\
- Please check that you are launching the application from \nthe application's website.
+m.insufficient_permissions_error = You did not accept this application's \
+ digital signature. If you want to run the application, you will need to accept \
+ its digital signature.\n\nTo do so, you will need to quit your web browser, \
+ restart it, and return to this web page to relaunch the application. When the \
+ security dialog is shown, click the button to accept the digital signature \
+ and grant this application the privileges it needs to run.
+
+m.corrupt_param_signature_error = We couldn't verify the application's digital \
+ signature.\nPlease check that you are launching the application from\nthe \
+ correct website.
m.default_install_error = the support section of the website