From 7ee7ef25f9c63831eed114c53db77ed9f1632a8c Mon Sep 17 00:00:00 2001 From: yushijinhun Date: Sat, 30 Jun 2018 16:44:37 +0800 Subject: [PATCH] Refactor integrity check in FileDownloadTask, support multi algorithms --- .../hmcl/game/HMCLGameDownloadTask.java | 3 +- .../hmcl/upgrade/AppDataUpgrader.java | 5 +- .../download/game/GameAssetDownloadTask.java | 3 +- .../hmcl/download/game/GameDownloadTask.java | 3 +- .../download/game/LibraryDownloadTask.java | 3 +- .../jackhuang/hmcl/task/FileDownloadTask.java | 58 ++++++++++++++----- .../hmcl/util/ChecksumMismatchException.java | 46 +++++++++++++++ 7 files changed, 101 insertions(+), 20 deletions(-) create mode 100644 HMCLCore/src/main/java/org/jackhuang/hmcl/util/ChecksumMismatchException.java diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java index b923b708d..e1402d55e 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/game/HMCLGameDownloadTask.java @@ -20,6 +20,7 @@ package org.jackhuang.hmcl.game; import org.jackhuang.hmcl.setting.Profile; import org.jackhuang.hmcl.setting.Settings; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.Logging; @@ -70,7 +71,7 @@ public class HMCLGameDownloadTask extends Task { NetworkUtils.toURL(profile.getDependency().getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())), cache, profile.getDependency().getProxy(), - version.getDownloadInfo().getSha1() + new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1()) ).then(Task.of(v -> FileUtils.copyFile(cache, jar)))); } diff --git a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java index 3c0955d33..03c4d5686 100644 --- a/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java +++ b/HMCL/src/main/java/org/jackhuang/hmcl/upgrade/AppDataUpgrader.java @@ -23,6 +23,7 @@ import com.jfoenix.concurrency.JFXUtilities; import javafx.scene.layout.Region; import org.jackhuang.hmcl.Launcher; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.task.TaskExecutor; import org.jackhuang.hmcl.ui.Controllers; @@ -182,7 +183,7 @@ public class AppDataUpgrader extends IUpgrader { @Override public Collection getDependents() { - return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash)); + return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash))); } @Override @@ -232,7 +233,7 @@ public class AppDataUpgrader extends IUpgrader { @Override public Collection getDependents() { - return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, hash)); + return Collections.singleton(new FileDownloadTask(downloadLink, tempFile, Proxy.NO_PROXY, new IntegrityCheck("SHA-1", hash))); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java index 43539e780..8f95de725 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameAssetDownloadTask.java @@ -21,6 +21,7 @@ import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.game.AssetObject; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.Logging; @@ -106,7 +107,7 @@ public final class GameAssetDownloadTask extends Task { flag = !file.exists(); } if (flag) { - FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), assetObject.getHash()); + FileDownloadTask task = new FileDownloadTask(NetworkUtils.toURL(url), file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", assetObject.getHash())); task.setName(assetObject.getHash()); dependencies.add(task); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java index 336c675b7..9400f771f 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/GameDownloadTask.java @@ -20,6 +20,7 @@ package org.jackhuang.hmcl.download.game; import org.jackhuang.hmcl.download.DefaultDependencyManager; import org.jackhuang.hmcl.game.Version; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.NetworkUtils; @@ -56,7 +57,7 @@ public final class GameDownloadTask extends Task { NetworkUtils.toURL(dependencyManager.getDownloadProvider().injectURL(version.getDownloadInfo().getUrl())), jar, dependencyManager.getProxy(), - version.getDownloadInfo().getSha1() + new IntegrityCheck("SHA-1", version.getDownloadInfo().getSha1()) )); } diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java index e5bac6171..fcc569197 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/download/game/LibraryDownloadTask.java @@ -4,6 +4,7 @@ import org.apache.commons.compress.compressors.xz.XZCompressorInputStream; import org.jackhuang.hmcl.download.AbstractDependencyManager; import org.jackhuang.hmcl.game.Library; import org.jackhuang.hmcl.task.FileDownloadTask; +import org.jackhuang.hmcl.task.FileDownloadTask.IntegrityCheck; import org.jackhuang.hmcl.task.Task; import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.IOUtils; @@ -48,7 +49,7 @@ public final class LibraryDownloadTask extends Task { setSignificance(TaskSignificance.MODERATE); task = new FileDownloadTask(NetworkUtils.toURL(url), - file, dependencyManager.getProxy(), library.getDownload().getSha1()); + file, dependencyManager.getProxy(), new IntegrityCheck("SHA-1", library.getDownload().getSha1())); } @Override diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java index 8e19ecb24..37f6aefc7 100644 --- a/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/task/FileDownloadTask.java @@ -19,6 +19,7 @@ package org.jackhuang.hmcl.task; import org.jackhuang.hmcl.event.EventManager; import org.jackhuang.hmcl.event.FailedEvent; +import org.jackhuang.hmcl.util.ChecksumMismatchException; import org.jackhuang.hmcl.util.FileUtils; import org.jackhuang.hmcl.util.IOUtils; import org.jackhuang.hmcl.util.Logging; @@ -35,6 +36,7 @@ import java.net.URL; import java.security.MessageDigest; import java.util.logging.Level; +import static java.util.Objects.requireNonNull; import static org.jackhuang.hmcl.util.DigestUtils.getDigest; /** @@ -44,9 +46,38 @@ import static org.jackhuang.hmcl.util.DigestUtils.getDigest; */ public class FileDownloadTask extends Task { + public static class IntegrityCheck { + private String algorithm; + private String checksum; + + public IntegrityCheck(String algorithm, String checksum) { + this.algorithm = requireNonNull(algorithm); + this.checksum = requireNonNull(checksum); + } + + public String getAlgorithm() { + return algorithm; + } + + public String getChecksum() { + return checksum; + } + + public MessageDigest createDigest() { + return getDigest(algorithm); + } + + public void performCheck(MessageDigest digest) throws ChecksumMismatchException { + String actualChecksum = String.format("%1$040x", new BigInteger(1, digest.digest())); + if (!checksum.equalsIgnoreCase(actualChecksum)) { + throw new ChecksumMismatchException(algorithm, checksum, actualChecksum); + } + } + } + private final URL url; private final File file; - private final String hash; + private final IntegrityCheck integrityCheck; private final int retry; private final Proxy proxy; private final EventManager> onFailed = new EventManager<>(); @@ -74,23 +105,23 @@ public class FileDownloadTask extends Task { * @param url the URL of remote file. * @param file the location that download to. * @param proxy the proxy. - * @param hash the SHA-1 hash code of remote file, null if the hash is unknown or it is no need to check the hash code. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed */ - public FileDownloadTask(URL url, File file, Proxy proxy, String hash) { - this(url, file, proxy, hash, 5); + public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck) { + this(url, file, proxy, integrityCheck, 5); } /** * @param url the URL of remote file. * @param file the location that download to. - * @param hash the SHA-1 hash code of remote file, null if the hash is unknown or it is no need to check the hash code. + * @param integrityCheck the integrity check to perform, null if no integrity check is to be performed * @param retry the times for retrying if downloading fails. * @param proxy the proxy. */ - public FileDownloadTask(URL url, File file, Proxy proxy, String hash, int retry) { + public FileDownloadTask(URL url, File file, Proxy proxy, IntegrityCheck integrityCheck, int retry) { this.url = url; this.file = file; - this.hash = hash; + this.integrityCheck = integrityCheck; this.retry = retry; this.proxy = proxy; @@ -159,7 +190,7 @@ public class FileDownloadTask extends Task { temp = FileUtils.createTempFile(); rFile = new RandomAccessFile(temp, "rw"); - MessageDigest digest = getDigest("SHA-1"); + MessageDigest digest = integrityCheck == null ? null : integrityCheck.createDigest(); stream = con.getInputStream(); int lastDownloaded = 0, downloaded = 0; @@ -175,8 +206,9 @@ public class FileDownloadTask extends Task { if (read == -1) break; - if (hash != null) + if (digest != null) { digest.update(buffer, 0, read); + } // Write buffer to file. rFile.write(buffer, 0, read); @@ -214,11 +246,9 @@ public class FileDownloadTask extends Task { if (downloaded != contentLength) throw new IllegalStateException("Unexpected file size: " + downloaded + ", expected: " + contentLength); - // Check hash code - if (hash != null) { - String hashCode = String.format("%1$040x", new BigInteger(1, digest.digest())); - if (!hash.equalsIgnoreCase(hashCode)) - throw new IllegalStateException("Unexpected hash code: " + hashCode + ", expected: " + hash); + // Integrity check + if (integrityCheck != null) { + integrityCheck.performCheck(digest); } return; diff --git a/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ChecksumMismatchException.java b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ChecksumMismatchException.java new file mode 100644 index 000000000..419ec9ee0 --- /dev/null +++ b/HMCLCore/src/main/java/org/jackhuang/hmcl/util/ChecksumMismatchException.java @@ -0,0 +1,46 @@ +/* + * Hello Minecraft! Launcher. + * Copyright (C) 2018 huangyuhui + * + * 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 {http://www.gnu.org/licenses/}. + */ +package org.jackhuang.hmcl.util; + +import java.io.IOException; + +public class ChecksumMismatchException extends IOException { + + private String algorithm; + private String expectedChecksum; + private String actualChecksum; + + public ChecksumMismatchException(String algorithm, String expectedChecksum, String actualChecksum) { + super("Incorrect checksum (" + algorithm + "), expected: " + expectedChecksum + ", actual: " + actualChecksum); + this.algorithm = algorithm; + this.expectedChecksum = expectedChecksum; + this.actualChecksum = actualChecksum; + } + + public String getAlgorithm() { + return algorithm; + } + + public String getExpectedChecksum() { + return expectedChecksum; + } + + public String getActualChecksum() { + return actualChecksum; + } +}