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 { try {
DownloadUtils.ensureSha1(mDestination, mSha1, (Callable<Void>) () -> { DownloadUtils.ensureSha1(mDestination, mSha1, (Callable<Void>) () -> {
IOException exception = tryDownload(sourceUrl); IOException exception = tryDownload(sourceUrl);
if(exception != null) { if(exception != null) {
downloadFailed(exception); throw exception;
} }
return null; return null;
}); });
}catch (IOException e) { }catch (IOException e) {
return; downloadFailed(e);
} }
} }
} }

View File

@ -18,7 +18,7 @@ import java.util.concurrent.Callable;
public class ModpackInstaller { 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 versionUrl = modDetail.versionUrls[selectedVersion];
String versionHash = modDetail.versionHashes[selectedVersion]; String versionHash = modDetail.versionHashes[selectedVersion];
String modpackName = modDetail.title.toLowerCase(Locale.ROOT).trim().replace(" ", "_" ); 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.R;
import net.kdt.pojavlaunch.Tools; import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.mirrors.DownloadMirror; import net.kdt.pojavlaunch.mirrors.DownloadMirror;
import net.kdt.pojavlaunch.mirrors.MirrorTamperedException;
import net.kdt.pojavlaunch.prefs.LauncherPreferences; import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.utils.DownloadUtils; import net.kdt.pojavlaunch.utils.DownloadUtils;
import net.kdt.pojavlaunch.utils.FileUtils; 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.AtomicLong;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
// TODO: implement mirror tamper checking.
public class NewMinecraftDownloader { public class NewMinecraftDownloader {
public static final String MINECRAFT_RES = "https://resources.download.minecraft.net/"; public static final String MINECRAFT_RES = "https://resources.download.minecraft.net/";
private AtomicReference<Exception> mThrownDownloaderException; private AtomicReference<Exception> mThrownDownloaderException;
@ -123,17 +123,22 @@ public class NewMinecraftDownloader {
return new File(Tools.DIR_HOME_VERSION, versionId + File.separator + versionId + ".jar"); 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); File targetFile = createGameJsonPath(verInfo.id);
if(verInfo.sha1 == null && targetFile.canRead() && targetFile.isFile()) if(verInfo.sha1 == null && targetFile.canRead() && targetFile.isFile())
return targetFile; return targetFile;
FileUtils.ensureParentDirectory(targetFile); FileUtils.ensureParentDirectory(targetFile);
DownloadUtils.ensureSha1(targetFile, LauncherPreferences.PREF_VERIFY_MANIFEST ? verInfo.sha1 : null, ()-> { try {
ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0, DownloadUtils.ensureSha1(targetFile, LauncherPreferences.PREF_VERIFY_MANIFEST ? verInfo.sha1 : null, () -> {
R.string.newdl_downloading_metadata, targetFile.getName()); ProgressLayout.setProgress(ProgressLayout.DOWNLOAD_MINECRAFT, 0,
DownloadMirror.downloadFileMirrored(DownloadMirror.DOWNLOAD_CLASS_METADATA, verInfo.url, targetFile); R.string.newdl_downloading_metadata, targetFile.getName());
return null; 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; return targetFile;
} }
@ -166,7 +171,7 @@ public class NewMinecraftDownloader {
* @return false if JRE17 installation failed, true otherwise * @return false if JRE17 installation failed, true otherwise
* @throws IOException if the download of any of the metadata files fails * @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; File versionJsonFile;
if(verInfo != null) versionJsonFile = downloadGameJson(verInfo); if(verInfo != null) versionJsonFile = downloadGameJson(verInfo);
else versionJsonFile = createGameJsonPath(versionName); else versionJsonFile = createGameJsonPath(versionName);

View File

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