diff --git a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java index d82fb98d..bf4e1ea6 100644 --- a/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java +++ b/src/main/java/org/qortal/api/gateway/resource/GatewayResource.java @@ -40,8 +40,8 @@ public class GatewayResource { // If "build=true" has been specified in the query string, build the resource before returning its status if (build != null && build == true) { - ArbitraryDataReader reader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, null); try { + ArbitraryDataReader reader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, null); if (!reader.isBuilding()) { reader.loadSynchronously(false); } diff --git a/src/main/java/org/qortal/api/resource/ArbitraryResource.java b/src/main/java/org/qortal/api/resource/ArbitraryResource.java index 22a74f42..9be74a2f 100644 --- a/src/main/java/org/qortal/api/resource/ArbitraryResource.java +++ b/src/main/java/org/qortal/api/resource/ArbitraryResource.java @@ -1287,8 +1287,8 @@ public class ArbitraryResource { private HttpServletResponse download(Service service, String name, String identifier, String filepath, String encoding, boolean rebuild, boolean async, Integer maxAttempts) { - ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); try { + ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); int attempts = 0; if (maxAttempts == null) { @@ -1394,8 +1394,8 @@ public class ArbitraryResource { } private FileProperties getFileProperties(Service service, String name, String identifier) { - ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); try { + ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); arbitraryDataReader.loadSynchronously(false); java.nio.file.Path outputPath = arbitraryDataReader.getFilePath(); if (outputPath == null) { diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java index f281cec5..8896f44f 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataReader.java @@ -66,7 +66,7 @@ public class ArbitraryDataReader { // TODO: all builds could be handled by the build queue (even synchronous ones), to avoid the need for this private static Map inProgress = Collections.synchronizedMap(new HashMap<>()); - public ArbitraryDataReader(String resourceId, ResourceIdType resourceIdType, Service service, String identifier) { + public ArbitraryDataReader(String resourceId, ResourceIdType resourceIdType, Service service, String identifier) throws DataException { // Ensure names are always lowercase if (resourceIdType == ResourceIdType.NAME) { resourceId = resourceId.toLowerCase(); @@ -90,11 +90,16 @@ public class ArbitraryDataReader { this.canRequestMissingFiles = true; } - private Path buildWorkingPath() { + private Path buildWorkingPath() throws DataException { // Use the user-specified temp dir, as it is deterministic, and is more likely to be located on reusable storage hardware String baseDir = Settings.getInstance().getTempDataPath(); String identifier = this.identifier != null ? this.identifier : "default"; - return Paths.get(baseDir, "reader", this.resourceIdType.toString(), this.resourceId, this.service.toString(), identifier); + + try { + return Paths.get(baseDir, "reader", this.resourceIdType.toString(), StringUtils.sanitizeString(this.resourceId), this.service.toString(), StringUtils.sanitizeString(identifier)); + } catch (InvalidPathException e) { + throw new DataException(String.format("Invalid path: %s", e.getMessage())); + } } public boolean isCachedDataAvailable() { diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java index 704533c8..0936b3ec 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataRenderer.java @@ -76,9 +76,11 @@ public class ArbitraryDataRenderer { return ArbitraryDataRenderer.getResponse(response, 500, "QDN is disabled in settings"); } - ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, identifier); - arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only + ArbitraryDataReader arbitraryDataReader; try { + arbitraryDataReader = new ArbitraryDataReader(resourceId, resourceIdType, service, identifier); + arbitraryDataReader.setSecret58(secret58); // Optional, used for loading encrypted file hashes only + if (!arbitraryDataReader.isCachedDataAvailable()) { // If async is requested, show a loading screen whilst build is in progress if (async) { diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java index aa311786..16faf838 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataResource.java @@ -111,10 +111,15 @@ public class ArbitraryDataResource { } // Firstly check the cache to see if it's already built - ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader( - resourceId, resourceIdType, service, identifier); - if (arbitraryDataReader.isCachedDataAvailable()) { - return new ArbitraryResourceStatus(Status.READY, this.localChunkCount, this.totalChunkCount); + try { + ArbitraryDataReader arbitraryDataReader = new ArbitraryDataReader( + resourceId, resourceIdType, service, identifier); + if (arbitraryDataReader.isCachedDataAvailable()) { + return new ArbitraryResourceStatus(Status.READY, this.localChunkCount, this.totalChunkCount); + } + } catch (DataException e) { + // Assume no usable data + return new ArbitraryResourceStatus(Status.PUBLISHED, this.localChunkCount, this.totalChunkCount); } // Check if we have all data locally for this resource diff --git a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java index a9dd4fcf..c32734dc 100644 --- a/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java +++ b/src/main/java/org/qortal/arbitrary/ArbitraryDataTransactionBuilder.java @@ -117,8 +117,9 @@ public class ArbitraryDataTransactionBuilder { } private Method determineMethodAutomatically() throws DataException { - ArbitraryDataReader reader = new ArbitraryDataReader(this.name, ResourceIdType.NAME, this.service, this.identifier); + ArbitraryDataReader reader; try { + reader = new ArbitraryDataReader(this.name, ResourceIdType.NAME, this.service, this.identifier); reader.loadSynchronously(true); } catch (Exception e) { // Catch all exceptions if the existing resource cannot be loaded first time diff --git a/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java b/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java index bf7e2aa6..65f3436c 100644 --- a/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java +++ b/src/main/java/org/qortal/utils/ArbitraryTransactionUtils.java @@ -397,8 +397,8 @@ public class ArbitraryTransactionUtils { // If "build" has been specified, build the resource before returning its status if (build != null && build == true) { - ArbitraryDataReader reader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); try { + ArbitraryDataReader reader = new ArbitraryDataReader(name, ArbitraryDataFile.ResourceIdType.NAME, service, identifier); if (!reader.isBuilding()) { reader.loadSynchronously(false); } diff --git a/src/main/java/org/qortal/utils/StringUtils.java b/src/main/java/org/qortal/utils/StringUtils.java new file mode 100644 index 00000000..b3ffcfa6 --- /dev/null +++ b/src/main/java/org/qortal/utils/StringUtils.java @@ -0,0 +1,13 @@ +package org.qortal.utils; + +public class StringUtils { + + public static String sanitizeString(String input) { + String sanitized = input + .replaceAll("[<>:\"/\\\\|?*]", "") // Remove invalid characters + .replaceAll("^\\s+|\\s+$", "") // Trim leading and trailing whitespace + .replaceAll("\\s+", "_"); // Replace consecutive whitespace with underscores + + return sanitized; + } +}