Redo hashing sytem, pack file reading, write json

This commit is contained in:
comp500 2019-06-12 14:28:38 +01:00
parent 8be5cb8e60
commit fbd54b4604
No known key found for this signature in database
GPG Key ID: 214C822FFEC586B5
9 changed files with 243 additions and 99 deletions

View File

@ -2,19 +2,21 @@ package link.infra.packwiz.installer;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.Writer;
import java.net.URI; import java.net.URI;
import java.nio.file.Paths; import java.nio.file.Paths;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonIOException; import com.google.gson.JsonIOException;
import com.google.gson.JsonSyntaxException; 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.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.request.HandlerManager;
import link.infra.packwiz.installer.ui.IUserInterface; import link.infra.packwiz.installer.ui.IUserInterface;
import link.infra.packwiz.installer.ui.InstallProgress; import link.infra.packwiz.installer.ui.InstallProgress;
@ -87,7 +89,7 @@ public class UpdateManager {
this.checkOptions(); this.checkOptions();
ui.submitProgress(new InstallProgress("Loading manifest file...")); ui.submitProgress(new InstallProgress("Loading manifest file..."));
Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(byte[].class, new HashTypeAdapter()).create(); Gson gson = new Gson();
ManifestFile manifest; ManifestFile manifest;
try { try {
manifest = gson.fromJson(new FileReader(Paths.get(opts.packFolder, opts.manifestFile).toString()), 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...")); ui.submitProgress(new InstallProgress("Loading pack file..."));
HashInputStream packFileStream; Hash.HashInputStream packFileStream;
try { try {
InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI); InputStream stream = HandlerManager.getFileInputStream(opts.downloadURI);
if (stream == null) { if (stream == null) {
throw new Exception("Pack file URI is invalid, is it supported?"); throw new Exception("Pack file URI is invalid, is it supported?");
} }
packFileStream = new HashInputStream(stream); packFileStream = new Hash.HashInputStream(stream, "sha256");
} catch (Exception e) { } catch (Exception e) {
// TODO: still launch the game if updating doesn't work?
ui.handleExceptionAndExit(e); ui.handleExceptionAndExit(e);
return; return;
} }
// TODO: read file PackFile pf;
try { try {
packFileStream.close(); pf = new Toml().read(packFileStream).to(PackFile.class);
} catch (IOException e) { } catch (IllegalStateException e) {
// TODO Auto-generated catch block ui.handleExceptionAndExit(e);
e.printStackTrace(); return;
} }
byte[] packFileHash = packFileStream.getHashValue();
Hash packFileHash = packFileStream.get();
if (packFileHash.equals(manifest.packFileHash)) { if (packFileHash.equals(manifest.packFileHash)) {
System.out.println("Hash already up to date!");
// WOOO it's already up to date // WOOO it's already up to date
// todo: --force? // 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() { protected void checkOptions() {

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
package link.infra.packwiz.installer.metadata;
public class IndexFile {
}

View File

@ -1,5 +1,13 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
import link.infra.packwiz.installer.metadata.hash.Hash;
public class ManifestFile { public class ManifestFile {
public byte[] packFileHash = null;
public Hash packFileHash = null;
public Hash indexFileHash = null;
public static class File {
public Hash hash = null;
}
} }

View File

@ -1,5 +1,21 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata;
public class PackFile { 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<String, String> versions;
public Map<String, Object> client;
public Map<String, Object> server;
} }

View File

@ -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<String, IHasher> hashTypeConversion = new HashMap<String, IHasher>();
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;
}
}
}

View File

@ -1,27 +1,7 @@
package link.infra.packwiz.installer.metadata; package link.infra.packwiz.installer.metadata.hash;
import java.lang.reflect.Type; public class HashUtils {
private HashUtils() {}
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<byte[]>, JsonDeserializer<byte[]> {
@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());
}
// Why did Java remove this in 1.9????! // Why did Java remove this in 1.9????!
public static byte[] parseHexBinary(String s) { public static byte[] parseHexBinary(String s) {
@ -52,7 +32,7 @@ public class HashTypeAdapter implements JsonSerializer<byte[]>, JsonDeserializer
return -1; return -1;
} }
private static final char[] hexCode = "0123456789ABCDEF".toCharArray(); private static final char[] hexCode = "0123456789abcdef".toCharArray();
public static String printHexBinary(byte[] data) { public static String printHexBinary(byte[] data) {
StringBuilder r = new StringBuilder(data.length*2); StringBuilder r = new StringBuilder(data.length*2);

View File

@ -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);
}
}

View File

@ -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);
}
}