package com.faforever.client.patch; import com.faforever.client.i18n.I18n; import com.faforever.client.preferences.PreferencesService; import com.faforever.client.task.CompletableTask; import com.google.common.hash.Hashing; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.core.env.Environment; import javax.annotation.Resource; import java.io.BufferedReader; import java.io.IOException; import java.lang.invoke.MethodHandles; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; public class GitCheckGameUpdateTask extends CompletableTask<Boolean> { private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); @Resource I18n i18n; @Resource PreferencesService preferencesService; @Resource GitWrapper gitWrapper; @Resource Environment environment; private Path binaryPatchRepoDirectory; private Gson gson; private Path migrationDataFile; public GitCheckGameUpdateTask() { super(Priority.LOW); gson = new GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .create(); } @Override protected Boolean call() throws Exception { logger.info("Checking for FA update"); updateTitle(i18n.get("updateCheckTask.title")); return (Files.notExists(binaryPatchRepoDirectory) || areNewPatchFilesAvailable() || !areLocalFilesPatched()); } /** * Checks whether the remote GIT repository has newer patch files available then the local repsitory. */ private boolean areNewPatchFilesAvailable() throws IOException { String remoteHead = gitWrapper.getRemoteHead(binaryPatchRepoDirectory); String localHead = gitWrapper.getLocalHead(binaryPatchRepoDirectory); boolean needsPatching = !localHead.equals(remoteHead); if (needsPatching) { logger.info("New patch files are available ({})", remoteHead); } else { logger.info("Local patch repository is up to date ({})", remoteHead); } return needsPatching; } private boolean areLocalFilesPatched() throws IOException { try (BufferedReader reader = Files.newBufferedReader(migrationDataFile, StandardCharsets.UTF_8)) { MigrationData migrationData = gson.fromJson(reader, MigrationData.class); for (Map.Entry<String, String> entry : migrationData.postPatchVerify.entrySet()) { String fileName = entry.getKey(); String expectedMd5 = entry.getValue(); Path fileToCheck = preferencesService.getFafBinDirectory().resolve(fileName); if (Files.notExists(fileToCheck)) { logger.info("Missing file: {}", fileToCheck); return false; } byte[] bytesOfFileToCheck = Files.readAllBytes(fileToCheck); String actualMd5 = Hashing.md5().hashBytes(bytesOfFileToCheck).toString(); if (!actualMd5.equals(expectedMd5)) { logger.info("At least one binary file is out of date: {}. Expected checksum: {} but got: {}", fileName, expectedMd5, actualMd5); return false; } } } logger.info("All binary files are up to date"); return true; } public void setBinaryPatchRepoDirectory(Path binaryPatchRepoDirectory) { this.binaryPatchRepoDirectory = binaryPatchRepoDirectory; } public void setMigrationDataFile(Path migrationDataFile) { this.migrationDataFile = migrationDataFile; } }