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