package the.bytecode.club.bytecodeviewer.bootloader; import de.skuzzle.semantic.Version; import me.konloch.kontainer.io.HTTPRequest; import the.bytecode.club.bytecodeviewer.BytecodeViewer; import the.bytecode.club.bytecodeviewer.Configuration; import the.bytecode.club.bytecodeviewer.api.BCV; import the.bytecode.club.bytecodeviewer.gui.components.FileChooser; import the.bytecode.club.bytecodeviewer.gui.components.MultipleChoiceDialog; import the.bytecode.club.bytecodeviewer.translation.TranslatedStrings; import javax.swing.*; import java.awt.*; import java.io.*; import java.net.URI; import java.net.URL; import static the.bytecode.club.bytecodeviewer.Constants.*; /*************************************************************************** * Bytecode Viewer (BCV) - Java & Android Reverse Engineering Suite * * Copyright (C) 2014 Kalen 'Konloch' Kinloch - http://bytecodeviewer.com * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ /** * @author Konloch */ public class UpdateCheck implements Runnable { //just brute force download the url path //one of these works for every single version of BCV public static final String[] remoteGithubReleases = new String[] { //current url scheme since v2.9.12 "https://github.com/Konloch/bytecode-viewer/releases/download/v{VERSION}/Bytecode-Viewer-{VERSION}.jar", //for v2.9.10 and v2.9.11 "https://github.com/Konloch/bytecode-viewer/releases/download/{VERSION}/Bytecode-Viewer-{VERSION}.jar", //for v2.7.0 to v2.9.8 "https://github.com/Konloch/bytecode-viewer/releases/download/v{VERSION}/BytecodeViewer.{VERSION}.zip", //for v2.0 to v2.6.0 "https://github.com/Konloch/bytecode-viewer/releases/download/v{VERSION}/BytecodeViewer.{VERSION}.jar", //for v1.1 to v1.5.3 "https://github.com/Konloch/bytecode-viewer/releases/download/b{VERSION}/BytecodeViewer.Beta.{VERSION}.jar", //for v1.4 "https://github.com/Konloch/bytecode-viewer/releases/download/b.{VERSION}/BytecodeViewer.Beta.{VERSION}.jar", //for v1.0 "https://github.com/Konloch/bytecode-viewer/releases/download/B{VERSION}/BytecodeViewer.jar", //zip variant of current url scheme since v2.9.12 (not currently used but incase it ever does) "https://github.com/Konloch/bytecode-viewer/releases/download/v{VERSION}/Bytecode-Viewer-{VERSION}.zip", }; //a list of all of the released versions of BCV public static final String[] versions = new String[] { //"2.11.0", //"2.10.15", "2.10.14", "2.10.13", "2.10.12", "2.10.11", "2.9.22", "2.9.21", "2.9.20", "2.9.19", "2.9.18", "2.9.17", "2.9.16", "2.9.15", "2.9.14", "2.9.13", "2.9.12", "2.9.11", "2.9.10", //broken due to repo change "2.9.8", //broken due to repo change & zip "2.9.7", //broken due to repo change & zip "2.9.6", //zip "2.9.5", //zip "2.9.4", //zip "2.9.3", //zip "2.9.2", //zip "2.9.1", //zip "2.9.0", //zip "2.8.1", //zip "2.8.0", //zip "2.7.1", //zip "2.7.0", //zip "2.6.0", "2.5.2", "2.5.1", "2.5.0", "2.4.0", "2.3.0", "2.2.1", "2.2.0", "2.1.1", "2.1.0", "2.0.1", "2.0", "1.5.3", "1.5.2", "1.5.1", "1.5", "1.4", "1.3.1", "1.3", "1.2", "1.1", "1.0", }; @Override public void run() { try { HTTPRequest r = new HTTPRequest(new URL("https://raw.githubusercontent.com/Konloch/bytecode-viewer/master/VERSION")); final Version version = Version.parseVersion(r.readSingle()); final Version localVersion = Version.parseVersion(VERSION); try { //developer version if (Version.compare(localVersion, version) > 0) return; } catch (Exception ignored) { } MultipleChoiceDialog outdatedDialog = new MultipleChoiceDialog("Bytecode Viewer - Outdated Version", "Your version: " + localVersion + ", latest version: " + version + nl + "What would you like to do?", new String[]{"Open The Download Page", "Download The Updated Jar", "Do Nothing (And Don't Ask Again)"}); int result = outdatedDialog.promptChoice(); if (result == 0) { if (Desktop.isDesktopSupported()) Desktop.getDesktop().browse(new URI("https://github.com/Konloch/bytecode-viewer/releases")); else BytecodeViewer.showMessage("Cannot open the page, please manually type it." + nl + "https://github.com/Konloch/bytecode-viewer/releases"); } else if (result == 1) { //TODO move this to after the file extension has been found final File file = promptFileSave("Jar Archives", "jar"); if(file != null) { Thread downloadThread = new Thread(() -> downloadBCV(version.toString(), file, ()->{}, ()->{}), "Downloader"); downloadThread.start(); } } else if(result == 2) { //TODO save version into a hashset called doNotPrompt } } catch (Exception e) { e.printStackTrace(); } } public static File promptFileSave(String description, String extension) throws IOException { JFileChooser fc = new FileChooser(new File("./").getCanonicalFile(), "Select Save File", description, extension); int returnVal = fc.showSaveDialog(BytecodeViewer.viewer); File file = null; if (returnVal == JFileChooser.APPROVE_OPTION) { Configuration.setLastOpenDirectory(fc.getSelectedFile()); file = fc.getSelectedFile(); String nameLowercase = file.getAbsolutePath().toLowerCase(); if (!nameLowercase.endsWith(".jar")) file = new File(file.getAbsolutePath() + ".jar"); if (file.exists()) { MultipleChoiceDialog overwriteDialog = new MultipleChoiceDialog("Bytecode Viewer - Overwrite File", "The file " + file + " exists, would you like to overwrite it?", new String[]{TranslatedStrings.YES.toString(), TranslatedStrings.NO.toString()}); if (overwriteDialog.promptChoice() != 0) return null; file.delete(); } } return file; } //used to download all released versions of BCV /*public static void main(String[] args) { BytecodeViewer.viewer = new MainViewerGUI(); for(String version : versions) { //TODO most are jars, check which are zip and append zip as needed File file = new File("./" + version + ".zip"); if(!file.exists()) downloadBCV(version, file, () -> {}, () -> {}); } }*/ private static void downloadBCV(String version, File saveTo, Runnable onFinish, Runnable onFail) { boolean found = false; for(String urlAttempt : remoteGithubReleases) { try { String url = urlAttempt.replace("{VERSION}", version); if(validURl(url)) { download(url, saveTo, onFinish); found = true; break; } } catch (FileNotFoundException ex) { //ignore 404s } catch (Exception e) { //print network errors but don't alert user e.printStackTrace(); } } if(!found) { BCV.logE("Failed to download BCV v" + version); BytecodeViewer.showMessage("Unable to download BCV v" + version + ", please let Konloch know."); onFail.run(); } } private static boolean validURl(String url) throws Exception { HTTPRequest request = new HTTPRequest(new URL(url)); request.readSingle(); return request.getStatusCode() == 200; } private static void download(String url, File saveTo, Runnable onFinish) throws Exception { BCV.log("Downloading from: " + url); BytecodeViewer.showMessage("Downloading the jar in the background, when it's finished you will be alerted with another message box." + nl + nl + "Expect this to take several minutes."); try (InputStream is = new URL(url).openConnection().getInputStream(); FileOutputStream fos = new FileOutputStream(saveTo)) { byte[] buffer = new byte[8192]; int len; int downloaded = 0; boolean flag = false; while ((len = is.read(buffer)) > 0) { fos.write(buffer, 0, len); fos.flush(); downloaded += 8192; int mbs = downloaded / 1048576; if (mbs % 5 == 0 && mbs != 0) { if (!flag) System.out.println("Downloaded " + mbs + "MBs so far"); flag = true; } else flag = false; } } BCV.log("Download finished!"); BytecodeViewer.showMessage("Download successful! You can find the updated program at " + saveTo.getAbsolutePath()); onFinish.run(); } }