From fbd54b4604232061b3d0fdbbabc8799daa561b05 Mon Sep 17 00:00:00 2001 From: comp500 Date: Wed, 12 Jun 2019 14:28:38 +0100 Subject: [PATCH] Redo hashing sytem, pack file reading, write json --- .../packwiz/installer/UpdateManager.java | 44 +++++-- .../installer/metadata/HashInputStream.java | 60 --------- .../packwiz/installer/metadata/IndexFile.java | 5 + .../installer/metadata/ManifestFile.java | 10 +- .../packwiz/installer/metadata/PackFile.java | 18 ++- .../packwiz/installer/metadata/hash/Hash.java | 115 ++++++++++++++++++ .../HashUtils.java} | 28 +---- .../metadata/hash/HasherMessageDigest.java | 45 +++++++ .../installer/metadata/hash/IHasher.java | 17 +++ 9 files changed, 243 insertions(+), 99 deletions(-) delete mode 100644 src/main/java/link/infra/packwiz/installer/metadata/HashInputStream.java create mode 100644 src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java create mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java rename src/main/java/link/infra/packwiz/installer/metadata/{HashTypeAdapter.java => hash/HashUtils.java} (53%) create mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/HasherMessageDigest.java create mode 100644 src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java diff --git a/src/main/java/link/infra/packwiz/installer/UpdateManager.java b/src/main/java/link/infra/packwiz/installer/UpdateManager.java index d1eb5f6..e7c4177 100644 --- a/src/main/java/link/infra/packwiz/installer/UpdateManager.java +++ b/src/main/java/link/infra/packwiz/installer/UpdateManager.java @@ -2,19 +2,21 @@ package link.infra.packwiz.installer; import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; +import java.io.Writer; import java.net.URI; import java.nio.file.Paths; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import com.google.gson.JsonIOException; import com.google.gson.JsonSyntaxException; +import com.moandjiezana.toml.Toml; -import link.infra.packwiz.installer.metadata.HashInputStream; -import link.infra.packwiz.installer.metadata.HashTypeAdapter; import link.infra.packwiz.installer.metadata.ManifestFile; +import link.infra.packwiz.installer.metadata.PackFile; +import link.infra.packwiz.installer.metadata.hash.Hash; import link.infra.packwiz.installer.request.HandlerManager; import link.infra.packwiz.installer.ui.IUserInterface; import link.infra.packwiz.installer.ui.InstallProgress; @@ -87,7 +89,7 @@ public class UpdateManager { this.checkOptions(); ui.submitProgress(new InstallProgress("Loading manifest file...")); - Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new HashTypeAdapter()).create(); + Gson gson = new Gson(); ManifestFile manifest; try { manifest = gson.fromJson(new FileReader(Paths.get(opts.packFolder, opts.manifestFile).toString()), @@ -100,31 +102,47 @@ public class UpdateManager { } ui.submitProgress(new InstallProgress("Loading pack file...")); - HashInputStream packFileStream; + Hash.HashInputStream packFileStream; try { InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI); if (stream == null) { throw new Exception("Pack file URI is invalid, is it supported?"); } - packFileStream = new HashInputStream(stream); + packFileStream = new Hash.HashInputStream(stream, "sha256"); } catch (Exception e) { + // TODO: still launch the game if updating doesn't work? ui.handleExceptionAndExit(e); return; } - // TODO: read file + PackFile pf; try { - packFileStream.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + pf = new Toml().read(packFileStream).to(PackFile.class); + } catch (IllegalStateException e) { + ui.handleExceptionAndExit(e); + return; } - byte[] packFileHash = packFileStream.getHashValue(); + + Hash packFileHash = packFileStream.get(); if (packFileHash.equals(manifest.packFileHash)) { + System.out.println("Hash already up to date!"); // WOOO it's already up to date // todo: --force? } - // TODO: save manifest file + System.out.println(pf.name); + + + // When successfully updated + manifest.packFileHash = packFileHash; + // update other hashes + // TODO: don't do this on failure? + try (Writer writer = new FileWriter(Paths.get(opts.packFolder, opts.manifestFile).toString())) { + gson.toJson(manifest, writer); + } catch (IOException e) { + // TODO: add message? + ui.handleException(e); + } + } protected void checkOptions() { diff --git a/src/main/java/link/infra/packwiz/installer/metadata/HashInputStream.java b/src/main/java/link/infra/packwiz/installer/metadata/HashInputStream.java deleted file mode 100644 index 5579d30..0000000 --- a/src/main/java/link/infra/packwiz/installer/metadata/HashInputStream.java +++ /dev/null @@ -1,60 +0,0 @@ -package link.infra.packwiz.installer.metadata; - -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; - -public class HashInputStream extends FilterInputStream { - - private MessageDigest md; - private byte[] output; - - public HashInputStream(InputStream in) throws NoSuchAlgorithmException { - super(in); - md = MessageDigest.getInstance("SHA-256"); - } - - @Override - public int read() throws IOException { - int value = super.read(); - if (value == -1) { - return value; - } - md.update((byte) value); - return value; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException { - int bytesRead = super.read(b, off, len); - if (bytesRead > 0) { - md.update(b, off, len); - } - return bytesRead; - } - - @Override - public void reset() throws IOException { - throw new IOException("HashInputStream doesn't support reset()"); - } - - @Override - public boolean markSupported() { - return false; - } - - @Override - public void mark(int readlimit) { - // Do nothing - } - - public byte[] getHashValue() { - if (output == null) { - output = md.digest(); - } - return output; - } - -} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java b/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java new file mode 100644 index 0000000..81d1809 --- /dev/null +++ b/src/main/java/link/infra/packwiz/installer/metadata/IndexFile.java @@ -0,0 +1,5 @@ +package link.infra.packwiz.installer.metadata; + +public class IndexFile { + +} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/ManifestFile.java b/src/main/java/link/infra/packwiz/installer/metadata/ManifestFile.java index e0b3637..826f44b 100644 --- a/src/main/java/link/infra/packwiz/installer/metadata/ManifestFile.java +++ b/src/main/java/link/infra/packwiz/installer/metadata/ManifestFile.java @@ -1,5 +1,13 @@ package link.infra.packwiz.installer.metadata; +import link.infra.packwiz.installer.metadata.hash.Hash; + public class ManifestFile { - public byte[] packFileHash = null; + + public Hash packFileHash = null; + public Hash indexFileHash = null; + + public static class File { + public Hash hash = null; + } } \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/PackFile.java b/src/main/java/link/infra/packwiz/installer/metadata/PackFile.java index d13ae09..d112b41 100644 --- a/src/main/java/link/infra/packwiz/installer/metadata/PackFile.java +++ b/src/main/java/link/infra/packwiz/installer/metadata/PackFile.java @@ -1,5 +1,21 @@ package link.infra.packwiz.installer.metadata; +import java.util.Map; + +import com.google.gson.annotations.SerializedName; + public class PackFile { - + public String name; + + public IndexFileLoc index; + public static class IndexFileLoc { + public String file; + @SerializedName("hash-format") + public String hashFormat; + public String hash; + } + + public Map versions; + public Map client; + public Map server; } \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java new file mode 100644 index 0000000..510602a --- /dev/null +++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/Hash.java @@ -0,0 +1,115 @@ +package link.infra.packwiz.installer.metadata.hash; + +import java.io.FilterInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; + +public class Hash { + public final String value; + public final String type; + + public Hash(String value, String type) { + this.value = value; + this.type = type; + } + + private static final Map hashTypeConversion = new HashMap(); + static { + try { + hashTypeConversion.put("sha256", new HasherMessageDigest("SHA-256")); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + } + + public IHasher getHasher() { + return hashTypeConversion.get(type); + } + + public static IHasher getHasher(String type) { + return hashTypeConversion.get(type); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof Hash)) { + return false; + } + Hash hash = (Hash)obj; + return type.equals(hash.type) && getHasher().equalValues(value, hash.value); + } + + public static class HashInputStream extends FilterInputStream { + + private IHasher md; + private Hash output; + private final String hashType; + private Hash compare = null; + + public HashInputStream(InputStream in, String hashType) throws NoSuchAlgorithmException { + super(in); + this.hashType = hashType; + md = hashTypeConversion.get(hashType); + } + + public HashInputStream(InputStream in, Hash compare) throws NoSuchAlgorithmException { + this(in, compare.type); + this.compare = compare; + } + + @Override + public int read() throws IOException { + int value = super.read(); + if (value == -1) { + return value; + } + md.update((byte) value); + return value; + } + + @Override + public int read(byte[] b, int off, int len) throws IOException { + int bytesRead = super.read(b, off, len); + if (bytesRead > 0) { + md.update(b, off, len); + } + return bytesRead; + } + + @Override + public void reset() throws IOException { + throw new IOException("HashInputStream doesn't support reset()"); + } + + @Override + public boolean markSupported() { + return false; + } + + @Override + public void mark(int readlimit) { + // Do nothing + } + + public Hash get() { + if (output == null) { + String value = md.get(); + if (value != null) { + output = new Hash(value, hashType); + } + } + return output; + } + + public boolean isNew() { + if (output != null) { + return !output.equals(compare); + } + return false; + } + + } +} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/HashTypeAdapter.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java similarity index 53% rename from src/main/java/link/infra/packwiz/installer/metadata/HashTypeAdapter.java rename to src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java index 40baa21..922ffb6 100644 --- a/src/main/java/link/infra/packwiz/installer/metadata/HashTypeAdapter.java +++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/HashUtils.java @@ -1,27 +1,7 @@ -package link.infra.packwiz.installer.metadata; +package link.infra.packwiz.installer.metadata.hash; -import java.lang.reflect.Type; - -import com.google.gson.JsonDeserializationContext; -import com.google.gson.JsonDeserializer; -import com.google.gson.JsonElement; -import com.google.gson.JsonParseException; -import com.google.gson.JsonPrimitive; -import com.google.gson.JsonSerializationContext; -import com.google.gson.JsonSerializer; - -public class HashTypeAdapter implements JsonSerializer, JsonDeserializer { - - @Override - public JsonElement serialize(byte[] src, Type typeOfSrc, JsonSerializationContext context) { - return new JsonPrimitive(printHexBinary(src)); - } - - @Override - public byte[] deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) - throws JsonParseException { - return parseHexBinary(json.getAsString()); - } +public class HashUtils { + private HashUtils() {} // Why did Java remove this in 1.9????! public static byte[] parseHexBinary(String s) { @@ -52,7 +32,7 @@ public class HashTypeAdapter implements JsonSerializer, JsonDeserializer return -1; } - private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); + private static final char[] hexCode = "0123456789abcdef".toCharArray(); public static String printHexBinary(byte[] data) { StringBuilder r = new StringBuilder(data.length*2); diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/HasherMessageDigest.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/HasherMessageDigest.java new file mode 100644 index 0000000..04aba5e --- /dev/null +++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/HasherMessageDigest.java @@ -0,0 +1,45 @@ +package link.infra.packwiz.installer.metadata.hash; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class HasherMessageDigest implements IHasher { + MessageDigest md; + + public HasherMessageDigest(String hashType) throws NoSuchAlgorithmException { + md = MessageDigest.getInstance(hashType); + } + + @Override + public void update(byte[] data) { + md.update(data); + } + + @Override + public void update(byte[] data, int offset, int length) { + md.update(data, offset, length); + } + + @Override + public void update(byte data) { + md.update(data); + } + + @Override + public String get() { + return HashUtils.printHexBinary(md.digest()); + } + + // Enforce case insensitivity + @Override + public boolean equalValues(String a, String b) { + if (a == null) { + if (b == null) { + return true; + } + return false; + } + return a.equalsIgnoreCase(b); + } + +} \ No newline at end of file diff --git a/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java b/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java new file mode 100644 index 0000000..95a76a5 --- /dev/null +++ b/src/main/java/link/infra/packwiz/installer/metadata/hash/IHasher.java @@ -0,0 +1,17 @@ +package link.infra.packwiz.installer.metadata.hash; + +public interface IHasher { + public void update(byte[] data); + public void update(byte[] data, int offset, int length); + public void update(byte data); + public String get(); + public default boolean equalValues(String a, String b) { + if (a == null) { + if (b == null) { + return true; + } + return false; + } + return a.equals(b); + } +} \ No newline at end of file