diff --git a/src/main/java/org/qortal/api/resource/WebsiteResource.java b/src/main/java/org/qortal/api/resource/WebsiteResource.java index 5965c5aa..1030e1e0 100644 --- a/src/main/java/org/qortal/api/resource/WebsiteResource.java +++ b/src/main/java/org/qortal/api/resource/WebsiteResource.java @@ -10,6 +10,7 @@ import java.io.*; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import io.swagger.v3.oas.annotations.Operation; @@ -38,6 +39,7 @@ import org.qortal.repository.Repository; import org.qortal.repository.RepositoryManager; import org.qortal.settings.Settings; import org.qortal.storage.DataFile; +import org.qortal.storage.DataFileChunk; import org.qortal.transaction.ArbitraryTransaction; import org.qortal.transaction.Transaction; import org.qortal.transform.TransformationException; @@ -263,22 +265,48 @@ public class WebsiteResource { if (!Files.exists(Paths.get(unzippedPath))) { - // Load file - DataFile dataFile = DataFile.fromHash58(resourceId); - if (dataFile == null || !dataFile.exists()) { - LOGGER.info("Unable to validate complete file hash"); - return this.get404Response(); - } + // Load the full transaction data so we can access the file hashes + try (final Repository repository = RepositoryManager.getRepository()) { - if (!dataFile.digest58().equals(resourceId)) { - LOGGER.info("Unable to validate complete file hash"); - return this.get404Response(); - } + ArbitraryTransactionData transactionData = (ArbitraryTransactionData) repository.getTransactionRepository().fromSignature(Base58.decode(resourceId)); + if (!(transactionData instanceof ArbitraryTransactionData)) { + return this.get404Response(); + } + + // Load hashes + byte[] digest = transactionData.getData(); + byte[] chunkHashes = transactionData.getChunkHashes(); + + // Load data file(s) + DataFile dataFile = DataFile.fromDigest(digest); + if (!dataFile.exists()) { + if (!dataFile.allChunksExist(chunkHashes)) { + // TODO: fetch them? + return this.get404Response(); + } + // We have all the chunks but not the complete file, so join them + dataFile.addChunkHashes(chunkHashes); + dataFile.join(); + } + + // If the complete file still doesn't exist then something went wrong + if (!dataFile.exists()) { + return this.get404Response(); + } + + if (!Arrays.equals(dataFile.digest(), digest)) { + LOGGER.info("Unable to validate complete file hash"); + return this.get404Response(); + } - try { - ZipUtils.unzip(dataFile.getFilePath(), destPath); - } catch (IOException e) { - LOGGER.info("Unable to unzip file"); + try { + ZipUtils.unzip(dataFile.getFilePath(), destPath); + } catch (IOException e) { + LOGGER.info("Unable to unzip file"); + } + + } catch (DataException e) { + return this.get500Response(); } } @@ -343,6 +371,19 @@ public class WebsiteResource { return response; } + private HttpServletResponse get500Response() { + try { + String responseString = "500: Internal Server Error"; + byte[] responseData = responseString.getBytes(); + response.setStatus(500); + response.setContentLength(responseData.length); + response.getOutputStream().write(responseData); + } catch (IOException e) { + LOGGER.info("Error writing 500 response"); + } + return response; + } + /** * Find relative links and prefix them with the resource ID, using Jsoup * @param path