Todo[mcdl]: implement mirror tamper checking

This commit is contained in:
artdeell 2023-12-17 13:50:12 +03:00
parent 77df7d528c
commit 6628869c67
4 changed files with 44 additions and 33 deletions

View File

@ -122,16 +122,14 @@ public class ModDownloader {
try {
DownloadUtils.ensureSha1(mDestination, mSha1, (Callable<Void>) () -> {
IOException exception = tryDownload(sourceUrl);
if(exception != null) {
downloadFailed(exception);
throw exception;
}
return null;
});
}catch (IOException e) {
return;
downloadFailed(e);
}
}
}

View File

@ -18,7 +18,7 @@ import java.util.concurrent.Callable;
public class ModpackInstaller {
public static ModLoader installModpack(ModDetail modDetail, int selectedVersion, InstallFunction installFunction) throws IOException{
public static ModLoader installModpack(ModDetail modDetail, int selectedVersion, InstallFunction installFunction) throws IOException {
String versionUrl = modDetail.versionUrls[selectedVersion];
String versionHash = modDetail.versionHashes[selectedVersion];
String modpackName = modDetail.title.toLowerCase(Locale.ROOT).trim().replace(" ", "_" );

View File

@ -16,6 +16,7 @@ import net.kdt.pojavlaunch.JRE17Util;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.mirrors.DownloadMirror;
import net.kdt.pojavlaunch.mirrors.MirrorTamperedException;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import net.kdt.pojavlaunch.utils.FileUtils;
@ -34,7 +35,6 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.AtomicReference;
// TODO: implement mirror tamper checking.
public class NewMinecraftDownloader {
public static final String MINECRAFT_RES = "https://resources.download.minecraft.net/";
private AtomicReference<Exception> mThrownDownloaderException;
@ -123,17 +123,22 @@ public class NewMinecraftDownloader {
return new File(Tools.DIR_HOME_VERSION, versionId + File.separator + versionId + ".jar");
}
private File downloadGameJson(JMinecraftVersionList.Version verInfo) throws IOException {
private File downloadGameJson(JMinecraftVersionList.Version verInfo) throws IOException, MirrorTamperedException {
File targetFile = createGameJsonPath(verInfo.id);
if(verInfo.sha1 == null && targetFile.canRead() && targetFile.isFile())
return targetFile;
FileUtils.ensureParentDirectory(targetFile);
DownloadUtils.ensureSha1(targetFile, LauncherPreferences.PREF_VERIFY_MANIFEST ? verInfo.sha1 : null, ()-> {
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0,
R.string.newdl_downloading_metadata, targetFile.getName());
DownloadMirror.downloadFileMirrored(DownloadMirror.DOWNLOAD_CLASS_METADATA, verInfo.url, targetFile);
return null;
});
try {
DownloadUtils.ensureSha1(targetFile, LauncherPreferences.PREF_VERIFY_MANIFEST ? verInfo.sha1 : null, () -> {
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0,
R.string.newdl_downloading_metadata, targetFile.getName());
DownloadMirror.downloadFileMirrored(DownloadMirror.DOWNLOAD_CLASS_METADATA, verInfo.url, targetFile);
return null;
});
}catch (DownloadUtils.SHA1VerificationException e) {
if(DownloadMirror.isMirrored()) throw new MirrorTamperedException();
else throw e;
}
return targetFile;
}
@ -166,7 +171,7 @@ public class NewMinecraftDownloader {
* @return false if JRE17 installation failed, true otherwise
* @throws IOException if the download of any of the metadata files fails
*/
private boolean downloadAndProcessMetadata(Activity activity, JMinecraftVersionList.Version verInfo, String versionName) throws IOException {
private boolean downloadAndProcessMetadata(Activity activity, JMinecraftVersionList.Version verInfo, String versionName) throws IOException, MirrorTamperedException {
File versionJsonFile;
if(verInfo != null) versionJsonFile = downloadGameJson(verInfo);
else versionJsonFile = createGameJsonPath(versionName);

View File

@ -120,33 +120,35 @@ public class DownloadUtils {
return parseResult;
}
private static <T> T downloadFile(Callable<T> downloadFunction) throws IOException{
try {
return downloadFunction.call();
} catch (IOException e){
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
private static boolean verifyFile(File file, String sha1) {
return file.exists() && Tools.compareSHA1(file, sha1);
}
public static <T> T ensureSha1(File outputFile, @Nullable String sha1, Callable<T> downloadFunction) throws IOException {
// Skip if needed
if(sha1 == null){
try {
return downloadFunction.call();
} catch (IOException e){
throw e;
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
if(sha1 == null) return downloadFile(downloadFunction);
int attempts = 0;
boolean fileOkay = verifyFile(outputFile, sha1);
T result = null;
while (attempts < 5 && (!outputFile.exists() || !Tools.compareSHA1(outputFile, sha1))){
while (attempts < 5 && !fileOkay){
attempts++;
try {
result = downloadFunction.call();
} catch (IOException e){
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
downloadFile(downloadFunction);
fileOkay = verifyFile(outputFile, sha1);
}
if(!fileOkay) throw new SHA1VerificationException("SHA1 verifcation failed after 5 download attempts");
return result;
}
public interface ParseCallback<T> {
@ -157,5 +159,11 @@ public class DownloadUtils {
super(e);
}
}
public static class SHA1VerificationException extends IOException {
public SHA1VerificationException(String message) {
super(message);
}
}
}