diff --git a/src/java/org/lwjgl/util/applet/AppletLoader.java b/src/java/org/lwjgl/util/applet/AppletLoader.java index c8d229ea..483d2c9a 100644 --- a/src/java/org/lwjgl/util/applet/AppletLoader.java +++ b/src/java/org/lwjgl/util/applet/AppletLoader.java @@ -41,6 +41,7 @@ import java.awt.Graphics; import java.awt.Image; import java.awt.MediaTracker; import java.awt.image.ImageObserver; +import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; @@ -77,7 +78,11 @@ import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Pack200; +import java.util.zip.CRC32; +import java.util.zip.CheckedInputStream; import java.util.zip.GZIPInputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import sun.security.util.SecurityConstants; @@ -141,6 +146,7 @@ import sun.security.util.SecurityConstants; *
  • Matthias Mann
  • *
  • Mickelukas
  • *
  • NateS
  • + *
  • Riven
  • *
  • Ruben01
  • *
  • Shannon Smith
  • *
  • pjohnsen
  • @@ -163,21 +169,24 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { /** extracting packages */ public static final int STATE_EXTRACTING_PACKAGES = 5; + + /** validating packages */ + public static final int STATE_VALIDATING_PACKAGES = 6; /** updating the classpath */ - public static final int STATE_UPDATING_CLASSPATH = 6; + public static final int STATE_UPDATING_CLASSPATH = 7; /** switching to real applet */ - public static final int STATE_SWITCHING_APPLET = 7; + public static final int STATE_SWITCHING_APPLET = 8; /** initializing real applet */ - public static final int STATE_INITIALIZE_REAL_APPLET = 8; + public static final int STATE_INITIALIZE_REAL_APPLET = 9; /** stating real applet */ - public static final int STATE_START_REAL_APPLET = 9; + public static final int STATE_START_REAL_APPLET = 10; /** done */ - public static final int STATE_DONE = 10; + public static final int STATE_DONE = 11; /** used to calculate length of progress bar */ protected int percentage; @@ -261,13 +270,13 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { protected boolean pack200Supported; /** whether to run in headless mode */ - protected boolean headless = false; + protected boolean headless = false; /** whether to switch applets in headless mode or wait longer */ - protected boolean headlessWaiting = true; + protected boolean headlessWaiting = true; /** messages to be passed via liveconnect in headless mode */ - protected String[] headlessMessage; + protected String[] headlessMessage; /** generic error message to display on error */ protected String[] genericErrorMessage = { "An error occured while loading the applet.", @@ -632,6 +641,8 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { return "Downloading packages"; case STATE_EXTRACTING_PACKAGES: return "Extracting downloaded packages"; + case STATE_VALIDATING_PACKAGES: + return "Validating packages"; case STATE_UPDATING_CLASSPATH: return "Updating classpath"; case STATE_SWITCHING_APPLET: @@ -827,7 +838,10 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { extractJars(path); // 55-65% // Extracts Native Files - extractNatives(path); // 65-85% + extractNatives(path); // 65-80% + + // Validate Jars // 80-90% + validateJars(path); // save version information once jars downloaded successfully if (version != null) { @@ -1519,7 +1533,7 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { setState(STATE_EXTRACTING_PACKAGES); - float percentageParts = 20f/nativeJarCount; // parts for each native jar from 20% + float percentageParts = 15f/nativeJarCount; // parts for each native jar from 15% // create native folder File nativeFolder = new File(path + "natives"); @@ -1649,6 +1663,87 @@ public class AppletLoader extends Applet implements Runnable, AppletStub { } } } + + /** + * Check and validate jar which will be loaded into the classloader to make + * sure that they are not corrupt. This will ensure that cached files are + * never marked as being successfully downloaded if they are corrupt. + * + * @param path - where the jars are stored + * @throws Exception if a corrupt jar is found + */ + protected void validateJars(String path) throws Exception { + + setState(STATE_VALIDATING_PACKAGES); + + percentage = 80; + + float percentageParts = 10f / urlList.length; // percentage for each file out of 10% + + for (int i = 0; i < urlList.length - nativeJarCount; i++) { + + debug_sleep(1000); + + // if file not downloaded, no need to validate again + if (fileSizes[i] == -2) continue; + + subtaskMessage = "Validating: " + getJarName(urlList[i]); + + File file = new File(path, getJarName(urlList[i])); + if (!isZipValid(file)) { + throw new Exception("The file " + getJarName(urlList[i]) + " is corrupt!"); + } + + percentage = 80 + (int)(percentageParts * i); + } + + subtaskMessage = ""; + } + + /** + * This method will check if a zip file is valid by running through it + * and checking for any corruption and CRC failures + * + * @param file - zip file to test + * @return boolean - runs false if the file is corrupt + */ + protected boolean isZipValid(File file) { + + try { + ZipFile zipFile = new ZipFile(file); + + try { + Enumeration e = zipFile.entries(); + + byte[] buffer = new byte[4096]; + + while(e.hasMoreElements()) { + ZipEntry zipEntry = (ZipEntry) e.nextElement(); + + CRC32 crc = new CRC32(); + + BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(zipEntry)); + CheckedInputStream cis = new CheckedInputStream(bis, crc); + + while(cis.read(buffer, 0, buffer.length) != -1) { + // scroll through zip entry + } + + if (crc.getValue() != zipEntry.getCrc()) { + System.out.println("CRC " + crc.getValue() + " " + zipEntry.getCrc()); + return false; // CRC match failed, corrupt zip + } + } + + return true; // valid zip file + } finally { + zipFile.close(); + } + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } /** * Get Image from path provided