/* * Decompiled with CFR 0.152. */ package net.minecraft; import SevenZip.LzmaAlone; import java.applet.Applet; import java.io.File; import java.io.FileOutputStream; import java.io.FilePermission; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.net.HttpURLConnection; import java.net.JarURLConnection; import java.net.SocketPermission; import java.net.URL; import java.net.URLClassLoader; import java.net.URLConnection; import java.security.AccessControlException; import java.security.AccessController; import java.security.CodeSource; import java.security.PermissionCollection; import java.security.PrivilegedExceptionAction; import java.security.SecureClassLoader; import java.security.cert.Certificate; import java.util.Enumeration; import java.util.Vector; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarOutputStream; import java.util.jar.Pack200; import net.minecraft.Launcher; import net.minecraft.MinecraftUtil; public class GameUpdater implements Runnable { public static final int STATE_INIT = 1; public static final int STATE_DETERMINING_PACKAGES = 2; public static final int STATE_CHECKING_CACHE = 3; public static final int STATE_DOWNLOADING = 4; public static final int STATE_EXTRACTING_PACKAGES = 5; public static final int STATE_UPDATING_CLASSPATH = 6; public static final int STATE_SWITCHING_APPLET = 7; public static final int STATE_INITIALIZE_REAL_APPLET = 8; public static final int STATE_START_REAL_APPLET = 9; public static final int STATE_DONE = 10; public int percentage; public int currentSizeDownload; public int totalSizeDownload; public int currentSizeExtract; public int totalSizeExtract; protected URL[] urlList; private static ClassLoader classLoader; protected Thread loaderThread; protected Thread animationThread; public boolean fatalError; public String fatalErrorDescription; protected String subtaskMessage = ""; protected int state = 1; protected boolean lzmaSupported = false; protected boolean pack200Supported = false; protected String[] genericErrorMessage = new String[]{"An error occured while loading the applet.", "Please contact support to resolve this issue.", ""}; protected boolean certificateRefused; protected String[] certificateRefusedMessage = new String[]{"Permissions for Applet Refused.", "Please accept the permissions dialog to allow", "the applet to continue the loading process."}; protected static boolean natives_loaded; public boolean forceUpdate = false; public static final String[] gameFiles; InputStream[] isp; URLConnection urlconnectionp; public void init() { this.state = 1; try { Class.forName("LZMA.LzmaInputStream"); this.lzmaSupported = true; } catch (Throwable localThrowable) { // empty catch block } try { Pack200.class.getSimpleName(); this.pack200Supported = true; } catch (Throwable throwable) { // empty catch block } } private String generateStacktrace(Exception exception) { StringWriter result = new StringWriter(); PrintWriter printWriter = new PrintWriter(result); exception.printStackTrace(printWriter); return ((Object)result).toString(); } protected String getDescriptionForState() { switch (this.state) { case 1: { return "Initializing loader"; } case 2: { return "Determining packages to load"; } case 3: { return "Checking cache for existing files"; } case 4: { return "Downloading packages"; } case 5: { return "Extracting downloaded packages"; } case 6: { return "Updating classpath"; } case 7: { return "Switching applet"; } case 8: { return "Initializing real applet"; } case 9: { return "Starting real applet"; } case 10: { return "Done loading"; } } return "unknown state"; } protected void loadJarURLs() throws Exception { this.state = 2; this.urlList = new URL[gameFiles.length + 1]; URL path = new URL("http://s3.amazonaws.com/MinecraftDownload/"); for (int i = 0; i < gameFiles.length; ++i) { this.urlList[i] = new URL(path, gameFiles[i]); } String osName = System.getProperty("os.name"); String nativeJar = null; if (osName.startsWith("Win")) { nativeJar = "windows_natives.jar.lzma"; } else if (osName.startsWith("Linux")) { nativeJar = "linux_natives.jar.lzma"; } else if (osName.startsWith("Mac")) { nativeJar = "macosx_natives.jar.lzma"; } else if (osName.startsWith("Solaris") || osName.startsWith("SunOS")) { nativeJar = "solaris_natives.jar.lzma"; } else { this.fatalErrorOccured("OS (" + osName + ") not supported", null); } if (nativeJar == null) { this.fatalErrorOccured("no lwjgl natives files found", null); } else { this.urlList[this.urlList.length - 1] = new URL(path, nativeJar); } } /* * WARNING - Removed try catching itself - possible behaviour change. */ public void run() { this.init(); this.state = 3; this.percentage = 5; try { this.loadJarURLs(); String path = (String)AccessController.doPrivileged(new PrivilegedExceptionAction(){ public Object run() throws Exception { return MinecraftUtil.getWorkingDirectory() + File.separator + "bin" + File.separator; } }); File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } int before = this.percentage; boolean cacheAvailable = false; if (this.canPlayOffline()) { cacheAvailable = true; this.percentage = 90; } if (this.forceUpdate || !cacheAvailable) { if (this.percentage != before) { this.percentage = before; } System.out.println("Path: " + path); this.downloadJars(path); this.extractJars(path); this.extractNatives(path); this.percentage = 90; } this.updateClassPath(dir); this.state = 10; } catch (AccessControlException ace) { this.fatalErrorOccured(ace.getMessage(), ace); this.certificateRefused = true; } catch (Exception e) { this.fatalErrorOccured(e.getMessage(), e); } finally { this.loaderThread = null; } } protected void updateClassPath(File dir) throws Exception { String path; this.state = 6; this.percentage = 95; URL[] urls = new URL[this.urlList.length]; for (int i = 0; i < this.urlList.length; ++i) { urls[i] = new File(dir, this.getJarName(this.urlList[i])).toURI().toURL(); System.out.println("URL: " + urls[i]); } if (classLoader == null) { classLoader = new URLClassLoader(urls){ protected PermissionCollection getPermissions(CodeSource codesource) { PermissionCollection perms = null; try { Method method = SecureClassLoader.class.getDeclaredMethod("getPermissions", CodeSource.class); method.setAccessible(true); perms = (PermissionCollection)method.invoke(this.getClass().getClassLoader(), codesource); String host = "www.minecraft.net"; if (host != null && host.length() > 0) { perms.add(new SocketPermission(host, "connect,accept")); } else { codesource.getLocation().getProtocol().equals("file"); } perms.add(new FilePermission("<>", "read")); } catch (Exception e) { e.printStackTrace(); } return perms; } }; } if (!(path = dir.getAbsolutePath()).endsWith(File.separator)) { path = path + File.separator; } this.unloadNatives(path); System.setProperty("org.lwjgl.librarypath", path + "natives"); System.setProperty("net.java.games.input.librarypath", path + "natives"); natives_loaded = true; } private void unloadNatives(String nativePath) { if (!natives_loaded) { return; } try { Field field = ClassLoader.class.getDeclaredField("loadedLibraryNames"); field.setAccessible(true); Vector libs = (Vector)field.get(this.getClass().getClassLoader()); String path = new File(nativePath).getCanonicalPath(); for (int i = 0; i < libs.size(); ++i) { String s = (String)libs.get(i); if (!s.startsWith(path)) continue; libs.remove(i); --i; } } catch (Exception e) { e.printStackTrace(); } } public Applet createApplet() throws ClassNotFoundException, InstantiationException, IllegalAccessException { Class appletClass = classLoader.loadClass("net.minecraft.client.MinecraftApplet"); return (Applet)appletClass.newInstance(); } protected void downloadJars(String path) throws Exception { this.state = 4; int[] fileSizes = new int[this.urlList.length]; for (int i = 0; i < this.urlList.length; ++i) { System.out.println(this.urlList[i]); URLConnection urlconnection = this.urlList[i].openConnection(); urlconnection.setDefaultUseCaches(false); if (urlconnection instanceof HttpURLConnection) { ((HttpURLConnection)urlconnection).setRequestMethod("HEAD"); } fileSizes[i] = urlconnection.getContentLength(); this.totalSizeDownload += fileSizes[i]; } this.percentage = 10; int initialPercentage = 10; byte[] buffer = new byte[65536]; for (int i = 0; i < this.urlList.length; ++i) { int unsuccessfulAttempts = 0; int maxUnsuccessfulAttempts = 3; boolean downloadFile = true; while (downloadFile) { int bufferSize; downloadFile = false; URLConnection urlconnection = this.urlList[i].openConnection(); if (urlconnection instanceof HttpURLConnection) { urlconnection.setRequestProperty("Cache-Control", "no-cache"); urlconnection.connect(); } String currentFile = this.getFileName(this.urlList[i]); InputStream inputstream = this.getJarInputStream(currentFile, urlconnection); FileOutputStream fos = new FileOutputStream(path + currentFile); long downloadStartTime = System.currentTimeMillis(); int downloadedAmount = 0; int fileSize = 0; String downloadSpeedMessage = ""; while ((bufferSize = inputstream.read(buffer, 0, buffer.length)) != -1) { fos.write(buffer, 0, bufferSize); this.currentSizeDownload += bufferSize; fileSize += bufferSize; this.percentage = initialPercentage + this.currentSizeDownload * 45 / this.totalSizeDownload; this.subtaskMessage = "Retrieving: " + currentFile + " " + this.currentSizeDownload * 100 / this.totalSizeDownload + "%"; downloadedAmount += bufferSize; long timeLapse = System.currentTimeMillis() - downloadStartTime; if (timeLapse >= 1000L) { float downloadSpeed = (float)downloadedAmount / (float)timeLapse; downloadSpeed = (float)((int)(downloadSpeed * 100.0f)) / 100.0f; downloadSpeedMessage = " @ " + downloadSpeed + " KB/sec"; downloadedAmount = 0; downloadStartTime += 1000L; } this.subtaskMessage = this.subtaskMessage + downloadSpeedMessage; } inputstream.close(); fos.close(); if (!(urlconnection instanceof HttpURLConnection) || fileSize == fileSizes[i] || fileSizes[i] <= 0) continue; if (++unsuccessfulAttempts < maxUnsuccessfulAttempts) { downloadFile = true; this.currentSizeDownload -= fileSize; continue; } throw new Exception("failed to download " + currentFile); } } this.subtaskMessage = ""; } protected InputStream getJarInputStream(String currentFile, URLConnection urlconnection) throws Exception { InputStream[] is = new InputStream[1]; this.isp = is; this.urlconnectionp = urlconnection; for (int j = 0; j < 3 && is[0] == null; ++j) { Thread t = new Thread(){ public void run() { try { GameUpdater.this.isp[0] = GameUpdater.this.urlconnectionp.getInputStream(); } catch (Exception exception) { // empty catch block } } }; t.setName("JarInputStreamThread"); t.start(); int iterationCount = 0; while (is[0] == null && iterationCount++ < 5) { try { t.join(1000L); } catch (InterruptedException localInterruptedException) {} } if (is[0] != null) continue; try { t.interrupt(); t.join(); continue; } catch (InterruptedException localInterruptedException1) { // empty catch block } } if (is[0] == null) { if (currentFile.equals("minecraft.jar")) { throw new Exception("Unable to download " + currentFile); } throw new Exception("Unable to download " + currentFile); } return is[0]; } protected void extractLZMA(String in, String out) throws Exception { File f = new File(in); File fout = new File(out); LzmaAlone.decompress(f, fout); f.delete(); } protected void extractPack(String in, String out) throws Exception { File f = new File(in); FileOutputStream fostream = new FileOutputStream(out); JarOutputStream jostream = new JarOutputStream(fostream); Pack200.Unpacker unpacker = Pack200.newUnpacker(); unpacker.unpack(f, jostream); jostream.close(); f.delete(); } protected void extractJars(String path) throws Exception { this.state = 5; float increment = 10.0f / (float)this.urlList.length; for (int i = 0; i < this.urlList.length; ++i) { this.percentage = 55 + (int)(increment * (float)(i + 1)); String filename = this.getFileName(this.urlList[i]); if (filename.endsWith(".pack.lzma")) { this.subtaskMessage = "Extracting: " + filename + " to " + filename.replaceAll(".lzma", ""); this.extractLZMA(path + filename, path + filename.replaceAll(".lzma", "")); this.subtaskMessage = "Extracting: " + filename.replaceAll(".lzma", "") + " to " + filename.replaceAll(".pack.lzma", ""); this.extractPack(path + filename.replaceAll(".lzma", ""), path + filename.replaceAll(".pack.lzma", "")); continue; } if (filename.endsWith(".pack")) { this.subtaskMessage = "Extracting: " + filename + " to " + filename.replace(".pack", ""); this.extractPack(path + filename, path + filename.replace(".pack", "")); continue; } if (!filename.endsWith(".lzma")) continue; this.subtaskMessage = "Extracting: " + filename + " to " + filename.replace(".lzma", ""); this.extractLZMA(path + filename, path + filename.replace(".lzma", "")); } } protected void extractNatives(String path) throws Exception { JarEntry entry; File nativeFolder; this.state = 5; int initialPercentage = this.percentage; String nativeJar = this.getJarName(this.urlList[this.urlList.length - 1]); Certificate[] certificate = Launcher.class.getProtectionDomain().getCodeSource().getCertificates(); if (certificate == null) { URL location = Launcher.class.getProtectionDomain().getCodeSource().getLocation(); JarURLConnection jurl = (JarURLConnection)new URL("jar:" + location.toString() + "!/net/minecraft/Launcher.class").openConnection(); jurl.setDefaultUseCaches(true); try { certificate = jurl.getCertificates(); } catch (Exception localException) { // empty catch block } } if (!(nativeFolder = new File(path + "natives")).exists()) { nativeFolder.mkdir(); } JarFile jarFile = new JarFile(path + nativeJar, true); Enumeration entities = jarFile.entries(); this.totalSizeExtract = 0; while (entities.hasMoreElements()) { entry = entities.nextElement(); if (entry.isDirectory() || entry.getName().indexOf(47) != -1) continue; this.totalSizeExtract = (int)((long)this.totalSizeExtract + entry.getSize()); } this.currentSizeExtract = 0; entities = jarFile.entries(); while (entities.hasMoreElements()) { int bufferSize; File f; entry = entities.nextElement(); if (entry.isDirectory() || entry.getName().indexOf(47) != -1 || (f = new File(path + "natives" + File.separator + entry.getName())).exists() && !f.delete()) continue; InputStream in = jarFile.getInputStream(jarFile.getEntry(entry.getName())); FileOutputStream out = new FileOutputStream(path + "natives" + File.separator + entry.getName()); byte[] buffer = new byte[65536]; while ((bufferSize = in.read(buffer, 0, buffer.length)) != -1) { ((OutputStream)out).write(buffer, 0, bufferSize); this.currentSizeExtract += bufferSize; this.percentage = initialPercentage + this.currentSizeExtract * 20 / this.totalSizeExtract; this.subtaskMessage = "Extracting: " + entry.getName() + " " + this.currentSizeExtract * 100 / this.totalSizeExtract + "%"; } GameUpdater.validateCertificateChain(certificate, entry.getCertificates()); in.close(); ((OutputStream)out).close(); } this.subtaskMessage = ""; jarFile.close(); File f = new File(path + nativeJar); f.delete(); } protected static void validateCertificateChain(Certificate[] ownCerts, Certificate[] native_certs) throws Exception { if (ownCerts == null) { return; } if (native_certs == null) { throw new Exception("Unable to validate certificate chain. Native entry did not have a certificate chain at all"); } if (ownCerts.length != native_certs.length) { throw new Exception("Unable to validate certificate chain. Chain differs in length [" + ownCerts.length + " vs " + native_certs.length + "]"); } for (int i = 0; i < ownCerts.length; ++i) { if (ownCerts[i].equals(native_certs[i])) continue; throw new Exception("Certificate mismatch: " + ownCerts[i] + " != " + native_certs[i]); } } protected String getJarName(URL url) { String fileName = url.getFile(); if (fileName.contains("?")) { fileName = fileName.substring(0, fileName.indexOf("?")); } if (fileName.endsWith(".pack.lzma")) { fileName = fileName.replaceAll(".pack.lzma", ""); } else if (fileName.endsWith(".pack")) { fileName = fileName.replaceAll(".pack", ""); } else if (fileName.endsWith(".lzma")) { fileName = fileName.replaceAll(".lzma", ""); } return fileName.substring(fileName.lastIndexOf(47) + 1); } protected String getFileName(URL url) { String fileName = url.getFile(); if (fileName.contains("?")) { fileName = fileName.substring(0, fileName.indexOf("?")); } return fileName.substring(fileName.lastIndexOf(47) + 1); } protected void fatalErrorOccured(String error, Exception e) { e.printStackTrace(); this.fatalError = true; this.fatalErrorDescription = "Fatal error occured (" + this.state + "): " + error; System.out.println(this.fatalErrorDescription); if (e != null) { System.out.println(this.generateStacktrace(e)); } } public boolean canPlayOffline() { if (!MinecraftUtil.getBinFolder().exists() || !MinecraftUtil.getBinFolder().isDirectory()) { return false; } if (!MinecraftUtil.getNativesFolder().exists() || !MinecraftUtil.getNativesFolder().isDirectory()) { return false; } if (MinecraftUtil.getBinFolder().list().length < gameFiles.length + 1) { return false; } if (MinecraftUtil.getNativesFolder().list().length < 1) { return false; } String[] bins = MinecraftUtil.getBinFolder().list(); for (String necessary : gameFiles) { boolean isThere = false; for (String found : bins) { if (!necessary.equalsIgnoreCase(found)) continue; isThere = true; break; } if (isThere) continue; return false; } return true; } static { natives_loaded = false; gameFiles = new String[]{"lwjgl.jar", "jinput.jar", "lwjgl_util.jar", "minecraft.jar"}; } }